Merge develop into artifact pipeline branch

This commit is contained in:
Richard Cordovano 2021-05-14 15:39:32 -04:00
commit 15af4bce1a
482 changed files with 16356 additions and 6278 deletions

1
.gitignore vendored
View File

@ -3,6 +3,7 @@
/*/build/
*/nbproject/private/*
/nbproject/private/*
/apidiff_output/
/Core/release/
/Core/src/org/sleuthkit/autopsy/coreutils/Version.properties

View File

@ -31,6 +31,17 @@
</target>
<target name="get-thirdparty-dependencies" description="get third-party dependencies">
<!--
Copy netbeans localization jars:
This contains jars provided in Netbeans 8 RCP that provide localization bundles.
They do not appear to be included in Netbeans >= 9.
See VIK-7434 for more information.
-->
<mkdir dir="${modules.dir}/locale"/>
<copy todir="${modules.dir}/locale" >
<fileset dir="${thirdparty.dir}/NetbeansLocalization"/>
</copy>
<!--Copy photorec to release-->
<copy todir="${basedir}/release/photorec_exec" >
<fileset dir="${thirdparty.dir}/photorec_exec"/>
@ -89,6 +100,11 @@
<fileset dir="${thirdparty.dir}/ImageMagick-7.0.10-27-portable-Q16-x64"/>
</copy>
<!--Copy DomainCategorization to release-->
<copy todir="${basedir}/release/DomainCategorization" >
<fileset dir="${thirdparty.dir}/DomainCategorization"/>
</copy>
<!-- The 'libgstlibav.dll' file is too big to store on GitHub, so we
have it stored in a ZIP file. We'll extract it in place and remove
the ZIP file afterward. -->

View File

@ -83,7 +83,6 @@ file.reference.jgraphx-4.1.0.jar=release\\modules\\ext\\jgraphx-4.1.0.jar
file.reference.jline-0.9.94.jar=release\\modules\\ext\\jline-0.9.94.jar
file.reference.jsoup-1.10.3.jar=release\\modules\\ext\\jsoup-1.10.3.jar
file.reference.jsr305-3.0.2.jar=release\\modules\\ext\\jsr305-3.0.2.jar
file.reference.junit-3.8.1.jar=release\\modules\\ext\\junit-3.8.1.jar
file.reference.jutf7-1.0.0.jar=release\\modules\\ext\\jutf7-1.0.0.jar
file.reference.jxmapviewer2-2.4.jar=release\\modules\\ext\\jxmapviewer2-2.4.jar
file.reference.jython-standalone-2.7.0.jar=release\\modules\\ext\\jython-standalone-2.7.0.jar

View File

@ -737,10 +737,6 @@
<runtime-relative-path>ext/jai_imageio-1.1.jar</runtime-relative-path>
<binary-origin>release\modules\ext\jai_imageio-1.1.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/junit-3.8.1.jar</runtime-relative-path>
<binary-origin>release\modules\ext\junit-3.8.1.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/curator-client-2.8.0.jar</runtime-relative-path>
<binary-origin>release\modules\ext\curator-client-2.8.0.jar</binary-origin>

View File

@ -45,3 +45,4 @@ OpenPythonModulesFolderAction.actionName.text=Python Plugins
OpenPythonModulesFolderAction.errorMsg.folderNotFound=Python plugins folder not found: {0}
CTL_OpenPythonModulesFolderAction=Python Plugins
GetTagNameAndCommentDialog.tagCombo.toolTipText=Select tag to use
CTL_ExitAction=Exit

View File

@ -96,3 +96,4 @@ OpenPythonModulesFolderAction.actionName.text=Python Plugins
OpenPythonModulesFolderAction.errorMsg.folderNotFound=Python plugins folder not found: {0}
CTL_OpenPythonModulesFolderAction=Python Plugins
GetTagNameAndCommentDialog.tagCombo.toolTipText=Select tag to use
CTL_ExitAction=Exit

View File

@ -40,7 +40,7 @@ import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
* The action associated with the Case/Exit menu item. It closes the current
* case, if any, and shuts down the application.
*/
@ActionRegistration(displayName = "Exit", iconInMenu = true)
@ActionRegistration(displayName = "#CTL_ExitAction", iconInMenu = true)
@ActionReference(path = "Menu/Case", position = 1000, separatorBefore = 999)
@ActionID(id = "org.sleuthkit.autopsy.casemodule.ExitAction", category = "Case")
final public class ExitAction implements ActionListener {

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -24,18 +24,10 @@ import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.stream.Collectors;
import javax.swing.SwingWorker;
import org.openide.awt.ActionID;
import org.openide.awt.ActionReference;
@ -46,6 +38,8 @@ import org.openide.util.actions.CallableSystemAction;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
import org.sleuthkit.autopsy.coreutils.ThreadUtils;
import org.sleuthkit.autopsy.coreutils.TimeStampUtils;
/**
* Action class for the Thread Dump help menu item. If there is no case open the
@ -63,8 +57,6 @@ public final class ThreadDumpAction extends CallableSystemAction implements Acti
private static final long serialVersionUID = 1L;
private static final Logger logger = Logger.getLogger(ThreadDumpAction.class.getName());
private static final DateFormat DATE_FORMAT = new SimpleDateFormat("MM-dd-yyyy-HH-mm-ss-SSSS");
@Override
public void performAction() {
(new ThreadDumper()).run();
@ -114,26 +106,13 @@ public final class ThreadDumpAction extends CallableSystemAction implements Acti
* @throws IOException
*/
private File createThreadDump() throws IOException {
// generate thread dump
String threadDump = ThreadUtils.generateThreadDump();
File dumpFile = createFilePath().toFile();
try (BufferedWriter writer = new BufferedWriter(new FileWriter(dumpFile, true))) {
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
ThreadInfo[] threadInfos = threadMXBean.getThreadInfo(threadMXBean.getAllThreadIds(), 100);
for (ThreadInfo threadInfo : threadInfos) {
writer.write(threadInfo.toString());
writer.write("\n");
}
long[] deadlockThreadIds = threadMXBean.findDeadlockedThreads();
if (deadlockThreadIds != null) {
writer.write("-------------------List of Deadlocked Thread IDs ---------------------");
String idsList = (Arrays
.stream(deadlockThreadIds)
.boxed()
.collect(Collectors.toList()))
.stream().map(n -> String.valueOf(n))
.collect(Collectors.joining("-", "{", "}"));
writer.write(idsList);
}
writer.write(threadDump);
}
return dumpFile;
@ -145,7 +124,7 @@ public final class ThreadDumpAction extends CallableSystemAction implements Acti
* @return Path for dump file.
*/
private Path createFilePath() {
String fileName = "ThreadDump_" + DATE_FORMAT.format(new Date()) + ".txt";
String fileName = "ThreadDump_" + TimeStampUtils.createTimeStamp() + ".txt";
if (Case.isCaseOpen()) {
return Paths.get(Case.getCurrentCase().getLogDirectoryPath(), fileName);
}

View File

@ -0,0 +1,4 @@
CTL_ResetWindowsAction=Reset Windows
ResetWindowAction.caseCloseFailure.text=Unable to close the current case, the software will restart and the windows locations will reset the next time the software is closed.
ResetWindowAction.caseSaveMetadata.text=Unable to save current case path, the software will restart and the windows locations will reset but the current case will not be opened upon restart.
ResetWindowAction.confirm.text=In order to perform the resetting of window locations the software will close and restart. If a case is currently open, it will be closed. If ingest or a search is currently running, it will be terminated. Are you sure you want to restart the software to reset all window locations?

View File

@ -0,0 +1,141 @@
/*
* Autopsy
*
* Copyright 2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.apputils;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.logging.Level;
import javax.swing.SwingUtilities;
import org.apache.commons.io.FileUtils;
import org.openide.LifecycleManager;
import org.openide.awt.ActionID;
import org.openide.awt.ActionReference;
import org.openide.awt.ActionReferences;
import org.openide.awt.ActionRegistration;
import org.openide.util.HelpCtx;
import org.openide.util.NbBundle;
import org.openide.util.actions.CallableSystemAction;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.CaseActionException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
/**
* Class to open the Discovery dialog. Allows the user to run searches and see
* results in the DiscoveryTopComponent.
*/
@ActionID(category = "Tools", id = "org.sleuthkit.autopsy.apputils.ResetWindowsAction")
@ActionReferences(value = {
@ActionReference(path = "Menu/Window", position = 205)})
@ActionRegistration(displayName = "#CTL_ResetWindowsAction", lazy = false)
@NbBundle.Messages({"CTL_ResetWindowsAction=Reset Windows"})
public final class ResetWindowsAction extends CallableSystemAction {
private static final String DISPLAY_NAME = Bundle.CTL_ResetWindowsAction();
private static final long serialVersionUID = 1L;
private final static Logger logger = Logger.getLogger(ResetWindowsAction.class.getName());
private final static String WINDOWS2LOCAL = "Windows2Local";
private final static String CASE_TO_REOPEN_FILE = "caseToOpen.txt";
@Override
public boolean isEnabled() {
return true;
}
@NbBundle.Messages({"ResetWindowAction.confirm.text=In order to perform the resetting of window locations the software will close and restart. "
+ "If a case is currently open, it will be closed. If ingest or a search is currently running, it will be terminated. "
+ "Are you sure you want to restart the software to reset all window locations?",
"ResetWindowAction.caseCloseFailure.text=Unable to close the current case, "
+ "the software will restart and the windows locations will reset the next time the software is closed.",
"ResetWindowAction.caseSaveMetadata.text=Unable to save current case path, "
+ "the software will restart and the windows locations will reset but the current case will not be opened upon restart."})
@Override
public void performAction() {
SwingUtilities.invokeLater(() -> {
boolean response = MessageNotifyUtil.Message.confirm(Bundle.ResetWindowAction_confirm_text());
if (response) {
//adding the shutdown hook, closing the current case, and marking for restart can be re-ordered if slightly different behavior is desired
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
try {
FileUtils.deleteDirectory(new File(PlatformUtil.getUserConfigDirectory() + File.separator + WINDOWS2LOCAL));
} catch (IOException ex) {
//While we would like the user to be aware of this in the unlikely event that the directory can not be deleted
//Because our deletion is being attempted in a shutdown hook I don't know that we can pop up UI elements during the shutdown proces
logger.log(Level.SEVERE, "Unable to delete config directory, window locations will not be reset. To manually reset the windows please delete the following directory while the software is closed. " + PlatformUtil.getUserConfigDirectory() + File.separator + "Windows2Local", ex);
}
}
});
try {
if (Case.isCaseOpen()) {
String caseMetadataFilePath = Case.getCurrentCase().getMetadata().getFilePath().toString();
File caseToOpenFile = new File(ResetWindowsAction.getCaseToReopenFilePath());
Charset encoding = null; //prevents writeStringToFile from having ambiguous arguments
FileUtils.writeStringToFile(caseToOpenFile, caseMetadataFilePath, encoding);
Case.closeCurrentCase();
}
// The method markForRestart can not be undone once it is called.
LifecycleManager.getDefault().markForRestart();
//we need to call exit last
LifecycleManager.getDefault().exit();
} catch (CaseActionException ex) {
logger.log(Level.WARNING, Bundle.ResetWindowAction_caseCloseFailure_text(), ex);
MessageNotifyUtil.Message.show(Bundle.ResetWindowAction_caseCloseFailure_text(), MessageNotifyUtil.MessageType.ERROR);
} catch (IOException ex) {
logger.log(Level.WARNING, Bundle.ResetWindowAction_caseSaveMetadata_text(), ex);
MessageNotifyUtil.Message.show(Bundle.ResetWindowAction_caseSaveMetadata_text(), MessageNotifyUtil.MessageType.ERROR);
}
}
});
}
public static String getCaseToReopenFilePath(){
return PlatformUtil.getUserConfigDirectory() + File.separator + CASE_TO_REOPEN_FILE;
}
/**
* Set this action to be enabled/disabled
*
* @param value whether to enable this action or not
*/
@Override
public void setEnabled(boolean value) {
super.setEnabled(value);
}
@Override
public String getName() {
return DISPLAY_NAME;
}
@Override
public HelpCtx getHelpCtx() {
return HelpCtx.DEFAULT_HELP;
}
@Override
public boolean asynchronous() {
return false; // run on edt
}
}

View File

@ -290,7 +290,7 @@ class AddImageWizardAddingProgressPanel extends ShortcutWizardDescriptorPanel {
*
*
* @param errorString the error string to be displayed
* @param critical true if this is a critical error
* @param critical true if this is a critical error
*/
void addErrors(String errorString, boolean critical) {
getComponent().showErrors(errorString, critical);
@ -325,10 +325,10 @@ class AddImageWizardAddingProgressPanel extends ShortcutWizardDescriptorPanel {
* Starts the Data source processing by kicking off the selected
* DataSourceProcessor
*
* @param dsp The data source processor providing configuration for how to
* process the specific data source type.
* @param dsp The data source processor providing configuration for
* how to process the specific data source type.
* @param selectedHost The host to which this data source belongs or null
* for a default host.
* for a default host.
*/
void startDataSourceProcessing(DataSourceProcessor dsp, Host selectedHost) {
if (dsProcessor == null) { //this can only be run once
@ -336,45 +336,44 @@ class AddImageWizardAddingProgressPanel extends ShortcutWizardDescriptorPanel {
newContents.clear();
cleanupTask = null;
dsProcessor = dsp;
// Add a cleanup task to interrupt the background process if the
// wizard exits while the background process is running.
cleanupTask = addImageAction.new CleanupTask() {
@Override
void cleanup() throws Exception {
WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
cancelDataSourceProcessing(dataSourceId);
cancelled = true;
WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
}
};
cleanupTask.enable();
new Thread(() -> {
// Add a cleanup task to interrupt the background process if the
// wizard exits while the background process is running.
cleanupTask = addImageAction.new CleanupTask() {
@Override
void cleanup() throws Exception {
WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
cancelDataSourceProcessing(dataSourceId);
cancelled = true;
WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
}
};
cleanupTask.enable();
try {
Case.getCurrentCaseThrows().notifyAddingDataSource(dataSourceId);
} catch (NoCurrentCaseException ex) {
Logger.getLogger(AddImageWizardAddingProgressVisual.class.getName()).log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS
}
}).start();
DataSourceProcessorCallback cbObj = new DataSourceProcessorCallback() {
@Override
public void doneEDT(DataSourceProcessorCallback.DataSourceProcessorResult result, List<String> errList, List<Content> contents) {
dataSourceProcessorDone(dataSourceId, result, errList, contents);
DataSourceProcessorCallback cbObj = new DataSourceProcessorCallback() {
@Override
public void doneEDT(DataSourceProcessorCallback.DataSourceProcessorResult result, List<String> errList, List<Content> contents) {
dataSourceProcessorDone(dataSourceId, result, errList, contents);
}
};
// Kick off the DSProcessor
if (dsProcessor.supportsIngestStream()) {
// Set readyToIngest to false to prevent the wizard from starting ingest a second time.
readyToIngest = false;
dsProcessor.runWithIngestStream(selectedHost, ingestJobSettings, getDSPProgressMonitorImpl(), cbObj);
} else {
dsProcessor.run(selectedHost, getDSPProgressMonitorImpl(), cbObj);
}
};
}).start();
setStateStarted();
// Kick off the DSProcessor
if (dsProcessor.supportsIngestStream()) {
// Set readyToIngest to false to prevent the wizard from starting ingest a second time.
readyToIngest = false;
dsProcessor.runWithIngestStream(selectedHost, ingestJobSettings, getDSPProgressMonitorImpl(), cbObj);
} else {
dsProcessor.run(selectedHost, getDSPProgressMonitorImpl(), cbObj);
}
}
}

View File

@ -79,7 +79,7 @@ class AddImageWizardSelectHostVisual extends javax.swing.JPanel {
@Override
public int hashCode() {
int hash = 7;
hash = 41 * hash + Objects.hashCode(this.host == null ? 0 : this.host.getId());
hash = 41 * hash + Objects.hashCode(this.host == null ? 0 : this.host.getHostId());
return hash;
}
@ -96,8 +96,8 @@ class AddImageWizardSelectHostVisual extends javax.swing.JPanel {
}
final HostListItem other = (HostListItem) obj;
if (!Objects.equals(
this.host == null ? 0 : this.host.getId(),
other.host == null ? 0 : other.host.getId())) {
this.host == null ? 0 : this.host.getHostId(),
other.host == null ? 0 : other.host.getHostId())) {
return false;
}
@ -166,7 +166,7 @@ class AddImageWizardSelectHostVisual extends javax.swing.JPanel {
if (specifyNewHostRadio.isSelected() && StringUtils.isNotEmpty(specifyNewHostTextField.getText())) {
String newHostName = specifyNewHostTextField.getText();
try {
return Case.getCurrentCaseThrows().getSleuthkitCase().getHostManager().createHost(newHostName);
return Case.getCurrentCaseThrows().getSleuthkitCase().getHostManager().newHost(newHostName);
} catch (NoCurrentCaseException | TskCoreException ex) {
logger.log(Level.WARNING, String.format("Unable to create host '%s'.", newHostName), ex);
return null;
@ -186,7 +186,7 @@ class AddImageWizardSelectHostVisual extends javax.swing.JPanel {
*/
private void loadHostData() {
try {
Collection<Host> hosts = Case.getCurrentCaseThrows().getSleuthkitCase().getHostManager().getHosts();
Collection<Host> hosts = Case.getCurrentCaseThrows().getSleuthkitCase().getHostManager().getAllHosts();
sanitizedHostSet = HostNameValidator.getSanitizedHostNames(hosts);
Vector<HostListItem> hostListItems = hosts.stream()

View File

@ -4,7 +4,6 @@ CTL_CaseCloseAct=Close Case
CTL_CaseNewAction=New Case
CTL_CaseDetailsAction=Case Details
CTL_CaseDeleteAction=Delete Case
Menu/Case/OpenRecentCase=Open Recent Case
CTL_CaseDeleteAction=Delete Case
OpenIDE-Module-Name=Case
NewCaseVisualPanel1.caseNameLabel.text_1=Case Name:

View File

@ -128,6 +128,7 @@ CTL_CaseCloseAct=Close Case
CTL_CaseNewAction=New Case
CTL_CaseDetailsAction=Case Details
CTL_CaseDeleteAction=Delete Case
CTL_CaseDeleteAction=Delete Case
CTL_CaseOpenAction=Open Case
CTL_UnpackagePortableCaseAction=Unpack and Open Portable Case
DeleteDataSourceAction.confirmationDialog.message=Are you sure you want to remove the selected data source from the case?\nNote that the case will be closed and re-opened during the removal.
@ -186,8 +187,6 @@ LogicalEvidenceFilePanel.pathValidation.getOpenCase.Error=Warning: Exception whi
LogicalEvidenceFilePanel.validatePanel.nonL01Error.text=Only files with the .l01 file extension are supported here.
LogicalFilesDspPanel.subTypeComboBox.l01FileOption.text=Logical evidence file (L01)
LogicalFilesDspPanel.subTypeComboBox.localFilesOption.text=Local files and folders
Menu/Case/OpenRecentCase=Open Recent Case
CTL_CaseDeleteAction=Delete Case
OpenIDE-Module-Name=Case
NewCaseVisualPanel1.caseNameLabel.text_1=Case Name:
NewCaseVisualPanel1.caseDirLabel.text=Base Directory:
@ -346,6 +345,12 @@ RecentCases.getName.text=Clear Recent Cases
RecentItems.openRecentCase.msgDlg.text=Case {0} no longer exists.
SelectDataSourceProcessorPanel.name.text=Select Data Source Type
StartupWindow.title.text=Welcome
# {0} - autFilePath
StartupWindowProvider.openCase.cantOpen=Unable to open previously open case with metadata file: {0}
# {0} - reOpenFilePath
StartupWindowProvider.openCase.deleteOpenFailure=Unable to open or delete file containing path {0} to previously open case. The previous case will not be opened.
# {0} - autFilePath
StartupWindowProvider.openCase.noFile=Unable to open previously open case because metadata file not found at: {0}
UnpackagePortableCaseDialog.title.text=Unpackage Portable Case
UnpackagePortableCaseDialog.UnpackagePortableCaseDialog.extensions=Portable case package (.zip, .zip.001)
UnpackagePortableCaseDialog.validatePaths.badExtension=File extension must be .zip or .zip.001

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2012-2020 Basis Technology Corp.
* Copyright 2012-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -29,6 +29,7 @@ import java.awt.event.ActionListener;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.nio.file.Paths;
@ -62,6 +63,7 @@ import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import org.apache.commons.lang3.StringUtils;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
@ -87,9 +89,10 @@ import org.sleuthkit.autopsy.casemodule.events.HostsChangedEvent;
import org.sleuthkit.autopsy.casemodule.events.HostsRemovedEvent;
import org.sleuthkit.autopsy.casemodule.events.OsAccountAddedEvent;
import org.sleuthkit.autopsy.casemodule.events.OsAccountChangedEvent;
import org.sleuthkit.autopsy.casemodule.events.OsAccountDeletedEvent;
import org.sleuthkit.autopsy.casemodule.events.PersonsAddedEvent;
import org.sleuthkit.autopsy.casemodule.events.PersonsChangedEvent;
import org.sleuthkit.autopsy.casemodule.events.PersonsRemovedEvent;
import org.sleuthkit.autopsy.casemodule.events.PersonsDeletedEvent;
import org.sleuthkit.autopsy.casemodule.events.ReportAddedEvent;
import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData.CaseNodeDataException;
import org.sleuthkit.autopsy.casemodule.multiusercases.CoordinationServiceUtils;
@ -114,6 +117,7 @@ import org.sleuthkit.autopsy.coreutils.ThreadUtils;
import org.sleuthkit.autopsy.coreutils.TimeZoneUtils;
import org.sleuthkit.autopsy.coreutils.Version;
import org.sleuthkit.autopsy.datamodel.hosts.OpenHostsAction;
import org.sleuthkit.autopsy.directorytree.DirectoryTreeTopComponent;
import org.sleuthkit.autopsy.events.AutopsyEvent;
import org.sleuthkit.autopsy.events.AutopsyEventException;
import org.sleuthkit.autopsy.events.AutopsyEventPublisher;
@ -124,6 +128,7 @@ import org.sleuthkit.autopsy.ingest.IngestServices;
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchService;
import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchServiceException;
import org.sleuthkit.autopsy.machinesettings.UserMachinePreferences;
import org.sleuthkit.autopsy.progress.LoggingProgressIndicator;
import org.sleuthkit.autopsy.progress.ModalDialogProgressIndicator;
import org.sleuthkit.autopsy.progress.ProgressIndicator;
@ -138,24 +143,16 @@ import org.sleuthkit.datamodel.ContentTag;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.FileSystem;
import org.sleuthkit.datamodel.Host;
import org.sleuthkit.datamodel.HostManager.HostsCreationEvent;
import org.sleuthkit.datamodel.HostManager.HostsUpdateEvent;
import org.sleuthkit.datamodel.HostManager.HostsDeletionEvent;
import org.sleuthkit.datamodel.Image;
import org.sleuthkit.datamodel.OsAccount;
import org.sleuthkit.datamodel.OsAccountManager;
import org.sleuthkit.datamodel.OsAccountManager.OsAccountsCreationEvent;
import org.sleuthkit.datamodel.OsAccountManager.OsAccountsUpdateEvent;
import org.sleuthkit.datamodel.Person;
import org.sleuthkit.datamodel.PersonManager.PersonsCreationEvent;
import org.sleuthkit.datamodel.PersonManager.PersonsUpdateEvent;
import org.sleuthkit.datamodel.PersonManager.PersonsDeletionEvent;
import org.sleuthkit.datamodel.Report;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TimelineManager;
import org.sleuthkit.datamodel.SleuthkitCaseAdminUtil;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskDataException;
import org.sleuthkit.datamodel.TskEvent;
import org.sleuthkit.datamodel.TskUnsupportedSchemaVersionException;
/**
@ -163,9 +160,10 @@ import org.sleuthkit.datamodel.TskUnsupportedSchemaVersionException;
*/
public class Case {
private static final String CASE_TEMP_DIR = Case.class.getSimpleName();
private static final int CASE_LOCK_TIMEOUT_MINS = 1;
private static final int CASE_RESOURCES_LOCK_TIMEOUT_HOURS = 1;
private static final String APP_NAME = UserPreferences.getAppName();
private static final String TEMP_FOLDER = "Temp";
private static final String SINGLE_USER_CASE_DB_NAME = "autopsy.db";
private static final String EVENT_CHANNEL_NAME = "%s-Case-Events"; //NON-NLS
private static final String CACHE_FOLDER = "Cache"; //NON-NLS
@ -438,41 +436,38 @@ public class Case {
*/
OS_ACCOUNT_ADDED,
/**
* OSAccount associated with the current case has changed.
* Call getOsAccount to get the changed account;
* OSAccount associated with the current case has changed. Call
* getOsAccount to get the changed account;
*/
OS_ACCOUNT_CHANGED,
/**
* OSAccount associated with the current case has been deleted.
*/
OS_ACCOUNT_REMOVED,
/**
* Hosts associated with the current case added.
*/
HOSTS_ADDED,
/**
* Hosts associated with the current case has changed.
*/
HOSTS_CHANGED,
/**
* Hosts associated with the current case has been deleted.
*/
HOSTS_DELETED,
/**
* Persons associated with the current case added.
*/
PERSONS_ADDED,
/**
* Persons associated with the current case has changed.
*/
PERSONS_CHANGED,
/**
* Persons associated with the current case has been deleted.
*/
PERSONS_DELETED
;
PERSONS_DELETED;
};
/**
@ -507,27 +502,34 @@ public class Case {
}
@Subscribe
public void publishOsAccountAddedEvent(OsAccountsCreationEvent event) {
for(OsAccount account: event.getOsAcounts()) {
public void publishOsAccountAddedEvent(TskEvent.OsAccountsAddedTskEvent event) {
for (OsAccount account : event.getOsAcounts()) {
eventPublisher.publish(new OsAccountAddedEvent(account));
}
}
@Subscribe
public void publishOsAccountChangedEvent(OsAccountsUpdateEvent event) {
for(OsAccount account: event.getOsAcounts()) {
public void publishOsAccountChangedEvent(TskEvent.OsAccountsChangedTskEvent event) {
for (OsAccount account : event.getOsAcounts()) {
eventPublisher.publish(new OsAccountChangedEvent(account));
}
}
@Subscribe
public void publishOsAccountDeletedEvent(TskEvent.OsAccountsDeletedTskEvent event) {
for (Long accountId : event.getOsAcountObjectIds()) {
eventPublisher.publish(new OsAccountDeletedEvent(accountId));
}
}
/**
* Publishes an autopsy event from the sleuthkit HostCreationEvent
* Publishes an autopsy event from the sleuthkit HostAddedEvent
* indicating that hosts have been created.
*
* @param event The sleuthkit event for the creation of hosts.
*/
@Subscribe
public void publishHostsAddedEvent(HostsCreationEvent event) {
public void publishHostsAddedEvent(TskEvent.HostsAddedTskEvent event) {
eventPublisher.publish(new HostsAddedEvent(
event == null ? Collections.emptyList() : event.getHosts()));
}
@ -539,7 +541,7 @@ public class Case {
* @param event The sleuthkit event for the updating of hosts.
*/
@Subscribe
public void publishHostsChangedEvent(HostsUpdateEvent event) {
public void publishHostsChangedEvent(TskEvent.HostsChangedTskEvent event) {
eventPublisher.publish(new HostsChangedEvent(
event == null ? Collections.emptyList() : event.getHosts()));
}
@ -551,31 +553,31 @@ public class Case {
* @param event The sleuthkit event for the deleting of hosts.
*/
@Subscribe
public void publishHostsDeletedEvent(HostsDeletionEvent event) {
public void publishHostsDeletedEvent(TskEvent.HostsDeletedTskEvent event) {
eventPublisher.publish(new HostsRemovedEvent(
event == null ? Collections.emptyList() : event.getHosts()));
}
/**
* Publishes an autopsy event from the sleuthkit PersonCreationEvent
* Publishes an autopsy event from the sleuthkit PersonAddedEvent
* indicating that persons have been created.
*
* @param event The sleuthkit event for the creation of persons.
*/
@Subscribe
public void publishPersonsAddedEvent(PersonsCreationEvent event) {
public void publishPersonsAddedEvent(TskEvent.PersonsAddedTskEvent event) {
eventPublisher.publish(new PersonsAddedEvent(
event == null ? Collections.emptyList() : event.getPersons()));
}
/**
* Publishes an autopsy event from the sleuthkit PersonUpdateEvent
* Publishes an autopsy event from the sleuthkit PersonChangedEvent
* indicating that persons have been updated.
*
* @param event The sleuthkit event for the updating of persons.
*/
@Subscribe
public void publishPersonsChangedEvent(PersonsUpdateEvent event) {
public void publishPersonsChangedEvent(TskEvent.PersonsChangedTskEvent event) {
eventPublisher.publish(new PersonsChangedEvent(
event == null ? Collections.emptyList() : event.getPersons()));
}
@ -587,8 +589,8 @@ public class Case {
* @param event The sleuthkit event for the deleting of persons.
*/
@Subscribe
public void publishPersonsDeletedEvent(PersonsDeletionEvent event) {
eventPublisher.publish(new PersonsRemovedEvent(
public void publishPersonsDeletedEvent(TskEvent.PersonsDeletedTskEvent event) {
eventPublisher.publish(new PersonsDeletedEvent(
event == null ? Collections.emptyList() : event.getPersons()));
}
}
@ -869,12 +871,12 @@ public class Case {
eventPublisher.publishLocally(new AutopsyEvent(Events.CURRENT_CASE.toString(), closedCase, null));
logger.log(Level.INFO, "Closing current case {0} ({1}) in {2}", new Object[]{closedCase.getDisplayName(), closedCase.getName(), closedCase.getCaseDirectory()}); //NON-NLS
closedCase.doCloseCaseAction();
currentCase = null;
logger.log(Level.INFO, "Closed current case {0} ({1}) in {2}", new Object[]{closedCase.getDisplayName(), closedCase.getName(), closedCase.getCaseDirectory()}); //NON-NLS
} catch (CaseActionException ex) {
logger.log(Level.SEVERE, String.format("Error closing current case %s (%s) in %s", closedCase.getDisplayName(), closedCase.getName(), closedCase.getCaseDirectory()), ex); //NON-NLS
throw ex;
} finally {
currentCase = null;
if (RuntimeProperties.runningWithGUI()) {
updateGUIForCaseClosed();
}
@ -1214,8 +1216,6 @@ public class Case {
* Update the GUI to to reflect the current case.
*/
private static void updateGUIForCaseOpened(Case newCurrentCase) {
if (RuntimeProperties.runningWithGUI()) {
SwingUtilities.invokeLater(() -> {
/*
* If the case database was upgraded for a new schema and a
* backup database was created, notify the user.
@ -1241,16 +1241,30 @@ public class Case {
String path = entry.getValue();
boolean fileExists = (new File(path).isFile() || DriveUtils.driveExists(path));
if (!fileExists) {
int response = JOptionPane.showConfirmDialog(
mainFrame,
NbBundle.getMessage(Case.class, "Case.checkImgExist.confDlg.doesntExist.msg", path),
NbBundle.getMessage(Case.class, "Case.checkImgExist.confDlg.doesntExist.title"),
JOptionPane.YES_NO_OPTION);
if (response == JOptionPane.YES_OPTION) {
MissingImageDialog.makeDialog(obj_id, caseDb);
} else {
logger.log(Level.SEVERE, "User proceeding with missing image files"); //NON-NLS
try {
// Using invokeAndWait means that the dialog will
// open on the EDT but this thread will wait for an
// answer. Using invokeLater would cause this loop to
// end before all of the dialogs appeared.
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
int response = JOptionPane.showConfirmDialog(
mainFrame,
NbBundle.getMessage(Case.class, "Case.checkImgExist.confDlg.doesntExist.msg", path),
NbBundle.getMessage(Case.class, "Case.checkImgExist.confDlg.doesntExist.title"),
JOptionPane.YES_NO_OPTION);
if (response == JOptionPane.YES_OPTION) {
MissingImageDialog.makeDialog(obj_id, caseDb);
} else {
logger.log(Level.SEVERE, "User proceeding with missing image files"); //NON-NLS
}
}
});
} catch (InterruptedException | InvocationTargetException ex) {
logger.log(Level.SEVERE, "Failed to show missing image confirmation dialog", ex); //NON-NLS
}
}
}
@ -1270,13 +1284,15 @@ public class Case {
CallableSystemAction.get(OpenOutputFolderAction.class).setEnabled(false);
CallableSystemAction.get(OpenDiscoveryAction.class).setEnabled(true);
/*
* Add the case to the recent cases tracker that supplies a list
* of recent cases to the recent cases menu item and the
* open/create case dialog.
*/
RecentCases.getInstance().addRecentCase(newCurrentCase.getDisplayName(), newCurrentCase.getMetadata().getFilePath().toString());
/*
* Add the case to the recent cases tracker that supplies a list
* of recent cases to the recent cases menu item and the
* open/create case dialog.
*/
RecentCases.getInstance().addRecentCase(newCurrentCase.getDisplayName(), newCurrentCase.getMetadata().getFilePath().toString());
final boolean hasData = newCurrentCase.hasData();
SwingUtilities.invokeLater(() -> {
/*
* Open the top components (windows within the main application
* window).
@ -1285,8 +1301,11 @@ public class Case {
* opened via the DirectoryTreeTopComponent 'propertyChange()'
* method on a DATA_SOURCE_ADDED event.
*/
if (newCurrentCase.hasData()) {
if (hasData) {
CoreComponentControl.openCoreWindows();
} else {
//ensure that the DirectoryTreeTopComponent is open so that it's listener can open the core windows including making it visible.
DirectoryTreeTopComponent.findInstance();
}
/*
@ -1296,7 +1315,6 @@ public class Case {
*/
mainFrame.setTitle(newCurrentCase.getDisplayName() + " - " + getNameForTitle());
});
}
}
/*
@ -1471,6 +1489,13 @@ public class Case {
return hostPath.toString();
}
/**
* @return A subdirectory of java.io.tmpdir.
*/
private Path getBaseSystemTempPath() {
return Paths.get(System.getProperty("java.io.tmpdir"), APP_NAME, getName());
}
/**
* Gets the full path to the temp directory for this case, creating it if it
* does not exist.
@ -1478,16 +1503,45 @@ public class Case {
* @return The temp subdirectory path.
*/
public String getTempDirectory() {
// get temp folder scoped to the combination of case name and timestamp
// provided by getName()
Path path = Paths.get(UserPreferences.getAppTempDirectory(), CASE_TEMP_DIR, getName());
File f = path.toFile();
// verify that the folder exists
if (!f.exists()) {
f.mkdirs();
// NOTE: UserPreferences may also be affected by changes in this method.
// See JIRA-7505 for more information.
Path basePath = null;
// get base temp path for the case based on user preference
switch (UserMachinePreferences.getTempDirChoice()) {
case CUSTOM:
String customDirectory = UserMachinePreferences.getCustomTempDirectory();
basePath = (StringUtils.isBlank(customDirectory))
? null
: Paths.get(customDirectory, APP_NAME, getName());
break;
case CASE:
basePath = Paths.get(getCaseDirectory());
break;
case SYSTEM:
default:
// at this level, if the case directory is specified for a temp
// directory, return the system temp directory instead.
basePath = getBaseSystemTempPath();
break;
}
return path.toAbsolutePath().toString();
basePath = basePath == null ? getBaseSystemTempPath() : basePath;
// get sub directories based on multi user vs. single user
Path caseRelPath = (CaseType.MULTI_USER_CASE.equals(getCaseType()))
? Paths.get(NetworkUtils.getLocalHostName(), TEMP_FOLDER)
: Paths.get(TEMP_FOLDER);
File caseTempDir = basePath
.resolve(caseRelPath)
.toFile();
// ensure directory exists
if (!caseTempDir.exists()) {
caseTempDir.mkdirs();
}
return caseTempDir.getAbsolutePath();
}
/**
@ -1798,8 +1852,13 @@ public class Case {
eventPublisher.publish(new OsAccountChangedEvent(account));
}
public void notifyOsAccountRemoved(Long osAccountObjectId) {
eventPublisher.publish(new OsAccountDeletedEvent(osAccountObjectId));
}
/**
* Notify via an autopsy event that a host has been added.
*
* @param host The host that has been added.
*/
public void notifyHostAdded(Host host) {
@ -1808,6 +1867,7 @@ public class Case {
/**
* Notify via an autopsy event that a host has been changed.
*
* @param newValue The host that has been updated.
*/
public void notifyHostChanged(Host newValue) {
@ -1816,6 +1876,7 @@ public class Case {
/**
* Notify via an autopsy event that a host has been deleted.
*
* @param host The host that has been deleted.
*/
public void notifyHostDeleted(Host host) {
@ -1824,6 +1885,7 @@ public class Case {
/**
* Notify via an autopsy event that a person has been added.
*
* @param person The person that has been added.
*/
public void notifyPersonAdded(Person person) {
@ -1832,6 +1894,7 @@ public class Case {
/**
* Notify via an autopsy event that a person has been changed.
*
* @param newValue The person that has been updated.
*/
public void notifyPersonChanged(Person newValue) {
@ -1840,10 +1903,11 @@ public class Case {
/**
* Notify via an autopsy event that a person has been deleted.
*
* @param person The person that has been deleted.
*/
public void notifyPersonDeleted(Person person) {
eventPublisher.publish(new PersonsRemovedEvent(Collections.singletonList(person)));
eventPublisher.publish(new PersonsDeletedEvent(Collections.singletonList(person)));
}
/**
@ -1924,7 +1988,7 @@ public class Case {
*
* @return A CaseMetaData object.
*/
CaseMetadata getMetadata() {
public CaseMetadata getMetadata() {
return metadata;
}

View File

@ -218,7 +218,7 @@ public final class CaseMetadata {
*
* @return The path to the metadata file
*/
Path getFilePath() {
public Path getFilePath() {
return metadataFilePath;
}

View File

@ -84,7 +84,9 @@ public final class CasePreferences {
*/
public static void setGroupItemsInTreeByDataSource(boolean value) {
groupItemsInTreeByDataSource = value;
DirectoryTreeTopComponent.getDefault().refreshContentTreeSafe();
if (Case.isCaseOpen()) {
DirectoryTreeTopComponent.getDefault().refreshContentTreeSafe();
}
}
/**

View File

@ -284,10 +284,10 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
ingestStream = IngestManager.getInstance().openIngestStream(image, settings);
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error starting ingest modules", ex);
final List<String> errors = new ArrayList<>();
errors.add(ex.getMessage());
callBack.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errors, new ArrayList<>());
return;
// There was an error with ingest, but the data source has already been added
// so proceed with the defaultIngestStream. Code in openIngestStream
// should have caused a dialog to popup with the errors.
ingestStream = new DefaultIngestStream();
}
doAddImageProcess(deviceId, imagePath, sectorSize, timeZone, ignoreFatOrphanFiles, md5, sha1, sha256, progress, callBack);

View File

@ -55,8 +55,6 @@ class NewCaseWizardPanel2 implements WizardDescriptor.ValidatingPanel<WizardDesc
public NewCaseVisualPanel2 getComponent() {
if (component == null) {
component = new NewCaseVisualPanel2();
} else {
component.refreshCaseDetailsFields();
}
return component;
@ -137,6 +135,7 @@ class NewCaseWizardPanel2 implements WizardDescriptor.ValidatingPanel<WizardDesc
@Override
public void readSettings(WizardDescriptor settings) {
NewCaseVisualPanel2 panel = getComponent();
panel.refreshCaseDetailsFields();
try {
String lastExaminerName = ModuleSettings.getConfigSetting(ModuleSettings.MAIN_SETTINGS, PROP_EXAMINER_NAME);
String lastExaminerPhone = ModuleSettings.getConfigSetting(ModuleSettings.MAIN_SETTINGS, PROP_EXAMINER_PHONE);

View File

@ -58,7 +58,6 @@ final class OptionalCasePropertiesPanel extends javax.swing.JPanel {
lbPointOfContactPhoneText.setVisible(false);
lbPointOfContactEmailLabel.setVisible(false);
lbPointOfContactEmailText.setVisible(false);
setUpCaseDetailsFields();
}
OptionalCasePropertiesPanel(boolean editCurrentCase) {

View File

@ -37,11 +37,8 @@ class SolrNotConfiguredDialog extends javax.swing.JDialog {
SolrNotConfiguredDialog() {
super((JFrame) WindowManager.getDefault().getMainWindow(), true);
// Center the startup window.
Dimension screenDimension = Toolkit.getDefaultToolkit().getScreenSize();
int width = getSize().width;
int height = getSize().height;
setLocation((screenDimension.width - width) / 2, (screenDimension.height - height) / 2);
initComponents();
setLocationRelativeTo(WindowManager.getDefault().getMainWindow());
setIconImage(ImageUtilities.loadImage("org/sleuthkit/autopsy/images/warning16.png", false));
}

View File

@ -18,12 +18,18 @@
*/
package org.sleuthkit.autopsy.casemodule;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Collection;
import java.util.Iterator;
import java.util.logging.Level;
import org.netbeans.spi.sendopts.OptionProcessor;
import javax.swing.SwingUtilities;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.apputils.ResetWindowsAction;
import org.sleuthkit.autopsy.commandlineingest.CommandLineIngestManager;
import org.sleuthkit.autopsy.commandlineingest.CommandLineOpenCaseManager;
import org.sleuthkit.autopsy.commandlineingest.CommandLineOptionProcessor;
@ -61,15 +67,22 @@ public class StartupWindowProvider implements StartupWindowInterface {
return instance;
}
@NbBundle.Messages({
"# {0} - autFilePath",
"StartupWindowProvider.openCase.noFile=Unable to open previously open case because metadata file not found at: {0}",
"# {0} - reOpenFilePath",
"StartupWindowProvider.openCase.deleteOpenFailure=Unable to open or delete file containing path {0} to previously open case. The previous case will not be opened.",
"# {0} - autFilePath",
"StartupWindowProvider.openCase.cantOpen=Unable to open previously open case with metadata file: {0}"})
private void init() {
if (startupWindowToUse == null) {
// first check whether we are running from command line
if (isRunningFromCommandLine()) {
String defaultArg = getDefaultArgument();
if(defaultArg != null) {
new CommandLineOpenCaseManager(defaultArg).start();
return;
if (defaultArg != null) {
new CommandLineOpenCaseManager(defaultArg).start();
return;
} else {
// Autopsy is running from command line
logger.log(Level.INFO, "Running from command line"); //NON-NLS
@ -83,36 +96,41 @@ public class StartupWindowProvider implements StartupWindowInterface {
if (RuntimeProperties.runningWithGUI()) {
checkSolr();
}
//discover the registered windows
Collection<? extends StartupWindowInterface> startupWindows
= Lookup.getDefault().lookupAll(StartupWindowInterface.class);
int windowsCount = startupWindows.size();
if (windowsCount == 1) {
startupWindowToUse = startupWindows.iterator().next();
logger.log(Level.INFO, "Will use the default startup window: " + startupWindowToUse.toString()); //NON-NLS
} else if (windowsCount == 2) {
//pick the non default one
Iterator<? extends StartupWindowInterface> it = startupWindows.iterator();
while (it.hasNext()) {
StartupWindowInterface window = it.next();
if (!org.sleuthkit.autopsy.casemodule.StartupWindow.class.isInstance(window)) {
startupWindowToUse = window;
logger.log(Level.INFO, "Will use the custom startup window: " + startupWindowToUse.toString()); //NON-NLS
break;
switch (windowsCount) {
case 1:
startupWindowToUse = startupWindows.iterator().next();
logger.log(Level.INFO, "Will use the default startup window: {0}", startupWindowToUse.toString()); //NON-NLS
break;
case 2: {
//pick the non default one
Iterator<? extends StartupWindowInterface> it = startupWindows.iterator();
while (it.hasNext()) {
StartupWindowInterface window = it.next();
if (!org.sleuthkit.autopsy.casemodule.StartupWindow.class.isInstance(window)) {
startupWindowToUse = window;
logger.log(Level.INFO, "Will use the custom startup window: {0}", startupWindowToUse.toString()); //NON-NLS
break;
}
}
break;
}
} else {
// select first non-Autopsy start up window
Iterator<? extends StartupWindowInterface> it = startupWindows.iterator();
while (it.hasNext()) {
StartupWindowInterface window = it.next();
if (!window.getClass().getCanonicalName().startsWith("org.sleuthkit.autopsy")) {
startupWindowToUse = window;
logger.log(Level.INFO, "Will use the custom startup window: " + startupWindowToUse.toString()); //NON-NLS
break;
default: {
// select first non-Autopsy start up window
Iterator<? extends StartupWindowInterface> it = startupWindows.iterator();
while (it.hasNext()) {
StartupWindowInterface window = it.next();
if (!window.getClass().getCanonicalName().startsWith("org.sleuthkit.autopsy")) {
startupWindowToUse = window;
logger.log(Level.INFO, "Will use the custom startup window: {0}", startupWindowToUse.toString()); //NON-NLS
break;
}
}
break;
}
}
@ -121,6 +139,45 @@ public class StartupWindowProvider implements StartupWindowInterface {
startupWindowToUse = new org.sleuthkit.autopsy.casemodule.StartupWindow();
}
}
File openPreviousCaseFile = new File(ResetWindowsAction.getCaseToReopenFilePath());
if (openPreviousCaseFile.exists()) {
//do actual opening on another thread
new Thread(() -> {
String caseFilePath = "";
String unableToOpenMessage = null;
try {
//avoid readFileToString having ambiguous arguments
Charset encoding = null;
caseFilePath = FileUtils.readFileToString(openPreviousCaseFile, encoding);
if (new File(caseFilePath).exists()) {
FileUtils.forceDelete(openPreviousCaseFile);
//close the startup window as we attempt to open the case
close();
Case.openAsCurrentCase(caseFilePath);
} else {
unableToOpenMessage = Bundle.StartupWindowProvider_openCase_noFile(caseFilePath);
logger.log(Level.WARNING, unableToOpenMessage);
}
} catch (IOException ex) {
unableToOpenMessage = Bundle.StartupWindowProvider_openCase_deleteOpenFailure(ResetWindowsAction.getCaseToReopenFilePath());
logger.log(Level.WARNING, unableToOpenMessage, ex);
} catch (CaseActionException ex) {
unableToOpenMessage = Bundle.StartupWindowProvider_openCase_cantOpen(caseFilePath);
logger.log(Level.WARNING, unableToOpenMessage, ex);
}
if (RuntimeProperties.runningWithGUI() && !StringUtils.isBlank(unableToOpenMessage)) {
final String message = unableToOpenMessage;
SwingUtilities.invokeLater(() -> {
MessageNotifyUtil.Message.warn(message);
//the case was not opened restore the startup window
open();
});
}
}).start();
}
}
private void checkSolr() {
@ -149,7 +206,7 @@ public class StartupWindowProvider implements StartupWindowInterface {
private boolean isRunningFromCommandLine() {
CommandLineOptionProcessor processor = Lookup.getDefault().lookup(CommandLineOptionProcessor.class);
if(processor != null) {
if (processor != null) {
return processor.isRunFromCommandLine();
}
return false;
@ -162,7 +219,7 @@ public class StartupWindowProvider implements StartupWindowInterface {
*/
private String getDefaultArgument() {
CommandLineOptionProcessor processor = Lookup.getDefault().lookup(CommandLineOptionProcessor.class);
if(processor != null) {
if (processor != null) {
return processor.getDefaultArgument();
}
return null;

View File

@ -33,6 +33,8 @@ import org.sleuthkit.datamodel.TskCoreException;
*/
public class HostsEvent extends TskDataModelChangeEvent<Host> {
private static final long serialVersionUID = 1L;
/**
* Retrieves a list of ids from a list of hosts.
*
@ -42,7 +44,7 @@ public class HostsEvent extends TskDataModelChangeEvent<Host> {
private static List<Long> getIds(List<Host> hosts) {
return getSafeList(hosts).stream()
.filter(h -> h != null)
.map(h -> h.getId()).collect(Collectors.toList());
.map(h -> h.getHostId()).collect(Collectors.toList());
}
/**
@ -76,7 +78,7 @@ public class HostsEvent extends TskDataModelChangeEvent<Host> {
continue;
}
Optional<Host> thisHostOpt = hostManager.getHost(id);
Optional<Host> thisHostOpt = hostManager.getHostById(id);
thisHostOpt.ifPresent((h) -> toRet.add(h));
}
}

View File

@ -0,0 +1,37 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.casemodule.events;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.events.AutopsyEvent;
/**
* Event published when an OsAccount is deleted.
*
* oldValue will contain the objectId of the account that was removed. newValue
* will be null.
*/
public final class OsAccountDeletedEvent extends AutopsyEvent {
private static final long serialVersionUID = 1L;
public OsAccountDeletedEvent(Long osAccountObjectId) {
super(Case.Events.OS_ACCOUNT_REMOVED.toString(), osAccountObjectId, null);
}
}

View File

@ -56,7 +56,7 @@ class OsAccountEvent extends TskDataModelChangeEvent<OsAccount> {
@Override
protected List<OsAccount> getDataModelObjects(SleuthkitCase caseDb, List<Long> ids) throws TskCoreException {
Long id = ids.get(0);
OsAccount account = caseDb.getOsAccountManager().getOsAccount(id);
OsAccount account = caseDb.getOsAccountManager().getOsAccountByObjectId(id);
List<OsAccount> accounts = new ArrayList<>();
accounts.add(account);
return accounts;

View File

@ -25,7 +25,7 @@ import org.sleuthkit.datamodel.Person;
/**
* Event fired when persons are removed.
*/
public class PersonsRemovedEvent extends PersonsEvent {
public class PersonsDeletedEvent extends PersonsEvent {
private static final long serialVersionUID = 1L;
@ -33,7 +33,7 @@ public class PersonsRemovedEvent extends PersonsEvent {
* Main constructor.
* @param dataModelObjects The list of persons that have been deleted.
*/
public PersonsRemovedEvent(List<Person> dataModelObjects) {
public PersonsDeletedEvent(List<Person> dataModelObjects) {
super(Case.Events.PERSONS_DELETED.name(), dataModelObjects);
}
}

View File

@ -42,7 +42,7 @@ public class PersonsEvent extends TskDataModelChangeEvent<Person> {
private static List<Long> getIds(List<Person> persons) {
return getSafeList(persons).stream()
.filter(h -> h != null)
.map(h -> h.getId()).collect(Collectors.toList());
.map(h -> h.getPersonId()).collect(Collectors.toList());
}
/**

View File

@ -23,25 +23,4 @@
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
<SubComponents>
<Container class="javax.swing.JScrollPane" name="caseTableScrollPane">
<Properties>
<Property name="horizontalScrollBarPolicy" type="int" value="31"/>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 5]"/>
</Property>
<Property name="opaque" type="boolean" value="false"/>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[500, 500]"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
<BorderConstraints direction="Center"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
</Container>
</SubComponents>
</Form>

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2017-2019 Basis Technology Corp.
* Copyright 2017-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -18,6 +18,7 @@
*/
package org.sleuthkit.autopsy.casemodule.multiusercasesbrowser;
import java.awt.BorderLayout;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -68,8 +69,7 @@ public final class MultiUserCasesBrowserPanel extends javax.swing.JPanel impleme
outlineView = new org.openide.explorer.view.OutlineView();
outline = this.outlineView.getOutline();
configureOutlineView();
caseTableScrollPane.add(outlineView);
caseTableScrollPane.setViewportView(outlineView);
add(outlineView, BorderLayout.CENTER);
this.setVisible(true);
}
@ -146,20 +146,11 @@ public final class MultiUserCasesBrowserPanel extends javax.swing.JPanel impleme
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
caseTableScrollPane = new javax.swing.JScrollPane();
setMinimumSize(new java.awt.Dimension(0, 5));
setPreferredSize(new java.awt.Dimension(5, 5));
setLayout(new java.awt.BorderLayout());
caseTableScrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
caseTableScrollPane.setMinimumSize(new java.awt.Dimension(0, 5));
caseTableScrollPane.setOpaque(false);
caseTableScrollPane.setPreferredSize(new java.awt.Dimension(500, 500));
add(caseTableScrollPane, java.awt.BorderLayout.CENTER);
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JScrollPane caseTableScrollPane;
// End of variables declaration//GEN-END:variables
}

View File

@ -51,7 +51,7 @@ import org.sleuthkit.datamodel.TskException;
* View correlation results from other cases
*/
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
@ServiceProvider(service = DataContentViewer.class, position = 9)
@ServiceProvider(service = DataContentViewer.class, position = 10)
@Messages({"DataContentViewerOtherCases.title=Other Occurrences",
"DataContentViewerOtherCases.toolTip=Displays instances of the selected file/artifact from other occurrences."})
public final class DataContentViewerOtherCases extends JPanel implements DataContentViewer {

View File

@ -130,7 +130,7 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel {
exportToCSVMenuItem.addActionListener(actList);
showCaseDetailsMenuItem.addActionListener(actList);
showCommonalityMenuItem.addActionListener(actList);
filesTable.setComponentPopupMenu(rightClickPopupMenu);
// Configure column sorting.
TableRowSorter<TableModel> sorter = new TableRowSorter<>(filesTable.getModel());
filesTable.setRowSorter(sorter);
@ -380,7 +380,6 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel {
int totalCount = 0;
Set<String> dataSources = new HashSet<>();
if (CentralRepository.isEnabled()) {
try {
List<CorrelationAttributeInstance> instances;
instances = CentralRepository.getInstance().getArtifactInstancesByTypeValue(aType, value);
@ -395,12 +394,12 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel {
// - the data source device ID is different
// - the file path is different
if (artifactInstance.getCorrelationCase().getCaseUUID().equals(caseUUID)
|| (!StringUtils.isBlank(dataSourceName) && artifactInstance.getCorrelationDataSource().getName().equals(dataSourceName))
|| (!StringUtils.isBlank(deviceId) && artifactInstance.getCorrelationDataSource().getDeviceID().equals(deviceId))
|| (file != null && artifactInstance.getFilePath().equalsIgnoreCase(file.getParentPath() + file.getName()))) {
correlationAttributes.add(artifactInstance);
&& (!StringUtils.isBlank(dataSourceName) && artifactInstance.getCorrelationDataSource().getName().equals(dataSourceName))
&& (!StringUtils.isBlank(deviceId) && artifactInstance.getCorrelationDataSource().getDeviceID().equals(deviceId))
&& (file != null && artifactInstance.getFilePath().equalsIgnoreCase(file.getParentPath() + file.getName()))) {
continue;
}
correlationAttributes.add(artifactInstance);
OtherOccurrenceNodeInstanceData newNode = new OtherOccurrenceNodeInstanceData(artifactInstance, aType, value);
UniquePathKey uniquePathKey = new UniquePathKey(newNode);
nodeDataMap.put(uniquePathKey, newNode);
@ -510,9 +509,7 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel {
* artifact. If the central repo is not enabled, this will only return files
* from the current case with matching MD5 hashes.
*
* @param corAttr CorrelationAttribute to query for
* @param dataSourceName Data source to filter results
* @param deviceId Device Id to filter results
* @param corAttr CorrelationAttribute to query for
*
* @return A collection of correlated artifact instances
*/
@ -535,9 +532,9 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel {
// - the data source device ID is different
// - the file path is different
if (artifactInstance.getCorrelationCase().getCaseUUID().equals(caseUUID)
|| (!StringUtils.isBlank(dataSourceName) && artifactInstance.getCorrelationDataSource().getName().equals(dataSourceName))
|| (!StringUtils.isBlank(deviceId) && artifactInstance.getCorrelationDataSource().getDeviceID().equals(deviceId))
|| (file != null && artifactInstance.getFilePath().equalsIgnoreCase(file.getParentPath() + file.getName()))) {
&& (!StringUtils.isBlank(dataSourceName) && artifactInstance.getCorrelationDataSource().getName().equals(dataSourceName))
&& (!StringUtils.isBlank(deviceId) && artifactInstance.getCorrelationDataSource().getDeviceID().equals(deviceId))
&& (file != null && artifactInstance.getFilePath().equalsIgnoreCase(file.getParentPath() + file.getName()))) {
continue;
}
OtherOccurrenceNodeInstanceData newNode = new OtherOccurrenceNodeInstanceData(artifactInstance, corAttr.getCorrelationType(), corAttr.getCorrelationValue());
@ -618,6 +615,7 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel {
*
* @param corAttr The CorrelationAttribute containing the MD5 to search for
* @param openCase The current case
* @param file The current file.
*
* @return List of matching AbstractFile objects
*

View File

@ -12,7 +12,7 @@ CentralRepoDbChoice.PostgreSQL.Text=Custom PostgreSQL
CentralRepoDbChoice.PostgreSQL_Multiuser.Text=PostgreSQL using multi-user settings
CentralRepoDbChoice.Sqlite.Text=SQLite
CentralRepoDbManager.connectionErrorMsg.text=Failed to connect to central repository database.
CentralRepositoryService.progressMsg.updatingSchema=Updating schema...
CentralRepositoryService.progressMsg.updatingSchema=Checking for schema updates...
CentralRepositoryService.progressMsg.waitingForListeners=Finishing adding data to central repository database....
CentralRepositoryService.serviceName=Central Repository Service
CorrelationAttributeInstance.invalidName.message=Invalid database table name. Name must start with a lowercase letter and can only contain lowercase letters, numbers, and '_'.

View File

@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.centralrepository.datamodel;
import java.sql.SQLException;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.autopsy.casemodule.Case;
@ -860,10 +861,10 @@ public interface CentralRepository {
* Get account type by type name.
*
* @param accountTypeName account type name to look for
* @return CR account type
* @return CR account type (if found)
* @throws CentralRepoException
*/
CentralRepoAccountType getAccountTypeByName(String accountTypeName) throws CentralRepoException;
Optional<CentralRepoAccountType> getAccountTypeByName(String accountTypeName) throws CentralRepoException;
/**
* Gets all account types.

View File

@ -47,7 +47,7 @@ public class CentralRepositoryService implements AutopsyService {
}
@NbBundle.Messages({
"CentralRepositoryService.progressMsg.updatingSchema=Updating schema..."
"CentralRepositoryService.progressMsg.updatingSchema=Checking for schema updates..."
})
@Override
public void openCaseResources(CaseContext context) throws AutopsyServiceException {

View File

@ -22,6 +22,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.logging.Level;
import org.openide.util.NbBundle.Messages;
@ -308,7 +309,11 @@ public class CorrelationAttributeUtil {
if (Account.Type.DEVICE.getTypeName().equalsIgnoreCase(accountTypeStr) == false && predefinedAccountType != null) {
// Get the corresponding CentralRepoAccountType from the database.
CentralRepoAccountType crAccountType = CentralRepository.getInstance().getAccountTypeByName(accountTypeStr);
Optional<CentralRepoAccountType> optCrAccountType = CentralRepository.getInstance().getAccountTypeByName(accountTypeStr);
if (!optCrAccountType.isPresent()) {
return;
}
CentralRepoAccountType crAccountType = optCrAccountType.get();
int corrTypeId = crAccountType.getCorrelationTypeId();
CorrelationAttributeInstance.Type corrType = CentralRepository.getInstance().getCorrelationTypeById(corrTypeId);

View File

@ -26,8 +26,10 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import org.apache.commons.lang3.StringUtils;
import org.sleuthkit.datamodel.Account;
import org.sleuthkit.datamodel.TskCoreException;
/**
* This class represents an association between a Persona and an Account.
@ -206,10 +208,15 @@ public class PersonaAccount {
);
// create account
CentralRepoAccount.CentralRepoAccountType crAccountType = getCRInstance().getAccountTypeByName(rs.getString("type_name"));
String accountTypeName = rs.getString("type_name");
Optional<CentralRepoAccount.CentralRepoAccountType> optCrAccountType = getCRInstance().getAccountTypeByName(accountTypeName);
if (! optCrAccountType.isPresent()) {
// The CR account can not be null, so throw an exception
throw new CentralRepoException("Account type with name '" + accountTypeName + "' not found in Central Repository");
}
CentralRepoAccount account = new CentralRepoAccount(
rs.getInt("account_id"),
crAccountType,
optCrAccountType.get(),
rs.getString("account_unique_identifier"));
// create persona account
@ -389,10 +396,15 @@ public class PersonaAccount {
while (rs.next()) {
// create account
CentralRepoAccount.CentralRepoAccountType crAccountType = getCRInstance().getAccountTypeByName(rs.getString("type_name"));
String accountTypeName = rs.getString("type_name");
Optional<CentralRepoAccount.CentralRepoAccountType> optCrAccountType = getCRInstance().getAccountTypeByName(accountTypeName);
if (! optCrAccountType.isPresent()) {
// The CR account can not be null, so throw an exception
throw new CentralRepoException("Account type with name '" + accountTypeName + "' not found in Central Repository");
}
CentralRepoAccount account = new CentralRepoAccount(
rs.getInt("account_id"),
crAccountType,
optCrAccountType.get(),
rs.getString("account_unique_identifier"));
accountsList.add(account);

View File

@ -37,6 +37,7 @@ import java.time.LocalDate;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
@ -78,7 +79,7 @@ abstract class RdbmsCentralRepo implements CentralRepository {
private static final int CASE_CACHE_TIMEOUT = 5;
private static final int DATA_SOURCE_CACHE_TIMEOUT = 5;
private static final int ACCOUNTS_CACHE_TIMEOUT = 5;
private static final Cache<String, CentralRepoAccountType> accountTypesCache = CacheBuilder.newBuilder().build();
private static final Cache<String, Optional<CentralRepoAccountType>> accountTypesCache = CacheBuilder.newBuilder().build();
private static final Cache<Pair<CentralRepoAccountType, String>, CentralRepoAccount> accountsCache = CacheBuilder.newBuilder()
.expireAfterWrite(ACCOUNTS_CACHE_TIMEOUT, TimeUnit.MINUTES).
build();
@ -1115,7 +1116,7 @@ abstract class RdbmsCentralRepo implements CentralRepository {
}
@Override
public CentralRepoAccountType getAccountTypeByName(String accountTypeName) throws CentralRepoException {
public Optional<CentralRepoAccountType> getAccountTypeByName(String accountTypeName) throws CentralRepoException {
try {
return accountTypesCache.get(accountTypeName, () -> getCRAccountTypeFromDb(accountTypeName));
} catch (CacheLoader.InvalidCacheLoadException | ExecutionException ex) {
@ -1155,7 +1156,7 @@ abstract class RdbmsCentralRepo implements CentralRepository {
*
* @throws CentralRepoException
*/
private CentralRepoAccountType getCRAccountTypeFromDb(String accountTypeName) throws CentralRepoException {
private Optional<CentralRepoAccountType> getCRAccountTypeFromDb(String accountTypeName) throws CentralRepoException {
String sql = "SELECT * FROM account_types WHERE type_name = ?";
try (Connection conn = connect();
@ -1166,10 +1167,11 @@ abstract class RdbmsCentralRepo implements CentralRepository {
if (resultSet.next()) {
Account.Type acctType = new Account.Type(accountTypeName, resultSet.getString("display_name"));
CentralRepoAccountType crAccountType = new CentralRepoAccountType(resultSet.getInt("id"), acctType, resultSet.getInt("correlation_type_id"));
accountTypesCache.put(accountTypeName, crAccountType);
return crAccountType;
accountTypesCache.put(accountTypeName, Optional.of(crAccountType));
return Optional.of(crAccountType);
} else {
throw new CentralRepoException("Failed to find entry for account type = " + accountTypeName);
accountTypesCache.put(accountTypeName, Optional.empty());
return Optional.empty();
}
}
} catch (SQLException ex) {

View File

@ -22,6 +22,8 @@ import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
@ -41,7 +43,15 @@ public final class SqliteCentralRepoSettings implements CentralRepoDbConnectivit
public final static String DEFAULT_DBNAME = "central_repository.db"; // NON-NLS
private final static Logger LOGGER = Logger.getLogger(SqliteCentralRepoSettings.class.getName());
private final Path userConfigDir = Paths.get(PlatformUtil.getUserDirectory().getAbsolutePath());
private final static String DEFAULT_DBDIRECTORY = PlatformUtil.getUserDirectory() + File.separator + "central_repository"; // NON-NLS
//property names
private static final String PROFILE_NAME = "CentralRepository";
private static final String DATABASE_NAME = "db.sqlite.dbName"; //NON-NLS
private static final String DATABASE_PATH = "db.sqlite.dbDirectory"; //NON-NLS
private static final String BULK_THRESHOLD = "db.sqlite.bulkThreshold"; //NON-NLS
private final static String JDBC_DRIVER = "org.sqlite.JDBC"; // NON-NLS
private final static String JDBC_BASE_URI = "jdbc:sqlite:"; // NON-NLS
private final static String VALIDATION_QUERY = "SELECT count(*) from sqlite_master"; // NON-NLS
@ -56,18 +66,18 @@ public final class SqliteCentralRepoSettings implements CentralRepoDbConnectivit
}
public void loadSettings() {
dbName = ModuleSettings.getConfigSetting("CentralRepository", "db.sqlite.dbName"); // NON-NLS
dbName = ModuleSettings.getConfigSetting(PROFILE_NAME, DATABASE_NAME); // NON-NLS
if (dbName == null || dbName.isEmpty()) {
dbName = DEFAULT_DBNAME;
}
dbDirectory = ModuleSettings.getConfigSetting("CentralRepository", "db.sqlite.dbDirectory"); // NON-NLS
dbDirectory = readDbPath(); // NON-NLS
if (dbDirectory == null || dbDirectory.isEmpty()) {
dbDirectory = DEFAULT_DBDIRECTORY;
}
try {
String bulkThresholdString = ModuleSettings.getConfigSetting("CentralRepository", "db.sqlite.bulkThreshold"); // NON-NLS
String bulkThresholdString = ModuleSettings.getConfigSetting(PROFILE_NAME, BULK_THRESHOLD); // NON-NLS
if (bulkThresholdString == null || bulkThresholdString.isEmpty()) {
this.bulkThreshold = RdbmsCentralRepo.DEFAULT_BULK_THRESHHOLD;
} else {
@ -96,9 +106,64 @@ public final class SqliteCentralRepoSettings implements CentralRepoDbConnectivit
public void saveSettings() {
createDbDirectory();
ModuleSettings.setConfigSetting("CentralRepository", "db.sqlite.dbName", getDbName()); // NON-NLS
ModuleSettings.setConfigSetting("CentralRepository", "db.sqlite.dbDirectory", getDbDirectory()); // NON-NLS
ModuleSettings.setConfigSetting("CentralRepository", "db.sqlite.bulkThreshold", Integer.toString(getBulkThreshold())); // NON-NLS
ModuleSettings.setConfigSetting(PROFILE_NAME, DATABASE_NAME, getDbName()); // NON-NLS
saveDbPath(getDbDirectory()); // NON-NLS
ModuleSettings.setConfigSetting(PROFILE_NAME, BULK_THRESHOLD, Integer.toString(getBulkThreshold())); // NON-NLS
}
/**
* Save CR database path. If the path is inside user directory (e.g.
* "C:\Users\USER_NAME\AppData\Roaming\autopsy"), trim that off and save it
* as a relative path (i.e it will not start with a / or drive letter). Otherwise,
* full path is saved. See JIRA-7348.
*
* @param fullPath Full path to the SQLite db file.
*/
private void saveDbPath(String fullPath) {
Path relativePath = Paths.get(fullPath);
// check if the path is within user directory
if (Paths.get(fullPath).startsWith(userConfigDir)) {
// relativize the path
relativePath = userConfigDir.relativize(relativePath);
}
// Use properties to persist the logo to use.
ModuleSettings.setConfigSetting(PROFILE_NAME, DATABASE_PATH, relativePath.toString());
}
/**
* Read CD database path from preferences file. Reverses the path relativization performed
* in saveDbPath(). If the stored path starts with either / or drive letter,
* it is a full path, and is returned to the caller. Otherwise, append current user
* directory to the saved relative path. See JIRA-7348.
*
* @return Full path to the SQLite CR database file.
*/
private String readDbPath() {
String curPath = ModuleSettings.getConfigSetting(PROFILE_NAME, DATABASE_PATH);
//if has been set, validate it's correct, if not set, return null
if (curPath != null && !curPath.isEmpty()) {
// check if the path is an absolute path (starts with either drive letter or "/")
Path driveLetterOrNetwork = Paths.get(curPath).getRoot();
if (driveLetterOrNetwork != null) {
// absolute path
return curPath;
}
// Path is a relative path. Reverse path relativization performed in saveDbPath()
Path absolutePath = userConfigDir.resolve(curPath);
curPath = absolutePath.toString();
if (new File(curPath).canRead() == false) {
//use default
LOGGER.log(Level.INFO, "Path to SQLite Central Repository database is not valid: {0}", curPath); //NON-NLS
curPath = null;
}
}
return curPath;
}
/**
@ -252,9 +317,9 @@ public final class SqliteCentralRepoSettings implements CentralRepoDbConnectivit
}
boolean isChanged() {
String dbNameString = ModuleSettings.getConfigSetting("CentralRepository", "db.sqlite.dbName"); // NON-NLS
String dbDirectoryString = ModuleSettings.getConfigSetting("CentralRepository", "db.sqlite.dbDirectory"); // NON-NLS
String bulkThresholdString = ModuleSettings.getConfigSetting("CentralRepository", "db.sqlite.bulkThreshold"); // NON-NLS
String dbNameString = ModuleSettings.getConfigSetting(PROFILE_NAME, DATABASE_NAME); // NON-NLS
String dbDirectoryString = readDbPath(); // NON-NLS
String bulkThresholdString = ModuleSettings.getConfigSetting(PROFILE_NAME, BULK_THRESHOLD); // NON-NLS
return !dbName.equals(dbNameString)
|| !dbDirectory.equals(dbDirectoryString)

View File

@ -185,6 +185,7 @@ final public class FiltersPanel extends JPanel {
refreshButton.addActionListener(e -> applyFilters());
}
/**
* Validate that filters are in a consistent state and will result in some
* results. Checks that at least one device and at least one account type is
@ -220,13 +221,18 @@ final public class FiltersPanel extends JPanel {
}
void initalizeFilters() {
Runnable runnable = new Runnable() {
applyFiltersButton.setEnabled(false);
refreshButton.setEnabled(true);
needsRefreshLabel.setText("Loading filters...");
needsRefreshLabel.setVisible(true);
(new Thread(new Runnable(){
@Override
public void run() {
new FilterPanelRefresher(true, true).refresh();
}
};
runnable.run();
})).start();
}
private void updateTimeZone() {
@ -1153,6 +1159,8 @@ final public class FiltersPanel extends JPanel {
setEnabled(true);
}
needsRefreshLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.needsRefreshLabel.text")); // NOI18N
validateFilters();
repaint();

View File

@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.communications.relationships;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.logging.Level;
import org.openide.nodes.AbstractNode;
@ -75,9 +76,9 @@ final class CorrelationCaseChildNodeFactory extends ChildFactory<CorrelationCase
accounts.forEach((account) -> {
try {
CorrelationAttributeInstance.Type correlationType = getCorrelationType(account.getAccountType());
if (correlationType != null) {
List<CorrelationAttributeInstance> correlationInstances = dbInstance.getArtifactInstancesByTypeValue(correlationType, account.getTypeSpecificID());
Optional<CorrelationAttributeInstance.Type> optCorrelationType = getCorrelationType(account.getAccountType());
if (optCorrelationType.isPresent()) {
List<CorrelationAttributeInstance> correlationInstances = dbInstance.getArtifactInstancesByTypeValue(optCorrelationType.get(), account.getTypeSpecificID());
correlationInstances.forEach((correlationInstance) -> {
CorrelationCase correlationCase = correlationInstance.getCorrelationCase();
uniqueCaseMap.put(correlationCase.getCaseUUID(), correlationCase);
@ -103,20 +104,22 @@ final class CorrelationCaseChildNodeFactory extends ChildFactory<CorrelationCase
*
* @param accountType Account type
*
* @return CorrelationAttributeInstance.Type for given account or null if
* @return CorrelationAttributeInstance.Type for given account or empty if
* there is no match
*
* @throws CentralRepoException
*/
private CorrelationAttributeInstance.Type getCorrelationType(Account.Type accountType) throws CentralRepoException {
private Optional<CorrelationAttributeInstance.Type> getCorrelationType(Account.Type accountType) throws CentralRepoException {
String accountTypeStr = accountType.getTypeName();
if (Account.Type.DEVICE.getTypeName().equalsIgnoreCase(accountTypeStr) == false) {
CentralRepoAccount.CentralRepoAccountType crAccountType = CentralRepository.getInstance().getAccountTypeByName(accountTypeStr);
int corrTypeId = crAccountType.getCorrelationTypeId();
return CentralRepository.getInstance().getCorrelationTypeById(corrTypeId);
Optional<CentralRepoAccount.CentralRepoAccountType> optCrAccountType = CentralRepository.getInstance().getAccountTypeByName(accountTypeStr);
if (optCrAccountType.isPresent()) {
int corrTypeId = optCrAccountType.get().getCorrelationTypeId();
return Optional.of(CentralRepository.getInstance().getCorrelationTypeById(corrTypeId));
}
}
return null;
return Optional.empty();
}
/**

View File

@ -49,6 +49,7 @@ import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.openide.nodes.Node.Property;
import org.openide.nodes.Node.PropertySet;
import org.openide.util.Exceptions;
import org.openide.util.Lookup;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.communications.ModifiableProxyLookup;
@ -146,14 +147,23 @@ final class MessageViewer extends JPanel implements RelationshipsViewer {
@Override
public void setSelectionInfo(SelectionInfo info) {
currentSelectionInfo = info;
if(currentSelectionInfo != null && currentSelectionInfo.equals(info)) {
try {
// Clear the currently selected thread so that clicks can
// be registered.
rootTablePane.getExplorerManager().setSelectedNodes(new Node[0]);
} catch (PropertyVetoException ex) {
logger.log(Level.WARNING, "Error clearing the selected node", ex);
}
} else {
currentSelectionInfo = info;
rootMessageFactory.refresh(info);
}
currentPanel = rootTablePane;
CardLayout layout = (CardLayout) this.getLayout();
layout.show(this, "threads");
rootMessageFactory.refresh(info);
}
@Override
@ -192,7 +202,7 @@ final class MessageViewer extends JPanel implements RelationshipsViewer {
if (isDescendingFrom(newFocusOwner, rootTablePane)) {
proxyLookup.setNewLookups(createLookup(rootTablePane.getExplorerManager(), getActionMap()));
} else if (isDescendingFrom(newFocusOwner, this)) {
proxyLookup.setNewLookups(createLookup(currentPanel.getExplorerManager(), getActionMap()));
proxyLookup.setNewLookups(createLookup(threadMessagesPanel.getExplorerManager(), getActionMap()));
}
}

View File

@ -113,7 +113,7 @@ class MessagesPanel extends javax.swing.JPanel implements Lookup.Provider {
outlineViewPanel.setTableColumnsWidth(5, 10, 10, 15, 50, 10);
}
public MessagesPanel(ChildFactory<?> nodeFactory) {
MessagesPanel(ChildFactory<?> nodeFactory) {
this();
setChildFactory(nodeFactory);
}
@ -123,6 +123,15 @@ class MessagesPanel extends javax.swing.JPanel implements Lookup.Provider {
return proxyLookup;
}
/**
* Return the explorerManager for the table.
*
* @return The explorer manager for the table.
*/
ExplorerManager getExplorerManager() {
return outlineViewPanel.getExplorerManager();
}
@Override
public void addNotify() {
super.addNotify();

View File

@ -21,10 +21,12 @@ package org.sleuthkit.autopsy.communications.relationships;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.logging.Level;
import javax.swing.SwingWorker;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.centralrepository.datamodel.Persona;
import org.sleuthkit.autopsy.centralrepository.datamodel.PersonaAccount;
@ -61,8 +63,10 @@ class SummaryPanelWorker extends SwingWorker<SummaryPanelWorker.SummaryWorkerRes
CentralRepoAccount crAccount = null;
List<String> stringList = new ArrayList<>();
List<AccountFileInstance> accountFileInstanceList = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().getAccountFileInstances(account);
for (AccountFileInstance instance : accountFileInstanceList) {
stringList.add(instance.getFile().getUniquePath());
if (accountFileInstanceList != null) {
for (AccountFileInstance instance : accountFileInstanceList) {
stringList.add(instance.getFile().getUniquePath());
}
}
List<Persona> personaList = new ArrayList<>();
@ -74,12 +78,15 @@ class SummaryPanelWorker extends SwingWorker<SummaryPanelWorker.SummaryWorkerRes
personaList.add(pAccount.getPersona());
}
try {
crAccount = CentralRepository.getInstance().getAccount(CentralRepository.getInstance().getAccountTypeByName(account.getAccountType().getTypeName()), account.getTypeSpecificID());
} catch (InvalidAccountIDException unused) {
// This was probably caused to a phone number not making
// threw the normalization.
logger.log(Level.WARNING, String.format("Exception thrown from CR getAccount for account %s (%d)", account.getTypeSpecificID(), account.getAccountID()));
Optional<CentralRepoAccount.CentralRepoAccountType> optCrAccountType = CentralRepository.getInstance().getAccountTypeByName(account.getAccountType().getTypeName());
if (optCrAccountType.isPresent()) {
try {
crAccount = CentralRepository.getInstance().getAccount(optCrAccountType.get(), account.getTypeSpecificID());
} catch (InvalidAccountIDException unused) {
// This was probably caused to a phone number not making
// threw the normalization.
logger.log(Level.WARNING, String.format("Exception thrown from CR getAccount for account %s (%d)", account.getTypeSpecificID(), account.getAccountID()));
}
}
}

View File

@ -216,9 +216,11 @@ public class SummaryViewer extends javax.swing.JPanel implements RelationshipsVi
List<String> fileRefList = results.getPaths();
fileRefList.forEach(value -> {
fileRefListModel.addElement(value);
});
if (fileRefList != null) {
fileRefList.forEach(value -> {
fileRefListModel.addElement(value);
});
}
CardLayout cardLayout = (CardLayout) fileRefPane.getLayout();
cardLayout.show(fileRefPane, "listPanelCard");

View File

@ -61,7 +61,7 @@ import org.jsoup.nodes.Element;
* Annotations view of file contents.
*/
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
@ServiceProvider(service = DataContentViewer.class, position = 8)
@ServiceProvider(service = DataContentViewer.class, position = 9)
@Messages({
"AnnotationsContentViewer.title=Annotations",
"AnnotationsContentViewer.toolTip=Displays tags and comments associated with the selected content.",

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2018-2020 Basis Technology Corp.
* Copyright 2018-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -18,6 +18,7 @@
*/
package org.sleuthkit.autopsy.contentviewers;
import com.google.common.collect.Lists;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.image.BufferedImage;
@ -32,7 +33,6 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;
import static java.util.Objects.nonNull;
import java.util.SortedSet;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.stream.Collectors;
@ -70,7 +70,6 @@ import javax.swing.SwingWorker;
import org.apache.commons.io.FilenameUtils;
import org.controlsfx.control.MaskerPane;
import org.openide.util.NbBundle;
import org.python.google.common.collect.Lists;
import org.sleuthkit.autopsy.actions.GetTagNameAndCommentDialog;
import org.sleuthkit.autopsy.actions.GetTagNameAndCommentDialog.TagNameAndComment;
import org.sleuthkit.autopsy.casemodule.Case;
@ -110,10 +109,6 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
private static final long serialVersionUID = 1L;
private static final Logger logger = Logger.getLogger(MediaViewImagePanel.class.getName());
private static final SortedSet<String> supportedMimes = ImageUtils.getSupportedImageMimeTypes();
private static final List<String> supportedExtensions = ImageUtils.getSupportedImageExtensions().stream()
.map("."::concat) //NOI18N
.collect(Collectors.toList());
private static final double[] ZOOM_STEPS = {
0.0625, 0.125, 0.25, 0.375, 0.5, 0.75,
1, 1.5, 2, 2.5, 3, 4, 5, 6, 8, 10};
@ -634,30 +629,25 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
}
/**
* @return supported mime types
* Gets the list of supported MIME types.
*
* @return A list of the supported MIME types as Strings.
*/
@Override
final public List<String> getSupportedMimeTypes() {
return Collections.unmodifiableList(Lists.newArrayList(supportedMimes));
return Collections.unmodifiableList(Lists.newArrayList(ImageUtils.getSupportedImageMimeTypes()));
}
/**
* returns supported extensions (each starting with .)
* Returns supported extensions (each starting with .)
*
* @return
* @return A unmodifiable list of image extensions as Strings.
*/
@Override
final public List<String> getSupportedExtensions() {
return getExtensions();
}
/**
* returns supported extensions (each starting with .)
*
* @return
*/
final public List<String> getExtensions() {
return Collections.unmodifiableList(supportedExtensions);
return ImageUtils.getSupportedImageExtensions().stream()
.map("."::concat) //NOI18N
.collect(Collectors.toList());
}
@Override

View File

@ -32,6 +32,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
@ -630,11 +631,13 @@ public class ContactArtifactViewer extends javax.swing.JPanel implements Artifac
// make a list of all unique accounts for this contact
if (!account.getAccountType().equals(Account.Type.DEVICE)) {
CentralRepoAccount.CentralRepoAccountType crAccountType = CentralRepository.getInstance().getAccountTypeByName(account.getAccountType().getTypeName());
CentralRepoAccount crAccount = CentralRepository.getInstance().getAccount(crAccountType, account.getTypeSpecificID());
Optional<CentralRepoAccount.CentralRepoAccountType> optCrAccountType = CentralRepository.getInstance().getAccountTypeByName(account.getAccountType().getTypeName());
if (optCrAccountType.isPresent()) {
CentralRepoAccount crAccount = CentralRepository.getInstance().getAccount(optCrAccountType.get(), account.getTypeSpecificID());
if (crAccount != null && uniqueAccountsList.contains(crAccount) == false) {
uniqueAccountsList.add(crAccount);
if (crAccount != null && uniqueAccountsList.contains(crAccount) == false) {
uniqueAccountsList.add(crAccount);
}
}
}

View File

@ -48,7 +48,7 @@ import org.sleuthkit.datamodel.TskCoreException;
* usage, if known.
*
*/
@ServiceProvider(service = DataContentViewer.class, position = 7)
@ServiceProvider(service = DataContentViewer.class, position = 8)
public final class ContextViewer extends javax.swing.JPanel implements DataContentViewer {
private static final long serialVersionUID = 1L;

View File

@ -1,3 +1,4 @@
OsAccountDataPanel_administrator_title=Administrator
OsAccountDataPanel_basic_address=Address
OsAccountDataPanel_basic_admin=Administrator
OsAccountDataPanel_basic_creationDate=Creation Date
@ -5,11 +6,16 @@ OsAccountDataPanel_basic_fullname=Full Name
OsAccountDataPanel_basic_login=Login
OsAccountDataPanel_basic_title=Basic Properties
OsAccountDataPanel_basic_type=Type
OsAccountDataPanel_data_accessed_title=Last Login
OsAccountDataPanel_host_count_title=Login Count
# {0} - hostName
OsAccountDataPanel_host_section_title={0} Details
OsAccountDataPanel_realm_address=Address
OsAccountDataPanel_realm_confidence=Confidence
OsAccountDataPanel_realm_name=Name
OsAccountDataPanel_realm_scope=Scope
OsAccountDataPanel_realm_title=Realm Properties
OsAccountDataPanel_realm_unknown=Unknown
OsAccountViewer_title=Os Account
OsAccountViewer_tooltip=Viewer for OS accounts related to the selected node.
OsAccountViewer_title=OS Account
OsAccountViewer_tooltip=Viewer for Operating System accounts related to the selected node.
TableDataPanel.titleLabel.text=Title

View File

@ -18,6 +18,7 @@
*/
package org.sleuthkit.autopsy.contentviewers.osaccount;
import java.awt.BorderLayout;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
@ -25,16 +26,30 @@ import java.awt.Insets;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import static java.util.Locale.US;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.Box;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingWorker;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.contentviewers.osaccount.SectionData.RowData;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.Host;
import org.sleuthkit.datamodel.OsAccount;
import org.sleuthkit.datamodel.OsAccount.OsAccountAttribute;
import org.sleuthkit.datamodel.OsAccountInstance;
import org.sleuthkit.datamodel.OsAccountManager;
import org.sleuthkit.datamodel.OsAccountRealm;
import org.sleuthkit.datamodel.SleuthkitCase;
/**
* Panel for displaying the properties of an OsAccount.
@ -42,12 +57,15 @@ import org.sleuthkit.datamodel.OsAccountRealm;
public class OsAccountDataPanel extends JPanel {
private static final long serialVersionUID = 1L;
final private static Logger logger = Logger.getLogger(OsAccountDataPanel.class.getName());
private static final int KEY_COLUMN = 0;
private static final int VALUE_COLUMN = 1;
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("MMM dd yyyy", US);
private PanelDataFetcher dataFetcher = null;
// Panel constructor.
OsAccountDataPanel() {
initialize();
@ -66,21 +84,38 @@ public class OsAccountDataPanel extends JPanel {
* @param account OsAccount to display, if null is passed the panel will
* appear blank.
*/
void setOsAccount(OsAccount account) {
void setOsAccountId(Long osAccountId) {
removeAll();
revalidate();
if (account != null) {
List<SectionData> data = new ArrayList<>();
data.add(buildBasicProperties(account));
if (osAccountId != null) {
setLayout(new BorderLayout());
add(new JLabel("Loading OsAccount Data..."), BorderLayout.NORTH);
OsAccountRealm realm = account.getRealm();
if (realm != null) {
data.add(buildRealmProperties(realm));
if (dataFetcher != null && !dataFetcher.isDone()) {
dataFetcher.cancel(true);
}
addDataComponents(data);
dataFetcher = new PanelDataFetcher(osAccountId);
dataFetcher.execute();
}
}
void setOsAccount(OsAccount account) {
removeAll();
revalidate();
if (account != null) {
setLayout(new BorderLayout());
add(new JLabel("Loading OsAccount Data..."), BorderLayout.NORTH);
if (dataFetcher != null && !dataFetcher.isDone()) {
dataFetcher.cancel(true);
}
dataFetcher = new PanelDataFetcher(account);
dataFetcher.execute();
}
}
/**
@ -122,8 +157,7 @@ public class OsAccountDataPanel extends JPanel {
"OsAccountDataPanel_basic_address=Address",
"OsAccountDataPanel_basic_admin=Administrator",
"OsAccountDataPanel_basic_type=Type",
"OsAccountDataPanel_basic_creationDate=Creation Date",
})
"OsAccountDataPanel_basic_creationDate=Creation Date",})
/**
* Returns the data for the Basic Properties section of the panel.
@ -146,13 +180,14 @@ public class OsAccountDataPanel extends JPanel {
data.addData(Bundle.OsAccountDataPanel_basic_address(),
account.getName() == null || account.getName().isEmpty() ? "" : account.getName());
data.addData(Bundle.OsAccountDataPanel_basic_type(), account.getOsAccountType().getName());
data.addData(Bundle.OsAccountDataPanel_basic_type(),
account.getOsAccountType().isPresent() ? account.getOsAccountType().get().getName() : "");
Optional<Long> crTime = account.getCreationTime();
if(crTime.isPresent()) {
if (crTime.isPresent()) {
data.addData(Bundle.OsAccountDataPanel_basic_creationDate(), DATE_FORMAT.format(new Date(crTime.get() * 1000)));
}
else {
} else {
data.addData(Bundle.OsAccountDataPanel_basic_creationDate(), "");
}
@ -177,16 +212,15 @@ public class OsAccountDataPanel extends JPanel {
private SectionData buildRealmProperties(OsAccountRealm realm) {
SectionData data = new SectionData(Bundle.OsAccountDataPanel_realm_title());
Optional<String> optional = realm.getRealmName();
data.addData(Bundle.OsAccountDataPanel_realm_name(),
optional.isPresent() ? optional.get() : Bundle.OsAccountDataPanel_realm_unknown());
String realmName = realm.getRealmNames().isEmpty() ? Bundle.OsAccountDataPanel_realm_unknown() : realm.getRealmNames().get(0);
data.addData(Bundle.OsAccountDataPanel_realm_name(), realmName);
optional = realm.getRealmAddr();
Optional<String> optional = realm.getRealmAddr();
data.addData(Bundle.OsAccountDataPanel_realm_address(),
optional.isPresent() ? optional.get() : "");
data.addData(Bundle.OsAccountDataPanel_realm_scope(),
realm.getScope().getName());
realm.getScope().getName());
data.addData(Bundle.OsAccountDataPanel_realm_confidence(),
realm.getScopeConfidence().getName());
@ -194,6 +228,38 @@ public class OsAccountDataPanel extends JPanel {
return data;
}
@Messages({
"# {0} - hostName",
"OsAccountDataPanel_host_section_title={0} Details",
"OsAccountDataPanel_host_count_title=Login Count",
"OsAccountDataPanel_data_accessed_title=Last Login",
"OsAccountDataPanel_administrator_title=Administrator"
})
private SectionData buildHostData(Host host, List<OsAccountAttribute> attributeList) {
SectionData data = new SectionData(Bundle.OsAccountDataPanel_host_section_title(host.getName()));
for (OsAccountAttribute attribute : attributeList) {
String displayName = attribute.getAttributeType().getDisplayName();
String value = attribute.getDisplayString();
if(attribute.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COUNT.getTypeID()) {
displayName = Bundle.OsAccountDataPanel_host_count_title();
} else if(attribute.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_IS_ADMIN.getTypeID()) {
displayName = Bundle.OsAccountDataPanel_administrator_title();
if(attribute.getValueInt() == 0) {
value = "False";
} else {
value = "True";
}
} else if(attribute.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED.getTypeID()) {
displayName = Bundle.OsAccountDataPanel_data_accessed_title();
}
data.addData(displayName, value);
}
return data;
}
/**
* Add a section title to the panel with the given title and location.
*
@ -214,7 +280,7 @@ public class OsAccountDataPanel extends JPanel {
* @param row The row in the layout.
*/
private void addPropertyName(String key, int row) {
JLabel label = new JLabel(key);
JLabel label = new JLabel(key + ":");
add(label, getPropertyNameContraints(row));
}
@ -291,4 +357,189 @@ public class OsAccountDataPanel extends JPanel {
return constraints;
}
/**
* A SwingWorker to gather the data for the content panel.
*/
private class PanelDataFetcher extends SwingWorker<WorkerResults, Void> {
private final Long accountId;
private OsAccount account;
/**
* Construct a new worker for the given account.
*
* @param account
*/
PanelDataFetcher(Long accountId) {
this.accountId = accountId;
this.account = null;
}
PanelDataFetcher(OsAccount account) {
this.account = account;
this.accountId = null;
}
@Override
protected WorkerResults doInBackground() throws Exception {
Map<Host, List<OsAccountAttribute>> hostMap = new HashMap<>();
Map<Host, DataSource> instanceMap = new HashMap<>();
SleuthkitCase skCase = Case.getCurrentCase().getSleuthkitCase();
OsAccountManager osAccountManager = skCase.getOsAccountManager();
if(account == null) {
account = osAccountManager.getOsAccountByObjectId(accountId);
}
OsAccountRealm realm = skCase.getOsAccountRealmManager().getRealmByRealmId(account.getRealmId());
List<Host> hosts = osAccountManager.getHosts(account);
List<OsAccountAttribute> attributeList = account.getExtendedOsAccountAttributes();
if (attributeList != null) {
if (hosts != null) {
// Organize the attributes by hostId
Map<Long, List<OsAccountAttribute>> idMap = new HashMap<>();
for (OsAccountAttribute attribute : attributeList) {
List<OsAccountAttribute> atList = null;
Optional<Long> optionalId = attribute.getHostId();
Long key = null;
if (optionalId.isPresent()) {
key = optionalId.get();
}
atList = idMap.get(key);
if (atList == null) {
atList = new ArrayList<>();
idMap.put(key, atList);
}
atList.add(attribute);
}
// Add attribute lists to the hostMap
for (Host host : hosts) {
List<OsAccountAttribute> atList = idMap.get(host.getHostId());
if (atList != null) {
hostMap.put(host, atList);
}
}
List<OsAccountAttribute> atList = idMap.get(null);
if (atList != null) {
hostMap.put(null, atList);
}
// Store both the host and the dataSource so that we get
// all of the calls to the db done in the thread.
for (OsAccountInstance instance : account.getOsAccountInstances()) {
instanceMap.put(instance.getDataSource().getHost(), instance.getDataSource());
}
} else {
hostMap.put(null, attributeList);
}
}
return new WorkerResults(hostMap, instanceMap, realm);
}
@Override
protected void done() {
WorkerResults results = null;
try {
if (this.isCancelled()) {
return;
} else {
results = get();
}
} catch (ExecutionException | InterruptedException ex) {
logger.log(Level.SEVERE, String.format("Failed to retrieve data for OsAccount (%d)", account.getId()), ex);
}
if (results != null) {
removeAll();
setLayout(new GridBagLayout());
List<SectionData> data = new ArrayList<>();
data.add(buildBasicProperties(account));
Map<Host, List<OsAccountAttribute>> hostDataMap = results.getAttributeMap();
if (hostDataMap != null && !hostDataMap.isEmpty()) {
hostDataMap.forEach((K, V) -> data.add(buildHostData(K, V)));
}
OsAccountRealm realm = results.getRealm();
if (realm != null) {
data.add(buildRealmProperties(realm));
}
// Removing the instance section for now. Leaving code here for
// future use.
// Map<Host, DataSource> instanceMap = results.getDataSourceMap();
// if (!instanceMap.isEmpty()) {
// SectionData instanceSection = new SectionData("Instances");
// instanceMap.forEach((K, V) -> instanceSection.addData(K.getName(), V.getName()));
//
// data.add(instanceSection);
// }
addDataComponents(data);
revalidate();
repaint();
}
}
}
/**
* Helper class for PanelDataFetcher that wraps the returned data needed for
* the panel.
*/
private final class WorkerResults {
private final Map<Host, List<OsAccountAttribute>> attributeMap;
private final Map<Host, DataSource> instanceMap;
private final OsAccountRealm realm;
/**
* Construct a new WorkerResult object.
*
* @param attributeMap Maps the OsAccountAttributes to the host they
* belong with.
* @param instanceMap A map of data to display OsAccount instance
* information.
*/
WorkerResults(Map<Host, List<OsAccountAttribute>> attributeMap, Map<Host, DataSource> instanceMap, OsAccountRealm realm) {
this.attributeMap = attributeMap;
this.instanceMap = instanceMap;
this.realm = realm;
}
/**
* Returns a map of OsAccountAttributes that belong to a specific Host.
* There maybe a null key in the map which represents properties that
* are not host specific.
*
* @return OsAccountAttribute map.
*/
Map<Host, List<OsAccountAttribute>> getAttributeMap() {
return attributeMap;
}
/**
* A map of the instance data for the OsAccount.
*
* @return
*/
Map<Host, DataSource> getDataSourceMap() {
return instanceMap;
}
OsAccountRealm getRealm() {
return realm;
}
}
}

View File

@ -11,14 +11,20 @@
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,44,0,0,1,-112"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
<SubComponents>
<Container class="javax.swing.JScrollPane" name="mainScrollPane">
<Properties>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[200, 0]"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
<BorderConstraints direction="Center"/>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="-1" gridY="-1" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="1.0" weightY="1.0"/>
</Constraint>
</Constraints>

View File

@ -18,7 +18,10 @@
*/
package org.sleuthkit.autopsy.contentviewers.osaccount;
import java.awt.BorderLayout;
import java.awt.Component;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Optional;
import java.util.logging.Level;
import org.openide.nodes.Node;
@ -34,7 +37,7 @@ import org.sleuthkit.datamodel.TskCoreException;
/**
* DataContentViewer for OsAccounts.
*/
@ServiceProvider(service = DataContentViewer.class, position = 12)
@ServiceProvider(service = DataContentViewer.class, position = 7)
public class OsAccountViewer extends javax.swing.JPanel implements DataContentViewer {
private static final long serialVersionUID = 1L;
@ -53,26 +56,29 @@ public class OsAccountViewer extends javax.swing.JPanel implements DataContentVi
@Override
public void setNode(Node node) {
OsAccount osAccount = null;
Long osAccountId = null;
try {
osAccount = node.getLookup().lookup(OsAccount.class);
if (osAccount == null) {
Optional<OsAccount> optional;
AbstractFile file = node.getLookup().lookup(AbstractFile.class);
if (file != null) {
optional = file.getOsAccount();
if (optional.isPresent()) {
osAccount = optional.get();
}
OsAccount osAccount = node.getLookup().lookup(OsAccount.class);
if (osAccount != null) {
dataPanel.setOsAccount(osAccount);
return;
}
Optional<Long> optional;
AbstractFile file = node.getLookup().lookup(AbstractFile.class);
if (file != null) {
optional = file.getOsAccountObjectId();
if (optional.isPresent()) {
osAccountId = optional.get();
}
}
if (osAccount == null) {
if (osAccountId == null) {
DataArtifact dataArtifact = node.getLookup().lookup(DataArtifact.class);
if (dataArtifact != null) {
Optional<OsAccount> optional = dataArtifact.getOsAccount();
optional = dataArtifact.getOsAccountObjectId();
if (optional.isPresent()) {
osAccount = optional.get();
osAccountId = optional.get();
}
}
@ -81,13 +87,13 @@ public class OsAccountViewer extends javax.swing.JPanel implements DataContentVi
logger.log(Level.SEVERE, String.format("Failed to get OsAccount for node %s", node.getDisplayName()), ex);
}
if (osAccount != null) {
dataPanel.setOsAccount(osAccount);
if (osAccountId != null) {
dataPanel.setOsAccountId(osAccountId);
}
}
@Messages({
"OsAccountViewer_title=Os Account"
"OsAccountViewer_title=OS Account"
})
@Override
public String getTitle() {
@ -95,7 +101,7 @@ public class OsAccountViewer extends javax.swing.JPanel implements DataContentVi
}
@Messages({
"OsAccountViewer_tooltip=Viewer for OS accounts related to the selected node."
"OsAccountViewer_tooltip=Viewer for Operating System accounts related to the selected node."
})
@Override
public String getToolTip() {
@ -125,8 +131,8 @@ public class OsAccountViewer extends javax.swing.JPanel implements DataContentVi
try {
return osAccount != null
|| (file != null && file.getOsAccount().isPresent())
|| (dataArtifact != null && dataArtifact.getOsAccount().isPresent());
|| (file != null && file.getOsAccountObjectId().isPresent())
|| (dataArtifact != null && dataArtifact.getOsAccountObjectId().isPresent());
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Failed to determine if node %s is Supported for OsAccountViewer", node.getDisplayName()), ex);
return false;
@ -150,8 +156,14 @@ public class OsAccountViewer extends javax.swing.JPanel implements DataContentVi
mainScrollPane = new javax.swing.JScrollPane();
setLayout(new java.awt.BorderLayout());
add(mainScrollPane, java.awt.BorderLayout.CENTER);
setLayout(new java.awt.GridBagLayout());
mainScrollPane.setPreferredSize(new java.awt.Dimension(200, 0));
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.weighty = 1.0;
add(mainScrollPane, gridBagConstraints);
}// </editor-fold>//GEN-END:initComponents

View File

@ -51,10 +51,10 @@ final class SectionData implements Iterable<SectionData.RowData<String, String>>
}
/**
* Add a new property name\property value pair.
* Add a new property name/property value pair.
*
* @param key The property display name.
* @param value The property value.
* @param properytName The property display name.
* @param propertyValue The property value.
*/
void addData(String properytName, String propertyValue) {
data.add(new RowData<>(properytName, propertyValue));

View File

@ -27,3 +27,7 @@ ServicesMonitor.remoteKeywordSearch.displayName.text=Multi-user keyword search s
ServicesMonitor.messaging.displayName.text=Messaging service
ServicesMonitor.databaseConnectionInfo.error.msg=Error accessing case database connection info
ServicesMonitor.messagingService.connErr.text=Error accessing messaging service connection info
Actions/Case=Case
Menu/Case=Case
Toolbars/Case=Case
Menu/Case/OpenRecentCase=Open Recent Case

View File

@ -31,3 +31,7 @@ ServicesMonitor.remoteKeywordSearch.displayName.text=Multi-user keyword search s
ServicesMonitor.messaging.displayName.text=Messaging service
ServicesMonitor.databaseConnectionInfo.error.msg=Error accessing case database connection info
ServicesMonitor.messagingService.connErr.text=Error accessing messaging service connection info
Actions/Case=Case
Menu/Case=Case
Toolbars/Case=Case
Menu/Case/OpenRecentCase=Open Recent Case

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2018 Basis Technology Corp.
* Copyright 2011-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -50,6 +50,7 @@ import org.sleuthkit.autopsy.coreutils.ModuleSettings;
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector;
import org.sleuthkit.autopsy.python.JythonModuleLoader;
import org.sleuthkit.autopsy.texttranslation.TextTranslationService;
/**
* Wrapper over Installers in packages in Core module. This is the main
@ -369,6 +370,7 @@ public class Installer extends ModuleInstall {
}
logger.log(Level.INFO, "Autopsy Core restore completed"); //NON-NLS
preloadJython();
preloadTranslationServices();
}
/**
@ -376,7 +378,7 @@ public class Installer extends ModuleInstall {
* because we encountered issues related to file locking when initialization
* was performed closer to where the bindings are used. See JIRA-6528.
*/
private void initializeSevenZip() {
private static void initializeSevenZip() {
try {
SevenZip.initSevenZipFromPlatformJAR();
logger.log(Level.INFO, "7zip-java bindings loaded"); //NON-NLS
@ -388,7 +390,7 @@ public class Installer extends ModuleInstall {
/**
* Runs an initial load of the Jython modules to speed up subsequent loads.
*/
private void preloadJython() {
private static void preloadJython() {
Runnable loader = () -> {
try {
JythonModuleLoader.getIngestModuleFactories();
@ -403,6 +405,22 @@ public class Installer extends ModuleInstall {
new Thread(loader).start();
}
/**
* Runs an initial load of the translation services to speed up subsequent loads.
*/
private static void preloadTranslationServices() {
Runnable loader = () -> {
try {
TextTranslationService.getInstance();
} catch (Exception ex) {
// This is a firewall exception to ensure that any possible exception caused
// by this initial load of the translation modules are caught and logged.
logger.log(Level.SEVERE, "There was an error while doing an initial load of translation services.", ex);
}
};
new Thread(loader).start();
}
@Override
public void validate() throws IllegalStateException {
super.validate();

View File

@ -18,12 +18,14 @@
*/
package org.sleuthkit.autopsy.core;
import java.io.File;
import java.nio.file.Paths;
import org.sleuthkit.autopsy.coreutils.TextConverter;
import java.util.prefs.BackingStoreException;
import org.sleuthkit.autopsy.events.MessageServiceConnectionInfo;
import java.util.prefs.PreferenceChangeListener;
import java.util.prefs.Preferences;
import org.apache.commons.lang3.StringUtils;
import org.openide.util.NbPreferences;
import org.python.icu.util.TimeZone;
import org.sleuthkit.autopsy.machinesettings.UserMachinePreferences;
@ -93,6 +95,7 @@ public final class UserPreferences {
private static final String GEO_OSM_SERVER_ADDRESS = "GeolocationOsmServerAddress";
private static final String GEO_MBTILES_FILE_PATH = "GeolcoationMBTilesFilePath";
private static final String HEALTH_MONITOR_REPORT_PATH = "HealthMonitorReportPath";
private static final String TEMP_FOLDER = "Temp";
// Prevent instantiation.
private UserPreferences() {
@ -536,10 +539,11 @@ public final class UserPreferences {
}
/**
* Get the maximum JVM heap size (in MB) for the embedded Solr server. The returned value
* depends on the platform (64bit vs 32bit).
* Get the maximum JVM heap size (in MB) for the embedded Solr server. The
* returned value depends on the platform (64bit vs 32bit).
*
* @return Saved value or default (2 GB for 64bit platforms, 512MB for 32bit)
* @return Saved value or default (2 GB for 64bit platforms, 512MB for
* 32bit)
*/
public static int getMaxSolrVMSize() {
if (PlatformUtil.is64BitJVM()) {
@ -605,8 +609,9 @@ public final class UserPreferences {
}
/**
* Retrieves the Geolocation tile option. If not found, the value will
* Retrieves the Geolocation tile option. If not found, the value will
* default to 0.
*
* @return
*/
public static int getGeolocationtTileOption() {
@ -633,7 +638,8 @@ public final class UserPreferences {
}
/**
* Sets the address of geolocation window user defined OSM server data source.
* Sets the address of geolocation window user defined OSM server data
* source.
*
* @param address
*/
@ -662,20 +668,51 @@ public final class UserPreferences {
/**
* Retrieves the path for the Geolocation MBTiles data source file.
*
* @return Absolute path to MBTiles file or empty string if none was found.
* @return Absolute path to MBTiles file or empty string if none was found.
*/
public static String getGeolocationMBTilesFilePath() {
return preferences.get(GEO_MBTILES_FILE_PATH, "");
}
/**
* Retrieves the root application temp directory.
* @return A subdirectory of java.io.tmpdir.
*/
private static File getSystemTempDirFile() {
return Paths.get(System.getProperty("java.io.tmpdir"), getAppName(), TEMP_FOLDER).toFile();
}
/**
* Retrieves the application temp directory and ensures the directory
* exists.
*
* @return The absolute path to the application temp directory.
*/
public static String getAppTempDirectory() {
return Paths.get(UserMachinePreferences.getBaseTempDirectory(), getAppName())
.toAbsolutePath().toString();
// NOTE: If this code changes, Case.getTempDirectory() should likely be checked
// as well. See JIRA 7505 for more information.
File appTempDir = null;
switch (UserMachinePreferences.getTempDirChoice()) {
case CUSTOM:
String customDirectory = UserMachinePreferences.getCustomTempDirectory();
appTempDir = (StringUtils.isBlank(customDirectory))
? null
: Paths.get(customDirectory, getAppName(), TEMP_FOLDER).toFile();
break;
case SYSTEM:
default:
// at this level, if the case directory is specified for a temp
// directory, return the system temp directory instead.
appTempDir = getSystemTempDirFile();
break;
}
appTempDir = appTempDir == null ? getSystemTempDirFile() : appTempDir;
if (!appTempDir.exists()) {
appTempDir.mkdirs();
}
return appTempDir.getAbsolutePath();
}
/**
@ -690,7 +727,8 @@ public final class UserPreferences {
/**
* Gets the last used health monitor report path.
*
* @return Last used health monitor report path. Empty string if no value has been recorded.
* @return Last used health monitor report path. Empty string if no value
* has been recorded.
*/
public static String getHealthMonitorReportPath() {
return preferences.get(HEALTH_MONITOR_REPORT_PATH, "");

View File

@ -40,6 +40,7 @@
====================================================== -->
<folder name="Actions">
<folder name="Case">
<attr name="SystemFileSystem.localizingBundle" stringvalue="org.sleuthkit.autopsy.core.Bundle"/>
<file name="org-sleuthkit-autopsy-casemodule-AddImageAction.instance"/>
<file name="org-sleuthkit-autopsy-casemodule-CaseCloseAction.instance"/>
<file name="org-sleuthkit-autopsy-casemodule-CaseNewAction.instance">
@ -140,13 +141,14 @@
<file name="Edit_hidden"/>
<file name="File_hidden"/>
<folder name="Case">
<attr name="SystemFileSystem.localizingBundle" stringvalue="org.sleuthkit.autopsy.core.Bundle"/>
<file name="org-sleuthkit-autopsy-casemodule-CaseNewAction.shadow">
<attr name="originalFile" stringvalue="Actions/Case/org-sleuthkit-autopsy-casemodule-CaseNewAction.instance"/>
<attr name="position" intvalue="100"/>
</file>
<folder name="Open Recent Case">
<folder name="OpenRecentCase">
<attr name="SystemFileSystem.localizingBundle" stringvalue="org.sleuthkit.autopsy.core.Bundle"/>
<attr name="position" intvalue="101"/>
<attr name="SystemFileSystem.localizingBundle" stringvalue="org.sleuthkit.autopsy.casemodule.Bundle"/>
<file name="org-sleuthkit-autopsy-casemodule-RecentCasesAction.shadow">
<attr name="originalFile" stringvalue="Actions/Case/org-sleuthkit-autopsy-casemodule-RecentCases.instance"/>
</file>
@ -248,18 +250,10 @@
<file name="org-netbeans-core-windows-actions-ResetWindowsAction.shadow_hidden"/>
<file name="testAction.shadow_hidden"/>
<file name="org-netbeans-core-io-ui-IOWindowAction.shadow_hidden"/>
<file name="org-sleuthkit-autopsy-menuactions-DataResultMenu-separatoBefore.instance">
<attr name="instanceClass" stringvalue="javax.swing.JSeparator"/>
<attr name="position" intvalue="75"/>
</file>
<file name="org-sleuthkit-autopsy-menuactions-DataResultMenu.instance">
<attr name="originalFile" stringvalue="Actions/Tools/org-sleuthkit-autopsy-menuactions-DataResultMenu.instance"/>
<attr name="position" intvalue="100"/>
</file>
<file name="org-sleuthkit-autopsy-menuactions-DataResultMenu-separatoAfter.instance">
<attr name="instanceClass" stringvalue="javax.swing.JSeparator"/>
<attr name="position" intvalue="125"/>
</file>
<file name="org-sleuthkit-autopsy-menuactions-DataContentMenu.instance">
<attr name="originalFile" stringvalue="Actions/Tools/org-sleuthkit-autopsy-menuactions-DataContentMenu.instance"/>
<attr name="position" intvalue="150"/>
@ -281,7 +275,6 @@
<folder name="Help">
<file name="org-netbeans-core-actions-AboutAction.shadow_hidden"/>
<file name="org-netbeans-modules-autoupdate-ui-actions-CheckForUpdatesAction.shadow_hidden"/>
<attr name="master-help.xml/org-sleuthkit-autopsy-corecomponents-CustomAboutAction.shadow" boolvalue="true"/>
</folder>
</folder>
@ -378,6 +371,7 @@
<file name="UndoRedo_hidden"/>
<file name="File_hidden"/>
<folder name="Case">
<attr name="SystemFileSystem.localizingBundle" stringvalue="org.sleuthkit.autopsy.core.Bundle"/>
<attr name="position" intvalue="90"/>
<!--<file name="org-sleuthkit-autopsy-casemodule-AddImageAction.instance">
<attr name="delegate" newvalue="org.sleuthkit.autopsy.casemodule.AddImageAction"/>

View File

@ -27,6 +27,7 @@ import org.openide.windows.TopComponent;
import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.corecomponents.DataContentTopComponent;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.directorytree.DirectoryTreeTopComponent;
/**
* Responsible for opening and closing the core windows when a case is opened
@ -49,7 +50,8 @@ final public class CoreComponentControl {
* ({@link DataExplorer}, {@link DataResult}, and {@link DataContent})
*/
public static void openCoreWindows() {
// TODO: there has to be a better way to do this.
// preload UI components (JIRA-7345). This only takes place the first time Autopsy opens a case.
DirectoryTreeTopComponent dtc = DirectoryTreeTopComponent.findInstance();
// find the data explorer top components
Collection<? extends DataExplorer> dataExplorers = Lookup.getDefault().lookupAll(DataExplorer.class);

View File

@ -24,15 +24,14 @@ import org.openide.DialogDescriptor;
import org.openide.DialogDisplayer;
import org.openide.awt.ActionID;
import org.openide.awt.ActionReference;
import org.openide.awt.ActionRegistration;
import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
/**
* Action to open custom implementation of the "About" window from the Help
* menu.
*/
@ActionID(id = "org.sleuthkit.autopsy.corecomponents.AboutWindowAction", category = "Help")
@ActionRegistration(displayName = "#CTL_CustomAboutAction", iconInMenu = true, lazy = false)
@ActionReference(path = "Menu/Help", position = 3000, separatorBefore = 2999)
public class AboutWindowAction extends AboutAction {

View File

@ -8,6 +8,8 @@
</Component>
<Component class="javax.swing.ButtonGroup" name="logoSourceButtonGroup">
</Component>
<Component class="javax.swing.ButtonGroup" name="tempDirChoiceGroup">
</Component>
</NonVisualComponents>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
@ -24,12 +26,12 @@
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="jScrollPane1" alignment="0" pref="648" max="32767" attributes="0"/>
<Component id="jScrollPane1" alignment="0" pref="860" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="jScrollPane1" alignment="0" pref="382" max="32767" attributes="0"/>
<Component id="jScrollPane1" alignment="0" pref="620" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
</Layout>
@ -242,7 +244,7 @@
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="23" max="-2" attributes="0"/>
<Component id="memFieldValidationLabel" min="-2" pref="478" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<EmptySpace pref="12" max="32767" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="18" max="-2" attributes="0"/>
@ -253,7 +255,24 @@
</Group>
</Group>
</Group>
<Component id="restartNecessaryWarning" alignment="0" min="-2" pref="615" max="-2" attributes="0"/>
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="restartNecessaryWarning" alignment="0" min="-2" pref="615" max="-2" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Component id="heapFileLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="heapFieldValidationLabel" min="-2" pref="478" max="-2" attributes="0"/>
<Group type="102" attributes="0">
<Component id="heapDumpFileField" min="-2" pref="415" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="heapDumpBrowseButton" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</Group>
</Group>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
</Group>
</Group>
</Group>
@ -291,7 +310,15 @@
<Component id="maxLogFileCount" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="logFileCount" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="heapFileLabel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="heapDumpFileField" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="heapDumpBrowseButton" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Component id="heapFieldValidationLabel" min="-2" pref="16" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="restartNecessaryWarning" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
@ -412,13 +439,47 @@
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="heapFileLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="AutopsyOptionsPanel.heapFileLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="heapDumpFileField">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="AutopsyOptionsPanel.heapDumpFileField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="heapDumpBrowseButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="AutopsyOptionsPanel.heapDumpBrowseButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="heapDumpBrowseButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="heapFieldValidationLabel">
<Properties>
<Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="0" green="0" red="ff" type="rgb"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="AutopsyOptionsPanel.heapFieldValidationLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="tempDirectoryPanel">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.TitledBorderInfo">
<TitledBorder title="Temp Directory">
<TitledBorder title="Root Temp Directory">
<ResourceString PropertyName="titleX" bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="AutopsyOptionsPanel.tempDirectoryPanel.border.title" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</TitledBorder>
</Border>
@ -444,37 +505,53 @@
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="tempDirectoryWarningLabel" min="-2" pref="615" max="-2" attributes="0"/>
<Group type="102" attributes="0">
<Component id="tempDirectoryField" min="-2" pref="367" max="-2" attributes="0"/>
<Component id="tempLocalRadio" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="tempCaseRadio" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="tempDirectoryWarningLabel" alignment="0" min="-2" pref="615" max="-2" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Component id="tempCustomRadio" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="tempDirectoryBrowseButton" min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="tempOnCustomNoPath" min="-2" max="-2" attributes="0"/>
<Group type="102" attributes="0">
<Component id="tempCustomField" min="-2" pref="459" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="tempDirectoryBrowseButton" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</Group>
</Group>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
<EmptySpace pref="164" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="tempLocalRadio" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="tempCaseRadio" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="tempDirectoryField" alignment="3" max="32767" attributes="0"/>
<Component id="tempCustomRadio" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="tempCustomField" alignment="3" max="32767" attributes="0"/>
<Component id="tempDirectoryBrowseButton" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="separate" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="tempOnCustomNoPath" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="32767" attributes="0"/>
<Component id="tempDirectoryWarningLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="14" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JTextField" name="tempDirectoryField">
<Component class="javax.swing.JTextField" name="tempCustomField">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="AutopsyOptionsPanel.tempDirectoryField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="AutopsyOptionsPanel.tempCustomField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
@ -498,6 +575,55 @@
</Property>
</Properties>
</Component>
<Component class="javax.swing.JRadioButton" name="tempLocalRadio">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="tempDirChoiceGroup"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="AutopsyOptionsPanel.tempLocalRadio.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="tempLocalRadioActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JRadioButton" name="tempCaseRadio">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="tempDirChoiceGroup"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="AutopsyOptionsPanel.tempCaseRadio.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="tempCaseRadioActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JRadioButton" name="tempCustomRadio">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="tempDirChoiceGroup"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="AutopsyOptionsPanel.tempCustomRadio.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="tempCustomRadioActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="tempOnCustomNoPath">
<Properties>
<Property name="foreground" type="java.awt.Color" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="java.awt.Color.RED" type="code"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="AutopsyOptionsPanel.tempOnCustomNoPath.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="rdpPanel">

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2019 Basis Technology Corp.
* Copyright 2011-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -29,8 +29,11 @@ import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.StringJoiner;
import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFileChooser;
@ -38,6 +41,9 @@ import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.tools.ant.types.Commandline;
import org.netbeans.spi.options.OptionsPanelController;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.Logger;
@ -47,9 +53,9 @@ import org.sleuthkit.autopsy.casemodule.GeneralFilter;
import org.sleuthkit.autopsy.machinesettings.UserMachinePreferences;
import org.sleuthkit.autopsy.machinesettings.UserMachinePreferencesException;
import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
import org.sleuthkit.autopsy.coreutils.Version;
import org.sleuthkit.autopsy.machinesettings.UserMachinePreferences.TempDirChoice;
import org.sleuthkit.autopsy.report.ReportBranding;
/**
@ -75,9 +81,9 @@ import org.sleuthkit.autopsy.report.ReportBranding;
final class AutopsyOptionsPanel extends javax.swing.JPanel {
private static final long serialVersionUID = 1L;
private static final String DEFAULT_HEAP_DUMP_FILE_FIELD = "";
private final JFileChooser logoFileChooser;
private final JFileChooser tempDirChooser;
private final TextFieldListener textFieldListener;
private static final String ETC_FOLDER_NAME = "etc";
private static final String CONFIG_FILE_EXTENSION = ".conf";
private static final long ONE_BILLION = 1000000000L; //used to roughly convert system memory from bytes to gigabytes
@ -87,6 +93,9 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
private static final Logger logger = Logger.getLogger(AutopsyOptionsPanel.class.getName());
private String initialMemValue = Long.toString(Runtime.getRuntime().maxMemory() / ONE_BILLION);
private final ReportBranding reportBranding;
private final JFileChooser heapFileChooser;
/**
* Instantiate the Autopsy options panel.
*/
@ -102,12 +111,18 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
tempDirChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
tempDirChooser.setMultiSelectionEnabled(false);
if (!PlatformUtil.is64BitJVM() || Version.getBuildType() == Version.Type.DEVELOPMENT) {
heapFileChooser = new JFileChooser();
heapFileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
heapFileChooser.setMultiSelectionEnabled(false);
if (!isJVMHeapSettingsCapable()) {
//32 bit JVM has a max heap size of 1.4 gb to 4 gb depending on OS
//So disabling the setting of heap size when the JVM is not 64 bit
//Is the safest course of action
//And the file won't exist in the install folder when running through netbeans
memField.setEnabled(false);
heapDumpFileField.setEnabled(false);
heapDumpBrowseButton.setEnabled(false);
solrMaxHeapSpinner.setEnabled(false);
}
systemMemoryTotal.setText(Long.toString(getSystemMemoryInGB()));
@ -116,10 +131,20 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
solrMaxHeapSpinner.setModel(new javax.swing.SpinnerNumberModel(UserPreferences.getMaxSolrVMSize(),
JVM_MEMORY_STEP_SIZE_MB, ((int) getSystemMemoryInGB()) * MEGA_IN_GIGA, JVM_MEMORY_STEP_SIZE_MB));
textFieldListener = new TextFieldListener();
agencyLogoPathField.getDocument().addDocumentListener(textFieldListener);
tempDirectoryField.getDocument().addDocumentListener(textFieldListener);
agencyLogoPathField.getDocument().addDocumentListener(new TextFieldListener(null));
heapDumpFileField.getDocument().addDocumentListener(new TextFieldListener(this::isHeapPathValid));
tempCustomField.getDocument().addDocumentListener(new TextFieldListener(this::evaluateTempDirState));
logFileCount.setText(String.valueOf(UserPreferences.getLogFileCount()));
reportBranding = new ReportBranding();
}
/**
* Returns whether or not the jvm runtime heap settings can effectively be changed.
* @return Whether or not the jvm runtime heap settings can effectively be changed.
*/
private static boolean isJVMHeapSettingsCapable() {
return PlatformUtil.is64BitJVM() && Version.getBuildType() != Version.Type.DEVELOPMENT;
}
/**
@ -136,18 +161,23 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
/**
* Gets the currently saved max java heap space in gigabytes.
* @param The conf file memory value (i.e. 4G).
*
* @return @throws IOException when unable to get a valid setting
* @return The value in gigabytes.
* @throws IOException when unable to get a valid setting
*/
private long getCurrentJvmMaxMemoryInGB() throws IOException {
String currentXmx = getCurrentXmxValue();
private long getCurrentJvmMaxMemoryInGB(String confFileMemValue) throws IOException {
char units = '-';
Long value = 0L;
if (currentXmx.length() > 1) {
units = currentXmx.charAt(currentXmx.length() - 1);
value = Long.parseLong(currentXmx.substring(0, currentXmx.length() - 1));
if (confFileMemValue.length() > 1) {
units = confFileMemValue.charAt(confFileMemValue.length() - 1);
try {
value = Long.parseLong(confFileMemValue.substring(0, confFileMemValue.length() - 1));
} catch (NumberFormatException ex) {
throw new IOException("Unable to properly parse memory number.", ex);
}
} else {
throw new IOException("No memory setting found in String: " + currentXmx);
throw new IOException("No memory setting found in String: " + confFileMemValue);
}
//some older .conf files might have the units as megabytes instead of gigabytes
switch (units) {
@ -162,33 +192,6 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
}
}
/*
* The value currently saved in the conf file as the max java heap space
* available to this application. Form will be an integer followed by a
* character indicating units. Helper method for
* getCurrentJvmMaxMemoryInGB()
*
* @return the saved value for the max java heap space
*
* @throws IOException if the conf file does not exist in either the user
* directory or the install directory
*/
private String getCurrentXmxValue() throws IOException {
String[] settings;
String currentSetting = "";
File userConfFile = getUserFolderConfFile();
if (!userConfFile.exists()) {
settings = getDefaultsFromFileContents(readConfFile(getInstallFolderConfFile()));
} else {
settings = getDefaultsFromFileContents(readConfFile(userConfFile));
}
for (String option : settings) {
if (option.startsWith("-J-Xmx")) {
currentSetting = option.replace("-J-Xmx", "").trim();
}
}
return currentSetting;
}
/**
* Get the conf file from the install directory which stores the default
@ -226,6 +229,60 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
return new File(userEtcFolder, confFileName);
}
private static final String JVM_SETTINGS_REGEX_PARAM = "options";
private static final String JVM_SETTINGS_REGEX_STR = "^\\s*default_options\\s*=\\s*\"?(?<" + JVM_SETTINGS_REGEX_PARAM + ">.+?)\"?\\s*$";
private static final Pattern JVM_SETTINGS_REGEX = Pattern.compile(JVM_SETTINGS_REGEX_STR);
private static final String XMX_REGEX_PARAM = "mem";
private static final String XMX_REGEX_STR = "^\\s*\\-J\\-Xmx(?<" + XMX_REGEX_PARAM + ">.+?)\\s*$";
private static final Pattern XMX_REGEX = Pattern.compile(XMX_REGEX_STR);
private static final String HEAP_DUMP_REGEX_PARAM = "path";
private static final String HEAP_DUMP_REGEX_STR = "^\\s*\\-J\\-XX:HeapDumpPath=(\\\")?\\s*(?<" + HEAP_DUMP_REGEX_PARAM + ">.+?)\\s*(\\\")?$";
private static final Pattern HEAP_DUMP_REGEX = Pattern.compile(HEAP_DUMP_REGEX_STR);
/**
* Parse the autopsy conf file line. If the line is the default_options line,
* then replaces with current memory and heap path value. Otherwise, returns
* the line provided as parameter.
*
* @param line The line.
* @param memText The text to add as an argument to be used as memory with -J-Xmx.
* @param heapText The text to add as an argument to be used as the heap dump path with
* -J-XX:HeapDumpPath.
* @return The line modified to contain memory and heap arguments.
*/
private static String updateConfLine(String line, String memText, String heapText) {
Matcher match = JVM_SETTINGS_REGEX.matcher(line);
if (match.find()) {
// split on command line arguments
String[] parsedArgs = Commandline.translateCommandline(match.group(JVM_SETTINGS_REGEX_PARAM));
String memString = "-J-Xmx" + memText.replaceAll("[^\\d]", "") + "g";
// only add in heap path argument if a heap path is specified
String heapString = null;
if (StringUtils.isNotBlank(heapText)) {
while (heapText.endsWith("\\") && heapText.length() > 0) {
heapText = heapText.substring(0, heapText.length() - 1);
}
heapString = String.format("-J-XX:HeapDumpPath=\"%s\"", heapText);
}
Stream<String> argsNoMemHeap = Stream.of(parsedArgs)
// remove saved version of memory and heap dump path
.filter(s -> !s.matches(XMX_REGEX_STR) && !s.matches(HEAP_DUMP_REGEX_STR));
String newArgs = Stream.concat(argsNoMemHeap, Stream.of(memString, heapString))
.filter(s -> s != null)
.collect(Collectors.joining(" "));
return String.format("default_options=\"%s\"", newArgs);
};
return line;
}
/**
* Take the conf file in the install directory and save a copy of it to the
* user directory. The copy will be modified to include the current memory
@ -235,25 +292,124 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
* install folders conf file
*/
private void writeEtcConfFile() throws IOException {
StringBuilder content = new StringBuilder();
List<String> confFile = readConfFile(getInstallFolderConfFile());
for (String line : confFile) {
if (line.contains("-J-Xmx")) {
String[] splitLine = line.split(" ");
StringJoiner modifiedLine = new StringJoiner(" ");
for (String piece : splitLine) {
if (piece.contains("-J-Xmx")) {
piece = "-J-Xmx" + memField.getText() + "g";
}
modifiedLine.add(piece);
}
content.append(modifiedLine.toString());
} else {
content.append(line);
}
content.append("\n");
String fileText = readConfFile(getInstallFolderConfFile()).stream()
.map((line) -> updateConfLine(line, memField.getText(), heapDumpFileField.getText()))
.collect(Collectors.joining("\n"));
FileUtils.writeStringToFile(getUserFolderConfFile(), fileText, "UTF-8");
}
/**
* Values for configuration located in the /etc/\*.conf file.
*/
private static class ConfValues {
private final String XmxVal;
private final String heapDumpPath;
/**
* Main constructor.
* @param XmxVal The heap memory size.
* @param heapDumpPath The heap dump path.
*/
ConfValues(String XmxVal, String heapDumpPath) {
this.XmxVal = XmxVal;
this.heapDumpPath = heapDumpPath;
}
Files.write(getUserFolderConfFile().toPath(), content.toString().getBytes());
/**
* Returns the heap memory value specified in the conf file.
* @return The heap memory value specified in the conf file.
*/
String getXmxVal() {
return XmxVal;
}
/**
* Returns path to the heap dump specified in the conf file.
* @return Path to the heap dump specified in the conf file.
*/
String getHeapDumpPath() {
return heapDumpPath;
}
}
/**
* Retrieve the /etc/\*.conf file values pertinent to settings.
* @return The conf file values.
* @throws IOException
*/
private ConfValues getEtcConfValues() throws IOException {
File userConfFile = getUserFolderConfFile();
String[] args = userConfFile.exists() ?
getDefaultsFromFileContents(readConfFile(userConfFile)) :
getDefaultsFromFileContents(readConfFile(getInstallFolderConfFile()));
String heapFile = "";
String memSize = "";
for (String arg : args) {
Matcher memMatch = XMX_REGEX.matcher(arg);
if (memMatch.find()) {
memSize = memMatch.group(XMX_REGEX_PARAM);
continue;
}
Matcher heapFileMatch = HEAP_DUMP_REGEX.matcher(arg);
if (heapFileMatch.find()) {
heapFile = heapFileMatch.group(HEAP_DUMP_REGEX_PARAM);
continue;
}
}
return new ConfValues(memSize, heapFile);
}
/**
* Checks current heap path value to see if it is valid, and displays an error message if invalid.
* @return True if the heap path is valid.
*/
@Messages({
"AutopsyOptionsPanel_isHeapPathValid_selectValidDirectory=Please select an existing directory.",
"AutopsyOptionsPanel_isHeapPathValid_developerMode=Cannot change heap dump path while in developer mode.",
"AutopsyOptionsPanel_isHeapPathValid_not64BitMachine=Changing heap dump path settings only enabled for 64 bit version.",
"AutopsyOPtionsPanel_isHeapPathValid_illegalCharacters=Please select a path with no quotes."
})
private boolean isHeapPathValid() {
if (Version.getBuildType() == Version.Type.DEVELOPMENT) {
heapFieldValidationLabel.setVisible(true);
heapFieldValidationLabel.setText(Bundle.AutopsyOptionsPanel_isHeapPathValid_developerMode());
return true;
}
if (!PlatformUtil.is64BitJVM()) {
heapFieldValidationLabel.setVisible(true);
heapFieldValidationLabel.setText(Bundle.AutopsyOptionsPanel_isHeapPathValid_not64BitMachine());
return true;
}
//allow blank field as the default will be used
if (StringUtils.isNotBlank(heapDumpFileField.getText())) {
String heapText = heapDumpFileField.getText().trim();
if (heapText.contains("\"") || heapText.contains("'")) {
heapFieldValidationLabel.setVisible(true);
heapFieldValidationLabel.setText(Bundle.AutopsyOPtionsPanel_isHeapPathValid_illegalCharacters());
return false;
}
File curHeapFile = new File(heapText);
if (!curHeapFile.exists() || !curHeapFile.isDirectory()) {
heapFieldValidationLabel.setVisible(true);
heapFieldValidationLabel.setText(Bundle.AutopsyOptionsPanel_isHeapPathValid_selectValidDirectory());
return false;
}
}
heapFieldValidationLabel.setVisible(false);
heapFieldValidationLabel.setText("");
return true;
}
/**
@ -291,51 +447,96 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
* options is not present.
*/
private static String[] getDefaultsFromFileContents(List<String> list) {
Optional<String> defaultSettings = list.stream().filter(line -> line.startsWith("default_options=")).findFirst();
Optional<String> defaultSettings = list.stream()
.filter(line -> line.matches(JVM_SETTINGS_REGEX_STR))
.findFirst();
if (defaultSettings.isPresent()) {
return defaultSettings.get().replace("default_options=", "").replaceAll("\"", "").split(" ");
Matcher match = JVM_SETTINGS_REGEX.matcher(defaultSettings.get());
if (match.find()) {
return Commandline.translateCommandline(match.group(JVM_SETTINGS_REGEX_PARAM));
}
}
return new String[]{};
}
private void evaluateTempDirState() {
boolean caseOpen = Case.isCaseOpen();
boolean customSelected = tempCustomRadio.isSelected();
tempDirectoryBrowseButton.setEnabled(!caseOpen && customSelected);
tempCustomField.setEnabled(!caseOpen && customSelected);
tempOnCustomNoPath.setVisible(customSelected && StringUtils.isBlank(tempCustomField.getText()));
}
/**
* Load the saved user preferences.
*/
void load() {
String path = ModuleSettings.getConfigSetting(ReportBranding.MODULE_NAME, ReportBranding.AGENCY_LOGO_PATH_PROP);
String path = reportBranding.getAgencyLogoPath();
boolean useDefault = (path == null || path.isEmpty());
defaultLogoRB.setSelected(useDefault);
specifyLogoRB.setSelected(!useDefault);
agencyLogoPathField.setEnabled(!useDefault);
browseLogosButton.setEnabled(!useDefault);
tempDirectoryField.setText(UserMachinePreferences.getBaseTempDirectory());
tempCustomField.setText(UserMachinePreferences.getCustomTempDirectory());
switch (UserMachinePreferences.getTempDirChoice()) {
case CASE:
tempCaseRadio.setSelected(true);
break;
case CUSTOM:
tempCustomRadio.setSelected(true);
break;
default:
case SYSTEM:
tempLocalRadio.setSelected(true);
break;
}
evaluateTempDirState();
logFileCount.setText(String.valueOf(UserPreferences.getLogFileCount()));
solrMaxHeapSpinner.setValue(UserPreferences.getMaxSolrVMSize());
tempDirectoryField.setText(UserMachinePreferences.getBaseTempDirectory());
try {
updateAgencyLogo(path);
} catch (IOException ex) {
logger.log(Level.WARNING, "Error loading image from previously saved agency logo path", ex);
}
if (memField.isEnabled()) {
boolean confLoaded = false;
if (isJVMHeapSettingsCapable()) {
try {
initialMemValue = Long.toString(getCurrentJvmMaxMemoryInGB());
ConfValues confValues = getEtcConfValues();
heapDumpFileField.setText(confValues.getHeapDumpPath());
initialMemValue = Long.toString(getCurrentJvmMaxMemoryInGB(confValues.getXmxVal()));
confLoaded = true;
} catch (IOException ex) {
logger.log(Level.SEVERE, "Can't read current Jvm max memory setting from file", ex);
memField.setEnabled(false);
heapDumpFileField.setText(DEFAULT_HEAP_DUMP_FILE_FIELD);
}
memField.setText(initialMemValue);
}
heapDumpBrowseButton.setEnabled(confLoaded);
heapDumpFileField.setEnabled(confLoaded);
setTempDirEnabled();
valid(); //ensure the error messages are up to date
}
private void setTempDirEnabled() {
boolean enabled = !Case.isCaseOpen();
this.tempDirectoryBrowseButton.setEnabled(enabled);
this.tempDirectoryField.setEnabled(enabled);
this.tempCaseRadio.setEnabled(enabled);
this.tempCustomRadio.setEnabled(enabled);
this.tempLocalRadio.setEnabled(enabled);
this.tempDirectoryWarningLabel.setVisible(!enabled);
evaluateTempDirState();
}
/**
@ -367,12 +568,14 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
@Messages({
"AutopsyOptionsPanel_storeTempDir_onError_title=Error Saving Temporary Directory",
"# {0} - path",
"AutopsyOptionsPanel_storeTempDir_onError_description=There was an error creating the temporary directory on the filesystem at: {0}.",})
"AutopsyOptionsPanel_storeTempDir_onError_description=There was an error creating the temporary directory on the filesystem at: {0}.",
"AutopsyOptionsPanel_storeTempDir_onChoiceError_title=Error Saving Temporary Directory Choice",
"AutopsyOptionsPanel_storeTempDir_onChoiceError_description=There was an error updating temporary directory choice selection.",})
private void storeTempDir() {
String tempDirectoryPath = tempDirectoryField.getText();
if (!UserMachinePreferences.getBaseTempDirectory().equals(tempDirectoryPath)) {
String tempDirectoryPath = tempCustomField.getText();
if (!UserMachinePreferences.getCustomTempDirectory().equals(tempDirectoryPath)) {
try {
UserMachinePreferences.setBaseTempDirectory(tempDirectoryPath);
UserMachinePreferences.setCustomTempDirectory(tempDirectoryPath);
} catch (UserMachinePreferencesException ex) {
logger.log(Level.WARNING, "There was an error creating the temporary directory defined by the user: " + tempDirectoryPath, ex);
SwingUtilities.invokeLater(() -> {
@ -383,6 +586,29 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
});
}
}
TempDirChoice choice;
if (tempCaseRadio.isSelected()) {
choice = TempDirChoice.CASE;
} else if (tempCustomRadio.isSelected()) {
choice = TempDirChoice.CUSTOM;
} else {
choice = TempDirChoice.SYSTEM;
}
if (!choice.equals(UserMachinePreferences.getTempDirChoice())) {
try {
UserMachinePreferences.setTempDirChoice(choice);
} catch (UserMachinePreferencesException ex) {
logger.log(Level.WARNING, "There was an error updating choice to: " + choice.name(), ex);
SwingUtilities.invokeLater(() -> {
JOptionPane.showMessageDialog(this,
String.format("<html>%s</html>", Bundle.AutopsyOptionsPanel_storeTempDir_onChoiceError_description()),
Bundle.AutopsyOptionsPanel_storeTempDir_onChoiceError_title(),
JOptionPane.ERROR_MESSAGE);
});
}
}
}
/**
@ -395,13 +621,13 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
if (!agencyLogoPathField.getText().isEmpty()) {
File file = new File(agencyLogoPathField.getText());
if (file.exists()) {
ModuleSettings.setConfigSetting(ReportBranding.MODULE_NAME, ReportBranding.AGENCY_LOGO_PATH_PROP, agencyLogoPathField.getText());
reportBranding.setAgencyLogoPath(agencyLogoPathField.getText());
}
} else {
ModuleSettings.setConfigSetting(ReportBranding.MODULE_NAME, ReportBranding.AGENCY_LOGO_PATH_PROP, "");
reportBranding.setAgencyLogoPath("");
}
UserPreferences.setMaxSolrVMSize((int) solrMaxHeapSpinner.getValue());
if (memField.isEnabled()) { //if the field could of been changed we need to try and save it
if (isJVMHeapSettingsCapable()) { //if the field could of been changed we need to try and save it
try {
writeEtcConfFile();
} catch (IOException ex) {
@ -416,18 +642,12 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
* @return True if valid; false otherwise.
*/
boolean valid() {
boolean valid = true;
if (!isAgencyLogoPathValid()) {
valid = false;
}
if (!isMemFieldValid()) {
valid = false;
}
if (!isLogNumFieldValid()) {
valid = false;
}
boolean agencyValid = isAgencyLogoPathValid();
boolean memFieldValid = isMemFieldValid();
boolean logNumValid = isLogNumFieldValid();
boolean heapPathValid = isHeapPathValid();
return valid;
return agencyValid && memFieldValid && logNumValid && heapPathValid;
}
/**
@ -531,24 +751,44 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
/**
* Listens for registered text fields that have changed and fires a
* PropertyChangeEvent accordingly.
* PropertyChangeEvent accordingly as well as firing an optional additional listener.
*/
private class TextFieldListener implements DocumentListener {
private final Runnable onChange;
@Override
public void insertUpdate(DocumentEvent e) {
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
/**
* Main constructor.
* @param onChange Additional listener for change events.
*/
TextFieldListener(Runnable onChange) {
this.onChange = onChange;
}
@Override
public void removeUpdate(DocumentEvent e) {
private void baseOnChange() {
if (onChange != null) {
onChange.run();
}
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}
@Override
public void changedUpdate(DocumentEvent e) {
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
baseOnChange();
}
@Override
public void removeUpdate(DocumentEvent e) {
baseOnChange();
}
@Override
public void insertUpdate(DocumentEvent e) {
baseOnChange();
}
}
/**
@ -563,6 +803,7 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
fileSelectionButtonGroup = new javax.swing.ButtonGroup();
displayTimesButtonGroup = new javax.swing.ButtonGroup();
logoSourceButtonGroup = new javax.swing.ButtonGroup();
tempDirChoiceGroup = new javax.swing.ButtonGroup();
jScrollPane1 = new javax.swing.JScrollPane();
javax.swing.JPanel mainPanel = new javax.swing.JPanel();
logoPanel = new javax.swing.JPanel();
@ -588,10 +829,18 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
maxMemoryUnitsLabel2 = new javax.swing.JLabel();
solrMaxHeapSpinner = new javax.swing.JSpinner();
solrJVMHeapWarning = new javax.swing.JLabel();
heapFileLabel = new javax.swing.JLabel();
heapDumpFileField = new javax.swing.JTextField();
heapDumpBrowseButton = new javax.swing.JButton();
heapFieldValidationLabel = new javax.swing.JLabel();
tempDirectoryPanel = new javax.swing.JPanel();
tempDirectoryField = new javax.swing.JTextField();
tempCustomField = new javax.swing.JTextField();
tempDirectoryBrowseButton = new javax.swing.JButton();
tempDirectoryWarningLabel = new javax.swing.JLabel();
tempLocalRadio = new javax.swing.JRadioButton();
tempCaseRadio = new javax.swing.JRadioButton();
tempCustomRadio = new javax.swing.JRadioButton();
tempOnCustomNoPath = new javax.swing.JLabel();
rdpPanel = new javax.swing.JPanel();
javax.swing.JScrollPane sizingScrollPane = new javax.swing.JScrollPane();
javax.swing.JTextPane sizingTextPane = new javax.swing.JTextPane();
@ -734,6 +983,20 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
org.openide.awt.Mnemonics.setLocalizedText(solrJVMHeapWarning, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.solrJVMHeapWarning.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(heapFileLabel, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.heapFileLabel.text")); // NOI18N
heapDumpFileField.setText(org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.heapDumpFileField.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(heapDumpBrowseButton, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.heapDumpBrowseButton.text")); // NOI18N
heapDumpBrowseButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
heapDumpBrowseButtonActionPerformed(evt);
}
});
heapFieldValidationLabel.setForeground(new java.awt.Color(255, 0, 0));
org.openide.awt.Mnemonics.setLocalizedText(heapFieldValidationLabel, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.heapFieldValidationLabel.text")); // NOI18N
javax.swing.GroupLayout runtimePanelLayout = new javax.swing.GroupLayout(runtimePanel);
runtimePanel.setLayout(runtimePanelLayout);
runtimePanelLayout.setHorizontalGroup(
@ -762,14 +1025,26 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
.addGroup(runtimePanelLayout.createSequentialGroup()
.addGap(23, 23, 23)
.addComponent(memFieldValidationLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 478, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addContainerGap(12, Short.MAX_VALUE))
.addGroup(runtimePanelLayout.createSequentialGroup()
.addGap(18, 18, 18)
.addComponent(solrJVMHeapWarning, javax.swing.GroupLayout.PREFERRED_SIZE, 331, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(44, 44, 44)
.addComponent(logNumAlert)
.addContainerGap())))
.addComponent(restartNecessaryWarning, javax.swing.GroupLayout.PREFERRED_SIZE, 615, javax.swing.GroupLayout.PREFERRED_SIZE)))
.addGroup(runtimePanelLayout.createSequentialGroup()
.addGroup(runtimePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(restartNecessaryWarning, javax.swing.GroupLayout.PREFERRED_SIZE, 615, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGroup(runtimePanelLayout.createSequentialGroup()
.addComponent(heapFileLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(runtimePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(heapFieldValidationLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 478, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGroup(runtimePanelLayout.createSequentialGroup()
.addComponent(heapDumpFileField, javax.swing.GroupLayout.PREFERRED_SIZE, 415, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(heapDumpBrowseButton)))))
.addGap(0, 0, Short.MAX_VALUE))))
);
runtimePanelLayout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {maxLogFileCount, maxMemoryLabel, totalMemoryLabel});
@ -803,7 +1078,14 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
.addGroup(runtimePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(maxLogFileCount)
.addComponent(logFileCount, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(runtimePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(heapFileLabel)
.addComponent(heapDumpFileField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(heapDumpBrowseButton))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(heapFieldValidationLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(restartNecessaryWarning)
.addContainerGap())
);
@ -818,7 +1100,7 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
tempDirectoryPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.tempDirectoryPanel.border.title"))); // NOI18N
tempDirectoryPanel.setName(org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.tempDirectoryPanel.name")); // NOI18N
tempDirectoryField.setText(org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.tempDirectoryField.text")); // NOI18N
tempCustomField.setText(org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.tempCustomField.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(tempDirectoryBrowseButton, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.tempDirectoryBrowseButton.text")); // NOI18N
tempDirectoryBrowseButton.addActionListener(new java.awt.event.ActionListener() {
@ -830,6 +1112,33 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
tempDirectoryWarningLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/warning16.png"))); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(tempDirectoryWarningLabel, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.tempDirectoryWarningLabel.text")); // NOI18N
tempDirChoiceGroup.add(tempLocalRadio);
org.openide.awt.Mnemonics.setLocalizedText(tempLocalRadio, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.tempLocalRadio.text")); // NOI18N
tempLocalRadio.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
tempLocalRadioActionPerformed(evt);
}
});
tempDirChoiceGroup.add(tempCaseRadio);
org.openide.awt.Mnemonics.setLocalizedText(tempCaseRadio, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.tempCaseRadio.text")); // NOI18N
tempCaseRadio.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
tempCaseRadioActionPerformed(evt);
}
});
tempDirChoiceGroup.add(tempCustomRadio);
org.openide.awt.Mnemonics.setLocalizedText(tempCustomRadio, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.tempCustomRadio.text")); // NOI18N
tempCustomRadio.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
tempCustomRadioActionPerformed(evt);
}
});
tempOnCustomNoPath.setForeground(java.awt.Color.RED);
org.openide.awt.Mnemonics.setLocalizedText(tempOnCustomNoPath, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.tempOnCustomNoPath.text")); // NOI18N
javax.swing.GroupLayout tempDirectoryPanelLayout = new javax.swing.GroupLayout(tempDirectoryPanel);
tempDirectoryPanel.setLayout(tempDirectoryPanelLayout);
tempDirectoryPanelLayout.setHorizontalGroup(
@ -837,23 +1146,37 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
.addGroup(tempDirectoryPanelLayout.createSequentialGroup()
.addContainerGap()
.addGroup(tempDirectoryPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(tempLocalRadio)
.addComponent(tempCaseRadio)
.addComponent(tempDirectoryWarningLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 615, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGroup(tempDirectoryPanelLayout.createSequentialGroup()
.addComponent(tempDirectoryField, javax.swing.GroupLayout.PREFERRED_SIZE, 367, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(tempCustomRadio)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(tempDirectoryBrowseButton)))
.addGap(0, 0, Short.MAX_VALUE))
.addGroup(tempDirectoryPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(tempOnCustomNoPath)
.addGroup(tempDirectoryPanelLayout.createSequentialGroup()
.addComponent(tempCustomField, javax.swing.GroupLayout.PREFERRED_SIZE, 459, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(tempDirectoryBrowseButton)))))
.addContainerGap(164, Short.MAX_VALUE))
);
tempDirectoryPanelLayout.setVerticalGroup(
tempDirectoryPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(tempDirectoryPanelLayout.createSequentialGroup()
.addContainerGap()
.addComponent(tempLocalRadio)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(tempCaseRadio)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(tempDirectoryPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(tempDirectoryField)
.addComponent(tempCustomRadio)
.addComponent(tempCustomField)
.addComponent(tempDirectoryBrowseButton))
.addGap(18, 18, 18)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(tempOnCustomNoPath)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(tempDirectoryWarningLabel)
.addContainerGap())
.addGap(14, 14, 14))
);
gridBagConstraints = new java.awt.GridBagConstraints();
@ -906,11 +1229,11 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 648, Short.MAX_VALUE)
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 860, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 382, Short.MAX_VALUE)
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 620, Short.MAX_VALUE)
);
}// </editor-fold>//GEN-END:initComponents
@ -927,7 +1250,7 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
if (!f.exists() && !f.mkdirs()) {
throw new InvalidPathException(specifiedPath, "Unable to create parent directories leading to " + specifiedPath);
}
tempDirectoryField.setText(specifiedPath);
tempCustomField.setText(specifiedPath);
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
} catch (InvalidPathException ex) {
logger.log(Level.WARNING, "Unable to create temporary directory in " + specifiedPath, ex);
@ -971,7 +1294,7 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
browseLogosButton.setEnabled(true);
try {
if (agencyLogoPathField.getText().isEmpty()) {
String path = ModuleSettings.getConfigSetting(ReportBranding.MODULE_NAME, ReportBranding.AGENCY_LOGO_PATH_PROP);
String path = reportBranding.getAgencyLogoPath();
if (path != null && !path.isEmpty()) {
updateAgencyLogo(path);
}
@ -1017,6 +1340,39 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
}
}//GEN-LAST:event_browseLogosButtonActionPerformed
private void tempLocalRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_tempLocalRadioActionPerformed
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
evaluateTempDirState();
}//GEN-LAST:event_tempLocalRadioActionPerformed
private void tempCaseRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_tempCaseRadioActionPerformed
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
evaluateTempDirState();
}//GEN-LAST:event_tempCaseRadioActionPerformed
private void tempCustomRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_tempCustomRadioActionPerformed
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
evaluateTempDirState();
}//GEN-LAST:event_tempCustomRadioActionPerformed
@Messages({
"AutopsyOptionsPanel_heapDumpBrowseButtonActionPerformed_fileAlreadyExistsTitle=File Already Exists",
"AutopsyOptionsPanel_heapDumpBrowseButtonActionPerformed_fileAlreadyExistsMessage=File already exists. Please select a new location."
})
private void heapDumpBrowseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_heapDumpBrowseButtonActionPerformed
String oldHeapPath = heapDumpFileField.getText();
if (!StringUtils.isBlank(oldHeapPath)) {
heapFileChooser.setCurrentDirectory(new File(oldHeapPath));
}
int returnState = heapFileChooser.showOpenDialog(this);
if (returnState == JFileChooser.APPROVE_OPTION) {
File selectedDirectory = heapFileChooser.getSelectedFile();
heapDumpFileField.setText(selectedDirectory.getAbsolutePath());
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}
}//GEN-LAST:event_heapDumpBrowseButtonActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JTextField agencyLogoPathField;
private javax.swing.JLabel agencyLogoPathFieldValidationLabel;
@ -1025,6 +1381,10 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
private javax.swing.JRadioButton defaultLogoRB;
private javax.swing.ButtonGroup displayTimesButtonGroup;
private javax.swing.ButtonGroup fileSelectionButtonGroup;
private javax.swing.JButton heapDumpBrowseButton;
private javax.swing.JTextField heapDumpFileField;
private javax.swing.JLabel heapFieldValidationLabel;
private javax.swing.JLabel heapFileLabel;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JTextField logFileCount;
private javax.swing.JTextField logNumAlert;
@ -1045,10 +1405,15 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
private javax.swing.JSpinner solrMaxHeapSpinner;
private javax.swing.JRadioButton specifyLogoRB;
private javax.swing.JLabel systemMemoryTotal;
private javax.swing.JRadioButton tempCaseRadio;
private javax.swing.JTextField tempCustomField;
private javax.swing.JRadioButton tempCustomRadio;
private javax.swing.ButtonGroup tempDirChoiceGroup;
private javax.swing.JButton tempDirectoryBrowseButton;
private javax.swing.JTextField tempDirectoryField;
private javax.swing.JPanel tempDirectoryPanel;
private javax.swing.JLabel tempDirectoryWarningLabel;
private javax.swing.JRadioButton tempLocalRadio;
private javax.swing.JLabel tempOnCustomNoPath;
private javax.swing.JLabel totalMemoryLabel;
// End of variables declaration//GEN-END:variables

View File

@ -1,6 +1,5 @@
CTL_DataContentAction=DataContent
CTL_DataContentTopComponent=Data Content
CTL_CustomAboutAction=About
OptionsCategory_Name_General=Application
OptionsCategory_Keywords_General=Autopsy Options
HINT_DataContentTopComponent=This is a DataContent window
@ -188,12 +187,11 @@ MultiUserSettingsPanel.restartRequiredLabel.text=Application restart required to
MultiUserSettingsPanel.MustRestart=Autopsy must be restarted for new configuration to take effect
MultiUserSettingsPanel.lbSolrNote1.text=Enter Solr 8 and/or Solr 4 server settings.
MultiUserSettingsPanel.lbSolrNote2.text=New text indexing can only be done with Solr 8.
AutopsyOptionsPanel.tempDirectoryField.text=
AutopsyOptionsPanel.tempDirectoryBrowseButton.text=Browse
AutopsyOptionsPanel.a.AccessibleContext.accessibleName=Temp Directory
AutopsyOptionsPanel.tempDirectoryPanel.AccessibleContext.accessibleName=Temp Directory
AutopsyOptionsPanel.tempDirectoryPanel.name=Temp Directory
AutopsyOptionsPanel.tempDirectoryPanel.border.title=Temp Directory
AutopsyOptionsPanel.tempDirectoryPanel.border.title=Root Temp Directory
AutopsyOptionsPanel.tempDirectoryWarningLabel.text=Close the current case to change the temporary directory.
AutopsyOptionsPanel.solrJVMHeapWarning.text=NOTE: Setting this too large may impact overall performance.
AutopsyOptionsPanel.maxMemoryUnitsLabel2.text=MB
@ -247,3 +245,12 @@ AutopsyOptionsPanel.agencyLogoPathField.text=
AutopsyOptionsPanel.logoPanel.border.title=Logo
ViewPreferencesPanel.radioGroupByPersonHost.text=Group by Person/Host
ViewPreferencesPanel.radioGroupByDataType.text=Group by Data Type
AutopsyOptionsPanel.tempLocalRadio.text=Local temp directory
AutopsyOptionsPanel.tempCaseRadio.text=Temp folder in case directory
AutopsyOptionsPanel.tempCustomRadio.text=Custom
AutopsyOptionsPanel.tempCustomField.text=
AutopsyOptionsPanel.tempOnCustomNoPath.text=Please select a path for the custom root temp directory.
AutopsyOptionsPanel.heapDumpFileField.text=
AutopsyOptionsPanel.heapDumpBrowseButton.text=Browse
AutopsyOptionsPanel.heapFileLabel.text=Custom Heap Dump Location:
AutopsyOptionsPanel.heapFieldValidationLabel.text=

View File

@ -12,6 +12,14 @@ AutopsyOptionsPanel.memFieldValidationLabel.noValueEntered.text=No value entered
AutopsyOptionsPanel.memFieldValidationLabel.overMaxMemory.text=Value must be less than the total system memory of {0}GB
# {0} - minimumMemory
AutopsyOptionsPanel.memFieldValidationLabel.underMinMemory.text=Value must be at least {0}GB
AutopsyOptionsPanel_heapDumpBrowseButtonActionPerformed_fileAlreadyExistsMessage=File already exists. Please select a new location.
AutopsyOptionsPanel_heapDumpBrowseButtonActionPerformed_fileAlreadyExistsTitle=File Already Exists
AutopsyOptionsPanel_isHeapPathValid_developerMode=Cannot change heap dump path while in developer mode.
AutopsyOPtionsPanel_isHeapPathValid_illegalCharacters=Please select a path with no quotes.
AutopsyOptionsPanel_isHeapPathValid_not64BitMachine=Changing heap dump path settings only enabled for 64 bit version.
AutopsyOptionsPanel_isHeapPathValid_selectValidDirectory=Please select an existing directory.
AutopsyOptionsPanel_storeTempDir_onChoiceError_description=There was an error updating temporary directory choice selection.
AutopsyOptionsPanel_storeTempDir_onChoiceError_title=Error Saving Temporary Directory Choice
# {0} - path
AutopsyOptionsPanel_storeTempDir_onError_description=There was an error creating the temporary directory on the filesystem at: {0}.
AutopsyOptionsPanel_storeTempDir_onError_title=Error Saving Temporary Directory
@ -20,7 +28,6 @@ AutopsyOptionsPanel_tempDirectoryBrowseButtonActionPerformed_onInvalidPath_descr
AutopsyOptionsPanel_tempDirectoryBrowseButtonActionPerformed_onInvalidPath_title=Path cannot be used
CTL_DataContentAction=DataContent
CTL_DataContentTopComponent=Data Content
CTL_CustomAboutAction=About
CTL_OfflineHelpAction=Offline Autopsy Documentation
CTL_OnlineHelpAction=Online Autopsy Documentation
DataContentViewerArtifact.failedToGetAttributes.message=Failed to get some or all attributes from case database
@ -246,12 +253,11 @@ MultiUserSettingsPanel.restartRequiredLabel.text=Application restart required to
MultiUserSettingsPanel.MustRestart=Autopsy must be restarted for new configuration to take effect
MultiUserSettingsPanel.lbSolrNote1.text=Enter Solr 8 and/or Solr 4 server settings.
MultiUserSettingsPanel.lbSolrNote2.text=New text indexing can only be done with Solr 8.
AutopsyOptionsPanel.tempDirectoryField.text=
AutopsyOptionsPanel.tempDirectoryBrowseButton.text=Browse
AutopsyOptionsPanel.a.AccessibleContext.accessibleName=Temp Directory
AutopsyOptionsPanel.tempDirectoryPanel.AccessibleContext.accessibleName=Temp Directory
AutopsyOptionsPanel.tempDirectoryPanel.name=Temp Directory
AutopsyOptionsPanel.tempDirectoryPanel.border.title=Temp Directory
AutopsyOptionsPanel.tempDirectoryPanel.border.title=Root Temp Directory
AutopsyOptionsPanel.tempDirectoryWarningLabel.text=Close the current case to change the temporary directory.
AutopsyOptionsPanel.solrJVMHeapWarning.text=NOTE: Setting this too large may impact overall performance.
AutopsyOptionsPanel.maxMemoryUnitsLabel2.text=MB
@ -305,3 +311,12 @@ AutopsyOptionsPanel.agencyLogoPathField.text=
AutopsyOptionsPanel.logoPanel.border.title=Logo
ViewPreferencesPanel.radioGroupByPersonHost.text=Group by Person/Host
ViewPreferencesPanel.radioGroupByDataType.text=Group by Data Type
AutopsyOptionsPanel.tempLocalRadio.text=Local temp directory
AutopsyOptionsPanel.tempCaseRadio.text=Temp folder in case directory
AutopsyOptionsPanel.tempCustomRadio.text=Custom
AutopsyOptionsPanel.tempCustomField.text=
AutopsyOptionsPanel.tempOnCustomNoPath.text=Please select a path for the custom root temp directory.
AutopsyOptionsPanel.heapDumpFileField.text=
AutopsyOptionsPanel.heapDumpBrowseButton.text=Browse
AutopsyOptionsPanel.heapFileLabel.text=Custom Heap Dump Location:
AutopsyOptionsPanel.heapFieldValidationLabel.text=

View File

@ -24,6 +24,7 @@ import java.util.Map;
import java.util.TreeMap;
import java.util.logging.Level;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UIManager.LookAndFeelInfo;
@ -75,6 +76,16 @@ public class Installer extends ModuleInstall {
}
private void setLookAndFeel() {
ImageIcon questionIcon = new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/question_32.png"));
ImageIcon warningIcon = new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/warning_32.png"));
ImageIcon informationIcon = new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/information_32.png"));
ImageIcon errorIcon = new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/error_32.png"));
UIManager.put("OptionPane.errorIcon", errorIcon);
UIManager.put("OptionPane.warningIcon", warningIcon);
UIManager.put("OptionPane.questionIcon", questionIcon);
UIManager.put("OptionPane.informationIcon", informationIcon);
if (System.getProperty("os.name").toLowerCase().contains("mac")) { //NON-NLS
setUnixLookAndFeel();
setModuleSettings("false");

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2017 Basis Technology Corp.
* Copyright 2017-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -18,8 +18,13 @@
*/
package org.sleuthkit.autopsy.coreutils;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.util.Arrays;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/*
* Concurrent programming utilities.
@ -50,6 +55,36 @@ final public class ThreadUtils {
}
}
/**
* Returns the thread info for all live threads with stack trace and
* synchronization information. Some threads included in the returned array
* may have been terminated when this method returns.
*
* @return Thread dump of all live threads
*/
public static String generateThreadDump() {
StringBuilder threadDump = new StringBuilder(System.lineSeparator());
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
ThreadInfo[] threadInfos = threadMXBean.getThreadInfo(threadMXBean.getAllThreadIds(), 100);
for (ThreadInfo threadInfo : threadInfos) {
threadDump.append(threadInfo.toString());
threadDump.append("\n");
}
long[] deadlockThreadIds = threadMXBean.findDeadlockedThreads();
if (deadlockThreadIds != null) {
threadDump.append("-------------------List of Deadlocked Thread IDs ---------------------");
String idsList = (Arrays
.stream(deadlockThreadIds)
.boxed()
.collect(Collectors.toList()))
.stream().map(n -> String.valueOf(n))
.collect(Collectors.joining("-", "{", "}"));
threadDump.append(idsList);
}
return threadDump.toString();
}
private ThreadUtils() {
}
}

View File

@ -0,0 +1,95 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.datamodel;
import org.openide.nodes.Children;
import org.openide.util.NbBundle;
import org.sleuthkit.datamodel.BlackboardArtifact;
/**
* Analysis Results node support.
*/
@NbBundle.Messages({
"AnalysisResults_name=Analysis Results",})
public class AnalysisResults implements AutopsyVisitableItem {
/**
* Returns the name of this node that is the key in the children object.
*
* @return The name of this node that is the key in the children object.
*/
public static String getName() {
return Bundle.AnalysisResults_name();
}
/**
* Parent node of all analysis results.
*/
static class RootNode extends Artifacts.BaseArtifactNode {
/**
* Main constructor.
*
* @param filteringDSObjId The data source object id for which results
* should be filtered. If no filtering should
* occur, this number should be less than or
* equal to 0.
*/
RootNode(long filteringDSObjId) {
super(Children.create(new Artifacts.TypeFactory(BlackboardArtifact.Category.ANALYSIS_RESULT, filteringDSObjId), true),
"org/sleuthkit/autopsy/images/analysis_result.png",
AnalysisResults.getName(),
AnalysisResults.getName());
}
}
private final long datasourceObjId;
/**
* Main constructor.
*/
public AnalysisResults() {
this(0);
}
/**
* Main constructor.
*
* @param dsObjId The data source object id.
*/
public AnalysisResults(long dsObjId) {
this.datasourceObjId = dsObjId;
}
@Override
public <T> T accept(AutopsyItemVisitor<T> visitor) {
return visitor.visit(this);
}
/**
* Returns whether or not there is a data source object for which results
* should be filtered.
*
* @return Whether or not there is a data source object for which results
* should be filtered.
*/
Long getFilteringDataSourceObjId() {
return datasourceObjId;
}
}

View File

@ -0,0 +1,704 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.datamodel;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.logging.Level;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.openide.nodes.Sheet;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.accounts.Accounts;
import org.sleuthkit.autopsy.datamodel.utils.IconsUtil;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.autopsy.guiutils.RefreshThrottler;
import org.sleuthkit.datamodel.BlackboardArtifact.Category;
import org.python.google.common.collect.Sets;
import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_ACCOUNT;
import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_DATA_SOURCE_USAGE;
import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_EMAIL_MSG;
import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_HASHSET_HIT;
import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT;
import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT;
import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_GEN_INFO;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE;
import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_TL_EVENT;
import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_ASSOCIATED_OBJECT;
import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_KEYWORD_HIT;
/**
* Classes for creating nodes for BlackboardArtifacts.
*/
public class Artifacts {
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST
= EnumSet.of(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED);
/**
* Base class for a parent node of artifacts.
*/
static class BaseArtifactNode extends DisplayableItemNode {
/**
* Main constructor.
*
* @param children The children of the node.
* @param icon The icon for the node.
* @param name The name identifier of the node.
* @param displayName The display name for the node.
*/
BaseArtifactNode(Children children, String icon, String name, String displayName) {
super(children, Lookups.singleton(name));
super.setName(name);
super.setDisplayName(displayName);
this.setIconBaseWithExtension(icon); //NON-NLS
}
@Override
public boolean isLeafTypeNode() {
return false;
}
@Override
public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
return visitor.visit(this);
}
@Override
protected Sheet createSheet() {
Sheet sheet = super.createSheet();
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
if (sheetSet == null) {
sheetSet = Sheet.createPropertiesSet();
sheet.put(sheetSet);
}
sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ExtractedContentNode.createSheet.name.name"),
NbBundle.getMessage(this.getClass(), "ExtractedContentNode.createSheet.name.displayName"),
NbBundle.getMessage(this.getClass(), "ExtractedContentNode.createSheet.name.desc"),
super.getDisplayName()));
return sheet;
}
@Override
public String getItemType() {
return getClass().getName();
}
}
/**
* A key to be used with the type factory.
*/
private static class TypeNodeKey {
private final UpdatableCountTypeNode node;
private final Set<BlackboardArtifact.Type> applicableTypes;
/**
* Constructor generating a generic TypeNode for a given artifact type.
*
* @param type The type for the key.
* @param dsObjId The data source object id if filtering should occur.
* If no filtering should occur, this number should be
* less than or equal to 0.
*/
TypeNodeKey(BlackboardArtifact.Type type, long dsObjId) {
this(new TypeNode(type, dsObjId), type);
}
/**
* Constructor for any UpdatableCountTypeNode.
*
* @param typeNode The UpdatableCountTypeNode.
* @param types The blackboard artifact types corresponding to this
* node.
*/
TypeNodeKey(UpdatableCountTypeNode typeNode, BlackboardArtifact.Type... types) {
this.node = typeNode;
this.applicableTypes = Stream.of(types)
.filter(t -> t != null)
.collect(Collectors.toSet());
}
/**
* Returns the node associated with this key.
*
* @return The node associated with this key.
*/
UpdatableCountTypeNode getNode() {
return node;
}
/**
* Returns the blackboard artifact types associated with this key.
*
* @return The blackboard artifact types associated with this key.
*/
Set<BlackboardArtifact.Type> getApplicableTypes() {
return applicableTypes;
}
@Override
public int hashCode() {
int hash = 3;
hash = 61 * hash + Objects.hashCode(this.applicableTypes);
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final TypeNodeKey other = (TypeNodeKey) obj;
if (!Objects.equals(this.applicableTypes, other.applicableTypes)) {
return false;
}
return true;
}
}
/**
* Factory for showing a list of artifact types (i.e. all the data artifact
* types).
*/
static class TypeFactory extends ChildFactory.Detachable<TypeNodeKey> implements RefreshThrottler.Refresher {
private static final Logger logger = Logger.getLogger(TypeNode.class.getName());
/**
* Types that should not be shown in the tree.
*/
@SuppressWarnings("deprecation")
private static final Set<BlackboardArtifact.Type> IGNORED_TYPES = Sets.newHashSet(
// these are shown in other parts of the UI (and different node types)
TSK_DATA_SOURCE_USAGE,
TSK_GEN_INFO,
new BlackboardArtifact.Type(TSK_DOWNLOAD_SOURCE),
TSK_TL_EVENT,
//This is not meant to be shown in the UI at all. It is more of a meta artifact.
TSK_ASSOCIATED_OBJECT
);
/**
* Returns a Children key to be use for a particular artifact type.
*
* @param type The artifact type.
* @param skCase The relevant Sleuthkit case in order to create the
* node.
* @param dsObjId The data source object id to use for filtering. If id
* is less than or equal to 0, no filtering will occur.
*
* @return The generated key.
*/
private static TypeNodeKey getTypeKey(BlackboardArtifact.Type type, SleuthkitCase skCase, long dsObjId) {
int typeId = type.getTypeID();
if (TSK_EMAIL_MSG.getTypeID() == typeId) {
EmailExtracted.RootNode emailNode = new EmailExtracted(skCase, dsObjId).new RootNode();
return new TypeNodeKey(emailNode, TSK_EMAIL_MSG);
} else if (TSK_ACCOUNT.getTypeID() == typeId) {
Accounts.AccountsRootNode accountsNode = new Accounts(skCase, dsObjId).new AccountsRootNode();
return new TypeNodeKey(accountsNode, TSK_ACCOUNT);
} else if (TSK_KEYWORD_HIT.getTypeID() == typeId) {
KeywordHits.RootNode keywordsNode = new KeywordHits(skCase, dsObjId).new RootNode();
return new TypeNodeKey(keywordsNode, TSK_KEYWORD_HIT);
} else if (TSK_INTERESTING_ARTIFACT_HIT.getTypeID() == typeId
|| TSK_INTERESTING_FILE_HIT.getTypeID() == typeId) {
InterestingHits.RootNode interestingHitsNode = new InterestingHits(skCase, dsObjId).new RootNode();
return new TypeNodeKey(interestingHitsNode,
TSK_INTERESTING_ARTIFACT_HIT,
TSK_INTERESTING_FILE_HIT);
} else if (TSK_HASHSET_HIT.getTypeID() == typeId) {
HashsetHits.RootNode hashsetHits = new HashsetHits(skCase, dsObjId).new RootNode();
return new TypeNodeKey(hashsetHits, TSK_HASHSET_HIT);
} else {
return new TypeNodeKey(type, dsObjId);
}
}
// maps the artifact type to its child node
private final Map<BlackboardArtifact.Type, TypeNodeKey> typeNodeMap = new HashMap<>();
private final long filteringDSObjId;
/**
* RefreshThrottler is used to limit the number of refreshes performed
* when CONTENT_CHANGED and DATA_ADDED ingest module events are
* received.
*/
private final RefreshThrottler refreshThrottler = new RefreshThrottler(this);
private final Category category;
/**
* Main constructor.
*
* @param category The category of types to be displayed.
* @param filteringDSObjId The data source object id to use for
* filtering. If id is less than or equal to 0,
* no filtering will occur.
*/
TypeFactory(Category category, long filteringDSObjId) {
super();
this.filteringDSObjId = filteringDSObjId;
this.category = category;
}
private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> {
String eventType = evt.getPropertyName();
if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
// case was closed. Remove listeners so that we don't get called with a stale case handle
if (evt.getNewValue() == null) {
removeNotify();
}
} else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
|| eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
/**
* This is a stop gap measure until a different way of handling
* the closing of cases is worked out. Currently, remote events
* may be received for a case that is already closed.
*/
try {
Case.getCurrentCaseThrows();
refresh(false);
} catch (NoCurrentCaseException notUsed) {
/**
* Case is closed, do nothing.
*/
}
}
};
@Override
protected void addNotify() {
refreshThrottler.registerForIngestModuleEvents();
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl);
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl);
}
@Override
protected void removeNotify() {
refreshThrottler.unregisterEventListener();
IngestManager.getInstance().removeIngestJobEventListener(pcl);
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl);
typeNodeMap.clear();
}
@Override
protected boolean createKeys(List<TypeNodeKey> list) {
try {
// Get all types in use
SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
List<BlackboardArtifact.Type> types = (this.filteringDSObjId > 0)
? skCase.getBlackboard().getArtifactTypesInUse(this.filteringDSObjId)
: skCase.getArtifactTypesInUse();
List<TypeNodeKey> allKeysSorted = types.stream()
// filter types by category and ensure they are not in the list of ignored types
.filter(tp -> category.equals(tp.getCategory()) && !IGNORED_TYPES.contains(tp))
.map(tp -> {
// if typeNodeMap already contains key, update the relevant node and return the node
if (typeNodeMap.containsKey(tp)) {
TypeNodeKey typeKey = typeNodeMap.get(tp);
typeKey.getNode().updateDisplayName();
return typeKey;
} else {
// if key is not in map, create the type key and add to map
TypeNodeKey newTypeKey = getTypeKey(tp, skCase, filteringDSObjId);
for (BlackboardArtifact.Type recordType : newTypeKey.getApplicableTypes()) {
typeNodeMap.put(recordType, newTypeKey);
}
return newTypeKey;
}
})
// ensure record is returned
.filter(record -> record != null)
// there are potentially multiple types that apply to the same node (i.e. Interesting Files / Artifacts)
// ensure the keys are distinct
.distinct()
// sort by display name
.sorted((a, b) -> {
String aSafe = (a.getNode() == null || a.getNode().getDisplayName() == null) ? "" : a.getNode().getDisplayName();
String bSafe = (b.getNode() == null || b.getNode().getDisplayName() == null) ? "" : b.getNode().getDisplayName();
return aSafe.compareToIgnoreCase(bSafe);
})
.collect(Collectors.toList());
list.addAll(allKeysSorted);
} catch (NoCurrentCaseException ex) {
logger.log(Level.WARNING, "Trying to access case when no case is open.", ex); //NON-NLS
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error getting list of artifacts in use: " + ex.getLocalizedMessage()); //NON-NLS
}
return true;
}
@Override
protected Node createNodeForKey(TypeNodeKey key) {
return key.getNode();
}
@Override
public void refresh() {
refresh(false);
}
@Override
public boolean isRefreshRequired(PropertyChangeEvent evt) {
String eventType = evt.getPropertyName();
if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
/**
* This is a stop gap measure until a different way of handling
* the closing of cases is worked out. Currently, remote events
* may be received for a case that is already closed.
*/
try {
Case.getCurrentCaseThrows();
/**
* Due to some unresolved issues with how cases are closed,
* it is possible for the event to have a null oldValue if
* the event is a remote event.
*/
final ModuleDataEvent event = (ModuleDataEvent) evt.getOldValue();
if (null != event && category.equals(event.getBlackboardArtifactType().getCategory())
&& !(IGNORED_TYPES.contains(event.getBlackboardArtifactType()))) {
return true;
}
} catch (NoCurrentCaseException notUsed) {
/**
* Case is closed, do nothing.
*/
}
}
return false;
}
}
/**
* Abstract class for type(s) nodes. This class allows for displaying a
* count artifacts with the type(s) associated with this node.
*/
public static abstract class UpdatableCountTypeNode extends DisplayableItemNode {
private static final Logger logger = Logger.getLogger(UpdatableCountTypeNode.class.getName());
private final Set<BlackboardArtifact.Type> types;
private final long filteringDSObjId;
private long childCount = 0;
private final String baseName;
/**
* Main constructor.
*
* @param children The Children to associated with this node.
* @param lookup The Lookup to use with this name.
* @param baseName The display name. The Node.displayName will
* be of format "[baseName] ([count])".
* @param filteringDSObjId The data source object id to use for
* filtering. If id is less than or equal to 0,
* no filtering will occur.
* @param types The types associated with this type node.
*/
public UpdatableCountTypeNode(Children children, Lookup lookup, String baseName,
long filteringDSObjId, BlackboardArtifact.Type... types) {
super(children, lookup);
this.types = Stream.of(types).collect(Collectors.toSet());
this.filteringDSObjId = filteringDSObjId;
this.baseName = baseName;
updateDisplayName();
}
/**
* Returns the count of artifacts associated with these type(s).
*
* @return The count of artifacts associated with these type(s).
*/
protected long getChildCount() {
return this.childCount;
}
/**
* Fetches the count to be displayed from the case.
*
* @param skCase The relevant SleuthkitCase.
*
* @return The count to be displayed.
*
* @throws TskCoreException
*/
protected long fetchChildCount(SleuthkitCase skCase) throws TskCoreException {
int count = 0;
for (BlackboardArtifact.Type type : this.types) {
if (filteringDSObjId > 0) {
count += skCase.getBlackboard().getArtifactsCount(type.getTypeID(), filteringDSObjId);
} else {
count += skCase.getBlackboardArtifactsTypeCount(type.getTypeID());
}
}
return count;
}
/**
* When this method is called, the count to be displayed will be
* updated.
*/
void updateDisplayName() {
try {
SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
this.childCount = fetchChildCount(skCase);
} catch (NoCurrentCaseException ex) {
logger.log(Level.WARNING, "Error fetching data when case closed.", ex);
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Error getting child count", ex); //NON-NLS
}
super.setDisplayName(this.baseName + " \u200E(\u200E" + this.childCount + ")\u200E");
}
}
/**
* Default node encapsulating a blackboard artifact type. This is used on
* the left-hand navigation side of the Autopsy UI as the parent node for
* all of the artifacts of a given type. Its children will be
* BlackboardArtifactNode objects.
*/
static class TypeNode extends UpdatableCountTypeNode {
private final BlackboardArtifact.Type type;
/**
* Main constructor.
*
* @param type The blackboard artifact type for this node.
* @param filteringDSObjId The data source object id to use for
* filtering. If id is less than or equal to 0,
* no filtering will occur.
*/
TypeNode(BlackboardArtifact.Type type, long filteringDSObjId) {
super(Children.create(new ArtifactFactory(type, filteringDSObjId), true),
Lookups.singleton(type.getDisplayName()),
type.getDisplayName(),
filteringDSObjId,
type);
super.setName(type.getTypeName());
this.type = type;
String iconPath = IconsUtil.getIconFilePath(type.getTypeID());
setIconBaseWithExtension(iconPath != null && iconPath.charAt(0) == '/' ? iconPath.substring(1) : iconPath);
}
@Override
protected Sheet createSheet() {
Sheet sheet = super.createSheet();
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
if (sheetSet == null) {
sheetSet = Sheet.createPropertiesSet();
sheet.put(sheetSet);
}
sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.artType.name"),
NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.artType.displayName"),
NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.artType.desc"),
type.getDisplayName()));
sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.childCnt.name"),
NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.childCnt.displayName"),
NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.childCnt.desc"),
getChildCount()));
return sheet;
}
@Override
public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
return visitor.visit(this);
}
@Override
public boolean isLeafTypeNode() {
return true;
}
@Override
public String getItemType() {
return getClass().getName() + type.getDisplayName();
}
}
/**
* Creates children for a given artifact type
*/
private static class ArtifactFactory extends BaseChildFactory<BlackboardArtifact> implements RefreshThrottler.Refresher {
private static final Logger logger = Logger.getLogger(ArtifactFactory.class.getName());
private final BlackboardArtifact.Type type;
/**
* RefreshThrottler is used to limit the number of refreshes performed
* when CONTENT_CHANGED and DATA_ADDED ingest module events are
* received.
*/
private final RefreshThrottler refreshThrottler = new RefreshThrottler(this);
private final long filteringDSObjId;
/**
* Main constructor.
*
* @param type The blackboard artifact type for this node.
* @param filteringDSObjId The data source object id to use for
* filtering. If id is less than or equal to 0,
* no filtering will occur.
*/
ArtifactFactory(BlackboardArtifact.Type type, long filteringDSObjId) {
super(type.getTypeName());
this.type = type;
this.filteringDSObjId = filteringDSObjId;
}
private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> {
String eventType = evt.getPropertyName();
if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
|| eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
/**
* Checking for a current case is a stop gap measure until a
* different way of handling the closing of cases is worked out.
* Currently, remote events may be received for a case that is
* already closed.
*/
try {
Case.getCurrentCaseThrows();
refresh(false);
} catch (NoCurrentCaseException notUsed) {
/**
* Case is closed, do nothing.
*/
}
}
};
@Override
protected void onAdd() {
refreshThrottler.registerForIngestModuleEvents();
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl);
}
@Override
protected void onRemove() {
refreshThrottler.unregisterEventListener();
IngestManager.getInstance().removeIngestJobEventListener(pcl);
}
@Override
protected Node createNodeForKey(BlackboardArtifact key) {
return new BlackboardArtifactNode(key);
}
@Override
protected List<BlackboardArtifact> makeKeys() {
try {
List<BlackboardArtifact> arts;
arts = (filteringDSObjId > 0)
? Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard().getArtifacts(type.getTypeID(), filteringDSObjId)
: Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboardArtifacts(type.getTypeID());
for (BlackboardArtifact art : arts) {
//Cache attributes while we are off the EDT.
//See JIRA-5969
art.getAttributes();
}
return arts;
} catch (NoCurrentCaseException ex) {
logger.log(Level.WARNING, "Trying to access case when no case is open.", ex); //NON-NLS
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Couldn't get blackboard artifacts from database", ex); //NON-NLS
}
return Collections.emptyList();
}
@Override
public void refresh() {
refresh(false);
}
@Override
public boolean isRefreshRequired(PropertyChangeEvent evt) {
String eventType = evt.getPropertyName();
if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
/**
* Checking for a current case is a stop gap measure until a
* different way of handling the closing of cases is worked out.
* Currently, remote events may be received for a case that is
* already closed.
*/
try {
Case.getCurrentCaseThrows();
/**
* Even with the check above, it is still possible that the
* case will be closed in a different thread before this
* code executes. If that happens, it is possible for the
* event to have a null oldValue.
*/
final ModuleDataEvent event = (ModuleDataEvent) evt.getOldValue();
if (null != event && event.getBlackboardArtifactType().equals(type)) {
return true;
}
} catch (NoCurrentCaseException notUsed) {
/**
* Case is closed, do nothing.
*/
}
}
return false;
}
}
}

View File

@ -53,8 +53,6 @@ public interface AutopsyItemVisitor<T> {
T visit(FileSize.FileSizeFilter fsf);
T visit(ExtractedContent ec);
T visit(KeywordHits kh);
T visit(HashsetHits hh);
@ -63,8 +61,6 @@ public interface AutopsyItemVisitor<T> {
T visit(InterestingHits ih);
T visit(Results r);
T visit(Tags tagsNodeKey);
T visit(Reports reportsItem);
@ -85,15 +81,15 @@ public interface AutopsyItemVisitor<T> {
T visit(DataSourcesByType aThis);
T visit(AnalysisResults aThis);
T visit(DataArtifacts aThis);
static abstract public class Default<T> implements AutopsyItemVisitor<T> {
protected abstract T defaultVisit(AutopsyVisitableItem ec);
@Override
public T visit(ExtractedContent ec) {
return defaultVisit(ec);
}
@Override
public T visit(FileTypesByExtension sf) {
return defaultVisit(sf);
@ -199,11 +195,6 @@ public interface AutopsyItemVisitor<T> {
return defaultVisit(personGrouping);
}
@Override
public T visit(Results r) {
return defaultVisit(r);
}
@Override
public T visit(FileTypes ft) {
return defaultVisit(ft);
@ -233,5 +224,15 @@ public interface AutopsyItemVisitor<T> {
public T visit(DataSourcesByType dataSourceHosts) {
return defaultVisit(dataSourceHosts);
}
@Override
public T visit(DataArtifacts aThis) {
return defaultVisit(aThis);
}
@Override
public T visit(AnalysisResults aThis) {
return defaultVisit(aThis);
}
}
}

View File

@ -111,24 +111,27 @@ public final class AutopsyTreeChildFactory extends ChildFactory.Detachable<Objec
if (CollectionUtils.isNotEmpty(personManager.getHostsForPerson(null))) {
list.add(new PersonGrouping(null));
}
return true;
} else {
// otherwise, just show host level
tskCase.getHostManager().getHosts().stream()
tskCase.getHostManager().getAllHosts().stream()
.map(HostGrouping::new)
.sorted()
.forEach(list::add);
return true;
}
list.add(new Reports());
return true;
} else {
// data source by type view
List<AutopsyVisitableItem> keys = new ArrayList<>(Arrays.asList(
new DataSourcesByType(),
new Views(tskCase),
new Results(tskCase),
new Views(Case.getCurrentCaseThrows().getSleuthkitCase()),
new DataArtifacts(),
new AnalysisResults(),
new OsAccounts(Case.getCurrentCaseThrows().getSleuthkitCase()),
new Tags(),
new Reports()));
new Reports()
));
list.addAll(keys);
}

View File

@ -171,7 +171,7 @@ TagNameNode.createSheet.name.displayName=Name
TagsNode.displayName.text=Tags
TagsNode.createSheet.name.name=Name
TagsNode.createSheet.name.displayName=Name
ViewsNode.name.text=Views
ViewsNode.name.text=File Views
ViewsNode.createSheet.name.name=Name
ViewsNode.createSheet.name.displayName=Name
ViewsNode.createSheet.name.desc=no description

View File

@ -39,6 +39,7 @@ AbstractAbstractFileNode.useridColLbl=UserID
AbstractContentNode.nodescription=no description
AbstractContentNode.valueLoading=value loading
AbstractFsContentNode.noDesc.text=no description
AnalysisResults_name=Analysis Results
ArtifactStringContent.attrsTableHeader.sources=Source(s)
ArtifactStringContent.attrsTableHeader.type=Type
ArtifactStringContent.attrsTableHeader.value=Value
@ -93,6 +94,7 @@ ContentTagNode.createSheet.artifactMD5.displayName=MD5 Hash
ContentTagNode.createSheet.artifactMD5.name=MD5 Hash
ContentTagNode.createSheet.origFileName=Original Name
ContentTagNode.createSheet.userName.text=User Name
DataArtifacts_name=Data Artifacts
DataSourcesHostsNode_name=Data Sources
DeletedContent.allDelFilter.text=All
DeletedContent.createSheet.filterType.desc=no description
@ -358,10 +360,6 @@ ReportNode.reportNameProperty.name=Report Name
ReportNode.reportNameProperty.displayName=Report Name
ReportNode.reportNameProperty.desc=Name of the report
ReportsListNode.displayName=Reports
ResultsNode.createSheet.name.desc=no description
ResultsNode.createSheet.name.displayName=Name
ResultsNode.createSheet.name.name=Name
ResultsNode.name.text=Results
SlackFileNode.getActions.viewInNewWin.text=View in New Window
SlackFileNode.getActions.viewFileInDir.text=View File in Directory
SpecialDirectoryNode.getActions.viewInNewWin.text=View in New Window
@ -376,7 +374,11 @@ TagNode.propertySheet.origNameDisplayName=Original Name
TagsNode.displayName.text=Tags
TagsNode.createSheet.name.name=Name
TagsNode.createSheet.name.displayName=Name
ViewsNode.name.text=Views
UnsupportedContentNode.createSheet.name.desc=no description
UnsupportedContentNode.createSheet.name.displayName=Name
UnsupportedContentNode.createSheet.name.name=Name
UnsupportedContentNode.displayName=Unsupported Content
ViewsNode.name.text=File Views
ViewsNode.createSheet.name.name=Name
ViewsNode.createSheet.name.displayName=Name
ViewsNode.createSheet.name.desc=no description

View File

@ -18,6 +18,8 @@
*/
package org.sleuthkit.autopsy.datamodel;
import org.sleuthkit.autopsy.datamodel.OsAccounts.OsAccountNode;
/**
* Visitor Pattern interface that goes over Content nodes in the data source
* area of the tree.
@ -50,6 +52,9 @@ interface ContentNodeVisitor<T> {
T visit(BlackboardArtifactNode bban);
T visit(UnsupportedContentNode ucn);
T visit(OsAccountNode bban);
/**
* Visitor with an implementable default behavior for all types. Override
@ -122,5 +127,15 @@ interface ContentNodeVisitor<T> {
public T visit(BlackboardArtifactNode bban) {
return defaultVisit(bban);
}
@Override
public T visit(UnsupportedContentNode ucn) {
return defaultVisit(ucn);
}
@Override
public T visit(OsAccountNode bban) {
return defaultVisit(bban);
}
}
}

View File

@ -32,6 +32,7 @@ import org.sleuthkit.datamodel.Pool;
import org.sleuthkit.datamodel.SlackFile;
import org.sleuthkit.datamodel.SleuthkitItemVisitor;
import org.sleuthkit.datamodel.SleuthkitVisitableItem;
import org.sleuthkit.datamodel.UnsupportedContent;
import org.sleuthkit.datamodel.VirtualDirectory;
import org.sleuthkit.datamodel.Volume;
@ -100,6 +101,11 @@ public class CreateSleuthkitNodeVisitor extends SleuthkitItemVisitor.Default<Abs
return new BlackboardArtifactNode(art);
}
@Override
public AbstractContentNode<? extends Content> visit(UnsupportedContent uc) {
return new UnsupportedContentNode(uc);
}
@Override
protected AbstractContentNode<? extends Content> defaultVisit(SleuthkitVisitableItem di) {
throw new UnsupportedOperationException(NbBundle.getMessage(this.getClass(),

View File

@ -0,0 +1,95 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.datamodel;
import org.openide.nodes.Children;
import org.openide.util.NbBundle;
import org.sleuthkit.datamodel.BlackboardArtifact;
/**
* Analysis Results node support.
*/
@NbBundle.Messages({
"DataArtifacts_name=Data Artifacts",})
public class DataArtifacts implements AutopsyVisitableItem {
/**
* Returns the name of this node that is the key in the children object.
*
* @return The name of this node that is the key in the children object.
*/
public static String getName() {
return Bundle.DataArtifacts_name();
}
/**
* Parent node of all data artifacts.
*/
static class RootNode extends Artifacts.BaseArtifactNode {
/**
* Main constructor.
*
* @param filteringDSObjId The data source object id for which results
* should be filtered. If no filtering should
* occur, this number should be less than or
* equal to 0.
*/
RootNode(long filteringDSObjId) {
super(Children.create(new Artifacts.TypeFactory(BlackboardArtifact.Category.DATA_ARTIFACT, filteringDSObjId), true),
"org/sleuthkit/autopsy/images/extracted_content.png",
DataArtifacts.getName(),
DataArtifacts.getName());
}
}
private final long datasourceObjId;
/**
* Main constructor.
*/
public DataArtifacts() {
this(0);
}
/**
* Main constructor.
*
* @param dsObjId The data source object id.
*/
public DataArtifacts(long dsObjId) {
this.datasourceObjId = dsObjId;
}
@Override
public <T> T accept(AutopsyItemVisitor<T> visitor) {
return visitor.visit(this);
}
/**
* Returns whether or not there is a data source object for which results
* should be filtered.
*
* @return Whether or not there is a data source object for which results
* should be filtered.
*/
Long getFilteringDataSourceObjId() {
return datasourceObjId;
}
}

View File

@ -35,6 +35,7 @@ import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction;
import org.sleuthkit.autopsy.actions.ReplaceBlackboardArtifactTagAction;
import org.sleuthkit.autopsy.actions.ReplaceContentTagAction;
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
import org.sleuthkit.autopsy.datamodel.OsAccounts.OsAccountNode;
import org.sleuthkit.autopsy.datamodel.Reports.ReportNode;
import org.sleuthkit.autopsy.directorytree.ExportCSVAction;
import org.sleuthkit.autopsy.directorytree.ExternalViewerAction;
@ -53,6 +54,7 @@ import org.sleuthkit.datamodel.File;
import org.sleuthkit.datamodel.LayoutFile;
import org.sleuthkit.datamodel.LocalFile;
import org.sleuthkit.datamodel.LocalDirectory;
import org.sleuthkit.datamodel.OsAccount;
import org.sleuthkit.datamodel.Report;
import org.sleuthkit.datamodel.SlackFile;
import org.sleuthkit.datamodel.VirtualDirectory;
@ -447,6 +449,18 @@ public class DataModelActionsFactory {
return actionsList;
}
public static List<Action> getActions(OsAccount osAccount) {
List<Action> actionsList = new ArrayList<>();
OsAccountNode node = new OsAccountNode(osAccount);
actionsList.add(null); // creates a menu separator
actionsList.add(new NewWindowViewAction(VIEW_IN_NEW_WINDOW, node));
actionsList.add(null);
actionsList.add(ExportCSVAction.getInstance());
actionsList.addAll(ContextMenuExtensionPoint.getActions());
return actionsList;
}
public static List<Action> getActions(Content content, boolean isArtifactSource) {
if (content instanceof File) {
return getActions((File) content, isArtifactSource);
@ -464,7 +478,9 @@ public class DataModelActionsFactory {
return getActions((SlackFile) content, isArtifactSource);
} else if (content instanceof Report) {
return getActions((Report) content, isArtifactSource);
} else {
} else if (content instanceof OsAccount) {
return getActions((OsAccount) content, isArtifactSource);
}else {
return new ArrayList<>();
}
}

View File

@ -30,9 +30,8 @@ import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.Image;
import org.sleuthkit.datamodel.LocalFilesDataSource;
/**
* Data source grouping node - an optional grouping node in the data tree view
* Data source grouping node - an optional grouping node in the data tree view
*
*/
class DataSourceGroupingNode extends DisplayableItemNode {
@ -46,8 +45,8 @@ class DataSourceGroupingNode extends DisplayableItemNode {
*/
DataSourceGroupingNode(DataSource dataSource) {
super (Optional.ofNullable(createDSGroupingNodeChildren(dataSource))
.orElse(new RootContentChildren(Arrays.asList(Collections.EMPTY_LIST))),
super(Optional.ofNullable(createDSGroupingNodeChildren(dataSource))
.orElse(new RootContentChildren(Arrays.asList(Collections.EMPTY_LIST))),
Lookups.singleton(dataSource));
if (dataSource instanceof Image) {
@ -78,10 +77,12 @@ class DataSourceGroupingNode extends DisplayableItemNode {
return new RootContentChildren(Arrays.asList(
new DataSources(dsObjId),
new Views(Case.getCurrentCaseThrows().getSleuthkitCase(), dsObjId),
new Results(Case.getCurrentCaseThrows().getSleuthkitCase(), dsObjId),
new Tags(dsObjId) )
);
new DataArtifacts(dsObjId),
new AnalysisResults(dsObjId),
new OsAccounts(Case.getCurrentCaseThrows().getSleuthkitCase(), dsObjId),
new Tags(dsObjId),
new Reports()
));
} catch (NoCurrentCaseException ex) {
logger.log(Level.SEVERE, "Error getting open case.", ex); //NON-NLS

View File

@ -22,7 +22,9 @@ import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.stream.Collectors;
import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
@ -48,13 +50,23 @@ public class DataSourcesByTypeNode extends DisplayableItemNode {
*/
public static class DataSourcesByTypeChildren extends ChildFactory.Detachable<HostDataSources> {
private static final Set<Case.Events> UPDATE_EVTS = EnumSet.of(
Case.Events.DATA_SOURCE_ADDED,
Case.Events.HOSTS_ADDED,
Case.Events.HOSTS_DELETED,
Case.Events.HOSTS_CHANGED);
private static final Set<String> UPDATE_EVT_STRS = UPDATE_EVTS.stream()
.map(evt -> evt.name())
.collect(Collectors.toSet());
private static final Logger logger = Logger.getLogger(DataSourcesByTypeChildren.class.getName());
private final PropertyChangeListener pcl = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
String eventType = evt.getPropertyName();
if (eventType.equals(Case.Events.DATA_SOURCE_ADDED.toString())) {
if (UPDATE_EVT_STRS.contains(eventType)) {
refresh(true);
}
}
@ -62,18 +74,18 @@ public class DataSourcesByTypeNode extends DisplayableItemNode {
@Override
protected void addNotify() {
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED), pcl);
Case.addEventTypeSubscriber(UPDATE_EVTS, pcl);
}
@Override
protected void removeNotify() {
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED), pcl);
Case.removeEventTypeSubscriber(UPDATE_EVTS, pcl);
}
@Override
protected boolean createKeys(List<HostDataSources> toPopulate) {
try {
Case.getCurrentCaseThrows().getSleuthkitCase().getHostManager().getHosts().stream()
Case.getCurrentCaseThrows().getSleuthkitCase().getHostManager().getAllHosts().stream()
.map(HostDataSources::new)
.sorted()
.forEach(toPopulate::add);

View File

@ -88,16 +88,11 @@ public interface DisplayableItemNodeVisitor<T> {
T visit(RecentFilesFilterNode rffn);
/*
* Extracted Results Area
*/
T visit(ResultsNode rn);
T visit(BlackboardArtifactNode ban);
T visit(ExtractedContent.TypeNode atn);
T visit(Artifacts.TypeNode atn);
T visit(ExtractedContent.RootNode ecn);
T visit(Artifacts.BaseArtifactNode ecn);
T visit(KeywordHits.RootNode khrn);
@ -203,6 +198,11 @@ public interface DisplayableItemNodeVisitor<T> {
T visit(DataSourcesByTypeNode node);
/*
* Unsupported node
*/
T visit(UnsupportedContentNode ucn);
/**
* Visitor with an implementable default behavior for all types. Override
* specific visit types to not use the default behavior.
@ -296,12 +296,12 @@ public interface DisplayableItemNodeVisitor<T> {
}
@Override
public T visit(ExtractedContent.TypeNode atn) {
public T visit(Artifacts.TypeNode atn) {
return defaultVisit(atn);
}
@Override
public T visit(ExtractedContent.RootNode ecn) {
public T visit(Artifacts.BaseArtifactNode ecn) {
return defaultVisit(ecn);
}
@ -400,11 +400,6 @@ public interface DisplayableItemNodeVisitor<T> {
return defaultVisit(dataSourceGroupingNode);
}
@Override
public T visit(ResultsNode rn) {
return defaultVisit(rn);
}
@Override
public T visit(FileTypesNode ft) {
return defaultVisit(ft);
@ -574,5 +569,10 @@ public interface DisplayableItemNodeVisitor<T> {
public T visit(PersonGroupingNode node) {
return defaultVisit(node);
}
@Override
public T visit(UnsupportedContentNode node) {
return defaultVisit(node);
}
}
}

View File

@ -44,10 +44,12 @@ import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
import org.sleuthkit.datamodel.BlackboardArtifact;
import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_EMAIL_MSG;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.autopsy.datamodel.Artifacts.UpdatableCountTypeNode;
/**
* Support for TSK_EMAIL_MSG nodes and displaying emails in the directory tree.
@ -57,8 +59,7 @@ import org.sleuthkit.datamodel.TskCoreException;
*/
public class EmailExtracted implements AutopsyVisitableItem {
private static final String LABEL_NAME = BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getLabel();
private static final String DISPLAY_NAME = BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getDisplayName();
private static final String LABEL_NAME = BlackboardArtifact.Type.TSK_EMAIL_MSG.getTypeName();
private static final Logger logger = Logger.getLogger(EmailExtracted.class.getName());
private static final String MAIL_ACCOUNT = NbBundle.getMessage(EmailExtracted.class, "EmailExtracted.mailAccount.text");
private static final String MAIL_FOLDER = NbBundle.getMessage(EmailExtracted.class, "EmailExtracted.mailFolder.text");
@ -77,7 +78,7 @@ public class EmailExtracted implements AutopsyVisitableItem {
*/
public static final Map<String, String> parsePath(String path) {
Map<String, String> parsed = new HashMap<>();
String[] split = path.split(MAIL_PATH_SEPARATOR);
String[] split = path == null ? new String[0] : path.split(MAIL_PATH_SEPARATOR);
if (split.length < 4) {
parsed.put(MAIL_ACCOUNT, NbBundle.getMessage(EmailExtracted.class, "EmailExtracted.defaultAcct.text"));
parsed.put(MAIL_FOLDER, NbBundle.getMessage(EmailExtracted.class, "EmailExtracted.defaultFolder.text"));
@ -147,50 +148,52 @@ public class EmailExtracted implements AutopsyVisitableItem {
@SuppressWarnings("deprecation")
public void update() {
synchronized (accounts) {
accounts.clear();
}
// clear cache if no case
if (skCase == null) {
synchronized (accounts) {
accounts.clear();
}
return;
}
int artId = BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID();
// get artifact id and path (if present) of all email artifacts
int emailArtifactId = BlackboardArtifact.Type.TSK_EMAIL_MSG.getTypeID();
int pathAttrId = BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH.getTypeID();
String query = "SELECT value_text,blackboard_attributes.artifact_id,attribute_type_id " //NON-NLS
+ "FROM blackboard_attributes,blackboard_artifacts WHERE " //NON-NLS
+ "attribute_type_id=" + pathAttrId //NON-NLS
+ " AND blackboard_attributes.artifact_id=blackboard_artifacts.artifact_id" //NON-NLS
+ " AND blackboard_artifacts.artifact_type_id=" + artId; //NON-NLS
if (filteringDSObjId > 0) {
query += " AND blackboard_artifacts.data_source_obj_id = " + filteringDSObjId;
}
String query = "SELECT \n"
+ " art.artifact_id AS artifact_id,\n"
+ " (SELECT value_text FROM blackboard_attributes attr\n"
+ " WHERE attr.artifact_id = art.artifact_id AND attr.attribute_type_id = " + pathAttrId + "\n"
+ " LIMIT 1) AS value_text\n"
+ "FROM \n"
+ " blackboard_artifacts art\n"
+ " WHERE art.artifact_type_id = " + emailArtifactId + "\n"
+ ((filteringDSObjId > 0) ? " AND art.data_source_obj_id = " + filteringDSObjId : "");
// form hierarchy of account -> folder -> account id
Map<String, Map<String, List<Long>>> newMapping = new HashMap<>();
try (CaseDbQuery dbQuery = skCase.executeQuery(query)) {
ResultSet resultSet = dbQuery.getResultSet();
synchronized (accounts) {
while (resultSet.next()) {
final String path = resultSet.getString("value_text"); //NON-NLS
final long artifactId = resultSet.getLong("artifact_id"); //NON-NLS
final Map<String, String> parsedPath = parsePath(path);
final String account = parsedPath.get(MAIL_ACCOUNT);
final String folder = parsedPath.get(MAIL_FOLDER);
while (resultSet.next()) {
Long artifactId = resultSet.getLong("artifact_id");
Map<String, String> accountFolderMap = parsePath(resultSet.getString("value_text"));
String account = accountFolderMap.get(MAIL_ACCOUNT);
String folder = accountFolderMap.get(MAIL_FOLDER);
Map<String, List<Long>> folders = accounts.get(account);
if (folders == null) {
folders = new LinkedHashMap<>();
accounts.put(account, folders);
}
List<Long> messages = folders.get(folder);
if (messages == null) {
messages = new ArrayList<>();
folders.put(folder, messages);
}
messages.add(artifactId);
}
Map<String, List<Long>> folders = newMapping.computeIfAbsent(account, (str) -> new LinkedHashMap<>());
List<Long> messages = folders.computeIfAbsent(folder, (str) -> new ArrayList<>());
messages.add(artifactId);
}
} catch (TskCoreException | SQLException ex) {
logger.log(Level.WARNING, "Cannot initialize email extraction: ", ex); //NON-NLS
}
synchronized (accounts) {
accounts.clear();
accounts.putAll(newMapping);
}
setChanged();
notifyObservers();
}
@ -200,12 +203,16 @@ public class EmailExtracted implements AutopsyVisitableItem {
* Mail root node grouping all mail accounts, supports account-> folder
* structure
*/
public class RootNode extends DisplayableItemNode {
public class RootNode extends UpdatableCountTypeNode {
public RootNode() {
super(Children.create(new AccountFactory(), true), Lookups.singleton(DISPLAY_NAME));
super(Children.create(new AccountFactory(), true),
Lookups.singleton(TSK_EMAIL_MSG.getDisplayName()),
TSK_EMAIL_MSG.getDisplayName(),
filteringDSObjId,
TSK_EMAIL_MSG);
//super(Children.create(new AccountFactory(), true), Lookups.singleton(DISPLAY_NAME));
super.setName(LABEL_NAME);
super.setDisplayName(DISPLAY_NAME);
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/mail-icon-16.png"); //NON-NLS
emailResults.update();
}
@ -272,7 +279,7 @@ public class EmailExtracted implements AutopsyVisitableItem {
* for the event to have a null oldValue.
*/
ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
if (null != eventData && eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID()) {
if (null != eventData && eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.Type.TSK_EMAIL_MSG.getTypeID()) {
emailResults.update();
}
} catch (NoCurrentCaseException notUsed) {

View File

@ -1,492 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.datamodel;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.openide.nodes.Sheet;
import org.openide.util.NbBundle;
import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.utils.IconsUtil;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
import org.sleuthkit.datamodel.Blackboard;
import org.sleuthkit.datamodel.BlackboardArtifact;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_TL_EVENT;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_DATA_SOURCE_USAGE;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_GEN_INFO;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.autopsy.guiutils.RefreshThrottler;
/**
* Parent of the "extracted content" artifacts to be displayed in the tree.
* Other artifacts are displayed under other more specific parents.
*/
public class ExtractedContent implements AutopsyVisitableItem {
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED);
public static final String NAME = NbBundle.getMessage(RootNode.class, "ExtractedContentNode.name.text");
private final long filteringDSObjId; // 0 if not filtering/grouping by data source
private SleuthkitCase skCase; // set to null after case has been closed
private Blackboard blackboard;
/**
* Constructs extracted content object
*
* @param skCase Case DB
*/
public ExtractedContent(SleuthkitCase skCase) {
this(skCase, 0);
}
/**
* Constructs extracted content object
*
* @param skCase Case DB
* @param objId Object id of the parent datasource
*/
public ExtractedContent(SleuthkitCase skCase, long objId) {
this.skCase = skCase;
this.filteringDSObjId = objId;
this.blackboard = skCase.getBlackboard();
}
@Override
public <T> T accept(AutopsyItemVisitor<T> visitor) {
return visitor.visit(this);
}
public SleuthkitCase getSleuthkitCase() {
return skCase;
}
public class RootNode extends DisplayableItemNode {
public RootNode(SleuthkitCase skCase) {
super(Children.create(new TypeFactory(), true), Lookups.singleton(NAME));
super.setName(NAME);
super.setDisplayName(NAME);
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/extracted_content.png"); //NON-NLS
}
@Override
public boolean isLeafTypeNode() {
return false;
}
@Override
public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
return visitor.visit(this);
}
@Override
protected Sheet createSheet() {
Sheet sheet = super.createSheet();
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
if (sheetSet == null) {
sheetSet = Sheet.createPropertiesSet();
sheet.put(sheetSet);
}
sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ExtractedContentNode.createSheet.name.name"),
NbBundle.getMessage(this.getClass(), "ExtractedContentNode.createSheet.name.displayName"),
NbBundle.getMessage(this.getClass(), "ExtractedContentNode.createSheet.name.desc"),
NAME));
return sheet;
}
@Override
public String getItemType() {
return getClass().getName();
}
}
/**
* Creates the children for the ExtractedContent area of the results tree.
* This area has all of the blackboard artifacts that are not displayed in a
* more specific form elsewhere in the tree.
*/
private class TypeFactory extends ChildFactory.Detachable<BlackboardArtifact.Type> implements RefreshThrottler.Refresher {
private final ArrayList<BlackboardArtifact.Type> doNotShow = new ArrayList<>();
// maps the artifact type to its child node
private final HashMap<BlackboardArtifact.Type, TypeNode> typeNodeList = new HashMap<>();
/**
* RefreshThrottler is used to limit the number of refreshes performed
* when CONTENT_CHANGED and DATA_ADDED ingest module events are
* received.
*/
private final RefreshThrottler refreshThrottler = new RefreshThrottler(this);
@SuppressWarnings("deprecation")
TypeFactory() {
super();
// these are shown in other parts of the UI
doNotShow.add(new BlackboardArtifact.Type(TSK_GEN_INFO));
doNotShow.add(new BlackboardArtifact.Type(TSK_EMAIL_MSG));
doNotShow.add(new BlackboardArtifact.Type(TSK_HASHSET_HIT));
doNotShow.add(new BlackboardArtifact.Type(TSK_KEYWORD_HIT));
doNotShow.add(new BlackboardArtifact.Type(TSK_INTERESTING_FILE_HIT));
doNotShow.add(new BlackboardArtifact.Type(TSK_INTERESTING_ARTIFACT_HIT));
doNotShow.add(new BlackboardArtifact.Type(TSK_ACCOUNT));
doNotShow.add(new BlackboardArtifact.Type(TSK_DATA_SOURCE_USAGE));
doNotShow.add(new BlackboardArtifact.Type(TSK_DOWNLOAD_SOURCE));
doNotShow.add(new BlackboardArtifact.Type(TSK_TL_EVENT));
//This is not meant to be shown in the UI at all. It is more of a meta artifact.
doNotShow.add(new BlackboardArtifact.Type(TSK_ASSOCIATED_OBJECT));
}
private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> {
String eventType = evt.getPropertyName();
if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
// case was closed. Remove listeners so that we don't get called with a stale case handle
if (evt.getNewValue() == null) {
removeNotify();
skCase = null;
}
} else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
|| eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
/**
* This is a stop gap measure until a different way of handling
* the closing of cases is worked out. Currently, remote events
* may be received for a case that is already closed.
*/
try {
Case.getCurrentCaseThrows();
refresh(false);
} catch (NoCurrentCaseException notUsed) {
/**
* Case is closed, do nothing.
*/
}
}
};
@Override
protected void addNotify() {
refreshThrottler.registerForIngestModuleEvents();
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl);
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl);
}
@Override
protected void removeNotify() {
refreshThrottler.unregisterEventListener();
IngestManager.getInstance().removeIngestJobEventListener(pcl);
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl);
typeNodeList.clear();
}
@Override
protected boolean createKeys(List<BlackboardArtifact.Type> list) {
if (skCase != null) {
try {
List<BlackboardArtifact.Type> types = (filteringDSObjId > 0)
? blackboard.getArtifactTypesInUse(filteringDSObjId)
: skCase.getArtifactTypesInUse();
types.removeAll(doNotShow);
Collections.sort(types,
new Comparator<BlackboardArtifact.Type>() {
@Override
public int compare(BlackboardArtifact.Type a, BlackboardArtifact.Type b) {
return a.getDisplayName().compareTo(b.getDisplayName());
}
});
list.addAll(types);
// the create node method will get called only for new types
// refresh the counts if we already created them from a previous update
for (BlackboardArtifact.Type art : types) {
TypeNode node = typeNodeList.get(art);
if (node != null) {
node.updateDisplayName();
}
}
} catch (TskCoreException ex) {
Logger.getLogger(TypeFactory.class.getName()).log(Level.SEVERE, "Error getting list of artifacts in use: " + ex.getLocalizedMessage()); //NON-NLS
}
}
return true;
}
@Override
protected Node createNodeForKey(BlackboardArtifact.Type key) {
TypeNode node = new TypeNode(key);
typeNodeList.put(key, node);
return node;
}
@Override
public void refresh() {
refresh(false);
}
@Override
public boolean isRefreshRequired(PropertyChangeEvent evt) {
String eventType = evt.getPropertyName();
if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
/**
* This is a stop gap measure until a different way of handling
* the closing of cases is worked out. Currently, remote events
* may be received for a case that is already closed.
*/
try {
Case.getCurrentCaseThrows();
/**
* Due to some unresolved issues with how cases are closed,
* it is possible for the event to have a null oldValue if
* the event is a remote event.
*/
final ModuleDataEvent event = (ModuleDataEvent) evt.getOldValue();
if (null != event && !(this.doNotShow.contains(event.getBlackboardArtifactType()))) {
return true;
}
} catch (NoCurrentCaseException notUsed) {
/**
* Case is closed, do nothing.
*/
}
}
return false;
}
}
/**
* Node encapsulating blackboard artifact type. This is used on the
* left-hand navigation side of the Autopsy UI as the parent node for all of
* the artifacts of a given type. Its children will be
* BlackboardArtifactNode objects.
*/
public class TypeNode extends DisplayableItemNode {
private final BlackboardArtifact.Type type;
private long childCount = 0;
TypeNode(BlackboardArtifact.Type type) {
super(Children.create(new ArtifactFactory(type), true), Lookups.singleton(type.getDisplayName()));
super.setName(type.getTypeName());
this.type = type;
String iconPath = IconsUtil.getIconFilePath(type.getTypeID());
setIconBaseWithExtension(iconPath != null && iconPath.charAt(0) == '/' ? iconPath.substring(1) : iconPath);
updateDisplayName();
}
final void updateDisplayName() {
if (skCase == null) {
return;
}
// NOTE: This completely destroys our lazy-loading ideal
// a performance increase might be had by adding a
// "getBlackboardArtifactCount()" method to skCase
try {
this.childCount = (filteringDSObjId > 0)
? blackboard.getArtifactsCount(type.getTypeID(), filteringDSObjId)
: skCase.getBlackboardArtifactsTypeCount(type.getTypeID());
} catch (TskCoreException ex) {
Logger.getLogger(TypeNode.class.getName())
.log(Level.WARNING, "Error getting child count", ex); //NON-NLS
}
super.setDisplayName(type.getDisplayName() + " \u200E(\u200E" + childCount + ")\u200E");
}
@Override
protected Sheet createSheet() {
Sheet sheet = super.createSheet();
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
if (sheetSet == null) {
sheetSet = Sheet.createPropertiesSet();
sheet.put(sheetSet);
}
sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.artType.name"),
NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.artType.displayName"),
NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.artType.desc"),
type.getDisplayName()));
sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.childCnt.name"),
NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.childCnt.displayName"),
NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.childCnt.desc"),
childCount));
return sheet;
}
@Override
public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
return visitor.visit(this);
}
@Override
public boolean isLeafTypeNode() {
return true;
}
@Override
public String getItemType() {
return getClass().getName() + type.getDisplayName();
}
}
/**
* Creates children for a given artifact type
*/
private class ArtifactFactory extends BaseChildFactory<BlackboardArtifact> implements RefreshThrottler.Refresher {
private final BlackboardArtifact.Type type;
/**
* RefreshThrottler is used to limit the number of refreshes performed
* when CONTENT_CHANGED and DATA_ADDED ingest module events are
* received.
*/
private final RefreshThrottler refreshThrottler = new RefreshThrottler(this);
ArtifactFactory(BlackboardArtifact.Type type) {
super(type.getTypeName());
this.type = type;
}
private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> {
String eventType = evt.getPropertyName();
if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
|| eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
/**
* Checking for a current case is a stop gap measure until a
* different way of handling the closing of cases is worked out.
* Currently, remote events may be received for a case that is
* already closed.
*/
try {
Case.getCurrentCaseThrows();
refresh(false);
} catch (NoCurrentCaseException notUsed) {
/**
* Case is closed, do nothing.
*/
}
}
};
@Override
protected void onAdd() {
refreshThrottler.registerForIngestModuleEvents();
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl);
}
@Override
protected void onRemove() {
refreshThrottler.unregisterEventListener();
IngestManager.getInstance().removeIngestJobEventListener(pcl);
}
@Override
protected Node createNodeForKey(BlackboardArtifact key) {
return new BlackboardArtifactNode(key);
}
@Override
protected List<BlackboardArtifact> makeKeys() {
if (skCase != null) {
try {
List<BlackboardArtifact> arts;
if (filteringDSObjId > 0) {
arts = blackboard.getArtifacts(type.getTypeID(), filteringDSObjId);
} else {
arts = skCase.getBlackboardArtifacts(type.getTypeID());
}
for (BlackboardArtifact art : arts) {
//Cache attributes while we are off the EDT.
//See JIRA-5969
art.getAttributes();
}
return arts;
} catch (TskCoreException ex) {
Logger.getLogger(ArtifactFactory.class.getName()).log(Level.SEVERE, "Couldn't get blackboard artifacts from database", ex); //NON-NLS
}
}
return Collections.emptyList();
}
@Override
public void refresh() {
refresh(false);
}
@Override
public boolean isRefreshRequired(PropertyChangeEvent evt) {
String eventType = evt.getPropertyName();
if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
/**
* Checking for a current case is a stop gap measure until a
* different way of handling the closing of cases is worked out.
* Currently, remote events may be received for a case that is
* already closed.
*/
try {
Case.getCurrentCaseThrows();
/**
* Even with the check above, it is still possible that the
* case will be closed in a different thread before this
* code executes. If that happens, it is possible for the
* event to have a null oldValue.
*/
final ModuleDataEvent event = (ModuleDataEvent) evt.getOldValue();
if (null != event && event.getBlackboardArtifactType().equals(type)) {
return true;
}
} catch (NoCurrentCaseException notUsed) {
/**
* Case is closed, do nothing.
*/
}
}
return false;
}
}
}

View File

@ -34,6 +34,8 @@ import org.openide.nodes.Sheet;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.AnalysisResult;
import org.sleuthkit.datamodel.AnalysisResultAdded;
@ -76,16 +78,10 @@ public final class FileTypes implements AutopsyVisitableItem {
*/
private boolean showCounts = true;
private final SleuthkitCase skCase;
private final long datasourceObjId;
FileTypes(SleuthkitCase skCase) {
this(skCase, 0);
}
FileTypes(SleuthkitCase skCase, long dsObjId) {
this.skCase = skCase;
FileTypes(long dsObjId) {
this.datasourceObjId = dsObjId;
updateShowCounts();
}
@ -95,10 +91,6 @@ public final class FileTypes implements AutopsyVisitableItem {
return visitor.visit(this);
}
SleuthkitCase getSleuthkitCase() {
return skCase;
}
long filteringDataSourceObjId() {
return this.datasourceObjId;
}
@ -112,10 +104,10 @@ public final class FileTypes implements AutopsyVisitableItem {
*/
if (showCounts) {
try {
if (skCase.countFilesWhere("1=1") > NODE_COUNT_FILE_TABLE_THRESHOLD) { //NON-NLS
if (Case.getCurrentCaseThrows().getSleuthkitCase().countFilesWhere("1=1") > NODE_COUNT_FILE_TABLE_THRESHOLD) { //NON-NLS
showCounts = false;
}
} catch (TskCoreException tskCoreException) {
} catch (NoCurrentCaseException | TskCoreException tskCoreException) {
showCounts = false;
logger.log(Level.SEVERE, "Error counting files.", tskCoreException); //NON-NLS
}
@ -397,8 +389,8 @@ public final class FileTypes implements AutopsyVisitableItem {
}
@Override
public DataArtifact newDataArtifact(BlackboardArtifact.Type artifactType, Collection<BlackboardAttribute> attributesList, OsAccount osAccount) throws TskCoreException {
return content.newDataArtifact(artifactType, attributesList, osAccount);
public DataArtifact newDataArtifact(BlackboardArtifact.Type artifactType, Collection<BlackboardAttribute> attributesList, Long osAccountId) throws TskCoreException {
return content.newDataArtifact(artifactType, attributesList, osAccountId);
}
@Override

View File

@ -34,6 +34,7 @@ import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.openide.nodes.Sheet;
import org.openide.util.Exceptions;
import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
import org.openide.util.lookup.Lookups;
@ -58,16 +59,18 @@ public final class FileTypesByExtension implements AutopsyVisitableItem {
private final static Logger logger = Logger.getLogger(FileTypesByExtension.class.getName());
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED);
private static final Set<IngestManager.IngestModuleEvent> INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestModuleEvent.CONTENT_CHANGED);
private final SleuthkitCase skCase;
private final FileTypes typesRoot;
public FileTypesByExtension(FileTypes typesRoot) {
this.skCase = typesRoot.getSleuthkitCase();
this.typesRoot = typesRoot;
}
public SleuthkitCase getSleuthkitCase() {
return this.skCase;
try {
return Case.getCurrentCaseThrows().getSleuthkitCase();
} catch (NoCurrentCaseException ex) {
return null;
}
}
@Override
@ -404,7 +407,11 @@ public final class FileTypesByExtension implements AutopsyVisitableItem {
@Override
long calculateChildCount() throws TskCoreException {
return skCase.countFilesWhere(createQuery(filter));
try {
return Case.getCurrentCaseThrows().getSleuthkitCase().countFilesWhere(createQuery(filter));
} catch (NoCurrentCaseException ex) {
throw new TskCoreException("No open case.", ex);
}
}
}

View File

@ -63,7 +63,7 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
private final static Logger logger = Logger.getLogger(FileTypesByMimeType.class.getName());
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED);
private final SleuthkitCase skCase;
/**
* The nodes of this tree will be determined dynamically by the mimetypes
* which exist in the database. This hashmap will store them with the media
@ -130,11 +130,8 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
+ " GROUP BY mime_type";
synchronized (existingMimeTypeCounts) {
existingMimeTypeCounts.clear();
if (skCase == null) {
return;
}
try (SleuthkitCase.CaseDbQuery dbQuery = skCase.executeQuery(query)) {
try
(SleuthkitCase.CaseDbQuery dbQuery = Case.getCurrentCaseThrows().getSleuthkitCase().executeQuery(query)) {
ResultSet resultSet = dbQuery.getResultSet();
while (resultSet.next()) {
final String mime_type = resultSet.getString("mime_type"); //NON-NLS
@ -149,7 +146,7 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
}
}
}
} catch (TskCoreException | SQLException ex) {
} catch (NoCurrentCaseException | TskCoreException | SQLException ex) {
logger.log(Level.SEVERE, "Unable to populate File Types by MIME Type tree view from DB: ", ex); //NON-NLS
}
}
@ -159,7 +156,6 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
}
FileTypesByMimeType(FileTypes typesRoot) {
this.skCase = typesRoot.getSleuthkitCase();
this.typesRoot = typesRoot;
this.pcl = (PropertyChangeEvent evt) -> {
String eventType = evt.getPropertyName();
@ -497,9 +493,10 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
@Override
protected List<FileTypesKey> makeKeys() {
try {
return skCase.findAllFilesWhere(createBaseWhereExpr() + " AND mime_type = '" + mimeType + "'")
return Case.getCurrentCaseThrows().getSleuthkitCase()
.findAllFilesWhere(createBaseWhereExpr() + " AND mime_type = '" + mimeType + "'")
.stream().map(f -> new FileTypesKey(f)).collect(Collectors.toList()); //NON-NLS
} catch (TskCoreException ex) {
} catch (NoCurrentCaseException | TskCoreException ex) {
logger.log(Level.SEVERE, "Couldn't get search results", ex); //NON-NLS
}
return Collections.emptyList();

View File

@ -46,19 +46,20 @@ import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_HASHSET_HIT;
import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.autopsy.datamodel.Artifacts.UpdatableCountTypeNode;
/**
* Hash set hits node support. Inner classes have all of the nodes in the tree.
*/
public class HashsetHits implements AutopsyVisitableItem {
private static final String HASHSET_HITS = BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getLabel();
private static final String DISPLAY_NAME = BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getDisplayName();
private static final String HASHSET_HITS = BlackboardArtifact.Type.TSK_HASHSET_HIT.getTypeName();
private static final String DISPLAY_NAME = BlackboardArtifact.Type.TSK_HASHSET_HIT.getDisplayName();
private static final Logger logger = Logger.getLogger(HashsetHits.class.getName());
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED);
private static final Set<IngestManager.IngestModuleEvent> INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestModuleEvent.DATA_ADDED);
@ -134,7 +135,7 @@ public class HashsetHits implements AutopsyVisitableItem {
}
int setNameId = ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID();
int artId = ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID();
int artId = TSK_HASHSET_HIT.getTypeID();
String query = "SELECT value_text,blackboard_attributes.artifact_id,attribute_type_id " //NON-NLS
+ "FROM blackboard_attributes,blackboard_artifacts WHERE " //NON-NLS
+ "attribute_type_id=" + setNameId //NON-NLS
@ -168,10 +169,15 @@ public class HashsetHits implements AutopsyVisitableItem {
/**
* Top-level node for all hash sets
*/
public class RootNode extends DisplayableItemNode {
public class RootNode extends UpdatableCountTypeNode {
public RootNode() {
super(Children.create(new HashsetNameFactory(), true), Lookups.singleton(DISPLAY_NAME));
super(Children.create(new HashsetNameFactory(), true),
Lookups.singleton(DISPLAY_NAME),
DISPLAY_NAME,
filteringDSObjId,
TSK_HASHSET_HIT);
super.setName(HASHSET_HITS);
super.setDisplayName(DISPLAY_NAME);
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/hashset_hits.png"); //NON-NLS
@ -239,7 +245,7 @@ public class HashsetHits implements AutopsyVisitableItem {
* oldValue if the event is a remote event.
*/
ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
if (null != eventData && eventData.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID()) {
if (null != eventData && eventData.getBlackboardArtifactType().getTypeID() == TSK_HASHSET_HIT.getTypeID()) {
hashsetResults.update();
}
} catch (NoCurrentCaseException notUsed) {

View File

@ -51,7 +51,7 @@ public class HostDataSources implements AutopsyVisitableItem, Comparable<HostDat
@Override
public int hashCode() {
return Objects.hashCode(this.host == null ? 0 : this.host.getId());
return Objects.hashCode(this.host == null ? 0 : this.host.getHostId());
}
@Override
@ -66,8 +66,8 @@ public class HostDataSources implements AutopsyVisitableItem, Comparable<HostDat
return false;
}
final HostDataSources other = (HostDataSources) obj;
long thisId = (this.getHost() == null) ? 0 : this.getHost().getId();
long otherId = (other.getHost() == null) ? 0 : other.getHost().getId();
long thisId = (this.getHost() == null) ? 0 : this.getHost().getHostId();
long otherId = (other.getHost() == null) ? 0 : other.getHost().getHostId();
return thisId == otherId;
}

View File

@ -51,7 +51,7 @@ public class HostGrouping implements AutopsyVisitableItem, Comparable<HostGroupi
@Override
public int hashCode() {
return Objects.hashCode(this.host == null ? 0 : this.host.getId());
return Objects.hashCode(this.host == null ? 0 : this.host.getHostId());
}
@Override
@ -66,8 +66,8 @@ public class HostGrouping implements AutopsyVisitableItem, Comparable<HostGroupi
return false;
}
final HostGrouping other = (HostGrouping) obj;
long thisId = (this.getHost() == null) ? 0 : this.getHost().getId();
long otherId = (other.getHost() == null) ? 0 : other.getHost().getId();
long thisId = (this.getHost() == null) ? 0 : this.getHost().getHostId();
long otherId = (other.getHost() == null) ? 0 : other.getHost().getHostId();
return thisId == otherId;
}

View File

@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.datamodel;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Optional;
@ -40,6 +41,7 @@ import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.casemodule.events.HostsChangedEvent;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.hosts.AssociatePersonsMenuAction;
import org.sleuthkit.autopsy.datamodel.hosts.MergeHostMenuAction;
import org.sleuthkit.autopsy.datamodel.hosts.RemoveParentPersonAction;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.Host;
@ -76,13 +78,16 @@ public class HostNode extends DisplayableItemNode {
}
/**
* Listener for handling DATA_SOURCE_ADDED events.
* Listener for handling DATA_SOURCE_ADDED / HOST_DELETED events.
* A host may have been deleted as part of a merge, which means its data sources could
* have moved to a different host requiring a refresh.
*/
private final PropertyChangeListener dataSourceAddedPcl = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
String eventType = evt.getPropertyName();
if (eventType.equals(Case.Events.DATA_SOURCE_ADDED.toString())) {
if (eventType.equals(Case.Events.DATA_SOURCE_ADDED.toString())
|| eventType.equals(Case.Events.HOSTS_DELETED.toString())) {
refresh(true);
}
}
@ -90,12 +95,12 @@ public class HostNode extends DisplayableItemNode {
@Override
protected void addNotify() {
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED), dataSourceAddedPcl);
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED, Case.Events.HOSTS_DELETED), dataSourceAddedPcl);
}
@Override
protected void removeNotify() {
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED), dataSourceAddedPcl);
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED, Case.Events.HOSTS_DELETED), dataSourceAddedPcl);
}
@Override
@ -174,7 +179,7 @@ public class HostNode extends DisplayableItemNode {
String eventType = evt.getPropertyName();
if (hostId != null && eventType.equals(Case.Events.HOSTS_CHANGED.toString()) && evt instanceof HostsChangedEvent) {
((HostsChangedEvent) evt).getNewValue().stream()
.filter(h -> h != null && h.getId() == hostId)
.filter(h -> h != null && h.getHostId() == hostId)
.findFirst()
.ifPresent((newHost) -> {
setName(newHost.getName());
@ -240,7 +245,7 @@ public class HostNode extends DisplayableItemNode {
super(children,
host == null ? Lookups.fixed(displayName) : Lookups.fixed(host, displayName));
hostId = host == null ? null : host.getId();
hostId = host == null ? null : host.getHostId();
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.HOSTS_CHANGED),
WeakListeners.propertyChange(hostChangePcl, this));
super.setName(displayName);
@ -287,12 +292,15 @@ public class HostNode extends DisplayableItemNode {
"HostNode_actions_removeFromPerson=Remove from person ({0})"})
public Action[] getActions(boolean context) {
Optional<Person> parent = Optional.empty();
List<Action> actionsList = new ArrayList<>();
// if there is a host, then provide actions
if (this.host != null) {
// Add the appropriate Person action
Optional<Person> parent;
try {
parent = Case.getCurrentCaseThrows().getSleuthkitCase().getHostManager().getPerson(this.host);
parent = Case.getCurrentCaseThrows().getSleuthkitCase().getPersonManager().getPerson(this.host);
} catch (NoCurrentCaseException | TskCoreException ex) {
logger.log(Level.WARNING, String.format("Error fetching parent person of host: %s", this.host.getName() == null ? "<null>" : this.host.getName()), ex);
return new Action[0];
@ -300,17 +308,14 @@ public class HostNode extends DisplayableItemNode {
// if there is a parent, only give option to remove parent person.
if (parent.isPresent()) {
return new Action[]{
new RemoveParentPersonAction(this.host, parent.get()),
null
};
actionsList.add(new RemoveParentPersonAction(this.host, parent.get()));
} else {
return new Action[]{
new AssociatePersonsMenuAction(this.host),
null
};
actionsList.add(new AssociatePersonsMenuAction(this.host));
}
// Add option to merge hosts
actionsList.add(new MergeHostMenuAction(this.host));
}
return new Action[0];
return actionsList.toArray(new Action[actionsList.size()]);
}
}

View File

@ -50,6 +50,7 @@ import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.autopsy.datamodel.Artifacts.UpdatableCountTypeNode;
public class InterestingHits implements AutopsyVisitableItem {
@ -110,8 +111,8 @@ public class InterestingHits implements AutopsyVisitableItem {
synchronized (interestingItemsMap) {
interestingItemsMap.clear();
}
loadArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT);
loadArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT);
loadArtifacts(BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT);
loadArtifacts(BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT);
setChanged();
notifyObservers();
}
@ -121,7 +122,7 @@ public class InterestingHits implements AutopsyVisitableItem {
* the interestingItemsMap
*/
@SuppressWarnings("deprecation")
private void loadArtifacts(BlackboardArtifact.ARTIFACT_TYPE artType) {
private void loadArtifacts(BlackboardArtifact.Type artType) {
if (skCase == null) {
return;
}
@ -145,8 +146,8 @@ public class InterestingHits implements AutopsyVisitableItem {
long artifactId = resultSet.getLong("artifact_id"); //NON-NLS
if (!interestingItemsMap.containsKey(value)) {
interestingItemsMap.put(value, new LinkedHashMap<>());
interestingItemsMap.get(value).put(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getDisplayName(), new HashSet<>());
interestingItemsMap.get(value).put(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getDisplayName(), new HashSet<>());
interestingItemsMap.get(value).put(BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT.getDisplayName(), new HashSet<>());
interestingItemsMap.get(value).put(BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT.getDisplayName(), new HashSet<>());
}
interestingItemsMap.get(value).get(artType.getDisplayName()).add(artifactId);
}
@ -165,12 +166,16 @@ public class InterestingHits implements AutopsyVisitableItem {
/**
* Node for the interesting items
*/
public class RootNode extends DisplayableItemNode {
public class RootNode extends UpdatableCountTypeNode {
public RootNode() {
super(Children.create(new SetNameFactory(), true), Lookups.singleton(DISPLAY_NAME));
super(Children.create(new SetNameFactory(), true),
Lookups.singleton(DISPLAY_NAME),
DISPLAY_NAME,
filteringDSObjId,
BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT,
BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT);
super.setName(INTERESTING_ITEMS);
super.setDisplayName(DISPLAY_NAME);
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/interesting_item.png"); //NON-NLS
}
@ -232,8 +237,8 @@ public class InterestingHits implements AutopsyVisitableItem {
* event to have a null oldValue.
*/
ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
if (null != eventData && (eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID()
|| eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID())) {
if (null != eventData && (eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT.getTypeID()
|| eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT.getTypeID())) {
interestingResults.update();
}
} catch (NoCurrentCaseException notUsed) {
@ -314,8 +319,8 @@ public class InterestingHits implements AutopsyVisitableItem {
}
private void updateDisplayName() {
int sizeOfSet = interestingResults.getArtifactIds(setName, BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getDisplayName()).size()
+ interestingResults.getArtifactIds(setName, BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getDisplayName()).size();
int sizeOfSet = interestingResults.getArtifactIds(setName, BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT.getDisplayName()).size()
+ interestingResults.getArtifactIds(setName, BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT.getDisplayName()).size();
super.setDisplayName(setName + " (" + sizeOfSet + ")");
}
@ -374,8 +379,8 @@ public class InterestingHits implements AutopsyVisitableItem {
@Override
protected boolean createKeys(List<String> list) {
list.add(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getDisplayName());
list.add(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getDisplayName());
list.add(BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT.getDisplayName());
list.add(BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT.getDisplayName());
return true;
}
@ -400,8 +405,8 @@ public class InterestingHits implements AutopsyVisitableItem {
this.typeName = typeName;
this.setName = setName;
/**
* We use the combination of setName and typeName as the name of
* the node to ensure that nodes have a unique name. This comes into
* We use the combination of setName and typeName as the name of the
* node to ensure that nodes have a unique name. This comes into
* play when associating paging state with the node.
*/
super.setName(setName + "_" + typeName);
@ -462,9 +467,9 @@ public class InterestingHits implements AutopsyVisitableItem {
private HitFactory(String setName, String typeName) {
/**
* The node name passed to the parent constructor must be the
* same as the name set in the InterestingItemTypeNode constructor,
* i.e. setName underscore typeName
* The node name passed to the parent constructor must be the same
* as the name set in the InterestingItemTypeNode constructor, i.e.
* setName underscore typeName
*/
super(setName + "_" + typeName);
this.setName = setName;

View File

@ -55,6 +55,8 @@ import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery;
import org.sleuthkit.datamodel.TskCoreException;
import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_KEYWORD_HIT;
import org.sleuthkit.autopsy.datamodel.Artifacts.UpdatableCountTypeNode;
/**
* Keyword hits node support
@ -71,7 +73,7 @@ public class KeywordHits implements AutopsyVisitableItem {
@NbBundle.Messages("KeywordHits.singleRegexSearch.text=Single Regular Expression Search")
private static final String SIMPLE_REGEX_SEARCH = KeywordHits_singleRegexSearch_text();
public static final String NAME = BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getLabel();
public static final String NAME = BlackboardArtifact.Type.TSK_KEYWORD_HIT.getTypeName();
private SleuthkitCase skCase;
private final KeywordResults keywordResults;
@ -93,7 +95,7 @@ public class KeywordHits implements AutopsyVisitableItem {
+ "blackboard_attributes.attribute_type_id "//NON-NLS
+ "FROM blackboard_attributes, blackboard_artifacts "//NON-NLS
+ "WHERE blackboard_attributes.artifact_id = blackboard_artifacts.artifact_id "//NON-NLS
+ " AND blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID() //NON-NLS
+ " AND blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.Type.TSK_KEYWORD_HIT.getTypeID() //NON-NLS
+ " AND (attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()//NON-NLS
+ " OR attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID()//NON-NLS
+ " OR attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE.getTypeID()//NON-NLS
@ -375,12 +377,16 @@ public class KeywordHits implements AutopsyVisitableItem {
}
// Created by CreateAutopsyNodeVisitor
public class RootNode extends DisplayableItemNode {
public class RootNode extends UpdatableCountTypeNode {
public RootNode() {
super(Children.create(new ListFactory(), true), Lookups.singleton(KEYWORD_HITS));
super(Children.create(new ListFactory(), true),
Lookups.singleton(KEYWORD_HITS),
KEYWORD_HITS,
filteringDSObjId,
TSK_KEYWORD_HIT);
super.setName(NAME);
super.setDisplayName(KEYWORD_HITS);
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/keyword_hits.png"); //NON-NLS
}
@ -464,7 +470,7 @@ public class KeywordHits implements AutopsyVisitableItem {
* for the event to have a null oldValue.
*/
ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
if (null != eventData && eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()) {
if (null != eventData && eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.Type.TSK_KEYWORD_HIT.getTypeID()) {
keywordResults.update();
}
} catch (NoCurrentCaseException notUsed) {

View File

@ -20,25 +20,36 @@ package org.sleuthkit.autopsy.datamodel;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.ref.WeakReference;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.logging.Level;
import javax.swing.Action;
import org.apache.commons.lang3.tuple.Pair;
import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.openide.nodes.Sheet;
import org.openide.util.Exceptions;
import org.openide.util.NbBundle.Messages;
import org.openide.util.lookup.Lookups;
import org.openide.util.WeakListeners;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.events.OsAccountChangedEvent;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
import org.sleuthkit.autopsy.coreutils.Logger;
import static org.sleuthkit.autopsy.datamodel.AbstractContentNode.backgroundTasksPool;
import org.sleuthkit.autopsy.events.AutopsyEvent;
import org.sleuthkit.datamodel.Host;
import org.sleuthkit.datamodel.OsAccount;
import org.sleuthkit.datamodel.OsAccountRealm;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.Tag;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskDataException;
@ -50,8 +61,9 @@ public final class OsAccounts implements AutopsyVisitableItem {
private static final Logger logger = Logger.getLogger(OsAccounts.class.getName());
private static final String ICON_PATH = "org/sleuthkit/autopsy/images/os-account.png";
private static final SimpleDateFormat DATE_FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
private static final String REALM_DATA_AVAILABLE_EVENT = "REALM_DATA_AVAILABLE_EVENT";
private final SleuthkitCase skCase;
private SleuthkitCase skCase;
private final long filteringDSObjId;
public OsAccounts(SleuthkitCase skCase) {
@ -111,34 +123,47 @@ public final class OsAccounts implements AutopsyVisitableItem {
private final PropertyChangeListener listener = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
refresh(true);
String eventType = evt.getPropertyName();
if (eventType.equals(Case.Events.OS_ACCOUNT_ADDED.toString())
|| eventType.equals(Case.Events.OS_ACCOUNT_REMOVED.toString())) {
refresh(true);
} else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
// case was closed. Remove listeners so that we don't get called with a stale case handle
if (evt.getNewValue() == null) {
removeNotify();
skCase = null;
}
}
}
};
@Override
protected void addNotify() {
Case.addEventTypeSubscriber(Collections.singleton(Case.Events.OS_ACCOUNT_ADDED), listener);
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.OS_ACCOUNT_ADDED, Case.Events.OS_ACCOUNT_REMOVED), listener);
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), listener);
}
@Override
protected void removeNotify() {
Case.removeEventTypeSubscriber(Collections.singleton(Case.Events.OS_ACCOUNT_ADDED), listener);
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), listener);
}
@Override
protected boolean createKeys(List<OsAccount> list) {
try {
if (filteringDSObjId == 0) {
list.addAll(skCase.getOsAccountManager().getAccounts());
} else {
Host host = skCase.getHostManager().getHost(skCase.getDataSource(filteringDSObjId));
list.addAll(skCase.getOsAccountManager().getAccounts(host));
if (skCase != null) {
try {
if (filteringDSObjId == 0) {
list.addAll(skCase.getOsAccountManager().getOsAccounts());
} else {
Host host = skCase.getHostManager().getHostByDataSource(skCase.getDataSource(filteringDSObjId));
list.addAll(skCase.getOsAccountManager().getOsAccounts(host));
}
} catch (TskCoreException | TskDataException ex) {
logger.log(Level.SEVERE, "Unable to retrieve list of OsAccounts for case", ex);
return false;
}
} catch (TskCoreException | TskDataException ex) {
logger.log(Level.SEVERE, "Unable to retrieve list of OsAccounts for case", ex);
return false;
}
return true;
}
@ -151,35 +176,52 @@ public final class OsAccounts implements AutopsyVisitableItem {
/**
* An OsAccount leaf Node.
*/
public static final class OsAccountNode extends DisplayableItemNode {
public static final class OsAccountNode extends AbstractContentNode<OsAccount> {
private OsAccount account;
private final PropertyChangeListener listener = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if(((OsAccountChangedEvent)evt).getOsAccount().getId() == account.getId()) {
// Update the account node to the new one
account = ((OsAccountChangedEvent)evt).getOsAccount();
updateSheet();
if (evt.getPropertyName().equals(Case.Events.OS_ACCOUNT_CHANGED.name())) {
if (((OsAccountChangedEvent) evt).getOsAccount().getId() == account.getId()) {
// Update the account node to the new one
account = ((OsAccountChangedEvent) evt).getOsAccount();
updateSheet();
}
} else if (evt.getPropertyName().equals(REALM_DATA_AVAILABLE_EVENT)) {
OsAccountRealm realm = (OsAccountRealm) evt.getNewValue();
// Currently only 0 or 1 names are supported, this will need
// to be modified if that changes.
List<String> realmNames = realm.getRealmNames();
if (!realmNames.isEmpty()) {
updateSheet(new NodeProperty<>(
Bundle.OsAccounts_accountRealmNameProperty_name(),
Bundle.OsAccounts_accountRealmNameProperty_displayName(),
Bundle.OsAccounts_accountRealmNameProperty_desc(),
""));
}
}
}
};
private final PropertyChangeListener weakListener = WeakListeners.propertyChange(listener, null);
/**
* Constructs a new OsAccountNode.
*
* @param account Node object.
*/
OsAccountNode(OsAccount account) {
super(Children.LEAF, Lookups.fixed(account));
super(account);
this.account = account;
setName(account.getName());
setDisplayName(account.getName());
setIconBaseWithExtension(ICON_PATH);
Case.addEventTypeSubscriber(Collections.singleton(Case.Events.OS_ACCOUNT_CHANGED), listener);
Case.addEventTypeSubscriber(Collections.singleton(Case.Events.OS_ACCOUNT_CHANGED), weakListener);
}
@Override
@ -197,6 +239,15 @@ public final class OsAccounts implements AutopsyVisitableItem {
return getClass().getName();
}
/**
* Returns the OsAccount associated with this node.
*
* @return
*/
OsAccount getOsAccount() {
return account;
}
@Messages({
"OsAccounts_accountNameProperty_name=Name",
"OsAccounts_accountNameProperty_displayName=Name",
@ -213,11 +264,11 @@ public final class OsAccounts implements AutopsyVisitableItem {
})
/**
* Refreshes this node's property sheet.
*/
void updateSheet() {
this.setSheet(createSheet());
}
* Refreshes this node's property sheet.
*/
void updateSheet() {
this.setSheet(createSheet());
}
@Override
protected Sheet createSheet() {
@ -240,13 +291,13 @@ public final class OsAccounts implements AutopsyVisitableItem {
Bundle.OsAccounts_loginNameProperty_displayName(),
Bundle.OsAccounts_loginNameProperty_desc(),
optional.isPresent() ? optional.get() : ""));
optional = account.getRealm().getRealmName();
// Fill with empty string, fetch on background task.
String realmName = "";
propertiesSet.put(new NodeProperty<>(
Bundle.OsAccounts_accountRealmNameProperty_name(),
Bundle.OsAccounts_accountRealmNameProperty_displayName(),
Bundle.OsAccounts_accountRealmNameProperty_desc(),
optional.isPresent() ? optional.get() : ""));
realmName));
Optional<Long> creationTimeValue = account.getCreationTime();
String timeDisplayStr
@ -258,7 +309,91 @@ public final class OsAccounts implements AutopsyVisitableItem {
Bundle.OsAccounts_createdTimeProperty_desc(),
timeDisplayStr));
backgroundTasksPool.submit(new GetOsAccountRealmTask(new WeakReference<>(this), weakListener));
return sheet;
}
@Override
public Action[] getActions(boolean popup) {
List<Action> actionsList = new ArrayList<>();
actionsList.addAll(Arrays.asList(super.getActions(popup)));
actionsList.addAll(DataModelActionsFactory.getActions(account));
return actionsList.toArray(new Action[actionsList.size()]);
}
@Override
protected List<Tag> getAllTagsFromDatabase() {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
protected CorrelationAttributeInstance getCorrelationAttributeInstance() {
return null;
}
@Override
protected Pair<DataResultViewerTable.Score, String> getScorePropertyAndDescription(List<Tag> tags) {
return null;
}
@Override
protected DataResultViewerTable.HasCommentStatus getCommentProperty(List<Tag> tags, CorrelationAttributeInstance attribute) {
return DataResultViewerTable.HasCommentStatus.NO_COMMENT;
}
@Override
protected Pair<Long, String> getCountPropertyAndDescription(CorrelationAttributeInstance.Type attributeType, String attributeValue, String defaultDescription) {
return null;
}
@Override
public <T> T accept(ContentNodeVisitor<T> visitor) {
return visitor.visit(this);
}
/**
* Task for grabbing the osAccount realm.
*/
static class GetOsAccountRealmTask implements Runnable {
private final WeakReference<OsAccountNode> weakNodeRef;
private final PropertyChangeListener listener;
/**
* Construct a new task.
*
* @param weakContentRef
* @param listener
*/
GetOsAccountRealmTask(WeakReference<OsAccountNode> weakContentRef, PropertyChangeListener listener) {
this.weakNodeRef = weakContentRef;
this.listener = listener;
}
@Override
public void run() {
OsAccountNode node = weakNodeRef.get();
if (node == null) {
return;
}
try {
long realmId = node.getOsAccount().getRealmId();
OsAccountRealm realm = Case.getCurrentCase().getSleuthkitCase().getOsAccountRealmManager().getRealmByRealmId(realmId);
if (listener != null && realm != null) {
listener.propertyChange(new PropertyChangeEvent(
AutopsyEvent.SourceType.LOCAL.toString(),
REALM_DATA_AVAILABLE_EVENT,
null, realm));
}
} catch (TskCoreException ex) {
Exceptions.printStackTrace(ex);
}
}
}
}
}

View File

@ -52,7 +52,7 @@ public class PersonGrouping implements AutopsyVisitableItem, Comparable<PersonGr
@Override
public int hashCode() {
return Objects.hashCode(this.person == null ? 0 : this.person.getId());
return Objects.hashCode(this.person == null ? 0 : this.person.getPersonId());
}
@Override
@ -67,8 +67,8 @@ public class PersonGrouping implements AutopsyVisitableItem, Comparable<PersonGr
return false;
}
final PersonGrouping other = (PersonGrouping) obj;
long thisId = (this.getPerson() == null) ? 0 : this.getPerson().getId();
long otherId = (other.getPerson() == null) ? 0 : other.getPerson().getId();
long thisId = (this.getPerson() == null) ? 0 : this.getPerson().getPersonId();
long otherId = (other.getPerson() == null) ? 0 : other.getPerson().getPersonId();
return thisId == otherId;
}

View File

@ -147,7 +147,7 @@ public class PersonGroupingNode extends DisplayableItemNode {
String eventType = evt.getPropertyName();
if (personId != null && eventType.equals(Case.Events.PERSONS_CHANGED.toString()) && evt instanceof PersonsChangedEvent) {
((PersonsChangedEvent) evt).getNewValue().stream()
.filter(p -> p != null && p.getId() == personId)
.filter(p -> p != null && p.getPersonId() == personId)
.findFirst()
.ifPresent((newPerson) -> {
setName(newPerson.getName());
@ -191,7 +191,7 @@ public class PersonGroupingNode extends DisplayableItemNode {
super.setDisplayName(displayName);
this.setIconBaseWithExtension(ICON_PATH);
this.person = person;
this.personId = person == null ? null : person.getId();
this.personId = person == null ? null : person.getPersonId();
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.PERSONS_CHANGED),
WeakListeners.propertyChange(personChangePcl, this));
}

View File

@ -1,52 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.datamodel;
import org.sleuthkit.datamodel.SleuthkitCase;
/**
* Results node support
*/
public class Results implements AutopsyVisitableItem {
private SleuthkitCase skCase;
private final long datasourceObjId;
public Results(SleuthkitCase skCase) {
this(skCase, 0);
}
public Results(SleuthkitCase skCase, long dsObjId) {
this.skCase = skCase;
this.datasourceObjId = dsObjId;
}
@Override
public <T> T accept(AutopsyItemVisitor<T> visitor) {
return visitor.visit(this);
}
public SleuthkitCase getSleuthkitCase() {
return skCase;
}
long filteringDataSourceObjId() {
return datasourceObjId;
}
}

View File

@ -1,97 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.datamodel;
import org.sleuthkit.autopsy.datamodel.accounts.Accounts;
import java.util.Arrays;
import org.openide.nodes.Sheet;
import org.openide.util.NbBundle;
import org.openide.util.lookup.Lookups;
import org.sleuthkit.datamodel.SleuthkitCase;
/**
* Node for the results section of the tree.
*/
public class ResultsNode extends DisplayableItemNode {
@NbBundle.Messages("ResultsNode.name.text=Results")
private static final String NAME = Bundle.ResultsNode_name_text();
public static String getNameIdentifier() {
return NAME;
}
public ResultsNode(SleuthkitCase sleuthkitCase) {
this(sleuthkitCase, 0);
}
public ResultsNode(SleuthkitCase sleuthkitCase, long dsObjId) {
super(
new RootContentChildren(Arrays.asList(
new ExtractedContent(sleuthkitCase, dsObjId ),
new KeywordHits(sleuthkitCase, dsObjId),
new HashsetHits(sleuthkitCase, dsObjId),
new EmailExtracted(sleuthkitCase, dsObjId),
new InterestingHits(sleuthkitCase, dsObjId ),
new Accounts(sleuthkitCase, dsObjId),
new OsAccounts(sleuthkitCase, dsObjId))
),
Lookups.singleton(NAME));
setName(NAME);
setDisplayName(NAME);
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/results.png"); //NON-NLS
}
@Override
public boolean isLeafTypeNode() {
return false;
}
@Override
public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
return visitor.visit(this);
}
@Override
@NbBundle.Messages({
"ResultsNode.createSheet.name.name=Name",
"ResultsNode.createSheet.name.displayName=Name",
"ResultsNode.createSheet.name.desc=no description"})
protected Sheet createSheet() {
Sheet sheet = super.createSheet();
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
if (sheetSet == null) {
sheetSet = Sheet.createPropertiesSet();
sheet.put(sheetSet);
}
sheetSet.put(new NodeProperty<>(Bundle.ResultsNode_createSheet_name_name(),
Bundle.ResultsNode_createSheet_name_displayName(),
Bundle.ResultsNode_createSheet_name_desc(),
NAME
));
return sheet;
}
@Override
public String getItemType() {
return getClass().getName();
}
}

View File

@ -83,11 +83,6 @@ public class RootContentChildren extends Children.Keys<Object> {
*/
static class CreateAutopsyNodeVisitor extends AutopsyItemVisitor.Default<AbstractNode> {
@Override
public ExtractedContent.RootNode visit(ExtractedContent ec) {
return ec.new RootNode(ec.getSleuthkitCase());
}
@Override
public AbstractNode visit(FileTypesByExtension sf) {
return sf.new FileTypesByExtNode(sf.getSleuthkitCase(), null);
@ -148,11 +143,6 @@ public class RootContentChildren extends Children.Keys<Object> {
return new ViewsNode(v.getSleuthkitCase(), v.filteringDataSourceObjId());
}
@Override
public AbstractNode visit(Results results) {
return new ResultsNode(results.getSleuthkitCase(), results.filteringDataSourceObjId());
}
@Override
public AbstractNode visit(FileTypes ft) {
return ft.new FileTypesNode();
@ -204,5 +194,17 @@ public class RootContentChildren extends Children.Keys<Object> {
public AbstractNode visit(DataSourcesByType dataSourceHosts) {
return new DataSourcesByTypeNode();
}
@Override
public AbstractNode visit(AnalysisResults analysisResults) {
return new AnalysisResults.RootNode(
analysisResults.getFilteringDataSourceObjId());
}
@Override
public AbstractNode visit(DataArtifacts dataArtifacts) {
return new DataArtifacts.RootNode(
dataArtifacts.getFilteringDataSourceObjId());
}
}
}

View File

@ -0,0 +1,186 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.datamodel;
import java.util.ArrayList;
import java.util.List;
import javax.swing.Action;
import org.apache.commons.lang3.tuple.Pair;
import org.openide.nodes.Sheet;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
import static org.sleuthkit.autopsy.datamodel.AbstractContentNode.NO_DESCR;
import org.sleuthkit.datamodel.UnsupportedContent;
import org.sleuthkit.datamodel.Tag;
/**
* This class is used to represent the "Node" for an unsupported content object.
*/
public class UnsupportedContentNode extends AbstractContentNode<UnsupportedContent> {
/**
*
* @param unsupportedContent underlying Content instance
*/
@NbBundle.Messages({
"UnsupportedContentNode.displayName=Unsupported Content",
})
public UnsupportedContentNode(UnsupportedContent unsupportedContent) {
super(unsupportedContent);
// set name, display name, and icon
this.setDisplayName(Bundle.UnsupportedContentNode_displayName());
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file-icon.png"); //NON-NLS
}
/**
* Right click action for UnsupportedContentNode node
*
* @param popup
*
* @return
*/
@Override
public Action[] getActions(boolean popup) {
List<Action> actionsList = new ArrayList<>();
for (Action a : super.getActions(true)) {
actionsList.add(a);
}
return actionsList.toArray(new Action[actionsList.size()]);
}
@NbBundle.Messages({
"UnsupportedContentNode.createSheet.name.name=Name",
"UnsupportedContentNode.createSheet.name.displayName=Name",
"UnsupportedContentNode.createSheet.name.desc=no description",
})
@Override
protected Sheet createSheet() {
Sheet sheet = super.createSheet();
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
if (sheetSet == null) {
sheetSet = Sheet.createPropertiesSet();
sheet.put(sheetSet);
}
sheetSet.put(new NodeProperty<>(Bundle.UnsupportedContentNode_createSheet_name_name(),
Bundle.UnsupportedContentNode_createSheet_name_displayName(),
Bundle.UnsupportedContentNode_createSheet_name_desc(),
this.getDisplayName()));
return sheet;
}
@Override
public <T> T accept(ContentNodeVisitor<T> visitor) {
return visitor.visit(this);
}
@Override
public boolean isLeafTypeNode() {
return false;
}
@Override
public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
return visitor.visit(this);
}
@Override
public String getItemType() {
return getClass().getName();
}
/**
* Reads and returns a list of all tags associated with this content node.
*
* Null implementation of an abstract method.
*
* @return list of tags associated with the node.
*/
@Override
protected List<Tag> getAllTagsFromDatabase() {
return new ArrayList<>();
}
/**
* Returns correlation attribute instance for the underlying content of the
* node.
*
* Null implementation of an abstract method.
*
* @return correlation attribute instance for the underlying content of the
* node.
*/
@Override
protected CorrelationAttributeInstance getCorrelationAttributeInstance() {
return null;
}
/**
* Returns Score property for the node.
*
* Null implementation of an abstract method.
*
* @param tags list of tags.
*
* @return Score property for the underlying content of the node.
*/
@Override
protected Pair<DataResultViewerTable.Score, String> getScorePropertyAndDescription(List<Tag> tags) {
return Pair.of(DataResultViewerTable.Score.NO_SCORE, NO_DESCR);
}
/**
* Returns comment property for the node.
*
* Null implementation of an abstract method.
*
* @param tags list of tags
* @param attribute correlation attribute instance
*
* @return Comment property for the underlying content of the node.
*/
@Override
protected DataResultViewerTable.HasCommentStatus getCommentProperty(List<Tag> tags, CorrelationAttributeInstance attribute) {
return DataResultViewerTable.HasCommentStatus.NO_COMMENT;
}
/**
* Returns occurrences/count property for the node.
*
* Null implementation of an abstract method.
*
* @param attributeType the type of the attribute to count
* @param attributeValue the value of the attribute to coun
* @param defaultDescription a description to use when none is determined by
* the getCountPropertyAndDescription method
*
* @return count property for the underlying content of the node.
*/
@Override
protected Pair<Long, String> getCountPropertyAndDescription(CorrelationAttributeInstance.Type attributeType, String attributeValue, String defaultDescription) {
return Pair.of(-1L, NO_DESCR);
}
}

View File

@ -41,7 +41,7 @@ public class ViewsNode extends DisplayableItemNode {
super(
new RootContentChildren(Arrays.asList(
new FileTypes(sleuthkitCase, dsObjId),
new FileTypes(dsObjId),
// June '15: Recent Files was removed because it was not useful w/out filtering
// add it back in if we can filter the results to a more managable size.
// new RecentFiles(sleuthkitCase),

View File

@ -69,6 +69,7 @@ import org.sleuthkit.autopsy.datamodel.CreditCards;
import org.sleuthkit.autopsy.datamodel.DataModelActionsFactory;
import org.sleuthkit.autopsy.datamodel.DisplayableItemNode;
import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor;
import org.sleuthkit.autopsy.datamodel.Artifacts.UpdatableCountTypeNode;
import org.sleuthkit.autopsy.datamodel.NodeProperty;
import org.sleuthkit.autopsy.directorytree.DirectoryTreeTopComponent;
import org.sleuthkit.autopsy.ingest.IngestManager;
@ -76,7 +77,8 @@ import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Account;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
import org.sleuthkit.datamodel.BlackboardArtifact.Type;
import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_ACCOUNT;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.SleuthkitCase;
@ -93,6 +95,7 @@ final public class Accounts implements AutopsyVisitableItem {
private static final String ICON_BASE_PATH = "/org/sleuthkit/autopsy/images/"; //NON-NLS
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED);
private static final Set<IngestManager.IngestModuleEvent> INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestModuleEvent.DATA_ADDED);
private static final String DISPLAY_NAME = Bundle.Accounts_RootNode_displayName();
@NbBundle.Messages("AccountsRootNode.name=Accounts")
final public static String NAME = Bundle.AccountsRootNode_name();
@ -231,12 +234,16 @@ final public class Accounts implements AutopsyVisitableItem {
* Top-level node for the accounts tree
*/
@NbBundle.Messages({"Accounts.RootNode.displayName=Accounts"})
final public class AccountsRootNode extends DisplayableItemNode {
final public class AccountsRootNode extends UpdatableCountTypeNode {
public AccountsRootNode() {
super(Children.create(new AccountTypeFactory(), true), Lookups.singleton(Accounts.this));
super(Children.create(new AccountTypeFactory(), true),
Lookups.singleton(Accounts.this),
DISPLAY_NAME,
filteringDSObjId,
TSK_ACCOUNT);
setName(Accounts.NAME);
setDisplayName(Bundle.Accounts_RootNode_displayName());
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/accounts.png"); //NON-NLS
}
@ -254,12 +261,42 @@ final public class Accounts implements AutopsyVisitableItem {
public String getItemType() {
return getClass().getName();
}
@Override
protected long fetchChildCount(SleuthkitCase skCase) throws TskCoreException {
long count = 0;
String dataSourceFilterClause = (filteringDSObjId > 0)
? " AND " + filteringDSObjId + " IN (SELECT art.data_source_obj_id FROM blackboard_artifacts art WHERE art.artifact_id = attr.artifact_id)"
: "";
String accountTypesInUseQuery
= "SELECT COUNT(attr.value_text) AS count"
+ " FROM blackboard_attributes attr"
+ " WHERE attr.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID()
+ " AND attr.artifact_type_id = " + BlackboardArtifact.Type.TSK_ACCOUNT.getTypeID()
+ dataSourceFilterClause
+ " GROUP BY attr.value_text";
try (SleuthkitCase.CaseDbQuery executeQuery = skCase.executeQuery(accountTypesInUseQuery);
ResultSet resultSet = executeQuery.getResultSet()) {
if (resultSet.next()) {
count = resultSet.getLong("count");
}
} catch (TskCoreException | SQLException ex) {
LOGGER.log(Level.SEVERE, "Error querying for count of all account types", ex);
}
return count;
}
}
/**
* Tracks the account types and the number of account types found.
*/
private class AccountTypeResults {
private final Map<String, Long> counts = new HashMap<>();
AccountTypeResults() {
@ -267,9 +304,12 @@ final public class Accounts implements AutopsyVisitableItem {
}
/**
* Given the type name of the Account.Type, provides the count of those type.
* @param accountType The type name of the Account.Type.
* @return The number of results found for the given account type.
* Given the type name of the Account.Type, provides the count of those
* type.
*
* @param accountType The type name of the Account.Type.
*
* @return The number of results found for the given account type.
*/
Long getCount(String accountType) {
return counts.get(accountType);
@ -277,7 +317,8 @@ final public class Accounts implements AutopsyVisitableItem {
/**
* Retrieves an alphabetically organized list of all the account types.
* @return An alphabetically organized list of all the account types.
*
* @return An alphabetically organized list of all the account types.
*/
List<String> getTypes() {
List<String> types = new ArrayList<>(counts.keySet());
@ -293,7 +334,7 @@ final public class Accounts implements AutopsyVisitableItem {
= "SELECT blackboard_attributes.value_text as account_type, COUNT(*) as count "
+ " FROM blackboard_artifacts " //NON-NLS
+ " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
+ " WHERE blackboard_artifacts.artifact_type_id = " + TSK_ACCOUNT.getTypeID() //NON-NLS
+ " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID() //NON-NLS
+ getFilterByDataSourceClause()
+ " GROUP BY blackboard_attributes.value_text ";
@ -343,7 +384,7 @@ final public class Accounts implements AutopsyVisitableItem {
*/
ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
if (null != eventData
&& eventData.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) {
&& eventData.getBlackboardArtifactType().getTypeID() == Type.TSK_ACCOUNT.getTypeID()) {
accountTypeResults.update();
reviewStatusBus.post(eventData);
}
@ -393,10 +434,12 @@ final public class Accounts implements AutopsyVisitableItem {
}
/**
* Registers the given node with the reviewStatusBus and returns
* the node wrapped in an array.
* @param node The node to be wrapped.
* @return The array containing this node.
* Registers the given node with the reviewStatusBus and returns the
* node wrapped in an array.
*
* @param node The node to be wrapped.
*
* @return The array containing this node.
*/
private Node[] getNodeArr(Node node) {
reviewStatusBus.register(node);
@ -476,7 +519,7 @@ final public class Accounts implements AutopsyVisitableItem {
*/
ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
if (null != eventData
&& eventData.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) {
&& eventData.getBlackboardArtifactType().getTypeID() == Type.TSK_ACCOUNT.getTypeID()) {
reviewStatusBus.post(eventData);
}
} catch (NoCurrentCaseException notUsed) {
@ -529,7 +572,7 @@ final public class Accounts implements AutopsyVisitableItem {
= "SELECT blackboard_artifacts.artifact_id " //NON-NLS
+ " FROM blackboard_artifacts " //NON-NLS
+ " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.Type.TSK_ACCOUNT.getTypeID() //NON-NLS
+ " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID() //NON-NLS
+ " AND blackboard_attributes.value_text = '" + accountType.getTypeName() + "'" //NON-NLS
+ getFilterByDataSourceClause()
@ -576,6 +619,7 @@ final public class Accounts implements AutopsyVisitableItem {
* no special behavior.
*/
final public class DefaultAccountTypeNode extends DisplayableItemNode {
private final Account.Type accountType;
private DefaultAccountTypeNode(Account.Type accountType) {
@ -583,6 +627,7 @@ final public class Accounts implements AutopsyVisitableItem {
this.accountType = accountType;
String iconPath = getIconFilePath(accountType);
this.setIconBaseWithExtension(iconPath != null && iconPath.charAt(0) == '/' ? iconPath.substring(1) : iconPath); //NON-NLS
setName(accountType.getTypeName());
updateName();
}
@ -601,7 +646,6 @@ final public class Accounts implements AutopsyVisitableItem {
return getClass().getName();
}
@Subscribe
void handleReviewStatusChange(ReviewStatusChangeEvent event) {
updateName();
@ -613,10 +657,11 @@ final public class Accounts implements AutopsyVisitableItem {
}
/**
* Gets the latest counts for the account type and then updates the name.
* Gets the latest counts for the account type and then updates the
* name.
*/
public void updateName() {
setName(String.format("%s (%d)", accountType.getDisplayName(), accountTypeResults.getCount(accountType.getTypeName())));
setDisplayName(String.format("%s (%d)", accountType.getDisplayName(), accountTypeResults.getCount(accountType.getTypeName())));
}
}
@ -651,7 +696,7 @@ final public class Accounts implements AutopsyVisitableItem {
*/
ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
if (null != eventData
&& eventData.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) {
&& eventData.getBlackboardArtifactType().getTypeID() == Type.TSK_ACCOUNT.getTypeID()) {
reviewStatusBus.post(eventData);
}
} catch (NoCurrentCaseException notUsed) {
@ -749,7 +794,8 @@ final public class Accounts implements AutopsyVisitableItem {
}
/**
* Gets the latest counts for the account type and then updates the name.
* Gets the latest counts for the account type and then updates the
* name.
*/
public void updateName() {
setName(String.format("%s (%d)", Account.Type.CREDIT_CARD.getDisplayName(), accountTypeResults.getCount(Account.Type.CREDIT_CARD.getTypeName())));
@ -804,7 +850,7 @@ final public class Accounts implements AutopsyVisitableItem {
*/
ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
if (null != eventData
&& eventData.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) {
&& eventData.getBlackboardArtifactType().getTypeID() == Type.TSK_ACCOUNT.getTypeID()) {
reviewStatusBus.post(eventData);
}
} catch (NoCurrentCaseException notUsed) {
@ -882,7 +928,7 @@ final public class Accounts implements AutopsyVisitableItem {
+ " LEFT JOIN blackboard_attributes as account_type ON blackboard_artifacts.artifact_id = account_type.artifact_id " //NON-NLS
+ " AND account_type.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID() //NON-NLS
+ " AND account_type.value_text = '" + Account.Type.CREDIT_CARD.getTypeName() + "'" //NON-NLS
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.Type.TSK_ACCOUNT.getTypeID() //NON-NLS
+ getFilterByDataSourceClause()
+ getRejectedArtifactFilterClause()
+ " GROUP BY blackboard_artifacts.obj_id, solr_document_id " //NON-NLS
@ -951,7 +997,7 @@ final public class Accounts implements AutopsyVisitableItem {
+ " LEFT JOIN blackboard_attributes as account_type ON blackboard_artifacts.artifact_id = account_type.artifact_id " //NON-NLS
+ " AND account_type.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID() //NON-NLS
+ " AND account_type.value_text = '" + Account.Type.CREDIT_CARD.getTypeName() + "'" //NON-NLS
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.Type.TSK_ACCOUNT.getTypeID() //NON-NLS
+ getFilterByDataSourceClause()
+ getRejectedArtifactFilterClause()
+ " GROUP BY blackboard_artifacts.obj_id, solr_attribute.value_text ) AS foo";
@ -1019,7 +1065,7 @@ final public class Accounts implements AutopsyVisitableItem {
*/
ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
if (null != eventData
&& eventData.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) {
&& eventData.getBlackboardArtifactType().getTypeID() == Type.TSK_ACCOUNT.getTypeID()) {
reviewStatusBus.post(eventData);
}
} catch (NoCurrentCaseException notUsed) { //NOPMD empy catch clause
@ -1087,7 +1133,7 @@ final public class Accounts implements AutopsyVisitableItem {
+ " COUNT(blackboard_artifacts.artifact_id) AS count " //NON-NLS
+ " FROM blackboard_artifacts " //NON-NLS
+ " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id" //NON-NLS
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.Type.TSK_ACCOUNT.getTypeID() //NON-NLS
+ " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID() //NON-NLS
+ getFilterByDataSourceClause()
+ getRejectedArtifactFilterClause()
@ -1154,7 +1200,7 @@ final public class Accounts implements AutopsyVisitableItem {
= "SELECT count(distinct SUBSTR(blackboard_attributes.value_text,1,8)) AS BINs " //NON-NLS
+ " FROM blackboard_artifacts " //NON-NLS
+ " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id" //NON-NLS
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.Type.TSK_ACCOUNT.getTypeID() //NON-NLS
+ " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID() //NON-NLS
+ getFilterByDataSourceClause()
+ getRejectedArtifactFilterClause(); //NON-NLS
@ -1449,7 +1495,7 @@ final public class Accounts implements AutopsyVisitableItem {
= "SELECT blackboard_artifacts.artifact_id " //NON-NLS
+ " FROM blackboard_artifacts " //NON-NLS
+ " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.Type.TSK_ACCOUNT.getTypeID() //NON-NLS
+ " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID() //NON-NLS
+ " AND blackboard_attributes.value_text >= '" + bin.getBINStart() + "' AND blackboard_attributes.value_text < '" + (bin.getBINEnd() + 1) + "'" //NON-NLS
+ getFilterByDataSourceClause()
@ -1523,7 +1569,7 @@ final public class Accounts implements AutopsyVisitableItem {
= "SELECT count(blackboard_artifacts.artifact_id ) AS count" //NON-NLS
+ " FROM blackboard_artifacts " //NON-NLS
+ " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.Type.TSK_ACCOUNT.getTypeID() //NON-NLS
+ " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID() //NON-NLS
+ " AND blackboard_attributes.value_text >= '" + bin.getBINStart() + "' AND blackboard_attributes.value_text < '" + (bin.getBINEnd() + 1) + "'" //NON-NLS
+ getFilterByDataSourceClause()

View File

@ -65,8 +65,8 @@ public class AssociateNewPersonAction extends AbstractAction {
try {
newPersonName = getAddDialogName();
if (StringUtils.isNotBlank(newPersonName)) {
Person person = Case.getCurrentCaseThrows().getSleuthkitCase().getPersonManager().createPerson(newPersonName);
Case.getCurrentCaseThrows().getSleuthkitCase().getHostManager().setPerson(host, person);
Person person = Case.getCurrentCaseThrows().getSleuthkitCase().getPersonManager().newPerson(newPersonName);
Case.getCurrentCaseThrows().getSleuthkitCase().getPersonManager().setPerson(host, person);
}
} catch (NoCurrentCaseException | TskCoreException ex) {
String hostName = this.host == null || this.host.getName() == null ? "" : this.host.getName();

Some files were not shown because too many files have changed in this diff Show More