7332 merge in develop

This commit is contained in:
Richard Cordovano 2021-06-02 12:55:36 -04:00
commit 3313f8052a
166 changed files with 5508 additions and 3624 deletions

View File

@ -1,24 +1,18 @@
AddBlackboardArtifactTagAction.pluralTagResult=Add Result Tags AddBlackboardArtifactTagAction.pluralTagResult=Add Result Tags
AddBlackboardArtifactTagAction.singularTagResult=Add Result Tag AddBlackboardArtifactTagAction.singularTagResult=Add Result Tag
AddBlackboardArtifactTagAction.taggingErr=Tagging Error AddBlackboardArtifactTagAction.taggingErr=Tagging Error
# {0} - artifactName
AddBlackboardArtifactTagAction.unableToTag.msg=Unable to tag {0}. AddBlackboardArtifactTagAction.unableToTag.msg=Unable to tag {0}.
AddContentTagAction.cannotApplyTagErr=Cannot Apply Tag AddContentTagAction.cannotApplyTagErr=Cannot Apply Tag
AddContentTagAction.pluralTagFile=Add File Tags AddContentTagAction.pluralTagFile=Add File Tags
AddContentTagAction.singularTagFile=Add File Tag AddContentTagAction.singularTagFile=Add File Tag
# {0} - fileName
# {1} - tagName
AddContentTagAction.tagExists={0} has been tagged as {1}. Cannot reapply the same tag. AddContentTagAction.tagExists={0} has been tagged as {1}. Cannot reapply the same tag.
AddContentTagAction.taggingErr=Tagging Error AddContentTagAction.taggingErr=Tagging Error
# {0} - fileName
AddContentTagAction.unableToTag.msg=Unable to tag {0}, not a regular file. AddContentTagAction.unableToTag.msg=Unable to tag {0}, not a regular file.
# {0} - fileName
AddContentTagAction.unableToTag.msg2=Unable to tag {0}. AddContentTagAction.unableToTag.msg2=Unable to tag {0}.
CTL_DumpThreadAction=Thread Dump CTL_DumpThreadAction=Thread Dump
CTL_ShowIngestProgressSnapshotAction=Ingest Status Details CTL_ShowIngestProgressSnapshotAction=Ingest Status Details
DeleteBlackboardArtifactTagAction.deleteTag=Remove Selected Tag(s) DeleteBlackboardArtifactTagAction.deleteTag=Remove Selected Tag(s)
DeleteBlackboardArtifactTagAction.tagDelErr=Tag Deletion Error DeleteBlackboardArtifactTagAction.tagDelErr=Tag Deletion Error
# {0} - tagName
DeleteBlackboardArtifactTagAction.unableToDelTag.msg=Unable to delete tag {0}. DeleteBlackboardArtifactTagAction.unableToDelTag.msg=Unable to delete tag {0}.
DeleteContentTagAction.deleteTag=Remove Selected Tag(s) DeleteContentTagAction.deleteTag=Remove Selected Tag(s)
DeleteContentTagAction.tagDelErr=Tag Deletion Error DeleteContentTagAction.tagDelErr=Tag Deletion Error
@ -84,8 +78,6 @@ CTL_OpenOutputFolder=Open Case Folder
OpenOutputFolder.error1=Case Folder Not Found: {0} OpenOutputFolder.error1=Case Folder Not Found: {0}
OpenOutputFolder.noCaseOpen=No open case, therefore no current case folder available. OpenOutputFolder.noCaseOpen=No open case, therefore no current case folder available.
OpenOutputFolder.CouldNotOpenOutputFolder=Could not open case folder OpenOutputFolder.CouldNotOpenOutputFolder=Could not open case folder
# {0} - old tag name
# {1} - artifactID
ReplaceBlackboardArtifactTagAction.replaceTag.alert=Unable to replace tag {0} for artifact {1}. ReplaceBlackboardArtifactTagAction.replaceTag.alert=Unable to replace tag {0} for artifact {1}.
# {0} - old tag name # {0} - old tag name
# {1} - content obj id # {1} - content obj id

View File

@ -247,15 +247,10 @@ AddImageWizardIngestConfigPanel.dsProcDone.errs.text=*Errors encountered in addi
AddImageWizardIngestConfigVisual.getName.text=Configure Ingest AddImageWizardIngestConfigVisual.getName.text=Configure Ingest
AddImageWizardIterator.stepXofN=Step {0} of {1} AddImageWizardIterator.stepXofN=Step {0} of {1}
AddLocalFilesTask.localFileAdd.progress.text=Adding: {0}/{1} AddLocalFilesTask.localFileAdd.progress.text=Adding: {0}/{1}
Case.getCurCase.exception.noneOpen=Cannot get the current case; there is no case open\! Case.getCurCase.exception.noneOpen=Cannot get the current case; there is no case open!
Case.open.msgDlg.updated.msg=Updated case database schema.\nA backup copy of the database with the following path has been made:\n {0} Case.open.msgDlg.updated.msg=Updated case database schema.\nA backup copy of the database with the following path has been made:\n {0}
Case.open.msgDlg.updated.title=Case Database Schema Update Case.open.msgDlg.updated.title=Case Database Schema Update
Case.checkImgExist.confDlg.doesntExist.msg=One of the images associated with \n\ Case.checkImgExist.confDlg.doesntExist.msg=One of the images associated with \nthis case are missing. Would you like to search for them now?\nPreviously, the image was located at:\n{0}\nPlease note that you will still be able to browse directories and generate reports\nif you choose No, but you will not be able to view file content or run the ingest process.
this case are missing. Would you like to search for them now?\n\
Previously, the image was located at:\n\
{0}\n\
Please note that you will still be able to browse directories and generate reports\n\
if you choose No, but you will not be able to view file content or run the ingest process.
Case.checkImgExist.confDlg.doesntExist.title=Missing Image Case.checkImgExist.confDlg.doesntExist.title=Missing Image
Case.addImg.exception.msg=Error adding image to the case Case.addImg.exception.msg=Error adding image to the case
Case.updateCaseName.exception.msg=Error while trying to update the case name. Case.updateCaseName.exception.msg=Error while trying to update the case name.
@ -274,12 +269,9 @@ Case.GetCaseTypeGivenPath.Failure=Unable to get case type
Case.metaDataFileCorrupt.exception.msg=The case metadata file (.aut) is corrupted. Case.metaDataFileCorrupt.exception.msg=The case metadata file (.aut) is corrupted.
Case.deleteReports.deleteFromDiskException.log.msg=Unable to delete the report from the disk. Case.deleteReports.deleteFromDiskException.log.msg=Unable to delete the report from the disk.
Case.deleteReports.deleteFromDiskException.msg=Unable to delete the report {0} from the disk.\nYou may manually delete it from {1} Case.deleteReports.deleteFromDiskException.msg=Unable to delete the report {0} from the disk.\nYou may manually delete it from {1}
CaseDeleteAction.closeConfMsg.text=Are you sure want to close and delete this case? \n\ CaseDeleteAction.closeConfMsg.text=Are you sure want to close and delete this case? \nCase Name: {0}\nCase Directory: {1}
Case Name: {0}\n\
Case Directory: {1}
CaseDeleteAction.closeConfMsg.title=Warning: Closing the Current Case CaseDeleteAction.closeConfMsg.title=Warning: Closing the Current Case
CaseDeleteAction.msgDlg.fileInUse.msg=The delete action cannot be fully completed because the folder or file in it is open by another program.\n\n\ CaseDeleteAction.msgDlg.fileInUse.msg=The delete action cannot be fully completed because the folder or file in it is open by another program.\n\nClose the folder and file and try again or you can delete the case manually.
Close the folder and file and try again or you can delete the case manually.
CaseDeleteAction.msgDlg.fileInUse.title=Error: Folder In Use CaseDeleteAction.msgDlg.fileInUse.title=Error: Folder In Use
CaseDeleteAction.msgDlg.caseDelete.msg=Case {0} has been deleted. CaseDeleteAction.msgDlg.caseDelete.msg=Case {0} has been deleted.
CaseOpenAction.autFilter.title={0} Case File ( {1}) CaseOpenAction.autFilter.title={0} Case File ( {1})
@ -311,8 +303,7 @@ NewCaseWizardAction.databaseProblem1.text=Cannot open database. Cancelling case
NewCaseWizardAction.databaseProblem2.text=Error NewCaseWizardAction.databaseProblem2.text=Error
NewCaseWizardPanel1.validate.errMsg.invalidSymbols=The Case Name cannot contain any of the following symbols: \\ / : * ? " < > | NewCaseWizardPanel1.validate.errMsg.invalidSymbols=The Case Name cannot contain any of the following symbols: \\ / : * ? " < > |
NewCaseWizardPanel1.validate.errMsg.dirExists=Case directory ''{0}'' already exists. NewCaseWizardPanel1.validate.errMsg.dirExists=Case directory ''{0}'' already exists.
NewCaseWizardPanel1.validate.confMsg.createDir.msg=The base directory "{0}" does not exist. \n\n\ NewCaseWizardPanel1.validate.confMsg.createDir.msg=The base directory "{0}" does not exist. \n\nDo you want to create that directory?
Do you want to create that directory?
NewCaseWizardPanel1.validate.confMsg.createDir.title=Create directory NewCaseWizardPanel1.validate.confMsg.createDir.title=Create directory
NewCaseWizardPanel1.validate.errMsg.cantCreateParDir.msg=Error: Could not create case parent directory {0} NewCaseWizardPanel1.validate.errMsg.cantCreateParDir.msg=Error: Could not create case parent directory {0}
NewCaseWizardPanel1.validate.errMsg.prevCreateBaseDir.msg=Prevented from creating base directory {0} NewCaseWizardPanel1.validate.errMsg.prevCreateBaseDir.msg=Prevented from creating base directory {0}
@ -341,7 +332,6 @@ OptionalCasePropertiesPanel.lbPointOfContactPhoneLabel.text=Phone:
OptionalCasePropertiesPanel.orgainizationPanel.border.title=Organization OptionalCasePropertiesPanel.orgainizationPanel.border.title=Organization
RecentCases.exception.caseIdxOutOfRange.msg=Recent case index {0} is out of range. RecentCases.exception.caseIdxOutOfRange.msg=Recent case index {0} is out of range.
RecentCases.getName.text=Clear Recent Cases RecentCases.getName.text=Clear Recent Cases
# {0} - case name
RecentItems.openRecentCase.msgDlg.text=Case {0} no longer exists. RecentItems.openRecentCase.msgDlg.text=Case {0} no longer exists.
SelectDataSourceProcessorPanel.name.text=Select Data Source Type SelectDataSourceProcessorPanel.name.text=Select Data Source Type
StartupWindow.title.text=Welcome StartupWindow.title.text=Welcome
@ -354,7 +344,6 @@ StartupWindowProvider.openCase.noFile=Unable to open previously open case becaus
UnpackagePortableCaseDialog.title.text=Unpackage Portable Case UnpackagePortableCaseDialog.title.text=Unpackage Portable Case
UnpackagePortableCaseDialog.UnpackagePortableCaseDialog.extensions=Portable case package (.zip, .zip.001) UnpackagePortableCaseDialog.UnpackagePortableCaseDialog.extensions=Portable case package (.zip, .zip.001)
UnpackagePortableCaseDialog.validatePaths.badExtension=File extension must be .zip or .zip.001 UnpackagePortableCaseDialog.validatePaths.badExtension=File extension must be .zip or .zip.001
# {0} - case folder
UnpackagePortableCaseDialog.validatePaths.caseFolderExists=Folder {0} already exists UnpackagePortableCaseDialog.validatePaths.caseFolderExists=Folder {0} already exists
UnpackagePortableCaseDialog.validatePaths.caseIsNotFile=Selected path is not a file UnpackagePortableCaseDialog.validatePaths.caseIsNotFile=Selected path is not a file
UnpackagePortableCaseDialog.validatePaths.caseNotFound=File does not exist UnpackagePortableCaseDialog.validatePaths.caseNotFound=File does not exist
@ -369,8 +358,8 @@ UnpackageWorker.doInBackground.previouslySeenCase=Case has been previously opene
UpdateRecentCases.menuItem.clearRecentCases.text=Clear Recent Cases UpdateRecentCases.menuItem.clearRecentCases.text=Clear Recent Cases
UpdateRecentCases.menuItem.empty=-Empty- UpdateRecentCases.menuItem.empty=-Empty-
AddImageWizardIngestConfigPanel.CANCEL_BUTTON.text=Cancel AddImageWizardIngestConfigPanel.CANCEL_BUTTON.text=Cancel
NewCaseVisualPanel1.CaseFolderOnCDriveError.text=Warning: Path to multi-user case folder is on \"C:\" drive NewCaseVisualPanel1.CaseFolderOnCDriveError.text=Warning: Path to multi-user case folder is on "C:" drive
NewCaseVisualPanel1.CaseFolderOnInternalDriveWindowsError.text=Warning: Path to case folder is on \"C:\" drive. Case folder is created on the target system NewCaseVisualPanel1.CaseFolderOnInternalDriveWindowsError.text=Warning: Path to case folder is on "C:" drive. Case folder is created on the target system
NewCaseVisualPanel1.CaseFolderOnInternalDriveLinuxError.text=Warning: Path to case folder is on the target system. Create case folder in mounted drive. NewCaseVisualPanel1.CaseFolderOnInternalDriveLinuxError.text=Warning: Path to case folder is on the target system. Create case folder in mounted drive.
NewCaseVisualPanel1.uncPath.error=Error: UNC paths are not allowed for Single-User cases NewCaseVisualPanel1.uncPath.error=Error: UNC paths are not allowed for Single-User cases
CollaborationMonitor.addingDataSourceStatus.msg={0} adding data source CollaborationMonitor.addingDataSourceStatus.msg={0} adding data source
@ -378,7 +367,7 @@ CollaborationMonitor.analyzingDataSourceStatus.msg={0} analyzing {1}
MissingImageDialog.lbWarning.text= MissingImageDialog.lbWarning.text=
MissingImageDialog.lbWarning.toolTipText= MissingImageDialog.lbWarning.toolTipText=
NewCaseVisualPanel1.caseParentDirWarningLabel.text= NewCaseVisualPanel1.caseParentDirWarningLabel.text=
NewCaseVisualPanel1.multiUserCaseRadioButton.text=Multi-User NewCaseVisualPanel1.multiUserCaseRadioButton.text=Multi-User\t\t
NewCaseVisualPanel1.singleUserCaseRadioButton.text=Single-User NewCaseVisualPanel1.singleUserCaseRadioButton.text=Single-User
NewCaseVisualPanel1.caseTypeLabel.text=Case Type: NewCaseVisualPanel1.caseTypeLabel.text=Case Type:
SingleUserCaseConverter.BadDatabaseFileName=Database file does not exist! SingleUserCaseConverter.BadDatabaseFileName=Database file does not exist!

View File

@ -85,13 +85,15 @@ import org.sleuthkit.autopsy.casemodule.events.DataSourceAddedEvent;
import org.sleuthkit.autopsy.casemodule.events.DataSourceDeletedEvent; import org.sleuthkit.autopsy.casemodule.events.DataSourceDeletedEvent;
import org.sleuthkit.autopsy.casemodule.events.DataSourceNameChangedEvent; import org.sleuthkit.autopsy.casemodule.events.DataSourceNameChangedEvent;
import org.sleuthkit.autopsy.casemodule.events.HostsAddedEvent; import org.sleuthkit.autopsy.casemodule.events.HostsAddedEvent;
import org.sleuthkit.autopsy.casemodule.events.HostsChangedEvent; import org.sleuthkit.autopsy.casemodule.events.HostsAddedToPersonEvent;
import org.sleuthkit.autopsy.casemodule.events.HostsRemovedEvent; import org.sleuthkit.autopsy.casemodule.events.HostsUpdatedEvent;
import org.sleuthkit.autopsy.casemodule.events.OsAccountAddedEvent; import org.sleuthkit.autopsy.casemodule.events.HostsDeletedEvent;
import org.sleuthkit.autopsy.casemodule.events.OsAccountChangedEvent; import org.sleuthkit.autopsy.casemodule.events.HostsRemovedFromPersonEvent;
import org.sleuthkit.autopsy.casemodule.events.OsAccountDeletedEvent; import org.sleuthkit.autopsy.casemodule.events.OsAccountsAddedEvent;
import org.sleuthkit.autopsy.casemodule.events.OsAccountsUpdatedEvent;
import org.sleuthkit.autopsy.casemodule.events.OsAccountsDeletedEvent;
import org.sleuthkit.autopsy.casemodule.events.PersonsAddedEvent; import org.sleuthkit.autopsy.casemodule.events.PersonsAddedEvent;
import org.sleuthkit.autopsy.casemodule.events.PersonsChangedEvent; import org.sleuthkit.autopsy.casemodule.events.PersonsUpdatedEvent;
import org.sleuthkit.autopsy.casemodule.events.PersonsDeletedEvent; import org.sleuthkit.autopsy.casemodule.events.PersonsDeletedEvent;
import org.sleuthkit.autopsy.casemodule.events.ReportAddedEvent; import org.sleuthkit.autopsy.casemodule.events.ReportAddedEvent;
import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData.CaseNodeDataException; import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData.CaseNodeDataException;
@ -191,6 +193,9 @@ public class Case {
private CollaborationMonitor collaborationMonitor; private CollaborationMonitor collaborationMonitor;
private Services caseServices; private Services caseServices;
private volatile boolean hasDataSource = false;
private volatile boolean hasData = false;
/* /*
* Get a reference to the main window of the desktop application to use to * Get a reference to the main window of the desktop application to use to
* parent pop up dialogs and initialize the application name for use in * parent pop up dialogs and initialize the application name for use in
@ -431,43 +436,50 @@ public class Case {
*/ */
CR_COMMENT_CHANGED, CR_COMMENT_CHANGED,
/** /**
* OSAccount associated with the current case added. Call getOsAccount * One or more OS accounts have been added to the case.
* to get the added account;
*/ */
OS_ACCOUNT_ADDED, OS_ACCOUNTS_ADDED,
/** /**
* OSAccount associated with the current case has changed. Call * One or more OS accounts in the case have been updated.
* getOsAccount to get the changed account;
*/ */
OS_ACCOUNT_CHANGED, OS_ACCOUNTS_UPDATED,
/** /**
* OSAccount associated with the current case has been deleted. * One or more OS accounts have been deleted from the case.
*/ */
OS_ACCOUNT_REMOVED, OS_ACCOUNTS_DELETED,
/** /**
* Hosts associated with the current case added. * One or more hosts have been added to the case.
*/ */
HOSTS_ADDED, HOSTS_ADDED,
/** /**
* Hosts associated with the current case has changed. * One or more hosts in the case have been updated.
*/ */
HOSTS_CHANGED, HOSTS_UPDATED,
/** /**
* Hosts associated with the current case has been deleted. * One or more hosts have been deleted from the case.
*/ */
HOSTS_DELETED, HOSTS_DELETED,
/** /**
* Persons associated with the current case added. * One or more persons have been added to the case.
*/ */
PERSONS_ADDED, PERSONS_ADDED,
/** /**
* Persons associated with the current case has changed. * One or more persons in the case have been updated.
*/ */
PERSONS_CHANGED, PERSONS_UPDATED,
/** /**
* Persons associated with the current case has been deleted. * One or more persons been deleted from the case.
*/ */
PERSONS_DELETED; PERSONS_DELETED,
/**
* One or more hosts have been added to a person.
*/
HOSTS_ADDED_TO_PERSON,
/**
* One or more hosts have been removed from a person.
*/
HOSTS_REMOVED_FROM_PERSON;
}; };
/** /**
@ -502,24 +514,24 @@ public class Case {
} }
@Subscribe @Subscribe
public void publishOsAccountAddedEvent(TskEvent.OsAccountsAddedTskEvent event) { public void publishOsAccountsAddedEvent(TskEvent.OsAccountsAddedTskEvent event) {
for (OsAccount account : event.getOsAcounts()) { hasData = true;
eventPublisher.publish(new OsAccountAddedEvent(account)); eventPublisher.publish(new OsAccountsAddedEvent(event.getOsAcounts()));
}
} }
@Subscribe @Subscribe
public void publishOsAccountChangedEvent(TskEvent.OsAccountsChangedTskEvent event) { public void publishOsAccountsUpdatedEvent(TskEvent.OsAccountsUpdatedTskEvent event) {
for (OsAccount account : event.getOsAcounts()) { eventPublisher.publish(new OsAccountsUpdatedEvent(event.getOsAcounts()));
eventPublisher.publish(new OsAccountChangedEvent(account));
}
} }
@Subscribe @Subscribe
public void publishOsAccountDeletedEvent(TskEvent.OsAccountsDeletedTskEvent event) { public void publishOsAccountDeletedEvent(TskEvent.OsAccountsDeletedTskEvent event) {
for (Long accountId : event.getOsAcountObjectIds()) { try {
eventPublisher.publish(new OsAccountDeletedEvent(accountId)); hasData = dbHasData();
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Unable to retrieve the hasData status from the db", ex);
} }
eventPublisher.publish(new OsAccountsDeletedEvent(event.getOsAccountObjectIds()));
} }
/** /**
@ -530,8 +542,8 @@ public class Case {
*/ */
@Subscribe @Subscribe
public void publishHostsAddedEvent(TskEvent.HostsAddedTskEvent event) { public void publishHostsAddedEvent(TskEvent.HostsAddedTskEvent event) {
eventPublisher.publish(new HostsAddedEvent( hasData = true;
event == null ? Collections.emptyList() : event.getHosts())); eventPublisher.publish(new HostsAddedEvent(event.getHosts()));
} }
/** /**
@ -541,9 +553,8 @@ public class Case {
* @param event The sleuthkit event for the updating of hosts. * @param event The sleuthkit event for the updating of hosts.
*/ */
@Subscribe @Subscribe
public void publishHostsChangedEvent(TskEvent.HostsChangedTskEvent event) { public void publishHostsUpdatedEvent(TskEvent.HostsUpdatedTskEvent event) {
eventPublisher.publish(new HostsChangedEvent( eventPublisher.publish(new HostsUpdatedEvent(event.getHosts()));
event == null ? Collections.emptyList() : event.getHosts()));
} }
/** /**
@ -554,8 +565,13 @@ public class Case {
*/ */
@Subscribe @Subscribe
public void publishHostsDeletedEvent(TskEvent.HostsDeletedTskEvent event) { public void publishHostsDeletedEvent(TskEvent.HostsDeletedTskEvent event) {
eventPublisher.publish(new HostsRemovedEvent( try {
event == null ? Collections.emptyList() : event.getHosts())); hasData = dbHasData();
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Unable to retrieve the hasData status from the db", ex);
}
eventPublisher.publish(new HostsDeletedEvent(event.getHostIds()));
} }
/** /**
@ -566,8 +582,7 @@ public class Case {
*/ */
@Subscribe @Subscribe
public void publishPersonsAddedEvent(TskEvent.PersonsAddedTskEvent event) { public void publishPersonsAddedEvent(TskEvent.PersonsAddedTskEvent event) {
eventPublisher.publish(new PersonsAddedEvent( eventPublisher.publish(new PersonsAddedEvent(event.getPersons()));
event == null ? Collections.emptyList() : event.getPersons()));
} }
/** /**
@ -577,9 +592,8 @@ public class Case {
* @param event The sleuthkit event for the updating of persons. * @param event The sleuthkit event for the updating of persons.
*/ */
@Subscribe @Subscribe
public void publishPersonsChangedEvent(TskEvent.PersonsChangedTskEvent event) { public void publishPersonsUpdatedEvent(TskEvent.PersonsUpdatedTskEvent event) {
eventPublisher.publish(new PersonsChangedEvent( eventPublisher.publish(new PersonsUpdatedEvent(event.getPersons()));
event == null ? Collections.emptyList() : event.getPersons()));
} }
/** /**
@ -590,9 +604,19 @@ public class Case {
*/ */
@Subscribe @Subscribe
public void publishPersonsDeletedEvent(TskEvent.PersonsDeletedTskEvent event) { public void publishPersonsDeletedEvent(TskEvent.PersonsDeletedTskEvent event) {
eventPublisher.publish(new PersonsDeletedEvent( eventPublisher.publish(new PersonsDeletedEvent(event.getPersonIds()));
event == null ? Collections.emptyList() : event.getPersons()));
} }
@Subscribe
public void publishHostsAddedToPersonEvent(TskEvent.HostsAddedToPersonTskEvent event) {
eventPublisher.publish(new HostsAddedToPersonEvent(event.getPerson(), event.getHosts()));
}
@Subscribe
public void publisHostsRemovedFromPersonEvent(TskEvent.HostsRemovedFromPersonTskEvent event) {
eventPublisher.publish(new HostsRemovedFromPersonEvent(event.getPerson(), event.getHostIds()));
}
} }
/** /**
@ -1215,106 +1239,105 @@ public class Case {
/** /**
* Update the GUI to to reflect the current case. * Update the GUI to to reflect the current case.
*/ */
private static void updateGUIForCaseOpened(Case newCurrentCase) { private static void updateGUIForCaseOpened(Case newCurrentCase) {
/* /*
* If the case database was upgraded for a new schema and a * If the case database was upgraded for a new schema and a backup
* backup database was created, notify the user. * database was created, notify the user.
*/ */
SleuthkitCase caseDb = newCurrentCase.getSleuthkitCase(); SleuthkitCase caseDb = newCurrentCase.getSleuthkitCase();
String backupDbPath = caseDb.getBackupDatabasePath(); String backupDbPath = caseDb.getBackupDatabasePath();
if (null != backupDbPath) { if (null != backupDbPath) {
JOptionPane.showMessageDialog( JOptionPane.showMessageDialog(
mainFrame, mainFrame,
NbBundle.getMessage(Case.class, "Case.open.msgDlg.updated.msg", backupDbPath), NbBundle.getMessage(Case.class, "Case.open.msgDlg.updated.msg", backupDbPath),
NbBundle.getMessage(Case.class, "Case.open.msgDlg.updated.title"), NbBundle.getMessage(Case.class, "Case.open.msgDlg.updated.title"),
JOptionPane.INFORMATION_MESSAGE); JOptionPane.INFORMATION_MESSAGE);
} }
/* /*
* Look for the files for the data sources listed in the case * Look for the files for the data sources listed in the case database
* database and give the user the opportunity to locate any that * and give the user the opportunity to locate any that are missing.
* are missing. */
*/ Map<Long, String> imgPaths = getImagePaths(caseDb);
Map<Long, String> imgPaths = getImagePaths(caseDb); for (Map.Entry<Long, String> entry : imgPaths.entrySet()) {
for (Map.Entry<Long, String> entry : imgPaths.entrySet()) { long obj_id = entry.getKey();
long obj_id = entry.getKey(); String path = entry.getValue();
String path = entry.getValue(); boolean fileExists = (new File(path).isFile() || DriveUtils.driveExists(path));
boolean fileExists = (new File(path).isFile() || DriveUtils.driveExists(path)); if (!fileExists) {
if (!fileExists) { try {
try { // Using invokeAndWait means that the dialog will
// Using invokeAndWait means that the dialog will // open on the EDT but this thread will wait for an
// open on the EDT but this thread will wait for an // answer. Using invokeLater would cause this loop to
// answer. Using invokeLater would cause this loop to // end before all of the dialogs appeared.
// end before all of the dialogs appeared. SwingUtilities.invokeAndWait(new Runnable() {
SwingUtilities.invokeAndWait(new Runnable() { @Override
@Override public void run() {
public void run() { int response = JOptionPane.showConfirmDialog(
int response = JOptionPane.showConfirmDialog(
mainFrame, mainFrame,
NbBundle.getMessage(Case.class, "Case.checkImgExist.confDlg.doesntExist.msg", path), NbBundle.getMessage(Case.class, "Case.checkImgExist.confDlg.doesntExist.msg", path),
NbBundle.getMessage(Case.class, "Case.checkImgExist.confDlg.doesntExist.title"), NbBundle.getMessage(Case.class, "Case.checkImgExist.confDlg.doesntExist.title"),
JOptionPane.YES_NO_OPTION); JOptionPane.YES_NO_OPTION);
if (response == JOptionPane.YES_OPTION) { if (response == JOptionPane.YES_OPTION) {
MissingImageDialog.makeDialog(obj_id, caseDb); MissingImageDialog.makeDialog(obj_id, caseDb);
} else { } else {
logger.log(Level.SEVERE, "User proceeding with missing image files"); //NON-NLS logger.log(Level.SEVERE, "User proceeding with missing image files"); //NON-NLS
} }
} }
}); });
} catch (InterruptedException | InvocationTargetException ex) { } catch (InterruptedException | InvocationTargetException ex) {
logger.log(Level.SEVERE, "Failed to show missing image confirmation dialog", ex); //NON-NLS logger.log(Level.SEVERE, "Failed to show missing image confirmation dialog", ex); //NON-NLS
}
}
} }
}
}
/* /*
* Enable the case-specific actions. * Enable the case-specific actions.
*/ */
CallableSystemAction.get(AddImageAction.class).setEnabled(FeatureAccessUtils.canAddDataSources()); CallableSystemAction.get(AddImageAction.class).setEnabled(FeatureAccessUtils.canAddDataSources());
CallableSystemAction.get(OpenHostsAction.class).setEnabled(true); CallableSystemAction.get(OpenHostsAction.class).setEnabled(true);
CallableSystemAction.get(CaseCloseAction.class).setEnabled(true); CallableSystemAction.get(CaseCloseAction.class).setEnabled(true);
CallableSystemAction.get(CaseDetailsAction.class).setEnabled(true); CallableSystemAction.get(CaseDetailsAction.class).setEnabled(true);
CallableSystemAction.get(DataSourceSummaryAction.class).setEnabled(true); CallableSystemAction.get(DataSourceSummaryAction.class).setEnabled(true);
CallableSystemAction.get(CaseDeleteAction.class).setEnabled(FeatureAccessUtils.canDeleteCurrentCase()); CallableSystemAction.get(CaseDeleteAction.class).setEnabled(FeatureAccessUtils.canDeleteCurrentCase());
CallableSystemAction.get(OpenTimelineAction.class).setEnabled(true); CallableSystemAction.get(OpenTimelineAction.class).setEnabled(true);
CallableSystemAction.get(OpenCommVisualizationToolAction.class).setEnabled(true); CallableSystemAction.get(OpenCommVisualizationToolAction.class).setEnabled(true);
CallableSystemAction.get(CommonAttributeSearchAction.class).setEnabled(true); CallableSystemAction.get(CommonAttributeSearchAction.class).setEnabled(true);
CallableSystemAction.get(OpenOutputFolderAction.class).setEnabled(false); CallableSystemAction.get(OpenOutputFolderAction.class).setEnabled(false);
CallableSystemAction.get(OpenDiscoveryAction.class).setEnabled(true); 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());
final boolean hasData = newCurrentCase.hasData();
SwingUtilities.invokeLater(() -> {
/* /*
* Add the case to the recent cases tracker that supplies a list * Open the top components (windows within the main application
* of recent cases to the recent cases menu item and the * window).
* open/create case dialog. *
* Note: If the core windows are not opened here, they will be
* opened via the DirectoryTreeTopComponent 'propertyChange()'
* method on a DATA_SOURCE_ADDED event.
*/ */
RecentCases.getInstance().addRecentCase(newCurrentCase.getDisplayName(), newCurrentCase.getMetadata().getFilePath().toString()); if (hasData) {
final boolean hasData = newCurrentCase.hasData(); CoreComponentControl.openCoreWindows();
} else {
SwingUtilities.invokeLater(() -> { //ensure that the DirectoryTreeTopComponent is open so that it's listener can open the core windows including making it visible.
/* DirectoryTreeTopComponent.findInstance();
* Open the top components (windows within the main application }
* window).
*
* Note: If the core windows are not opened here, they will be
* opened via the DirectoryTreeTopComponent 'propertyChange()'
* method on a DATA_SOURCE_ADDED event.
*/
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();
}
/* /*
* Reset the main window title to: * Reset the main window title to:
* *
* [curent case display name] - [application name]. * [curent case display name] - [application name].
*/ */
mainFrame.setTitle(newCurrentCase.getDisplayName() + " - " + getNameForTitle()); mainFrame.setTitle(newCurrentCase.getDisplayName() + " - " + getNameForTitle());
}); });
} }
/* /*
@ -1666,26 +1689,21 @@ public class Case {
} }
/** /**
* Queries whether or not the case has data, i.e., whether or not at least * Returns true if there is any data in the case.
* one data source has been added to the case.
* *
* @return True or false. * @return True or false.
*/ */
public boolean hasData() { public boolean hasData() {
boolean hasDataSources = false; return hasData;
String query = "SELECT count(*) AS count FROM data_source_info"; }
try (SleuthkitCase.CaseDbQuery dbQuery = caseDb.executeQuery(query)) {
ResultSet resultSet = dbQuery.getResultSet(); /**
if (resultSet.next()) { * Returns true if there is one or more data sources in the case.
long numDataSources = resultSet.getLong("count"); *
if (numDataSources > 0) { * @return True or false.
hasDataSources = true; */
} public boolean hasDataSource() {
} return hasDataSource;
} catch (TskCoreException | SQLException ex) {
logger.log(Level.SEVERE, "Error accessing case database", ex); //NON-NLS
}
return hasDataSources;
} }
/** /**
@ -1699,6 +1717,8 @@ public class Case {
* notifyNewDataSource after the data source is added. * notifyNewDataSource after the data source is added.
*/ */
public void notifyAddingDataSource(UUID eventId) { public void notifyAddingDataSource(UUID eventId) {
hasDataSource = true;
hasData = true;
eventPublisher.publish(new AddingDataSourceEvent(eventId)); eventPublisher.publish(new AddingDataSourceEvent(eventId));
} }
@ -1844,72 +1864,6 @@ public class Case {
eventPublisher.publish(new BlackBoardArtifactTagDeletedEvent(deletedTag)); eventPublisher.publish(new BlackBoardArtifactTagDeletedEvent(deletedTag));
} }
public void notifyOsAccountAdded(OsAccount account) {
eventPublisher.publish(new OsAccountAddedEvent(account));
}
public void notifyOsAccountChanged(OsAccount account) {
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) {
eventPublisher.publish(new HostsAddedEvent(Collections.singletonList(host)));
}
/**
* Notify via an autopsy event that a host has been changed.
*
* @param newValue The host that has been updated.
*/
public void notifyHostChanged(Host newValue) {
eventPublisher.publish(new HostsChangedEvent(Collections.singletonList(newValue)));
}
/**
* Notify via an autopsy event that a host has been deleted.
*
* @param host The host that has been deleted.
*/
public void notifyHostDeleted(Host host) {
eventPublisher.publish(new HostsRemovedEvent(Collections.singletonList(host)));
}
/**
* Notify via an autopsy event that a person has been added.
*
* @param person The person that has been added.
*/
public void notifyPersonAdded(Person person) {
eventPublisher.publish(new PersonsAddedEvent(Collections.singletonList(person)));
}
/**
* Notify via an autopsy event that a person has been changed.
*
* @param newValue The person that has been updated.
*/
public void notifyPersonChanged(Person newValue) {
eventPublisher.publish(new PersonsChangedEvent(Collections.singletonList(newValue)));
}
/**
* 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 PersonsDeletedEvent(Collections.singletonList(person)));
}
/** /**
* Adds a report to the case. * Adds a report to the case.
* *
@ -1951,6 +1905,8 @@ public class Case {
String errorMsg = "Invalid local path provided: " + localPath; // NON-NLS String errorMsg = "Invalid local path provided: " + localPath; // NON-NLS
throw new TskCoreException(errorMsg, ex); throw new TskCoreException(errorMsg, ex);
} }
hasData = true;
Report report = this.caseDb.addReport(normalizedLocalPath, srcModuleName, reportName, parent); Report report = this.caseDb.addReport(normalizedLocalPath, srcModuleName, reportName, parent);
eventPublisher.publish(new ReportAddedEvent(report)); eventPublisher.publish(new ReportAddedEvent(report));
return report; return report;
@ -1979,6 +1935,15 @@ public class Case {
public void deleteReports(Collection<? extends Report> reports) throws TskCoreException { public void deleteReports(Collection<? extends Report> reports) throws TskCoreException {
for (Report report : reports) { for (Report report : reports) {
this.caseDb.deleteReport(report); this.caseDb.deleteReport(report);
}
try {
hasData = dbHasData();
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Unable to retrieve the hasData status from the db", ex);
}
for (Report report : reports) {
eventPublisher.publish(new AutopsyEvent(Events.REPORT_DELETED.toString(), report, null)); eventPublisher.publish(new AutopsyEvent(Events.REPORT_DELETED.toString(), report, null));
} }
} }
@ -2736,6 +2701,7 @@ public class Case {
} else { } else {
throw new CaseActionException(Bundle.Case_open_exception_multiUserCaseNotEnabled()); throw new CaseActionException(Bundle.Case_open_exception_multiUserCaseNotEnabled());
} }
updateDataParameters();
} catch (TskUnsupportedSchemaVersionException ex) { } catch (TskUnsupportedSchemaVersionException ex) {
throw new CaseActionException(Bundle.Case_exceptionMessage_unsupportedSchemaVersionMessage(ex.getLocalizedMessage()), ex); throw new CaseActionException(Bundle.Case_exceptionMessage_unsupportedSchemaVersionMessage(ex.getLocalizedMessage()), ex);
} catch (UserPreferencesException ex) { } catch (UserPreferencesException ex) {
@ -3513,6 +3479,78 @@ public class Case {
} }
} }
/**
* Initialize the hasData and hasDataSource parameters by checking the
* database.
*
* hasDataSource will be true if any data Source exists the db.
*
* hasData will be true if hasDataSource is true or if there are entries in
* the tsk_object or tsk_host tables.
*
* @throws TskCoreException
*/
private void updateDataParameters() throws TskCoreException {
hasDataSource = dbHasDataSource();
if (!hasDataSource) {
hasData = dbHasData();
} else {
hasData = true;
}
}
/**
* Returns true of there are any data sources in the case database.
*
* @return True if this case as a data source.
*
* @throws TskCoreException
*/
private boolean dbHasDataSource() throws TskCoreException {
String query = "SELECT count(*) AS count FROM (SELECT * FROM data_source_info LIMIT 1)t";
try (SleuthkitCase.CaseDbQuery dbQuery = caseDb.executeQuery(query)) {
ResultSet resultSet = dbQuery.getResultSet();
if (resultSet.next()) {
return resultSet.getLong("count") > 0;
}
return false;
} catch (SQLException ex) {
logger.log(Level.SEVERE, "Error accessing case database", ex); //NON-NLS
throw new TskCoreException("Error accessing case databse", ex);
}
}
/**
* Returns true if the case has data. A case has data if there is at least
* one row in either the tsk_objects or tsk_hosts table.
*
* @return True if there is data in this case.
*
* @throws TskCoreException
*/
private boolean dbHasData() throws TskCoreException {
// The LIMIT 1 in the subquery should limit the data returned and
// make the overall query more efficent.
String query = "SELECT SUM(cnt) total FROM "
+ "(SELECT COUNT(*) AS cnt FROM "
+ "(SELECT * FROM tsk_objects LIMIT 1)t "
+ "UNION ALL "
+ "SELECT COUNT(*) AS cnt FROM "
+ "(SELECT * FROM tsk_hosts LIMIT 1)r) s";
try (SleuthkitCase.CaseDbQuery dbQuery = caseDb.executeQuery(query)) {
ResultSet resultSet = dbQuery.getResultSet();
if (resultSet.next()) {
return resultSet.getLong("total") > 0;
} else {
return false;
}
} catch (SQLException ex) {
logger.log(Level.SEVERE, "Error accessing case database", ex); //NON-NLS
throw new TskCoreException("Error accessing case databse", ex);
}
}
/** /**
* Defines the signature for case action methods that can be passed as * Defines the signature for case action methods that can be passed as
* arguments to the doCaseAction method. * arguments to the doCaseAction method.

View File

@ -1,11 +1,6 @@
<?xml version="1.0" encoding="UTF-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo"> <Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<Properties>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[32767, 32767]"/>
</Property>
</Properties>
<AuxValues> <AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/> <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/> <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
@ -16,6 +11,7 @@
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/> <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/> <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/> <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> </AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/> <Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
@ -35,9 +31,6 @@
<SubComponents> <SubComponents>
<Container class="javax.swing.JPanel" name="contentPanel"> <Container class="javax.swing.JPanel" name="contentPanel">
<Properties> <Properties>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[32767, 32767]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[625, 150]"/> <Dimension value="[625, 150]"/>
</Property> </Property>
@ -60,9 +53,6 @@
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[16, 16]"/> <Dimension value="[16, 16]"/>
</Property> </Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="null"/>
</Property>
</Properties> </Properties>
<AuxValues> <AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/> <AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
@ -85,6 +75,12 @@
<Property name="columnModel" type="javax.swing.table.TableColumnModel" editor="org.netbeans.modules.form.editors2.TableColumnModelEditor"> <Property name="columnModel" type="javax.swing.table.TableColumnModel" editor="org.netbeans.modules.form.editors2.TableColumnModelEditor">
<TableColumnModel selectionModel="1"/> <TableColumnModel selectionModel="1"/>
</Property> </Property>
<Property name="gridColor" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="f0" green="f0" id="InternalFrame.borderColor" palette="3" red="f0" type="palette"/>
</Property>
<Property name="intercellSpacing" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[4, 2]"/>
</Property>
<Property name="tableHeader" type="javax.swing.table.JTableHeader" editor="org.netbeans.modules.form.editors2.JTableHeaderEditor"> <Property name="tableHeader" type="javax.swing.table.JTableHeader" editor="org.netbeans.modules.form.editors2.JTableHeaderEditor">
<TableHeader reorderingAllowed="false" resizingAllowed="true"/> <TableHeader reorderingAllowed="false" resizingAllowed="true"/>
</Property> </Property>
@ -157,6 +153,12 @@
<Property name="columnModel" type="javax.swing.table.TableColumnModel" editor="org.netbeans.modules.form.editors2.TableColumnModelEditor"> <Property name="columnModel" type="javax.swing.table.TableColumnModel" editor="org.netbeans.modules.form.editors2.TableColumnModelEditor">
<TableColumnModel selectionModel="0"/> <TableColumnModel selectionModel="0"/>
</Property> </Property>
<Property name="gridColor" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="f0" green="f0" id="InternalFrame.borderColor" palette="3" red="f0" type="palette"/>
</Property>
<Property name="intercellSpacing" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[4, 2]"/>
</Property>
<Property name="tableHeader" type="javax.swing.table.JTableHeader" editor="org.netbeans.modules.form.editors2.JTableHeaderEditor"> <Property name="tableHeader" type="javax.swing.table.JTableHeader" editor="org.netbeans.modules.form.editors2.JTableHeaderEditor">
<TableHeader reorderingAllowed="true" resizingAllowed="true"/> <TableHeader reorderingAllowed="true" resizingAllowed="true"/>
</Property> </Property>

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2016-2019 Basis Technology Corp. * Copyright 2016-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -50,7 +50,7 @@ public final class IngestJobInfoPanel extends javax.swing.JPanel {
private static final Logger logger = Logger.getLogger(IngestJobInfoPanel.class.getName()); private static final Logger logger = Logger.getLogger(IngestJobInfoPanel.class.getName());
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.STARTED, IngestManager.IngestJobEvent.CANCELLED, IngestManager.IngestJobEvent.COMPLETED); private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.STARTED, IngestManager.IngestJobEvent.CANCELLED, IngestManager.IngestJobEvent.COMPLETED);
private static final Set<Case.Events> CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.CURRENT_CASE); private static final Set<Case.Events> CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.CURRENT_CASE);
private static final int EXTRA_ROW_HEIGHT = 4;
private List<IngestJobInfo> ingestJobs; private List<IngestJobInfo> ingestJobs;
private final List<IngestJobInfo> ingestJobsForSelectedDataSource = new ArrayList<>(); private final List<IngestJobInfo> ingestJobsForSelectedDataSource = new ArrayList<>();
private IngestJobTableModel ingestJobTableModel = new IngestJobTableModel(); private IngestJobTableModel ingestJobTableModel = new IngestJobTableModel();
@ -100,6 +100,9 @@ public final class IngestJobInfoPanel extends javax.swing.JPanel {
} }
} }
}); });
ingestJobTable.setRowHeight(ingestJobTable.getRowHeight() + EXTRA_ROW_HEIGHT);
ingestModuleTable.setRowHeight(ingestModuleTable.getRowHeight() + EXTRA_ROW_HEIGHT);
} }
/** /**
@ -267,19 +270,18 @@ public final class IngestJobInfoPanel extends javax.swing.JPanel {
javax.swing.JScrollPane ingestModulesScrollPane = new javax.swing.JScrollPane(); javax.swing.JScrollPane ingestModulesScrollPane = new javax.swing.JScrollPane();
ingestModuleTable = new javax.swing.JTable(); ingestModuleTable = new javax.swing.JTable();
setMaximumSize(new java.awt.Dimension(32767, 32767));
setLayout(new java.awt.BorderLayout()); setLayout(new java.awt.BorderLayout());
contentPanel.setMaximumSize(new java.awt.Dimension(32767, 32767));
contentPanel.setMinimumSize(new java.awt.Dimension(625, 150)); contentPanel.setMinimumSize(new java.awt.Dimension(625, 150));
contentPanel.setPreferredSize(new java.awt.Dimension(625, 150)); contentPanel.setPreferredSize(new java.awt.Dimension(625, 150));
contentPanel.setLayout(new java.awt.GridBagLayout()); contentPanel.setLayout(new java.awt.GridBagLayout());
ingestJobsScrollPane.setBorder(null); ingestJobsScrollPane.setBorder(null);
ingestJobsScrollPane.setMinimumSize(new java.awt.Dimension(16, 16)); ingestJobsScrollPane.setMinimumSize(new java.awt.Dimension(16, 16));
ingestJobsScrollPane.setPreferredSize(null);
ingestJobTable.setModel(ingestJobTableModel); ingestJobTable.setModel(ingestJobTableModel);
ingestJobTable.setGridColor(javax.swing.UIManager.getDefaults().getColor("InternalFrame.borderColor"));
ingestJobTable.setIntercellSpacing(new java.awt.Dimension(4, 2));
ingestJobTable.getTableHeader().setReorderingAllowed(false); ingestJobTable.getTableHeader().setReorderingAllowed(false);
ingestJobsScrollPane.setViewportView(ingestJobTable); ingestJobsScrollPane.setViewportView(ingestJobTable);
ingestJobTable.getColumnModel().getSelectionModel().setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION); ingestJobTable.getColumnModel().getSelectionModel().setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
@ -315,6 +317,8 @@ public final class IngestJobInfoPanel extends javax.swing.JPanel {
ingestModulesScrollPane.setPreferredSize(new java.awt.Dimension(254, 16)); ingestModulesScrollPane.setPreferredSize(new java.awt.Dimension(254, 16));
ingestModuleTable.setModel(ingestModuleTableModel); ingestModuleTable.setModel(ingestModuleTableModel);
ingestModuleTable.setGridColor(javax.swing.UIManager.getDefaults().getColor("InternalFrame.borderColor"));
ingestModuleTable.setIntercellSpacing(new java.awt.Dimension(4, 2));
ingestModulesScrollPane.setViewportView(ingestModuleTable); ingestModulesScrollPane.setViewportView(ingestModuleTable);
gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints = new java.awt.GridBagConstraints();

View File

@ -23,17 +23,21 @@ import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.datamodel.Host; import org.sleuthkit.datamodel.Host;
/** /**
* Event fired when new hosts are added. * Application events published when hosts have been added to the Sleuth Kit
* data model for a case.
*/ */
public class HostsAddedEvent extends HostsEvent { public final class HostsAddedEvent extends HostsEvent {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
* Main constructor. * Constructs an application event published when hosts have been added to
* @param dataModelObjects The hosts that have been added. * the Sleuth Kit data model for a case.
*
* @param hosts The hosts that have been added.
*/ */
public HostsAddedEvent(List<Host> dataModelObjects) { public HostsAddedEvent(List<Host> hosts) {
super(Case.Events.HOSTS_ADDED.name(), dataModelObjects); super(Case.Events.HOSTS_ADDED.name(), hosts);
} }
} }

View File

@ -0,0 +1,92 @@
/*
* 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 java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.datamodel.Host;
import org.sleuthkit.datamodel.Person;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Application events published when one or more hosts have been added to a
* person.
*/
public final class HostsAddedToPersonEvent extends TskDataModelChangedEvent<Person, Host> {
private static final long serialVersionUID = 1L;
/**
* Constructs an application event published when one or more hosts have
* been added to a person.
*
* @param person The person.
* @param hosts The hosts.
*/
public HostsAddedToPersonEvent(Person person, List<Host> hosts) {
super(Case.Events.HOSTS_ADDED_TO_PERSON.toString(), Collections.singletonList(person), Person::getPersonId, hosts, Host::getHostId);
}
/**
* Gets the person.
*
* @return The person.
*/
public Person getPerson() {
return getOldValue().get(0);
}
/**
* Gets the hosts.
*
* @return The hosts.
*/
public List<Host> getHosts() {
return getNewValue();
}
@Override
protected List<Person> getOldValueObjects(SleuthkitCase caseDb, List<Long> ids) throws TskCoreException {
List<Person> persons = new ArrayList<>();
for (Long id : ids) {
Optional<Person> person = caseDb.getPersonManager().getPerson(id);
if (person.isPresent()) {
persons.add(person.get());
}
}
return persons;
}
@Override
protected List<Host> getNewValueObjects(SleuthkitCase caseDb, List<Long> ids) throws TskCoreException {
List<Host> hosts = new ArrayList<>();
for (Long id : ids) {
Optional<Host> host = caseDb.getHostManager().getHostById(id);
if (host.isPresent()) {
hosts.add(host.get());
}
}
return hosts;
}
}

View File

@ -20,20 +20,32 @@ package org.sleuthkit.autopsy.casemodule.events;
import java.util.List; import java.util.List;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.datamodel.Host;
/** /**
* Event fired when hosts are removed. * Application events published when hosts have been deleted from the Sleuth
* Kit data model for a case.
*/ */
public class HostsRemovedEvent extends HostsEvent { public final class HostsDeletedEvent extends TskDataModelObjectsDeletedEvent {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
* Main constructor. * Constructs an application event published when hosts have been deleted
* @param dataModelObjects The list of hosts that have been deleted. * from the Sleuth Kit data model for a case.
*
* @param hostIds The host IDs of the deleted hosts.
*/ */
public HostsRemovedEvent(List<Host> dataModelObjects) { public HostsDeletedEvent(List<Long> hostIds) {
super(Case.Events.HOSTS_DELETED.name(), dataModelObjects); super(Case.Events.HOSTS_DELETED.name(), hostIds);
} }
/**
* Gets the host IDs of the hosts that have been deleted.
*
* @return The host IDs.
*/
public List<Long> getHostIds() {
return getOldValue();
}
} }

View File

@ -19,71 +19,51 @@
package org.sleuthkit.autopsy.casemodule.events; package org.sleuthkit.autopsy.casemodule.events;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Collectors;
import org.sleuthkit.datamodel.Host; import org.sleuthkit.datamodel.Host;
import org.sleuthkit.datamodel.HostManager;
import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
/** /**
* Base event class for when something pertaining to hosts changes. * A base class for application events published when hosts in the Sleuth Kit
* data model for a case have been added or updated.
*/ */
public class HostsEvent extends TskDataModelChangeEvent<Host> { public class HostsEvent extends TskDataModelChangedEvent<Host, Host> {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
* Retrieves a list of ids from a list of hosts. * Constructs the base class part of an application event published when
* * hosts in the Sleuth Kit data model for a case have been added or updated.
* @param hosts The hosts.
* @return The list of ids.
*/
private static List<Long> getIds(List<Host> hosts) {
return getSafeList(hosts).stream()
.filter(h -> h != null)
.map(h -> h.getHostId()).collect(Collectors.toList());
}
/**
* Returns the hosts or an empty list.
*
* @param hosts The host list.
* @return The host list or an empty list if the parameter is null.
*/
private static List<Host> getSafeList(List<Host> hosts) {
return hosts == null ? Collections.emptyList() : hosts;
}
/**
* Main constructor.
* *
* @param eventName The name of the Case.Events enum value for the event * @param eventName The name of the Case.Events enum value for the event
* type. * type.
* @param dataModelObjects The list of hosts for the event. * @param hosts The hosts.
*/ */
protected HostsEvent(String eventName, List<Host> dataModelObjects) { HostsEvent(String eventName, List<Host> hosts) {
super(eventName, getIds(dataModelObjects), new ArrayList<>(getSafeList(dataModelObjects))); super(eventName, null, null, hosts, Host::getHostId);
}
/**
* Gets the hosts that have been added or updated.
*
* @return The hosts.
*/
public List<Host> getHosts() {
return getNewValue();
} }
@Override @Override
protected List<Host> getDataModelObjects(SleuthkitCase caseDb, List<Long> ids) throws TskCoreException { protected List<Host> getNewValueObjects(SleuthkitCase caseDb, List<Long> ids) throws TskCoreException {
HostManager hostManager = caseDb.getHostManager(); List<Host> hosts = new ArrayList<>();
List<Host> toRet = new ArrayList<>(); for (Long id : ids) {
if (ids != null) { Optional<Host> host = caseDb.getHostManager().getHostById(id);
for (Long id : ids) { if (host.isPresent()) {
if (id == null) { hosts.add(host.get());
continue;
}
Optional<Host> thisHostOpt = hostManager.getHostById(id);
thisHostOpt.ifPresent((h) -> toRet.add(h));
} }
} }
return hosts;
return toRet;
} }
} }

View File

@ -0,0 +1,85 @@
/*
* 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 java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.datamodel.Host;
import org.sleuthkit.datamodel.Person;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Application events published when one or more hosts have been removed from a
* person.
*/
public class HostsRemovedFromPersonEvent extends TskDataModelChangedEvent<Person, Long> {
private static final long serialVersionUID = 1L;
/**
* Constructs an application event published when one or more hosts have
* been removed from a person.
*
* @param person The person.
* @param hostIds The host IDs of the removed hosts.
*/
public HostsRemovedFromPersonEvent(Person person, List<Long> hostIds) {
super(Case.Events.HOSTS_REMOVED_FROM_PERSON.toString(), Collections.singletonList(person), Person::getPersonId, hostIds, (id -> id));
}
/**
* Gets the person.
*
* @return The person.
*/
public Person getPerson() {
return getOldValue().get(0);
}
/**
* Gets the host IDs of the removed hosts.
*
* @return The host IDs.
*/
public List<Long> getHostIds() {
return getNewValue();
}
@Override
protected List<Person> getOldValueObjects(SleuthkitCase caseDb, List<Long> ids) throws TskCoreException {
List<Person> persons = new ArrayList<>();
for (Long id : ids) {
Optional<Person> person = caseDb.getPersonManager().getPerson(id);
if (person.isPresent()) {
persons.add(person.get());
}
}
return persons;
}
@Override
protected List<Long> getNewValueObjects(SleuthkitCase caseDb, List<Long> ids) throws TskCoreException {
return ids;
}
}

View File

@ -23,19 +23,21 @@ import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.datamodel.Host; import org.sleuthkit.datamodel.Host;
/** /**
* Event fired when hosts are changed. * Application events published when hosts in the Sleuth Kit data model for
* a case have been updated.
*/ */
public class HostsChangedEvent extends HostsEvent { public class HostsUpdatedEvent extends HostsEvent {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
* Main constructor. * Constructs an application event published when hosts in the Sleuth Kit
* data model for a case have been updated.
* *
* @param dataModelObjects The new values for the hosts that have been * @param hosts The updated persons.
* changed.
*/ */
public HostsChangedEvent(List<Host> dataModelObjects) { public HostsUpdatedEvent(List<Host> hosts) {
super(Case.Events.HOSTS_CHANGED.name(), dataModelObjects); super(Case.Events.HOSTS_UPDATED.name(), hosts);
} }
} }

View File

@ -1,37 +0,0 @@
/*
* 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

@ -1,64 +0,0 @@
/*
* 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 java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.sleuthkit.datamodel.OsAccount;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Parent class for specific OsAccount event classes.
*/
class OsAccountEvent extends TskDataModelChangeEvent<OsAccount> {
private static final long serialVersionUID = 1L;
/**
* Construct a new OsAccountEvent.
*
* @param eventName The name of the event.
* @param account The OsAccount the event applies to.
*/
OsAccountEvent(String eventName, OsAccount account) {
super(eventName, Stream.of(account.getId()).collect(Collectors.toList()), Stream.of(account).collect(Collectors.toList()));
}
/**
* Returns the OsAccount that changed.
*
* @return The OsAccount that was changed.
*/
public OsAccount getOsAccount() {
List<OsAccount> accounts = getNewValue();
return accounts.get(0);
}
@Override
protected List<OsAccount> getDataModelObjects(SleuthkitCase caseDb, List<Long> ids) throws TskCoreException {
Long id = ids.get(0);
OsAccount account = caseDb.getOsAccountManager().getOsAccountByObjectId(id);
List<OsAccount> accounts = new ArrayList<>();
accounts.add(account);
return accounts;
}
}

View File

@ -18,17 +18,26 @@
*/ */
package org.sleuthkit.autopsy.casemodule.events; package org.sleuthkit.autopsy.casemodule.events;
import org.sleuthkit.autopsy.casemodule.Case; import java.util.List;
import static org.sleuthkit.autopsy.casemodule.Case.Events.OS_ACCOUNTS_ADDED;
import org.sleuthkit.datamodel.OsAccount; import org.sleuthkit.datamodel.OsAccount;
/** /**
* Event published when an OsAccount is updated. * An application event published when OS accounts are added to the Sleuth Kit
* data model for a case.
*/ */
public final class OsAccountChangedEvent extends OsAccountEvent { public final class OsAccountsAddedEvent extends OsAccountsEvent {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
public OsAccountChangedEvent(OsAccount account) { /**
super(Case.Events.OS_ACCOUNT_CHANGED.toString(), account); * Constructs an application event published when OS accounts are added to
} * the Sleuth Kit data model for a case.
*
* @param osAccounts The OS accounts that were added.
*/
public OsAccountsAddedEvent(List<OsAccount> osAccounts) {
super(OS_ACCOUNTS_ADDED.toString(), osAccounts);
}
} }

View File

@ -0,0 +1,51 @@
/*
* 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 java.util.List;
import org.sleuthkit.autopsy.casemodule.Case;
/**
* An application event published when OS accounts have been deleted from the
* Sleuth Kit data model for a case.
*/
public final class OsAccountsDeletedEvent extends TskDataModelObjectsDeletedEvent {
private static final long serialVersionUID = 1L;
/**
* Constructs an application event published when OS accounts have been
* deleted from the Sleuth Kit data model for a case.
*
* @param osAccountObjectIds TSK object IDs of the deleted accounts.
*/
public OsAccountsDeletedEvent(List<Long> osAccountObjectIds) {
super(Case.Events.OS_ACCOUNTS_DELETED.toString(), osAccountObjectIds);
}
/**
* Gets the Sleuth Kit object IDs of the deleted OS accounts.
*
* @return The object IDs.
*/
List<Long> getOsAccountObjectIds() {
return getOldValue();
}
}

View File

@ -0,0 +1,66 @@
/*
* 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 java.util.ArrayList;
import java.util.List;
import org.sleuthkit.datamodel.OsAccount;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
/**
* A base class for application events published when OS accounts in the Sleuth
* Kit data model for a case have been added or updated.
*/
class OsAccountsEvent extends TskDataModelChangedEvent<OsAccount, OsAccount> {
private static final long serialVersionUID = 1L;
/**
* Constructs the base class part of an application event published when
* OS accounts in the Sleuth Kit data model for a case have been added or
* updated.
*
* @param eventName The name of the Case.Events enum value for the event
* type.
* @param account The OS accounts.
*/
OsAccountsEvent(String eventName, List<OsAccount> osAccounts) {
super(eventName, null, null, osAccounts, OsAccount::getId);
}
/**
* Gets the OS accounts that have been added or updated.
*
* @return The OS accounts.
*/
public List<OsAccount> getOsAccounts() {
return getNewValue();
}
@Override
protected List<OsAccount> getNewValueObjects(SleuthkitCase caseDb, List<Long> ids) throws TskCoreException {
List<OsAccount> osAccounts = new ArrayList<>();
for (Long id : ids) {
osAccounts.add(caseDb.getOsAccountManager().getOsAccountByObjectId(id));
}
return osAccounts;
}
}

View File

@ -18,18 +18,26 @@
*/ */
package org.sleuthkit.autopsy.casemodule.events; package org.sleuthkit.autopsy.casemodule.events;
import java.util.List;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.datamodel.OsAccount; import org.sleuthkit.datamodel.OsAccount;
/** /**
* Event published when an OsAccount is added to a case. * An application event published when OS accounts in the Sleuth Kit data model
* for a case have been updated.
*/ */
public final class OsAccountAddedEvent extends OsAccountEvent { public final class OsAccountsUpdatedEvent extends OsAccountsEvent {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
public OsAccountAddedEvent(OsAccount account) {
super(Case.Events.OS_ACCOUNT_ADDED.toString(), account);
}
/**
* Constructs an application event published when OS accounts in the Sleuth
* Kit data model for a case have been updated.
*
* @param osAccounts The OS accounts that were updated.
*/
public OsAccountsUpdatedEvent(List<OsAccount> osAccounts) {
super(Case.Events.OS_ACCOUNTS_UPDATED.toString(), osAccounts);
}
} }

View File

@ -23,17 +23,21 @@ import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.datamodel.Person; import org.sleuthkit.datamodel.Person;
/** /**
* Event fired when new persons are added. * An application event published when persons have been added to the Sleuth Kit
* data model for a case.
*/ */
public class PersonsAddedEvent extends PersonsEvent { public class PersonsAddedEvent extends PersonsEvent {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
* Main constructor. * Constructs an application event published when persons have been added to
* @param dataModelObjects The persons that have been added. * the Sleuth Kit data model for a case.
*
* @param persons The persons that have been added.
*/ */
public PersonsAddedEvent(List<Person> dataModelObjects) { public PersonsAddedEvent(List<Person> persons) {
super(Case.Events.PERSONS_ADDED.name(), dataModelObjects); super(Case.Events.PERSONS_ADDED.name(), persons);
} }
} }

View File

@ -20,20 +20,32 @@ package org.sleuthkit.autopsy.casemodule.events;
import java.util.List; import java.util.List;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.datamodel.Person;
/** /**
* Event fired when persons are removed. * Application events published when persons have been deleted from the Sleuth
* Kit data model for a case.
*/ */
public class PersonsDeletedEvent extends PersonsEvent { public class PersonsDeletedEvent extends TskDataModelObjectsDeletedEvent {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
* Main constructor. * Constructs an application event published when persons have been deleted
* @param dataModelObjects The list of persons that have been deleted. * from the Sleuth Kit data model for a case.
*
* @param personIds The IDs of the persons that have been deleted.
*/ */
public PersonsDeletedEvent(List<Person> dataModelObjects) { public PersonsDeletedEvent(List<Long> personIds) {
super(Case.Events.PERSONS_DELETED.name(), dataModelObjects); super(Case.Events.PERSONS_DELETED.name(), personIds);
} }
/**
* Gets the person IDs of the persons that have been deleted.
*
* @return The person IDs.
*/
List<Long> getPersonIds() {
return getOldValue();
}
} }

View File

@ -19,69 +19,52 @@
package org.sleuthkit.autopsy.casemodule.events; package org.sleuthkit.autopsy.casemodule.events;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Collectors;
import org.sleuthkit.datamodel.Person; import org.sleuthkit.datamodel.Person;
import org.sleuthkit.datamodel.PersonManager;
import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
/** /**
* Base event class for when something pertaining to persons changes. * A base class for application events published when persons in the Sleuth Kit
* data model for a case have been added or updated.
*/ */
public class PersonsEvent extends TskDataModelChangeEvent<Person> { public class PersonsEvent extends TskDataModelChangedEvent<Person, Person> {
private static final long serialVersionUID = 1L;
/** /**
* Retrieves a list of ids from a list of persons. * Constructs the base class part of an application event published when
* * persons in the Sleuth Kit data model for a case have been added or
* @param persons The persons. * updated.
* @return The list of ids.
*/
private static List<Long> getIds(List<Person> persons) {
return getSafeList(persons).stream()
.filter(h -> h != null)
.map(h -> h.getPersonId()).collect(Collectors.toList());
}
/**
* Returns the persons or an empty list.
*
* @param persons The person list.
* @return The person list or an empty list if the parameter is null.
*/
private static List<Person> getSafeList(List<Person> persons) {
return persons == null ? Collections.emptyList() : persons;
}
/**
* Main constructor.
* *
* @param eventName The name of the Case.Events enum value for the event * @param eventName The name of the Case.Events enum value for the event
* type. * type.
* @param dataModelObjects The list of persons for the event. * @param persons The persons.
*/ */
protected PersonsEvent(String eventName, List<Person> dataModelObjects) { PersonsEvent(String eventName, List<Person> persons) {
super(eventName, getIds(dataModelObjects), new ArrayList<>(getSafeList(dataModelObjects))); super(eventName, null, null, persons, Person::getPersonId);
}
/**
* Gets the persons that have been added or updated.
*
* @return The persons.
*/
public List<Person> getPersons() {
return getNewValue();
} }
@Override @Override
protected List<Person> getDataModelObjects(SleuthkitCase caseDb, List<Long> ids) throws TskCoreException { protected List<Person> getNewValueObjects(SleuthkitCase caseDb, List<Long> ids) throws TskCoreException {
PersonManager personManager = caseDb.getPersonManager(); List<Person> persons = new ArrayList<>();
List<Person> toRet = new ArrayList<>(); for (Long id : ids) {
if (ids != null) { Optional<Person> person = caseDb.getPersonManager().getPerson(id);
for (Long id : ids) { if (person.isPresent()) {
if (id == null) { persons.add(person.get());
continue;
}
Optional<Person> thisPersonOpt = personManager.getPerson(id);
thisPersonOpt.ifPresent((h) -> toRet.add(h));
} }
} }
return persons;
return toRet;
} }
} }

View File

@ -23,19 +23,21 @@ import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.datamodel.Person; import org.sleuthkit.datamodel.Person;
/** /**
* Event fired when persons are changed. * Application events published when persons in the Sleuth Kit data model for
* a case have been updated.
*/ */
public class PersonsChangedEvent extends PersonsEvent { public class PersonsUpdatedEvent extends PersonsEvent {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
* Main constructor. * Constructs an application event published when persons in the Sleuth Kit
* data model for a case have been updated.
* *
* @param dataModelObjects The new values for the persons that have been * @param persons The updated persons.
* changed.
*/ */
public PersonsChangedEvent(List<Person> dataModelObjects) { public PersonsUpdatedEvent(List<Person> persons) {
super(Case.Events.PERSONS_CHANGED.name(), dataModelObjects); super(Case.Events.PERSONS_UPDATED.name(), persons);
} }
} }

View File

@ -28,33 +28,38 @@ import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
/** /**
* Event published when a report is added to a case. * An application event published when a report is added to a case.
*/ */
public final class ReportAddedEvent extends TskDataModelChangeEvent<Report> { public final class ReportAddedEvent extends TskDataModelChangedEvent<Report, Report> {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
* Constructs an event published when a report is added to a case. * Constructs an application event published when a report is added to a
* case.
* *
* @param report The data source that was added. * @param report The report that was added.
*/ */
public ReportAddedEvent(Report report) { public ReportAddedEvent(Report report) {
super(Case.Events.REPORT_ADDED.toString(), Stream.of(report.getId()).collect(Collectors.toList()), Stream.of(report).collect(Collectors.toList())); super(Case.Events.REPORT_ADDED.toString(), null, null, Stream.of(report).collect(Collectors.toList()), Report::getId);
} }
/**
* Gets the reoprt that was added to the case.
*
* @return The report.
*/
public Report getReport() { public Report getReport() {
List<Report> reports = getNewValue(); List<Report> reports = getNewValue();
return reports.get(0); return reports.get(0);
} }
@Override @Override
protected List<Report> getDataModelObjects(SleuthkitCase caseD, List<Long> ids) throws TskCoreException { protected List<Report> getNewValueObjects(SleuthkitCase caseDb, List<Long> ids) throws TskCoreException {
Long id = ids.get(0); Long id = ids.get(0);
Report report = caseD.getReportById(id);
List<Report> reports = new ArrayList<>(); List<Report> reports = new ArrayList<>();
reports.add(report); reports.add(caseDb.getReportById(id));
return reports; return reports;
} }
} }

View File

@ -1,124 +0,0 @@
/*
* 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 java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.events.AutopsyEvent;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
/**
* An application event generic used as a superclass for events published when
* something changes in the Sleuth Kit Data Model for a case.
*
* @param <T> A Sleuth Kit Data Model object type.
*/
public abstract class TskDataModelChangeEvent<T> extends AutopsyEvent {
private static final long serialVersionUID = 1L;
private static final Logger logger = Logger.getLogger(TskDataModelChangeEvent.class.getName());
private final List<Long> dataModelObjectIds;
private transient List<T> dataModelObjects;
/**
* Constructs an application event generic used as a superclass for events
* published when something changes in the Sleuth Kit Data Model for a case.
*
* @param eventName The event name.
* @param dataModelObjectIds The unique numeric IDs (TSK object IDs, case
* database row IDs, etc.) of the Sleuth Kit Data
* Model objects associated with this application
* event.
* @param dataModelObjects The Sleuth Kit Data Model objects associated
* with this application event
*/
protected TskDataModelChangeEvent(String eventName, List<Long> dataModelObjectIds, List<T> dataModelObjects) {
super(eventName, null, null);
this.dataModelObjectIds = dataModelObjectIds;
this.dataModelObjects = dataModelObjects;
if (eventName == null) {
throw new IllegalArgumentException("eventName is null");
}
if (dataModelObjectIds == null) {
throw new IllegalArgumentException("dataModelObjectIds is null");
}
if (dataModelObjects == null) {
throw new IllegalArgumentException("dataModelObjects is null");
}
}
/**
* Gets the unique numeric IDs (TSK object IDs, case database row IDs, etc.)
* of the Sleuth Kit Data Model objects associated with this application
* event.
*
* @return The unique IDs.
*/
public final List<Long> getDataModelObjectIds() {
return Collections.unmodifiableList(dataModelObjectIds);
}
/**
* Gets the Sleuth Kit Data Model objects associated with this application
* event.
*
* @return The objects.
*/
@Override
public List<T> getNewValue() {
/*
* If this event came from another host collaborating on a multi-user
* case, the transient list of Sleuth Kit Data Model objects will be
* null and will need to be reconstructed on the current host.
*/
if (dataModelObjects == null) {
try {
Case currentCase = Case.getCurrentCaseThrows();
SleuthkitCase caseDb = currentCase.getSleuthkitCase();
dataModelObjects = getDataModelObjects(caseDb, dataModelObjectIds);
} catch (NoCurrentCaseException | TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Error geting TSK Data Model objects for %s event (%s)", getPropertyName(), getSourceType()), ex);
return Collections.emptyList();
}
}
return Collections.unmodifiableList(dataModelObjects);
}
/**
* Gets the Sleuth Kit Data Model objects associated with this application
* event.
*
* @param caseDb The case database.
* @param ids The unique, numeric IDs (TSK object IDs, case database row
* IDs, etc.) of the Sleuth Kit Data Model objects.
*
* @return The objects.
*
* @throws org.sleuthkit.datamodel.TskCoreException If there is an error
* getting the Sleuth Kit
* Data Model objects.
*/
abstract protected List<T> getDataModelObjects(SleuthkitCase caseDb, List<Long> ids) throws TskCoreException;
}

View File

@ -0,0 +1,205 @@
/*
* 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 java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import java.util.logging.Level;
import java.util.stream.Collectors;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.events.AutopsyEvent;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
/**
* An abstract base class for application events published when one or more
* Sleuth Kit Data Model objects for a case change in some way.
*
* This class extends AutopsyEvent. The AutopsyEvent class extends
* PropertyChangeEvent to integrate with legacy use of JavaBeans
* PropertyChangeEvents and PropertyChangeListeners as an application event
* publisher-subcriber mechanism. Subclasses need to decide what constitutes
* "old" and "new" objects for them and are encouraged to provide getters for
* these values that do not require clients to cast the return values.
*
* The AutopsyEvent class implements Serializable to allow local event instances
* to be published to other Autopsy nodes over a network in serialized form. TSK
* Data Model objects are generally not serializable because they encapsulate a
* reference to a SleuthkitCase object that represents the case database and
* which has local JDBC Connection objects. For this reason, this class supports
* serialization of the unique numeric IDs (TSK object IDs, case database row
* IDs, etc.) of the subject TSK Data Model objects and the "reconstruction" of
* those objects on other Autopsy nodes by querying the case database by unique
* ID.
*
* @param <T> The Sleuth Kit Data Model object type of the "old" data model
* objects.
* @param <U> The Sleuth Kit Data Model object type of the "new" data model
* objects.
*/
public abstract class TskDataModelChangedEvent<T, U> extends AutopsyEvent {
private static final long serialVersionUID = 1L;
private static final Logger logger = Logger.getLogger(TskDataModelChangedEvent.class.getName());
private final boolean hasOldValue;
private final List<Long> oldValueIds;
private transient List<T> oldValueObjects;
private final boolean hasNewValue;
private final List<Long> newValueIds;
private transient List<U> newValueObjects;
/**
* Constructs the base class part for application events published when one
* or more Sleuth Kit Data Model objects for a case change in some way.
*
* @param eventName The event name.
* @param oldValueObjects A list of he Data Model objects that have been
* designated as the "old" objects in the event.
* May be null.
* @param oldValueGetIdMethod A method that can be applied to the "old" data
* model objects to get their unique numeric IDs
* (TSK object IDs, case database row IDs, etc.).
* May be null if there are no "old" objects.
* @param newValueObjects A list of he Data Model objects that have been
* designated as the "new" objects in the event.
* May be null.
* @param newValueGetIdMethod A method that can be applied to the "new" data
* model objects to get their unique numeric IDs
* (TSK object IDs, case database row IDs, etc.).
* May be null if there are no "new" objects.
*/
protected TskDataModelChangedEvent(String eventName, List<T> oldValueObjects, Function<T, Long> oldValueGetIdMethod, List<U> newValueObjects, Function<U, Long> newValueGetIdMethod) {
super(eventName, null, null);
oldValueIds = new ArrayList<>();
this.oldValueObjects = new ArrayList<>();
if (oldValueObjects != null) {
hasOldValue = true;
oldValueIds.addAll(oldValueObjects.stream()
.map(o -> oldValueGetIdMethod.apply(o))
.collect(Collectors.toList()));
this.oldValueObjects.addAll(oldValueObjects);
} else {
hasOldValue = false;
}
newValueIds = new ArrayList<>();
this.newValueObjects = new ArrayList<>();
if (newValueObjects != null) {
hasNewValue = true;
newValueIds.addAll(newValueObjects.stream()
.map(o -> newValueGetIdMethod.apply(o))
.collect(Collectors.toList()));
this.newValueObjects.addAll(newValueObjects);
} else {
hasNewValue = false;
}
}
/**
* Gets a list of the Data Model objects that have been designated as the
* "old" objects in the event.
*
* @return The list of the "old" data model objects. May be empty.
*/
@Override
public List<T> getOldValue() {
if (hasOldValue) {
if (oldValueObjects == null) {
try {
Case currentCase = Case.getCurrentCaseThrows();
SleuthkitCase caseDb = currentCase.getSleuthkitCase();
oldValueObjects = getOldValueObjects(caseDb, oldValueIds);
} catch (NoCurrentCaseException | TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Error getting oldValue() TSK Data Model objects for %s event (%s)", getPropertyName(), getSourceType()), ex);
return Collections.emptyList();
}
}
return Collections.unmodifiableList(oldValueObjects);
} else {
return Collections.emptyList();
}
}
/**
* Gets a list of the Data Model objects that have been designated as the
* "new" objects in the event.
*
* @return The list of the "new" data model objects. May be empty.
*/
@Override
public List<U> getNewValue() {
if (hasNewValue) {
if (newValueObjects == null) {
try {
Case currentCase = Case.getCurrentCaseThrows();
SleuthkitCase caseDb = currentCase.getSleuthkitCase();
newValueObjects = getNewValueObjects(caseDb, newValueIds);
} catch (NoCurrentCaseException | TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Error getting newValue() TSK Data Model objects for %s event (%s)", getPropertyName(), getSourceType()), ex);
return Collections.emptyList();
}
}
return Collections.unmodifiableList(newValueObjects);
} else {
return Collections.emptyList();
}
}
/**
* Reconstructs the "old" Sleuth Kit Data Model objects associated with this
* application event, if any, using the given unique numeric IDs (TSK object
* IDs, case database row IDs, etc.) to query the given case database.
*
* @param caseDb The case database.
* @param ids The unique, numeric IDs (TSK object IDs, case database row
* IDs, etc.) of the Sleuth Kit Data Model objects.
*
* @return The objects.
*
* @throws org.sleuthkit.datamodel.TskCoreException If there is an error
* getting the Sleuth Kit
* Data Model objects.
*/
protected List<T> getOldValueObjects(SleuthkitCase caseDb, List<Long> ids) throws TskCoreException {
return Collections.emptyList();
}
/**
* Reconstructs the "new" Sleuth Kit Data Model objects associated with this
* application event, if any, using the given unique numeric IDs (TSK object
* IDs, case database row IDs, etc.) to query the given case database.
*
* @param caseDb The case database.
* @param ids The unique, numeric IDs (TSK object IDs, case database row
* IDs, etc.) of the Sleuth Kit Data Model objects.
*
* @return The objects.
*
* @throws org.sleuthkit.datamodel.TskCoreException If there is an error
* getting the Sleuth Kit
* Data Model objects.
*/
protected List<U> getNewValueObjects(SleuthkitCase caseDb, List<Long> ids) throws TskCoreException {
return Collections.emptyList();
}
}

View File

@ -0,0 +1,60 @@
/*
* 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 java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.sleuthkit.autopsy.events.AutopsyEvent;
/**
* An abstract base class for application events published when one or more
* Sleuth Kit Data Model objects for a case have been deleted.
*
* This class extends AutopsyEvent. The AutopsyEvent class extends
* PropertyChangeEvent to integrate with legacy use of JavaBeans
* PropertyChangeEvents and PropertyChangeListeners as an application event
* publisher-subcriber mechanism. Subclasses need to decide what constitutes
* "old" and "new" objects for them.
*
* For this class the "old" values are the unique numeric IDs (TSK object IDs,
* case database row IDs, etc.) of the deleted TSK Data Model objects. There are
* no "new" values. Subclasses are encouraged to provide less generic getters
* with descriptive names for the unique IDs than the override of the inherited
* getOldValue() method below. These getters can be implemented by delegating to
* getOldValue().
*/
public class TskDataModelObjectsDeletedEvent extends AutopsyEvent {
private static final long serialVersionUID = 1L;
private final List<Long> deletedObjectIds;
protected TskDataModelObjectsDeletedEvent(String eventName, List<Long> deletedObjectIds) {
super(eventName, null, null);
this.deletedObjectIds = new ArrayList<>();
this.deletedObjectIds.addAll(deletedObjectIds);
}
@Override
public List<Long> getOldValue() {
return Collections.unmodifiableList(deletedObjectIds);
}
}

View File

@ -5,10 +5,7 @@ CentralRepoCommentDialog.title.addEditCentralRepoComment=Add/Edit Central Reposi
OpenIDE-Module-Name=Central Repository OpenIDE-Module-Name=Central Repository
OpenIDE-Module-Display-Category=Ingest Module OpenIDE-Module-Display-Category=Ingest Module
OpenIDE-Module-Short-Description=Central Repository Ingest Module OpenIDE-Module-Short-Description=Central Repository Ingest Module
OpenIDE-Module-Long-Description=\ OpenIDE-Module-Long-Description=Central Repository ingest module and central database. \n\nThe Central Repository ingest module stores attributes of artifacts matching selected correlation types into a central database.\nStored attributes are used in future cases to correlate and analyzes files and artifacts during ingest.
Central Repository ingest module and central database. \n\n\
The Central Repository ingest module stores attributes of artifacts matching selected correlation types into a central database.\n\
Stored attributes are used in future cases to correlate and analyzes files and artifacts during ingest.
CentralRepoCommentDialog.commentLabel.text=Comment: CentralRepoCommentDialog.commentLabel.text=Comment:
CentralRepoCommentDialog.okButton.text=&OK CentralRepoCommentDialog.okButton.text=&OK
CentralRepoCommentDialog.cancelButton.text=C&ancel CentralRepoCommentDialog.cancelButton.text=C&ancel

View File

@ -0,0 +1,8 @@
OtherOccurrences.csvHeader.attribute=Matched Attribute
OtherOccurrences.csvHeader.case=Case
OtherOccurrences.csvHeader.comment=Comment
OtherOccurrences.csvHeader.dataSource=Data Source
OtherOccurrences.csvHeader.device=Device
OtherOccurrences.csvHeader.known=Known
OtherOccurrences.csvHeader.path=Path
OtherOccurrences.csvHeader.value=Attribute Value

View File

@ -16,7 +16,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.sleuthkit.autopsy.centralrepository.contentviewer; package org.sleuthkit.autopsy.centralrepository.application;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
@ -30,7 +30,7 @@ import org.sleuthkit.datamodel.TskDataException;
/** /**
* Class for populating the Other Occurrences tab * Class for populating the Other Occurrences tab
*/ */
class OtherOccurrenceNodeInstanceData implements OtherOccurrenceNodeData { public class NodeData {
// For now hard code the string for the central repo files type, since // For now hard code the string for the central repo files type, since
// getting it dynamically can fail. // getting it dynamically can fail.
@ -56,7 +56,7 @@ class OtherOccurrenceNodeInstanceData implements OtherOccurrenceNodeData {
* @param type The type of the instance * @param type The type of the instance
* @param value The value of the instance * @param value The value of the instance
*/ */
OtherOccurrenceNodeInstanceData(CorrelationAttributeInstance instance, CorrelationAttributeInstance.Type type, String value) { public NodeData(CorrelationAttributeInstance instance, CorrelationAttributeInstance.Type type, String value) {
caseName = instance.getCorrelationCase().getDisplayName(); caseName = instance.getCorrelationCase().getDisplayName();
deviceID = instance.getCorrelationDataSource().getDeviceID(); deviceID = instance.getCorrelationDataSource().getDeviceID();
dataSourceName = instance.getCorrelationDataSource().getName(); dataSourceName = instance.getCorrelationDataSource().getName();
@ -77,7 +77,7 @@ class OtherOccurrenceNodeInstanceData implements OtherOccurrenceNodeData {
* *
* @throws CentralRepoException * @throws CentralRepoException
*/ */
OtherOccurrenceNodeInstanceData(AbstractFile newFile, Case autopsyCase) throws CentralRepoException { NodeData(AbstractFile newFile, Case autopsyCase) throws CentralRepoException {
caseName = autopsyCase.getDisplayName(); caseName = autopsyCase.getDisplayName();
try { try {
DataSource dataSource = autopsyCase.getSleuthkitCase().getDataSource(newFile.getDataSource().getId()); DataSource dataSource = autopsyCase.getSleuthkitCase().getDataSource(newFile.getDataSource().getId());
@ -119,7 +119,7 @@ class OtherOccurrenceNodeInstanceData implements OtherOccurrenceNodeData {
* *
* @param newComment The new comment * @param newComment The new comment
*/ */
void updateComment(String newComment) { public void updateComment(String newComment) {
comment = newComment; comment = newComment;
} }
@ -129,7 +129,7 @@ class OtherOccurrenceNodeInstanceData implements OtherOccurrenceNodeData {
* @return true if this node was created from a central repo instance, false * @return true if this node was created from a central repo instance, false
* otherwise * otherwise
*/ */
boolean isCentralRepoNode() { public boolean isCentralRepoNode() {
return (originalCorrelationInstance != null); return (originalCorrelationInstance != null);
} }
@ -138,7 +138,7 @@ class OtherOccurrenceNodeInstanceData implements OtherOccurrenceNodeData {
* *
* @return the case name * @return the case name
*/ */
String getCaseName() { public String getCaseName() {
return caseName; return caseName;
} }
@ -147,7 +147,7 @@ class OtherOccurrenceNodeInstanceData implements OtherOccurrenceNodeData {
* *
* @return the device ID * @return the device ID
*/ */
String getDeviceID() { public String getDeviceID() {
return deviceID; return deviceID;
} }
@ -156,7 +156,7 @@ class OtherOccurrenceNodeInstanceData implements OtherOccurrenceNodeData {
* *
* @return the data source name * @return the data source name
*/ */
String getDataSourceName() { public String getDataSourceName() {
return dataSourceName; return dataSourceName;
} }
@ -165,7 +165,7 @@ class OtherOccurrenceNodeInstanceData implements OtherOccurrenceNodeData {
* *
* @return the file path * @return the file path
*/ */
String getFilePath() { public String getFilePath() {
return filePath; return filePath;
} }
@ -174,7 +174,7 @@ class OtherOccurrenceNodeInstanceData implements OtherOccurrenceNodeData {
* *
* @return the type * @return the type
*/ */
String getType() { public String getType() {
return typeStr; return typeStr;
} }
@ -183,7 +183,7 @@ class OtherOccurrenceNodeInstanceData implements OtherOccurrenceNodeData {
* *
* @return the value * @return the value
*/ */
String getValue() { public String getValue() {
return value; return value;
} }
@ -192,7 +192,7 @@ class OtherOccurrenceNodeInstanceData implements OtherOccurrenceNodeData {
* *
* @return the known status * @return the known status
*/ */
TskData.FileKnown getKnown() { public TskData.FileKnown getKnown() {
return known; return known;
} }
@ -201,7 +201,7 @@ class OtherOccurrenceNodeInstanceData implements OtherOccurrenceNodeData {
* *
* @return the comment * @return the comment
*/ */
String getComment() { public String getComment() {
return comment; return comment;
} }
@ -211,7 +211,7 @@ class OtherOccurrenceNodeInstanceData implements OtherOccurrenceNodeData {
* *
* @return the original abstract file * @return the original abstract file
*/ */
AbstractFile getAbstractFile() throws CentralRepoException { public AbstractFile getAbstractFile() throws CentralRepoException {
if (originalAbstractFile == null) { if (originalAbstractFile == null) {
throw new CentralRepoException("AbstractFile is null"); throw new CentralRepoException("AbstractFile is null");
} }
@ -226,7 +226,7 @@ class OtherOccurrenceNodeInstanceData implements OtherOccurrenceNodeData {
* *
* @throws CentralRepoException * @throws CentralRepoException
*/ */
CorrelationAttributeInstance getCorrelationAttributeInstance() throws CentralRepoException { public CorrelationAttributeInstance getCorrelationAttributeInstance() throws CentralRepoException {
if (originalCorrelationInstance == null) { if (originalCorrelationInstance == null) {
throw new CentralRepoException("CorrelationAttributeInstance is null"); throw new CentralRepoException("CorrelationAttributeInstance is null");
} }
@ -239,7 +239,7 @@ class OtherOccurrenceNodeInstanceData implements OtherOccurrenceNodeData {
* *
* @return the CSV_ITEM_SEPARATOR string * @return the CSV_ITEM_SEPARATOR string
*/ */
static String getCsvItemSeparator() { public static String getCsvItemSeparator() {
return CSV_ITEM_SEPARATOR; return CSV_ITEM_SEPARATOR;
} }

View File

@ -0,0 +1,438 @@
/*
* Central Repository
*
* 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.centralrepository.application;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.logging.Level;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTimeZone;
import org.joda.time.LocalDateTime;
import org.openide.nodes.Node;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeNormalizationException;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeUtil;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationDataSource;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardArtifactTag;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.ContentTag;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
/**
* Contains most of the methods for gathering data from the DB and CR for the
* OtherOccurrencesPanel.
*/
public final class OtherOccurrences {
private static final Logger logger = Logger.getLogger(OtherOccurrences.class.getName());
private static final String UUID_PLACEHOLDER_STRING = "NoCorrelationAttributeInstance";
private OtherOccurrences() {
}
/**
* Determine what attributes can be used for correlation based on the node.
* If EamDB is not enabled, get the default Files correlation.
*
* @param node The node to correlate
*
* @return A list of attributes that can be used for correlation
*/
public static Collection<CorrelationAttributeInstance> getCorrelationAttributesFromNode(Node node, AbstractFile file) {
Collection<CorrelationAttributeInstance> ret = new ArrayList<>();
// correlate on blackboard artifact attributes if they exist and supported
BlackboardArtifact bbArtifact = getBlackboardArtifactFromNode(node);
if (bbArtifact != null && CentralRepository.isEnabled()) {
ret.addAll(CorrelationAttributeUtil.makeCorrAttrsForCorrelation(bbArtifact));
}
// we can correlate based on the MD5 if it is enabled
if (file != null && CentralRepository.isEnabled() && file.getSize() > 0) {
try {
List<CorrelationAttributeInstance.Type> artifactTypes = CentralRepository.getInstance().getDefinedCorrelationTypes();
String md5 = file.getMd5Hash();
if (md5 != null && !md5.isEmpty() && null != artifactTypes && !artifactTypes.isEmpty()) {
for (CorrelationAttributeInstance.Type aType : artifactTypes) {
if (aType.getId() == CorrelationAttributeInstance.FILES_TYPE_ID) {
CorrelationCase corCase = CentralRepository.getInstance().getCase(Case.getCurrentCase());
try {
ret.add(new CorrelationAttributeInstance(
aType,
md5,
corCase,
CorrelationDataSource.fromTSKDataSource(corCase, file.getDataSource()),
file.getParentPath() + file.getName(),
"",
file.getKnown(),
file.getId()));
} catch (CorrelationAttributeNormalizationException ex) {
logger.log(Level.INFO, String.format("Unable to check create CorrelationAttribtueInstance for value %s and type %s.", md5, aType.toString()), ex);
}
break;
}
}
}
} catch (CentralRepoException | TskCoreException ex) {
logger.log(Level.SEVERE, "Error connecting to DB", ex); // NON-NLS
}
// If EamDb not enabled, get the Files default correlation type to allow Other Occurances to be enabled.
} else if (file != null && file.getSize() > 0) {
String md5 = file.getMd5Hash();
if (md5 != null && !md5.isEmpty()) {
try {
final CorrelationAttributeInstance.Type fileAttributeType
= CorrelationAttributeInstance.getDefaultCorrelationTypes()
.stream()
.filter(attrType -> attrType.getId() == CorrelationAttributeInstance.FILES_TYPE_ID)
.findAny()
.get();
//The Central Repository is not enabled
ret.add(new CorrelationAttributeInstance(fileAttributeType, md5, null, null, "", "", TskData.FileKnown.UNKNOWN, file.getId()));
} catch (CentralRepoException ex) {
logger.log(Level.SEVERE, "Error connecting to DB", ex); // NON-NLS
} catch (CorrelationAttributeNormalizationException ex) {
logger.log(Level.INFO, String.format("Unable to create CorrelationAttributeInstance for value %s", md5), ex); // NON-NLS
}
}
}
return ret;
}
/**
* Get the associated BlackboardArtifact from a node, if it exists.
*
* @param node The node
*
* @return The associated BlackboardArtifact, or null
*/
public static BlackboardArtifact getBlackboardArtifactFromNode(Node node) {
BlackboardArtifactTag nodeBbArtifactTag = node.getLookup().lookup(BlackboardArtifactTag.class);
BlackboardArtifact nodeBbArtifact = node.getLookup().lookup(BlackboardArtifact.class);
if (nodeBbArtifactTag != null) {
return nodeBbArtifactTag.getArtifact();
} else if (nodeBbArtifact != null) {
return nodeBbArtifact;
}
return null;
}
/**
* Get the associated AbstractFile from a node, if it exists.
*
* @param node The node
*
* @return The associated AbstractFile, or null
*/
public static AbstractFile getAbstractFileFromNode(Node node) {
BlackboardArtifactTag nodeBbArtifactTag = node.getLookup().lookup(BlackboardArtifactTag.class);
ContentTag nodeContentTag = node.getLookup().lookup(ContentTag.class);
BlackboardArtifact nodeBbArtifact = node.getLookup().lookup(BlackboardArtifact.class);
AbstractFile nodeAbstractFile = node.getLookup().lookup(AbstractFile.class);
if (nodeBbArtifactTag != null) {
Content content = nodeBbArtifactTag.getContent();
if (content instanceof AbstractFile) {
return (AbstractFile) content;
}
} else if (nodeContentTag != null) {
Content content = nodeContentTag.getContent();
if (content instanceof AbstractFile) {
return (AbstractFile) content;
}
} else if (nodeBbArtifact != null) {
Content content;
try {
content = nodeBbArtifact.getSleuthkitCase().getContentById(nodeBbArtifact.getObjectID());
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error retrieving blackboard artifact", ex); // NON-NLS
return null;
}
if (content instanceof AbstractFile) {
return (AbstractFile) content;
}
} else if (nodeAbstractFile != null) {
return nodeAbstractFile;
}
return null;
}
/**
* Query the central repo database (if enabled) and the case database to
* find all artifact instances correlated to the given central repository
* 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
*
* @return A collection of correlated artifact instances
*/
public static Map<UniquePathKey, NodeData> getCorrelatedInstances(AbstractFile file, String deviceId, String dataSourceName, CorrelationAttributeInstance corAttr) {
// @@@ Check exception
try {
final Case openCase = Case.getCurrentCaseThrows();
String caseUUID = openCase.getName();
HashMap<UniquePathKey, NodeData> nodeDataMap = new HashMap<>();
if (CentralRepository.isEnabled()) {
List<CorrelationAttributeInstance> instances = CentralRepository.getInstance().getArtifactInstancesByTypeValue(corAttr.getCorrelationType(), corAttr.getCorrelationValue());
for (CorrelationAttributeInstance artifactInstance : instances) {
// Only add the attribute if it isn't the object the user selected.
// We consider it to be a different object if at least one of the following is true:
// - the case UUID is different
// - the data source name is different
// - 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()))) {
continue;
}
NodeData newNode = new NodeData(artifactInstance, corAttr.getCorrelationType(), corAttr.getCorrelationValue());
UniquePathKey uniquePathKey = new UniquePathKey(newNode);
nodeDataMap.put(uniquePathKey, newNode);
}
if (file != null && corAttr.getCorrelationType().getDisplayName().equals("Files")) {
List<AbstractFile> caseDbFiles = getCaseDbMatches(corAttr, openCase, file);
for (AbstractFile caseDbFile : caseDbFiles) {
addOrUpdateNodeData(openCase, nodeDataMap, caseDbFile);
}
}
}
return nodeDataMap;
} catch (CentralRepoException ex) {
logger.log(Level.SEVERE, "Error getting artifact instances from database.", ex); // NON-NLS
} catch (CorrelationAttributeNormalizationException ex) {
logger.log(Level.INFO, "Error getting artifact instances from database.", ex); // NON-NLS
} catch (NoCurrentCaseException ex) {
logger.log(Level.SEVERE, "Exception while getting open case.", ex); // NON-NLS
} catch (TskCoreException ex) {
// do nothing.
// @@@ Review this behavior
logger.log(Level.SEVERE, "Exception while querying open case.", ex); // NON-NLS
}
return new HashMap<>(
0);
}
/**
* Get all other abstract files in the current case with the same MD5 as the
* selected node.
*
* @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
*
* @throws NoCurrentCaseException
* @throws TskCoreException
* @throws CentralRepoException
*/
public static List<AbstractFile> getCaseDbMatches(CorrelationAttributeInstance corAttr, Case openCase, AbstractFile file) throws NoCurrentCaseException, TskCoreException, CentralRepoException {
List<AbstractFile> caseDbArtifactInstances = new ArrayList<>();
if (file != null) {
String md5 = corAttr.getCorrelationValue();
SleuthkitCase tsk = openCase.getSleuthkitCase();
List<AbstractFile> matches = tsk.findAllFilesWhere(String.format("md5 = '%s'", new Object[]{md5}));
for (AbstractFile fileMatch : matches) {
if (file.equals(fileMatch)) {
continue; // If this is the file the user clicked on
}
caseDbArtifactInstances.add(fileMatch);
}
}
return caseDbArtifactInstances;
}
/**
* Adds the file to the nodeDataMap map if it does not already exist
*
* @param autopsyCase
* @param nodeDataMap
* @param newFile
*
* @throws TskCoreException
* @throws CentralRepoException
*/
public static void addOrUpdateNodeData(final Case autopsyCase, Map<UniquePathKey, NodeData> nodeDataMap, AbstractFile newFile) throws TskCoreException, CentralRepoException {
NodeData newNode = new NodeData(newFile, autopsyCase);
// If the caseDB object has a notable tag associated with it, update
// the known status to BAD
if (newNode.getKnown() != TskData.FileKnown.BAD) {
List<ContentTag> fileMatchTags = autopsyCase.getServices().getTagsManager().getContentTagsByContent(newFile);
for (ContentTag tag : fileMatchTags) {
TskData.FileKnown tagKnownStatus = tag.getName().getKnownStatus();
if (tagKnownStatus.equals(TskData.FileKnown.BAD)) {
newNode.updateKnown(TskData.FileKnown.BAD);
break;
}
}
}
// Make a key to see if the file is already in the map
UniquePathKey uniquePathKey = new UniquePathKey(newNode);
// If this node is already in the list, the only thing we need to do is
// update the known status to BAD if the caseDB version had known status BAD.
// Otherwise this is a new node so add the new node to the map.
if (nodeDataMap.containsKey(uniquePathKey)) {
if (newNode.getKnown() == TskData.FileKnown.BAD) {
NodeData prevInstance = nodeDataMap.get(uniquePathKey);
prevInstance.updateKnown(newNode.getKnown());
}
} else {
nodeDataMap.put(uniquePathKey, newNode);
}
}
/**
* Create a unique string to be used as a key for deduping data sources as
* best as possible
*/
public static String makeDataSourceString(String caseUUID, String deviceId, String dataSourceName) {
return caseUUID + deviceId + dataSourceName;
}
/**
* Gets the list of Eam Cases and determines the earliest case creation
* date. Sets the label to display the earliest date string to the user.
*/
public static String getEarliestCaseDate() throws CentralRepoException {
String dateStringDisplay = "";
if (CentralRepository.isEnabled()) {
LocalDateTime earliestDate = LocalDateTime.now(DateTimeZone.UTC);
DateFormat datetimeFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.US);
CentralRepository dbManager = CentralRepository.getInstance();
List<CorrelationCase> cases = dbManager.getCases();
for (CorrelationCase aCase : cases) {
LocalDateTime caseDate;
try {
caseDate = LocalDateTime.fromDateFields(datetimeFormat.parse(aCase.getCreationDate()));
if (caseDate.isBefore(earliestDate)) {
earliestDate = caseDate;
dateStringDisplay = aCase.getCreationDate();
}
} catch (ParseException ex) {
throw new CentralRepoException("Failed to format case creation date " + aCase.getCreationDate(), ex);
}
}
}
return dateStringDisplay;
}
@NbBundle.Messages({
"OtherOccurrences.csvHeader.case=Case",
"OtherOccurrences.csvHeader.device=Device",
"OtherOccurrences.csvHeader.dataSource=Data Source",
"OtherOccurrences.csvHeader.attribute=Matched Attribute",
"OtherOccurrences.csvHeader.value=Attribute Value",
"OtherOccurrences.csvHeader.known=Known",
"OtherOccurrences.csvHeader.path=Path",
"OtherOccurrences.csvHeader.comment=Comment"
})
/**
* Create a cvs file of occurrences for the given parameters.
*
* @param destFile Output file for the csv data.
* @param abstractFile Source file.
* @param correlationAttList List of correclationAttributeInstances, should
* not be null.
* @param dataSourceName Name of the data source.
* @param deviceId Device id.
*
* @throws IOException
*/
public static void writeOtherOccurrencesToFileAsCSV(File destFile, AbstractFile abstractFile, Collection<CorrelationAttributeInstance> correlationAttList, String dataSourceName, String deviceId) throws IOException {
try (BufferedWriter writer = Files.newBufferedWriter(destFile.toPath())) {
//write headers
StringBuilder headers = new StringBuilder("\"");
headers.append(Bundle.OtherOccurrences_csvHeader_case())
.append(NodeData.getCsvItemSeparator()).append(Bundle.OtherOccurrences_csvHeader_dataSource())
.append(NodeData.getCsvItemSeparator()).append(Bundle.OtherOccurrences_csvHeader_attribute())
.append(NodeData.getCsvItemSeparator()).append(Bundle.OtherOccurrences_csvHeader_value())
.append(NodeData.getCsvItemSeparator()).append(Bundle.OtherOccurrences_csvHeader_known())
.append(NodeData.getCsvItemSeparator()).append(Bundle.OtherOccurrences_csvHeader_path())
.append(NodeData.getCsvItemSeparator()).append(Bundle.OtherOccurrences_csvHeader_comment())
.append('"').append(System.getProperty("line.separator"));
writer.write(headers.toString());
//write content
for (CorrelationAttributeInstance corAttr : correlationAttList) {
Map<UniquePathKey, NodeData> correlatedNodeDataMap = new HashMap<>(0);
// get correlation and reference set instances from DB
correlatedNodeDataMap.putAll(getCorrelatedInstances(abstractFile, deviceId, dataSourceName, corAttr));
for (NodeData nodeData : correlatedNodeDataMap.values()) {
writer.write(nodeData.toCsvString());
}
}
}
}
/**
* Get a placeholder string to use in place of case uuid when it isn't
* available
*
* @return UUID_PLACEHOLDER_STRING
*/
public static String getPlaceholderUUID() {
return UUID_PLACEHOLDER_STRING;
}
}

View File

@ -16,12 +16,13 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.sleuthkit.autopsy.centralrepository.contentviewer; package org.sleuthkit.autopsy.centralrepository.application;
import java.util.Objects; import java.util.Objects;
import java.util.logging.Level; import java.util.logging.Level;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.centralrepository.contentviewer.OtherOccurrencesPanel;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
@ -29,7 +30,7 @@ import org.sleuthkit.autopsy.coreutils.Logger;
* Used as a key to ensure we eliminate duplicates from the result set by not * Used as a key to ensure we eliminate duplicates from the result set by not
* overwriting CR correlation instances. * overwriting CR correlation instances.
*/ */
final class UniquePathKey { public final class UniquePathKey {
private static final Logger logger = Logger.getLogger(UniquePathKey.class.getName()); private static final Logger logger = Logger.getLogger(UniquePathKey.class.getName());
private final String dataSourceID; private final String dataSourceID;
@ -37,7 +38,7 @@ final class UniquePathKey {
private final String type; private final String type;
private final String caseUUID; private final String caseUUID;
UniquePathKey(OtherOccurrenceNodeInstanceData nodeData) { public UniquePathKey(NodeData nodeData) {
super(); super();
dataSourceID = nodeData.getDeviceID(); dataSourceID = nodeData.getDeviceID();
if (nodeData.getFilePath() != null) { if (nodeData.getFilePath() != null) {
@ -56,7 +57,7 @@ final class UniquePathKey {
//place holder value will be used since correlation attribute was unavailble //place holder value will be used since correlation attribute was unavailble
} catch (NoCurrentCaseException ex) { } catch (NoCurrentCaseException ex) {
logger.log(Level.WARNING, "Unable to get current case", ex); logger.log(Level.WARNING, "Unable to get current case", ex);
tempCaseUUID = OtherOccurrencesPanel.getPlaceholderUUID(); tempCaseUUID = OtherOccurrences.getPlaceholderUUID();
} }
} }
caseUUID = tempCaseUUID; caseUUID = tempCaseUUID;

View File

@ -31,18 +31,9 @@ OtherOccurrencesPanel.correlatedArtifacts.byType={0}% of data sources have {2} (
OtherOccurrencesPanel.correlatedArtifacts.failed=Failed to get frequency details. OtherOccurrencesPanel.correlatedArtifacts.failed=Failed to get frequency details.
OtherOccurrencesPanel.correlatedArtifacts.isEmpty=There are no files or artifacts to correlate. OtherOccurrencesPanel.correlatedArtifacts.isEmpty=There are no files or artifacts to correlate.
OtherOccurrencesPanel.correlatedArtifacts.title=Attribute Frequency OtherOccurrencesPanel.correlatedArtifacts.title=Attribute Frequency
OtherOccurrencesPanel.csvHeader.attribute=Matched Attribute
OtherOccurrencesPanel.csvHeader.case=Case
OtherOccurrencesPanel.csvHeader.comment=Comment
OtherOccurrencesPanel.csvHeader.dataSource=Data Source
OtherOccurrencesPanel.csvHeader.device=Device
OtherOccurrencesPanel.csvHeader.known=Known
OtherOccurrencesPanel.csvHeader.path=Path
OtherOccurrencesPanel.csvHeader.value=Attribute Value
OtherOccurrencesPanel.earliestCaseLabel.toolTipText= OtherOccurrencesPanel.earliestCaseLabel.toolTipText=
OtherOccurrencesPanel.earliestCaseLabel.text=Central Repository Starting Date: OtherOccurrencesPanel.earliestCaseLabel.text=Central Repository Starting Date:
OtherOccurrencesPanel.earliestCaseDate.text=Earliest Case Date OtherOccurrencesPanel.earliestCaseDate.text=Earliest Case Date
OtherOccurrencesPanel.earliestCaseNotAvailable=\ Not Enabled.
OtherOccurrencesPanel.foundIn.text=Found %d instances in %d cases and %d data sources. OtherOccurrencesPanel.foundIn.text=Found %d instances in %d cases and %d data sources.
OtherOccurrencesPanel.foundInLabel.text= OtherOccurrencesPanel.foundInLabel.text=
OtherOccurrencesPanel.filesTable.toolTipText=Click column name to sort. Right-click on the table for more options. OtherOccurrencesPanel.filesTable.toolTipText=Click column name to sort. Right-click on the table for more options.
@ -52,3 +43,5 @@ OtherOccurrencesPanel.showCommonalityMenuItem.text=Show Frequency
OtherOccurrencesPanel.showCaseDetailsMenuItem.text=Show Case Details OtherOccurrencesPanel.showCaseDetailsMenuItem.text=Show Case Details
OtherOccurrencesPanel.table.noArtifacts=Item has no attributes with which to search. OtherOccurrencesPanel.table.noArtifacts=Item has no attributes with which to search.
OtherOccurrencesPanel.table.noResultsFound=No results found. OtherOccurrencesPanel.table.noResultsFound=No results found.
OtherOccurrencesPanel_earliestCaseNotAvailable=Not Availble.
OtherOccurrencesPanel_table_loadingResults=Loading results

View File

@ -20,6 +20,7 @@
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/> <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/> <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/> <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> </AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/> <Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>

View File

@ -19,33 +19,18 @@
package org.sleuthkit.autopsy.centralrepository.contentviewer; package org.sleuthkit.autopsy.centralrepository.contentviewer;
import java.awt.Component; import java.awt.Component;
import java.util.ArrayList; import java.awt.Cursor;
import java.util.Collection; import java.util.concurrent.ExecutionException;
import java.util.List;
import java.util.logging.Level; import java.util.logging.Level;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import javax.swing.JPanel; import javax.swing.JPanel;
import org.openide.nodes.Node; import org.openide.nodes.Node;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.openide.util.lookup.ServiceProvider; import org.openide.util.lookup.ServiceProvider;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.centralrepository.application.OtherOccurrences;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeNormalizationException;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeUtil;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationDataSource;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardArtifactTag;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.ContentTag;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.datamodel.TskException;
/** /**
* View correlation results from other cases * View correlation results from other cases
@ -57,13 +42,10 @@ import org.sleuthkit.datamodel.TskException;
public final class DataContentViewerOtherCases extends JPanel implements DataContentViewer { public final class DataContentViewerOtherCases extends JPanel implements DataContentViewer {
private static final long serialVersionUID = -1L; private static final long serialVersionUID = -1L;
private static final Logger LOGGER = Logger.getLogger(DataContentViewerOtherCases.class.getName()); private static final Logger logger = Logger.getLogger(DataContentViewerOtherCases.class.getName());
private final OtherOccurrencesPanel otherOccurrencesPanel = new OtherOccurrencesPanel(); private final OtherOccurrencesPanel otherOccurrencesPanel = new OtherOccurrencesPanel();
/** private OtherOccurrencesNodeWorker worker = null;
* Could be null.
*/
private AbstractFile file; //the file which the content viewer is being populated for
/** /**
* Creates new form DataContentViewerOtherCases * Creates new form DataContentViewerOtherCases
@ -104,146 +86,6 @@ public final class DataContentViewerOtherCases extends JPanel implements DataCon
} }
/**
* Get the associated BlackboardArtifact from a node, if it exists.
*
* @param node The node
*
* @return The associated BlackboardArtifact, or null
*/
private BlackboardArtifact
getBlackboardArtifactFromNode(Node node) {
BlackboardArtifactTag nodeBbArtifactTag = node.getLookup().lookup(BlackboardArtifactTag.class
);
BlackboardArtifact nodeBbArtifact = node.getLookup().lookup(BlackboardArtifact.class
);
if (nodeBbArtifactTag != null) {
return nodeBbArtifactTag.getArtifact();
} else if (nodeBbArtifact != null) {
return nodeBbArtifact;
}
return null;
}
/**
* Get the associated AbstractFile from a node, if it exists.
*
* @param node The node
*
* @return The associated AbstractFile, or null
*/
private AbstractFile getAbstractFileFromNode(Node node) {
BlackboardArtifactTag nodeBbArtifactTag = node.getLookup().lookup(BlackboardArtifactTag.class
);
ContentTag nodeContentTag = node.getLookup().lookup(ContentTag.class
);
BlackboardArtifact nodeBbArtifact = node.getLookup().lookup(BlackboardArtifact.class
);
AbstractFile nodeAbstractFile = node.getLookup().lookup(AbstractFile.class
);
if (nodeBbArtifactTag != null) {
Content content = nodeBbArtifactTag.getContent();
if (content instanceof AbstractFile) {
return (AbstractFile) content;
}
} else if (nodeContentTag != null) {
Content content = nodeContentTag.getContent();
if (content instanceof AbstractFile) {
return (AbstractFile) content;
}
} else if (nodeBbArtifact != null) {
Content content;
try {
content = nodeBbArtifact.getSleuthkitCase().getContentById(nodeBbArtifact.getObjectID());
} catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, "Error retrieving blackboard artifact", ex); // NON-NLS
return null;
}
if (content instanceof AbstractFile) {
return (AbstractFile) content;
}
} else if (nodeAbstractFile != null) {
return nodeAbstractFile;
}
return null;
}
/**
* Determine what attributes can be used for correlation based on the node.
* If EamDB is not enabled, get the default Files correlation.
*
* @param node The node to correlate
*
* @return A list of attributes that can be used for correlation
*/
private Collection<CorrelationAttributeInstance> getCorrelationAttributesFromNode(Node node) {
Collection<CorrelationAttributeInstance> ret = new ArrayList<>();
// correlate on blackboard artifact attributes if they exist and supported
BlackboardArtifact bbArtifact = getBlackboardArtifactFromNode(node);
if (bbArtifact != null && CentralRepository.isEnabled()) {
ret.addAll(CorrelationAttributeUtil.makeCorrAttrsForCorrelation(bbArtifact));
}
// we can correlate based on the MD5 if it is enabled
if (this.file != null && CentralRepository.isEnabled() && this.file.getSize() > 0) {
try {
List<CorrelationAttributeInstance.Type> artifactTypes = CentralRepository.getInstance().getDefinedCorrelationTypes();
String md5 = this.file.getMd5Hash();
if (md5 != null && !md5.isEmpty() && null != artifactTypes && !artifactTypes.isEmpty()) {
for (CorrelationAttributeInstance.Type aType : artifactTypes) {
if (aType.getId() == CorrelationAttributeInstance.FILES_TYPE_ID) {
CorrelationCase corCase = CentralRepository.getInstance().getCase(Case.getCurrentCase());
try {
ret.add(new CorrelationAttributeInstance(
aType,
md5,
corCase,
CorrelationDataSource.fromTSKDataSource(corCase, file.getDataSource()),
file.getParentPath() + file.getName(),
"",
file.getKnown(),
file.getId()));
} catch (CorrelationAttributeNormalizationException ex) {
LOGGER.log(Level.INFO, String.format("Unable to check create CorrelationAttribtueInstance for value %s and type %s.", md5, aType.toString()), ex);
}
break;
}
}
}
} catch (CentralRepoException | TskCoreException ex) {
LOGGER.log(Level.SEVERE, "Error connecting to DB", ex); // NON-NLS
}
// If EamDb not enabled, get the Files default correlation type to allow Other Occurances to be enabled.
} else if (this.file != null && this.file.getSize() > 0) {
String md5 = this.file.getMd5Hash();
if (md5 != null && !md5.isEmpty()) {
try {
final CorrelationAttributeInstance.Type fileAttributeType
= CorrelationAttributeInstance.getDefaultCorrelationTypes()
.stream()
.filter(attrType -> attrType.getId() == CorrelationAttributeInstance.FILES_TYPE_ID)
.findAny()
.get();
//The Central Repository is not enabled
ret.add(new CorrelationAttributeInstance(fileAttributeType, md5, null, null, "", "", TskData.FileKnown.UNKNOWN, this.file.getId()));
} catch (CentralRepoException ex) {
LOGGER.log(Level.SEVERE, "Error connecting to DB", ex); // NON-NLS
} catch (CorrelationAttributeNormalizationException ex) {
LOGGER.log(Level.INFO, String.format("Unable to create CorrelationAttributeInstance for value %s", md5), ex); // NON-NLS
}
}
}
return ret;
}
@Override @Override
public boolean isSupported(Node node) { public boolean isSupported(Node node) {
@ -251,39 +93,44 @@ public final class DataContentViewerOtherCases extends JPanel implements DataCon
// - The central repo is enabled and the node has correlatable content // - The central repo is enabled and the node has correlatable content
// (either through the MD5 hash of the associated file or through a BlackboardArtifact) // (either through the MD5 hash of the associated file or through a BlackboardArtifact)
// - The central repo is disabled and the backing file has a valid MD5 hash // - The central repo is disabled and the backing file has a valid MD5 hash
this.file = this.getAbstractFileFromNode(node); AbstractFile file = OtherOccurrences.getAbstractFileFromNode(node);
if (CentralRepository.isEnabled()) { if (CentralRepository.isEnabled()) {
return !getCorrelationAttributesFromNode(node).isEmpty(); return !OtherOccurrences.getCorrelationAttributesFromNode(node, file).isEmpty();
} else { } else {
return this.file != null return file != null
&& this.file.getSize() > 0 && file.getSize() > 0
&& ((this.file.getMd5Hash() != null) && (!this.file.getMd5Hash().isEmpty())); && ((file.getMd5Hash() != null) && (!file.getMd5Hash().isEmpty()));
} }
} }
@Override @Override
public void setNode(Node node) { public void setNode(Node node) {
otherOccurrencesPanel.reset(); // reset the table to empty. otherOccurrencesPanel.reset(); // reset the table to empty.
otherOccurrencesPanel.showPanelLoadingMessage();
if (node == null) { if (node == null) {
return; return;
} }
//could be null
this.file = this.getAbstractFileFromNode(node);
String dataSourceName = "";
String deviceId = "";
try {
if (this.file != null) {
Content dataSource = this.file.getDataSource();
dataSourceName = dataSource.getName();
deviceId = Case.getCurrentCaseThrows().getSleuthkitCase().getDataSource(dataSource.getId()).getDeviceId();
}
} catch (TskException | NoCurrentCaseException ex) {
// do nothing.
// @@@ Review this behavior
}
otherOccurrencesPanel.populateTable(getCorrelationAttributesFromNode(node), dataSourceName, deviceId, file);
if (worker != null) {
worker.cancel(true);
}
worker = new OtherOccurrencesNodeWorker(node) {
@Override
public void done() {
try {
if (!isCancelled()) {
OtherOccurrencesData data = get();
otherOccurrencesPanel.populateTable(data);
otherOccurrencesPanel.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
}
} catch (InterruptedException | ExecutionException ex) {
DataContentViewerOtherCases.logger.log(Level.SEVERE, "Failed to update OtherOccurrencesPanel", ex);
}
}
};
otherOccurrencesPanel.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
worker.execute();
} }
/** /**

View File

@ -18,6 +18,7 @@
*/ */
package org.sleuthkit.autopsy.centralrepository.contentviewer; package org.sleuthkit.autopsy.centralrepository.contentviewer;
import org.sleuthkit.autopsy.centralrepository.application.NodeData;
import java.awt.Color; import java.awt.Color;
import java.awt.Font; import java.awt.Font;
import java.util.ArrayList; import java.util.ArrayList;
@ -50,7 +51,7 @@ final class OccurrencePanel extends javax.swing.JPanel {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private int gridY = 0; private int gridY = 0;
private final List<OtherOccurrenceNodeData> nodeDataList; private final List<NodeData> nodeDataList;
private final Map<String, String> caseNamesAndDates = new HashMap<>(); private final Map<String, String> caseNamesAndDates = new HashMap<>();
private final Set<String> dataSourceNames = new HashSet<>(); private final Set<String> dataSourceNames = new HashSet<>();
private final Set<String> filePaths = new HashSet<>(); private final Set<String> filePaths = new HashSet<>();
@ -97,7 +98,7 @@ final class OccurrencePanel extends javax.swing.JPanel {
* @param nodeDataList the list of OtherOccurrenceNodeData representing * @param nodeDataList the list of OtherOccurrenceNodeData representing
* common properties for the file * common properties for the file
*/ */
OccurrencePanel(List<OtherOccurrenceNodeData> nodeDataList) { OccurrencePanel(List<NodeData> nodeDataList) {
this.nodeDataList = nodeDataList; this.nodeDataList = nodeDataList;
customizeComponents(); customizeComponents();
} }
@ -148,9 +149,9 @@ final class OccurrencePanel extends javax.swing.JPanel {
addItemToBag(gridY, 0, TOP_INSET, 0, commonPropertiesLabel); addItemToBag(gridY, 0, TOP_INSET, 0, commonPropertiesLabel);
gridY++; gridY++;
//for each other occurrence //for each other occurrence
for (OtherOccurrenceNodeData occurrence : nodeDataList) { for (NodeData occurrence : nodeDataList) {
if (occurrence instanceof OtherOccurrenceNodeInstanceData) { if (occurrence instanceof NodeData) {
String type = ((OtherOccurrenceNodeInstanceData) occurrence).getType(); String type = occurrence.getType();
if (!type.isEmpty()) { if (!type.isEmpty()) {
javax.swing.JLabel typeLabel = new javax.swing.JLabel(); javax.swing.JLabel typeLabel = new javax.swing.JLabel();
org.openide.awt.Mnemonics.setLocalizedText(typeLabel, Bundle.OccurrencePanel_commonPropertyTypeLabel_text()); org.openide.awt.Mnemonics.setLocalizedText(typeLabel, Bundle.OccurrencePanel_commonPropertyTypeLabel_text());
@ -160,7 +161,7 @@ final class OccurrencePanel extends javax.swing.JPanel {
addItemToBag(gridY, 1, VERTICAL_GAP, 0, typeFieldValue); addItemToBag(gridY, 1, VERTICAL_GAP, 0, typeFieldValue);
gridY++; gridY++;
} }
String value = ((OtherOccurrenceNodeInstanceData) occurrence).getValue(); String value = occurrence.getValue();
if (!value.isEmpty()) { if (!value.isEmpty()) {
javax.swing.JLabel valueLabel = new javax.swing.JLabel(); javax.swing.JLabel valueLabel = new javax.swing.JLabel();
org.openide.awt.Mnemonics.setLocalizedText(valueLabel, Bundle.OccurrencePanel_commonPropertyValueLabel_text()); org.openide.awt.Mnemonics.setLocalizedText(valueLabel, Bundle.OccurrencePanel_commonPropertyValueLabel_text());
@ -170,7 +171,7 @@ final class OccurrencePanel extends javax.swing.JPanel {
addItemToBag(gridY, 1, 0, 0, valueFieldValue); addItemToBag(gridY, 1, 0, 0, valueFieldValue);
gridY++; gridY++;
} }
TskData.FileKnown knownStatus = ((OtherOccurrenceNodeInstanceData) occurrence).getKnown(); TskData.FileKnown knownStatus = occurrence.getKnown();
javax.swing.JLabel knownStatusLabel = new javax.swing.JLabel(); javax.swing.JLabel knownStatusLabel = new javax.swing.JLabel();
org.openide.awt.Mnemonics.setLocalizedText(knownStatusLabel, Bundle.OccurrencePanel_commonPropertyKnownStatusLabel_text()); org.openide.awt.Mnemonics.setLocalizedText(knownStatusLabel, Bundle.OccurrencePanel_commonPropertyKnownStatusLabel_text());
addItemToBag(gridY, 0, 0, 0, knownStatusLabel); addItemToBag(gridY, 0, 0, 0, knownStatusLabel);
@ -181,7 +182,7 @@ final class OccurrencePanel extends javax.swing.JPanel {
} }
addItemToBag(gridY, 1, 0, 0, knownStatusValue); addItemToBag(gridY, 1, 0, 0, knownStatusValue);
gridY++; gridY++;
String comment = ((OtherOccurrenceNodeInstanceData) occurrence).getComment(); String comment = occurrence.getComment();
if (!comment.isEmpty()) { if (!comment.isEmpty()) {
javax.swing.JLabel commentLabel = new javax.swing.JLabel(); javax.swing.JLabel commentLabel = new javax.swing.JLabel();
org.openide.awt.Mnemonics.setLocalizedText(commentLabel, Bundle.OccurrencePanel_commonPropertyCommentLabel_text()); org.openide.awt.Mnemonics.setLocalizedText(commentLabel, Bundle.OccurrencePanel_commonPropertyCommentLabel_text());
@ -201,10 +202,9 @@ final class OccurrencePanel extends javax.swing.JPanel {
} }
String caseDate = ""; String caseDate = "";
try { try {
OtherOccurrenceNodeInstanceData nodeData = ((OtherOccurrenceNodeInstanceData) occurrence); if (occurrence.isCentralRepoNode()) {
if (nodeData.isCentralRepoNode()) {
if (CentralRepository.isEnabled()) { if (CentralRepository.isEnabled()) {
CorrelationCase partialCase = nodeData.getCorrelationAttributeInstance().getCorrelationCase(); CorrelationCase partialCase = occurrence.getCorrelationAttributeInstance().getCorrelationCase();
caseDate = CentralRepository.getInstance().getCaseByUUID(partialCase.getCaseUUID()).getCreationDate(); caseDate = CentralRepository.getInstance().getCaseByUUID(partialCase.getCaseUUID()).getCreationDate();
} }
} else { } else {
@ -214,9 +214,9 @@ final class OccurrencePanel extends javax.swing.JPanel {
LOGGER.log(Level.WARNING, "Error getting case created date for other occurrence content viewer", ex); LOGGER.log(Level.WARNING, "Error getting case created date for other occurrence content viewer", ex);
} }
//Collect the data that is necessary for the other sections //Collect the data that is necessary for the other sections
caseNamesAndDates.put(((OtherOccurrenceNodeInstanceData) occurrence).getCaseName(), caseDate); caseNamesAndDates.put(occurrence.getCaseName(), caseDate);
dataSourceNames.add(((OtherOccurrenceNodeInstanceData) occurrence).getDataSourceName()); dataSourceNames.add(occurrence.getDataSourceName());
filePaths.add(((OtherOccurrenceNodeInstanceData) occurrence).getFilePath()); filePaths.add(occurrence.getFilePath());
} }
} }
//end for each //end for each

View File

@ -1,25 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 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.centralrepository.contentviewer;
/**
* Marker interface for Other Occurrences nodes.
*/
interface OtherOccurrenceNodeData {
}

View File

@ -1,34 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 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.centralrepository.contentviewer;
/**
* Class for populating the Other Occurrences tab with a single message.
*/
final class OtherOccurrenceNodeMessageData implements OtherOccurrenceNodeData {
private final String displayMessage;
OtherOccurrenceNodeMessageData(String displayMessage) {
this.displayMessage = displayMessage;
}
String getDisplayMessage() {
return displayMessage;
}
}

View File

@ -0,0 +1,196 @@
/*
* Central Repository
*
* 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.centralrepository.contentviewer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import javax.swing.SwingWorker;
import org.apache.commons.lang3.StringUtils;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.centralrepository.application.NodeData;
import org.sleuthkit.autopsy.centralrepository.application.OtherOccurrences;
import org.sleuthkit.autopsy.centralrepository.application.UniquePathKey;
import org.sleuthkit.autopsy.centralrepository.contentviewer.OtherOccurrenceOneTypeWorker.OneTypeData;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.AbstractFile;
/**
* Swing worker for getting the Other Occurrence data for the Domain Discovery
* window.
*
* This logic differs a bit from the OtherOcurrencesNodeWorker.
*/
class OtherOccurrenceOneTypeWorker extends SwingWorker<OneTypeData, Void> {
private static final Logger logger = Logger.getLogger(OtherOccurrenceOneTypeWorker.class.getName());
private final CorrelationAttributeInstance.Type aType;
private final String value;
private final AbstractFile file;
private final String deviceId;
private final String dataSourceName;
/**
* Construct the worker.
*
* @param aType
* @param value
* @param file Source file, this maybe null.
* @param deviceId DeviceID string, this maybe an empty string.
* @param dataSourceName DataSourceName, this maybe an empty string.
*/
OtherOccurrenceOneTypeWorker(CorrelationAttributeInstance.Type aType, String value, AbstractFile file, String deviceId, String dataSourceName) {
this.aType = aType;
this.value = value;
this.file = file;
this.deviceId = deviceId;
this.dataSourceName = dataSourceName;
}
@Override
protected OneTypeData doInBackground() throws Exception {
Map<String, CorrelationCase> caseNames = new HashMap<>();
int totalCount = 0;
Set<String> dataSources = new HashSet<>();
Collection<CorrelationAttributeInstance> correlationAttributesToAdd = new ArrayList<>();
String earliestDate = OtherOccurrences.getEarliestCaseDate();
OneTypeData results = null;
if (CentralRepository.isEnabled()) {
List<CorrelationAttributeInstance> instances;
instances = CentralRepository.getInstance().getArtifactInstancesByTypeValue(aType, value);
HashMap<UniquePathKey, NodeData> nodeDataMap = new HashMap<>();
String caseUUID = Case.getCurrentCase().getName();
for (CorrelationAttributeInstance artifactInstance : instances) {
if (isCancelled()) {
break;
}
// Only add the attribute if it isn't the object the user selected.
// We consider it to be a different object if at least one of the following is true:
// - the case UUID is different
// - the data source name is different
// - 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()))) {
continue;
}
correlationAttributesToAdd.add(artifactInstance);
NodeData newNode = new NodeData(artifactInstance, aType, value);
UniquePathKey uniquePathKey = new UniquePathKey(newNode);
nodeDataMap.put(uniquePathKey, newNode);
}
for (NodeData nodeData : nodeDataMap.values()) {
if (isCancelled()) {
break;
}
if (nodeData.isCentralRepoNode()) {
try {
dataSources.add(OtherOccurrences.makeDataSourceString(nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID(), nodeData.getDeviceID(), nodeData.getDataSourceName()));
caseNames.put(nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID(), nodeData.getCorrelationAttributeInstance().getCorrelationCase());
} catch (CentralRepoException ex) {
logger.log(Level.WARNING, "Unable to get correlation case for displaying other occurrence for case: " + nodeData.getCaseName(), ex);
}
} else {
try {
dataSources.add(OtherOccurrences.makeDataSourceString(Case.getCurrentCaseThrows().getName(), nodeData.getDeviceID(), nodeData.getDataSourceName()));
caseNames.put(Case.getCurrentCaseThrows().getName(), new CorrelationCase(Case.getCurrentCaseThrows().getName(), Case.getCurrentCaseThrows().getDisplayName()));
} catch (NoCurrentCaseException ex) {
logger.log(Level.WARNING, "No current case open for other occurrences", ex);
}
}
totalCount++;
}
}
if (!isCancelled()) {
results = new OneTypeData(caseNames, totalCount, dataSources.size(), earliestDate, correlationAttributesToAdd);
}
return results;
}
/**
* Class to store the results of the worker thread.
*/
static final class OneTypeData {
private final Map<String, CorrelationCase> caseNames;
private final int totalCount;
private final int dataSourceCount;
private final Collection<CorrelationAttributeInstance> correlationAttributesToAdd;
private final String earliestCaseDate;
/**
* Construct the results.
*
* @param caseNames Map of correlation cases.
* @param totalCount Total count of instances.
* @param dataSourceCount Data source count.
* @param earliestCaseDate Formatted string which contains the
* earliest case date.
* @param correlationAttributesToAdd The attributes to add to the main
* panel list.
*/
OneTypeData(Map<String, CorrelationCase> caseNames, int totalCount, int dataSourceCount, String earliestCaseDate, Collection<CorrelationAttributeInstance> correlationAttributesToAdd) {
this.caseNames = caseNames;
this.totalCount = totalCount;
this.dataSourceCount = dataSourceCount;
this.correlationAttributesToAdd = correlationAttributesToAdd;
this.earliestCaseDate = earliestCaseDate;
}
public Map<String, CorrelationCase> getCaseNames() {
return caseNames;
}
public int getTotalCount() {
return totalCount;
}
public int getDataSourceCount() {
return dataSourceCount;
}
public Collection<CorrelationAttributeInstance> getCorrelationAttributesToAdd() {
return correlationAttributesToAdd;
}
public String getEarliestCaseDate() {
return earliestCaseDate;
}
}
}

View File

@ -18,6 +18,7 @@
*/ */
package org.sleuthkit.autopsy.centralrepository.contentviewer; package org.sleuthkit.autopsy.centralrepository.contentviewer;
import org.sleuthkit.autopsy.centralrepository.application.NodeData;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
@ -26,6 +27,7 @@ import javax.swing.table.AbstractTableModel;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.centralrepository.application.OtherOccurrences;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
@ -139,11 +141,10 @@ final class OtherOccurrencesDataSourcesTableModel extends AbstractTableModel {
* *
* @param newNodeData data to add to the table * @param newNodeData data to add to the table
*/ */
void addNodeData(OtherOccurrenceNodeData newNodeData) { void addNodeData(NodeData newNodeData) {
OtherOccurrenceNodeInstanceData nodeData = (OtherOccurrenceNodeInstanceData) newNodeData;
String caseUUID; String caseUUID;
try { try {
caseUUID = nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID(); caseUUID = newNodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID();
} catch (CentralRepoException ignored) { } catch (CentralRepoException ignored) {
//non central repo nodeData won't have a correlation case //non central repo nodeData won't have a correlation case
try { try {
@ -151,10 +152,10 @@ final class OtherOccurrencesDataSourcesTableModel extends AbstractTableModel {
//place holder value will be used since correlation attribute was unavailble //place holder value will be used since correlation attribute was unavailble
} catch (NoCurrentCaseException ex) { } catch (NoCurrentCaseException ex) {
logger.log(Level.WARNING, "Unable to get current case", ex); logger.log(Level.WARNING, "Unable to get current case", ex);
caseUUID = OtherOccurrencesPanel.getPlaceholderUUID(); caseUUID = OtherOccurrences.getPlaceholderUUID();
} }
} }
dataSourceSet.add(new DataSourceColumnItem(nodeData.getCaseName(), nodeData.getDeviceID(), nodeData.getDataSourceName(), caseUUID)); dataSourceSet.add(new DataSourceColumnItem(newNodeData.getCaseName(), newNodeData.getDeviceID(), newNodeData.getDataSourceName(), caseUUID));
fireTableDataChanged(); fireTableDataChanged();
} }

View File

@ -18,6 +18,7 @@
*/ */
package org.sleuthkit.autopsy.centralrepository.contentviewer; package org.sleuthkit.autopsy.centralrepository.contentviewer;
import org.sleuthkit.autopsy.centralrepository.application.NodeData;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@ -28,6 +29,7 @@ import org.openide.util.NbBundle.Messages;
import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.FilenameUtils;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.centralrepository.application.OtherOccurrences;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
@ -40,7 +42,7 @@ public class OtherOccurrencesFilesTableModel extends AbstractTableModel {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static final Logger logger = Logger.getLogger(OtherOccurrencesFilesTableModel.class.getName()); private static final Logger logger = Logger.getLogger(OtherOccurrencesFilesTableModel.class.getName());
private final List<String> nodeKeys = new ArrayList<>(); private final List<String> nodeKeys = new ArrayList<>();
private final Map<String, List<OtherOccurrenceNodeData>> nodeMap = new HashMap<>(); private final Map<String, List<NodeData>> nodeMap = new HashMap<>();
/** /**
* Create a table model for displaying file names * Create a table model for displaying file names
@ -75,7 +77,7 @@ public class OtherOccurrencesFilesTableModel extends AbstractTableModel {
|| nodeMap.get(nodeKeys.get(rowIdx)).isEmpty()) { || nodeMap.get(nodeKeys.get(rowIdx)).isEmpty()) {
return Bundle.OtherOccurrencesFilesTableModel_noData(); return Bundle.OtherOccurrencesFilesTableModel_noData();
} }
return FilenameUtils.getName(((OtherOccurrenceNodeInstanceData) nodeMap.get(nodeKeys.get(rowIdx)).get(0)).getFilePath()); return FilenameUtils.getName( nodeMap.get(nodeKeys.get(rowIdx)).get(0).getFilePath());
} }
/** /**
@ -87,7 +89,7 @@ public class OtherOccurrencesFilesTableModel extends AbstractTableModel {
* @return a list of OtherOccurrenceNodeData for the specified index or an * @return a list of OtherOccurrenceNodeData for the specified index or an
* empty list if no data was found * empty list if no data was found
*/ */
List<OtherOccurrenceNodeData> getListOfNodesForFile(int rowIdx) { List<NodeData> getListOfNodesForFile(int rowIdx) {
//if anything would prevent this from working return an empty list //if anything would prevent this from working return an empty list
if (nodeMap.isEmpty() || nodeKeys.isEmpty() || rowIdx < 0 if (nodeMap.isEmpty() || nodeKeys.isEmpty() || rowIdx < 0
|| rowIdx >= nodeKeys.size() || nodeKeys.get(rowIdx) == null || rowIdx >= nodeKeys.size() || nodeKeys.get(rowIdx) == null
@ -107,9 +109,9 @@ public class OtherOccurrencesFilesTableModel extends AbstractTableModel {
* *
* @param newNodeData data to add to the table * @param newNodeData data to add to the table
*/ */
void addNodeData(OtherOccurrenceNodeData newNodeData) { void addNodeData(NodeData newNodeData) {
String newNodeKey = createNodeKey((OtherOccurrenceNodeInstanceData) newNodeData);//FilenameUtils.getName(((OtherOccurrenceNodeInstanceData)newNodeData).getFilePath()); String newNodeKey = createNodeKey(newNodeData);
List<OtherOccurrenceNodeData> nodeList = nodeMap.get(newNodeKey); List<NodeData> nodeList = nodeMap.get(newNodeKey);
if (nodeList == null) { if (nodeList == null) {
nodeKeys.add(newNodeKey); nodeKeys.add(newNodeKey);
nodeList = new ArrayList<>(); nodeList = new ArrayList<>();
@ -119,7 +121,7 @@ public class OtherOccurrencesFilesTableModel extends AbstractTableModel {
fireTableDataChanged(); fireTableDataChanged();
} }
private String createNodeKey(OtherOccurrenceNodeInstanceData nodeData) { private String createNodeKey(NodeData nodeData) {
String caseUUID; String caseUUID;
try { try {
caseUUID = nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID(); caseUUID = nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID();
@ -130,7 +132,7 @@ public class OtherOccurrencesFilesTableModel extends AbstractTableModel {
//place holder value will be used since correlation attribute was unavailble //place holder value will be used since correlation attribute was unavailble
} catch (NoCurrentCaseException ex) { } catch (NoCurrentCaseException ex) {
logger.log(Level.WARNING, "Unable to get current case", ex); logger.log(Level.WARNING, "Unable to get current case", ex);
caseUUID = OtherOccurrencesPanel.getPlaceholderUUID(); caseUUID = OtherOccurrences.getPlaceholderUUID();
} }
} }
return nodeData.getCaseName() + nodeData.getDataSourceName() + nodeData.getDeviceID() + nodeData.getFilePath() + caseUUID; return nodeData.getCaseName() + nodeData.getDataSourceName() + nodeData.getDeviceID() + nodeData.getFilePath() + caseUUID;

View File

@ -0,0 +1,179 @@
/*
* Central Repository
*
* 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.centralrepository.contentviewer;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import javax.swing.SwingWorker;
import org.openide.nodes.Node;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.centralrepository.application.NodeData;
import org.sleuthkit.autopsy.centralrepository.application.OtherOccurrences;
import org.sleuthkit.autopsy.centralrepository.contentviewer.OtherOccurrencesNodeWorker.OtherOccurrencesData;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.TskException;
/**
* A SwingWorker that gathers data for the OtherOccurencesPanel which appears in
* the dataContentViewerOtherCases panel.
*/
class OtherOccurrencesNodeWorker extends SwingWorker<OtherOccurrencesData, Void> {
private static final Logger logger = Logger.getLogger(OtherOccurrencesNodeWorker.class.getName());
private final Node node;
/**
* Constructs a new instance for the given node.
*
* @param node
*/
OtherOccurrencesNodeWorker(Node node) {
this.node = node;
}
@Override
protected OtherOccurrencesData doInBackground() throws Exception {
AbstractFile file = OtherOccurrences.getAbstractFileFromNode(node);
String deviceId = "";
String dataSourceName = "";
Map<String, CorrelationCase> caseNames = new HashMap<>();
Case currentCase = Case.getCurrentCaseThrows();
OtherOccurrencesData data = null;
try {
if (file != null) {
Content dataSource = file.getDataSource();
deviceId = currentCase.getSleuthkitCase().getDataSource(dataSource.getId()).getDeviceId();
dataSourceName = dataSource.getName();
}
} catch (TskException ex) {
// do nothing.
// @@@ Review this behavior
return null;
}
Collection<CorrelationAttributeInstance> correlationAttributes = OtherOccurrences.getCorrelationAttributesFromNode(node, file);
int totalCount = 0;
Set<String> dataSources = new HashSet<>();
for (CorrelationAttributeInstance corAttr : correlationAttributes) {
for (NodeData nodeData : OtherOccurrences.getCorrelatedInstances(file, deviceId, dataSourceName, corAttr).values()) {
if (nodeData.isCentralRepoNode()) {
try {
dataSources.add(OtherOccurrences.makeDataSourceString(nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID(), nodeData.getDeviceID(), nodeData.getDataSourceName()));
caseNames.put(nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID(), nodeData.getCorrelationAttributeInstance().getCorrelationCase());
} catch (CentralRepoException ex) {
logger.log(Level.WARNING, "Unable to get correlation case for displaying other occurrence for case: " + nodeData.getCaseName(), ex);
}
} else {
try {
dataSources.add(OtherOccurrences.makeDataSourceString(Case.getCurrentCaseThrows().getName(), nodeData.getDeviceID(), nodeData.getDataSourceName()));
caseNames.put(Case.getCurrentCaseThrows().getName(), new CorrelationCase(Case.getCurrentCaseThrows().getName(), Case.getCurrentCaseThrows().getDisplayName()));
} catch (NoCurrentCaseException ex) {
logger.log(Level.WARNING, "No current case open for other occurrences", ex);
}
}
totalCount++;
if (isCancelled()) {
break;
}
}
}
if (!isCancelled()) {
data = new OtherOccurrencesData(correlationAttributes, file, dataSourceName, deviceId, caseNames, totalCount, dataSources.size(), OtherOccurrences.getEarliestCaseDate());
}
return data;
}
/**
* Object to store all of the data gathered in the OtherOccurrencesWorker
* doInBackground method.
*/
static class OtherOccurrencesData {
private final String deviceId;
private final AbstractFile file;
private final String dataSourceName;
private final Map<String, CorrelationCase> caseMap;
private final int instanceDataCount;
private final int dataSourceCount;
private final String earliestCaseDate;
private final Collection<CorrelationAttributeInstance> correlationAttributes;
private OtherOccurrencesData(Collection<CorrelationAttributeInstance> correlationAttributes, AbstractFile file, String dataSourceName, String deviceId, Map<String, CorrelationCase> caseMap, int instanceCount, int dataSourceCount, String earliestCaseDate) {
this.file = file;
this.deviceId = deviceId;
this.dataSourceName = dataSourceName;
this.caseMap = caseMap;
this.instanceDataCount = instanceCount;
this.dataSourceCount = dataSourceCount;
this.earliestCaseDate = earliestCaseDate;
this.correlationAttributes = correlationAttributes;
}
public String getDeviceId() {
return deviceId;
}
public AbstractFile getFile() {
return file;
}
public String getDataSourceName() {
return dataSourceName;
}
public Map<String, CorrelationCase> getCaseMap() {
return caseMap;
}
public int getInstanceDataCount() {
return instanceDataCount;
}
public int getDataSourceCount() {
return dataSourceCount;
}
/**
* Returns the earliest date in the case.
*
* @return Formatted date string, or message that one was not found.
*/
public String getEarliestCaseDate() {
return earliestCaseDate;
}
public Collection<CorrelationAttributeInstance> getCorrelationAttributes() {
return correlationAttributes;
}
}
}

View File

@ -18,26 +18,23 @@
*/ */
package org.sleuthkit.autopsy.centralrepository.contentviewer; package org.sleuthkit.autopsy.centralrepository.contentviewer;
import org.sleuthkit.autopsy.centralrepository.application.NodeData;
import org.sleuthkit.autopsy.centralrepository.application.UniquePathKey;
import org.sleuthkit.autopsy.centralrepository.application.OtherOccurrences;
import java.awt.Cursor;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter; import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent; import java.awt.event.ComponentEvent;
import java.io.BufferedWriter;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Calendar; import java.util.Calendar;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.concurrent.ExecutionException;
import java.util.logging.Level; import java.util.logging.Level;
import javax.swing.JFileChooser; import javax.swing.JFileChooser;
import javax.swing.JMenuItem; import javax.swing.JMenuItem;
@ -45,15 +42,15 @@ import javax.swing.JOptionPane;
import static javax.swing.JOptionPane.DEFAULT_OPTION; import static javax.swing.JOptionPane.DEFAULT_OPTION;
import static javax.swing.JOptionPane.ERROR_MESSAGE; import static javax.swing.JOptionPane.ERROR_MESSAGE;
import static javax.swing.JOptionPane.PLAIN_MESSAGE; import static javax.swing.JOptionPane.PLAIN_MESSAGE;
import javax.swing.SwingWorker;
import javax.swing.filechooser.FileNameExtensionFilter; import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.table.TableModel; import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter; import javax.swing.table.TableRowSorter;
import org.apache.commons.lang3.StringUtils; import org.openide.util.Exceptions;
import org.joda.time.DateTimeZone;
import org.joda.time.LocalDateTime;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.centralrepository.contentviewer.OtherOccurrencesNodeWorker.OtherOccurrencesData;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
@ -61,22 +58,20 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeNor
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.ContentTag;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
/** /**
* Panel for displaying other occurrences results. * Panel for displaying other occurrences results.
*/ */
@NbBundle.Messages({ @NbBundle.Messages({
"OtherOccurrencesPanel.table.noArtifacts=Item has no attributes with which to search.", "OtherOccurrencesPanel.table.noArtifacts=Item has no attributes with which to search.",
"OtherOccurrencesPanel.table.noResultsFound=No results found."}) "OtherOccurrencesPanel.table.noResultsFound=No results found.",
"OtherOccurrencesPanel_table_loadingResults=Loading results"
})
public final class OtherOccurrencesPanel extends javax.swing.JPanel { public final class OtherOccurrencesPanel extends javax.swing.JPanel {
private static final CorrelationCaseWrapper NO_ARTIFACTS_CASE = new CorrelationCaseWrapper(Bundle.OtherOccurrencesPanel_table_noArtifacts()); private static final CorrelationCaseWrapper NO_ARTIFACTS_CASE = new CorrelationCaseWrapper(Bundle.OtherOccurrencesPanel_table_noArtifacts());
private static final CorrelationCaseWrapper NO_RESULTS_CASE = new CorrelationCaseWrapper(Bundle.OtherOccurrencesPanel_table_noResultsFound()); private static final CorrelationCaseWrapper NO_RESULTS_CASE = new CorrelationCaseWrapper(Bundle.OtherOccurrencesPanel_table_noResultsFound());
private static final String UUID_PLACEHOLDER_STRING = "NoCorrelationAttributeInstance"; private static final CorrelationCaseWrapper LOADING_CASE = new CorrelationCaseWrapper(Bundle.OtherOccurrencesPanel_table_loadingResults());
private static final Logger logger = Logger.getLogger(OtherOccurrencesPanel.class.getName()); private static final Logger logger = Logger.getLogger(OtherOccurrencesPanel.class.getName());
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private final OtherOccurrencesFilesTableModel filesTableModel; private final OtherOccurrencesFilesTableModel filesTableModel;
@ -88,6 +83,8 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel {
private String deviceId = ""; //the device id of the data source for the file which the content viewer is being populated for private String deviceId = ""; //the device id of the data source for the file which the content viewer is being populated for
private AbstractFile file = null; private AbstractFile file = null;
private SwingWorker<?, ?> worker;
/** /**
* Creates new form OtherOccurrencesPanel * Creates new form OtherOccurrencesPanel
*/ */
@ -101,16 +98,6 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel {
customizeComponents(); customizeComponents();
} }
/**
* Get a placeholder string to use in place of case uuid when it isn't
* available
*
* @return UUID_PLACEHOLDER_STRING
*/
static String getPlaceholderUUID() {
return UUID_PLACEHOLDER_STRING;
}
private void customizeComponents() { private void customizeComponents() {
ActionListener actList = (ActionEvent e) -> { ActionListener actList = (ActionEvent e) -> {
JMenuItem jmi = (JMenuItem) e.getSource(); JMenuItem jmi = (JMenuItem) e.getSource();
@ -182,14 +169,16 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel {
*/ */
private void showCommonalityDetails() { private void showCommonalityDetails() {
if (correlationAttributes.isEmpty()) { if (correlationAttributes.isEmpty()) {
JOptionPane.showConfirmDialog(showCommonalityMenuItem, JOptionPane.showConfirmDialog(OtherOccurrencesPanel.this,
Bundle.OtherOccurrencesPanel_correlatedArtifacts_isEmpty(), Bundle.OtherOccurrencesPanel_correlatedArtifacts_isEmpty(),
Bundle.OtherOccurrencesPanel_correlatedArtifacts_title(), Bundle.OtherOccurrencesPanel_correlatedArtifacts_title(),
DEFAULT_OPTION, PLAIN_MESSAGE); DEFAULT_OPTION, PLAIN_MESSAGE);
} else { } else {
StringBuilder msg = new StringBuilder(correlationAttributes.size()); StringBuilder msg = new StringBuilder(correlationAttributes.size());
int percentage; int percentage;
this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
try { try {
// Leaving these calls on the EDT but adding wait cursor
CentralRepository dbManager = CentralRepository.getInstance(); CentralRepository dbManager = CentralRepository.getInstance();
for (CorrelationAttributeInstance eamArtifact : correlationAttributes) { for (CorrelationAttributeInstance eamArtifact : correlationAttributes) {
try { try {
@ -201,13 +190,15 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel {
logger.log(Level.WARNING, String.format("Error getting commonality details for artifact with ID: %s.", eamArtifact.getID()), ex); logger.log(Level.WARNING, String.format("Error getting commonality details for artifact with ID: %s.", eamArtifact.getID()), ex);
} }
} }
JOptionPane.showConfirmDialog(showCommonalityMenuItem, this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
JOptionPane.showConfirmDialog(OtherOccurrencesPanel.this,
msg.toString(), msg.toString(),
Bundle.OtherOccurrencesPanel_correlatedArtifacts_title(), Bundle.OtherOccurrencesPanel_correlatedArtifacts_title(),
DEFAULT_OPTION, PLAIN_MESSAGE); DEFAULT_OPTION, PLAIN_MESSAGE);
} catch (CentralRepoException ex) { } catch (CentralRepoException ex) {
this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
logger.log(Level.SEVERE, "Error getting commonality details.", ex); logger.log(Level.SEVERE, "Error getting commonality details.", ex);
JOptionPane.showConfirmDialog(showCommonalityMenuItem, JOptionPane.showConfirmDialog(OtherOccurrencesPanel.this,
Bundle.OtherOccurrencesPanel_correlatedArtifacts_failed(), Bundle.OtherOccurrencesPanel_correlatedArtifacts_failed(),
Bundle.OtherOccurrencesPanel_correlatedArtifacts_title(), Bundle.OtherOccurrencesPanel_correlatedArtifacts_title(),
DEFAULT_OPTION, ERROR_MESSAGE); DEFAULT_OPTION, ERROR_MESSAGE);
@ -227,20 +218,16 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel {
if (-1 != selectedRowViewIdx) { if (-1 != selectedRowViewIdx) {
CentralRepository dbManager = CentralRepository.getInstance(); CentralRepository dbManager = CentralRepository.getInstance();
int selectedRowModelIdx = filesTable.convertRowIndexToModel(selectedRowViewIdx); int selectedRowModelIdx = filesTable.convertRowIndexToModel(selectedRowViewIdx);
List<OtherOccurrenceNodeData> rowList = filesTableModel.getListOfNodesForFile(selectedRowModelIdx); List<NodeData> rowList = filesTableModel.getListOfNodesForFile(selectedRowModelIdx);
if (!rowList.isEmpty()) { if (!rowList.isEmpty()) {
if (rowList.get(0) instanceof OtherOccurrenceNodeInstanceData) { CorrelationCase eamCasePartial = rowList.get(0).getCorrelationAttributeInstance().getCorrelationCase();
CorrelationCase eamCasePartial = ((OtherOccurrenceNodeInstanceData) rowList.get(0)).getCorrelationAttributeInstance().getCorrelationCase(); caseDisplayName = eamCasePartial.getDisplayName();
caseDisplayName = eamCasePartial.getDisplayName(); // query case details
// query case details CorrelationCase eamCase = dbManager.getCaseByUUID(eamCasePartial.getCaseUUID());
CorrelationCase eamCase = dbManager.getCaseByUUID(eamCasePartial.getCaseUUID()); if (eamCase != null) {
if (eamCase != null) { details = eamCase.getCaseDetailsOptionsPaneDialog();
details = eamCase.getCaseDetailsOptionsPaneDialog();
} else {
details = Bundle.OtherOccurrencesPanel_caseDetailsDialog_noDetails();
}
} else { } else {
details = Bundle.OtherOccurrencesPanel_caseDetailsDialog_notSelected(); details = Bundle.OtherOccurrencesPanel_caseDetailsDialog_noDetails();
} }
} else { } else {
details = Bundle.OtherOccurrencesPanel_caseDetailsDialog_noDetailsReference(); details = Bundle.OtherOccurrencesPanel_caseDetailsDialog_noDetailsReference();
@ -249,7 +236,7 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel {
} catch (CentralRepoException ex) { } catch (CentralRepoException ex) {
logger.log(Level.SEVERE, "Error loading case details", ex); logger.log(Level.SEVERE, "Error loading case details", ex);
} finally { } finally {
JOptionPane.showConfirmDialog(showCaseDetailsMenuItem, JOptionPane.showConfirmDialog(OtherOccurrencesPanel.this,
details, details,
caseDisplayName, caseDisplayName,
DEFAULT_OPTION, PLAIN_MESSAGE); DEFAULT_OPTION, PLAIN_MESSAGE);
@ -271,85 +258,13 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel {
if (!selectedFile.getName().endsWith(".csv")) { // NON-NLS if (!selectedFile.getName().endsWith(".csv")) { // NON-NLS
selectedFile = new File(selectedFile.toString() + ".csv"); // NON-NLS selectedFile = new File(selectedFile.toString() + ".csv"); // NON-NLS
} }
writeOtherOccurrencesToFileAsCSV(selectedFile); CSVWorker worker = new CSVWorker(selectedFile, file, dataSourceName, deviceId, Collections.unmodifiableCollection(correlationAttributes));
worker.execute();
} }
} }
} }
@NbBundle.Messages({ @NbBundle.Messages({"OtherOccurrencesPanel_earliestCaseNotAvailable=Not Availble."})
"OtherOccurrencesPanel.csvHeader.case=Case",
"OtherOccurrencesPanel.csvHeader.device=Device",
"OtherOccurrencesPanel.csvHeader.dataSource=Data Source",
"OtherOccurrencesPanel.csvHeader.attribute=Matched Attribute",
"OtherOccurrencesPanel.csvHeader.value=Attribute Value",
"OtherOccurrencesPanel.csvHeader.known=Known",
"OtherOccurrencesPanel.csvHeader.path=Path",
"OtherOccurrencesPanel.csvHeader.comment=Comment"
})
/**
* Write data for all cases in the content viewer to a CSV file
*/
private void writeOtherOccurrencesToFileAsCSV(File destFile) {
try (BufferedWriter writer = Files.newBufferedWriter(destFile.toPath())) {
//write headers
StringBuilder headers = new StringBuilder("\"");
headers.append(Bundle.OtherOccurrencesPanel_csvHeader_case())
.append(OtherOccurrenceNodeInstanceData.getCsvItemSeparator()).append(Bundle.OtherOccurrencesPanel_csvHeader_dataSource())
.append(OtherOccurrenceNodeInstanceData.getCsvItemSeparator()).append(Bundle.OtherOccurrencesPanel_csvHeader_attribute())
.append(OtherOccurrenceNodeInstanceData.getCsvItemSeparator()).append(Bundle.OtherOccurrencesPanel_csvHeader_value())
.append(OtherOccurrenceNodeInstanceData.getCsvItemSeparator()).append(Bundle.OtherOccurrencesPanel_csvHeader_known())
.append(OtherOccurrenceNodeInstanceData.getCsvItemSeparator()).append(Bundle.OtherOccurrencesPanel_csvHeader_path())
.append(OtherOccurrenceNodeInstanceData.getCsvItemSeparator()).append(Bundle.OtherOccurrencesPanel_csvHeader_comment())
.append('"').append(System.getProperty("line.separator"));
writer.write(headers.toString());
//write content
for (CorrelationAttributeInstance corAttr : correlationAttributes) {
Map<UniquePathKey, OtherOccurrenceNodeInstanceData> correlatedNodeDataMap = new HashMap<>(0);
// get correlation and reference set instances from DB
correlatedNodeDataMap.putAll(getCorrelatedInstances(corAttr));
for (OtherOccurrenceNodeInstanceData nodeData : correlatedNodeDataMap.values()) {
writer.write(nodeData.toCsvString());
}
}
} catch (IOException ex) {
logger.log(Level.SEVERE, "Error writing selected rows to CSV.", ex);
}
}
@NbBundle.Messages({"OtherOccurrencesPanel.earliestCaseNotAvailable= Not Enabled."})
/**
* Gets the list of Eam Cases and determines the earliest case creation
* date. Sets the label to display the earliest date string to the user.
*/
private void setEarliestCaseDate() {
String dateStringDisplay = Bundle.OtherOccurrencesPanel_earliestCaseNotAvailable();
if (CentralRepository.isEnabled()) {
LocalDateTime earliestDate = LocalDateTime.now(DateTimeZone.UTC);
DateFormat datetimeFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.US);
try {
CentralRepository dbManager = CentralRepository.getInstance();
List<CorrelationCase> cases = dbManager.getCases();
for (CorrelationCase aCase : cases) {
LocalDateTime caseDate = LocalDateTime.fromDateFields(datetimeFormat.parse(aCase.getCreationDate()));
if (caseDate.isBefore(earliestDate)) {
earliestDate = caseDate;
dateStringDisplay = aCase.getCreationDate();
}
}
} catch (CentralRepoException ex) {
logger.log(Level.SEVERE, "Error getting list of cases from database.", ex); // NON-NLS
} catch (ParseException ex) {
logger.log(Level.SEVERE, "Error parsing date of cases from database.", ex); // NON-NLS
}
}
earliestCaseDate.setText(dateStringDisplay);
}
/** /**
* Reset the UI and clear cached data. * Reset the UI and clear cached data.
*/ */
@ -371,123 +286,82 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel {
* Populate the other occurrences table for one Correlation Attribute type * Populate the other occurrences table for one Correlation Attribute type
* and value. * and value.
* *
* This method contains its own SwingWorker togather data.
*
* @param aType The correlation attribute type to display other occurrences * @param aType The correlation attribute type to display other occurrences
* for. * for.
* @param value The value being correlated on. * @param value The value being correlated on.
*/ */
public void populateTableForOneType(CorrelationAttributeInstance.Type aType, String value) { public void populateTableForOneType(CorrelationAttributeInstance.Type aType, String value) throws CentralRepoException {
Map<String, CorrelationCase> caseNames = new HashMap<>(); if (worker != null) {
int totalCount = 0; worker.cancel(true);
Set<String> dataSources = new HashSet<>(); worker = null;
if (CentralRepository.isEnabled()) { }
try {
List<CorrelationAttributeInstance> instances;
instances = CentralRepository.getInstance().getArtifactInstancesByTypeValue(aType, value);
HashMap<UniquePathKey, OtherOccurrenceNodeInstanceData> nodeDataMap = new HashMap<>();
String caseUUID = Case.getCurrentCase().getName();
for (CorrelationAttributeInstance artifactInstance : instances) {
// Only add the attribute if it isn't the object the user selected. casesTableModel.addCorrelationCase(NO_ARTIFACTS_CASE);
// We consider it to be a different object if at least one of the following is true:
// - the case UUID is different worker = new OtherOccurrenceOneTypeWorker(aType, value, file, deviceId, dataSourceName) {
// - the data source name is different @Override
// - the data source device ID is different public void done() {
// - the file path is different try {
if (artifactInstance.getCorrelationCase().getCaseUUID().equals(caseUUID) if (isCancelled()) {
&& (!StringUtils.isBlank(dataSourceName) && artifactInstance.getCorrelationDataSource().getName().equals(dataSourceName)) return;
&& (!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); casesTableModel.clearTable();
UniquePathKey uniquePathKey = new UniquePathKey(newNode);
nodeDataMap.put(uniquePathKey, newNode); OtherOccurrenceOneTypeWorker.OneTypeData data = get();
} for (CorrelationCase corCase : data.getCaseNames().values()) {
for (OtherOccurrenceNodeInstanceData nodeData : nodeDataMap.values()) { casesTableModel.addCorrelationCase(new CorrelationCaseWrapper(corCase));
if (nodeData.isCentralRepoNode()) {
try {
dataSources.add(makeDataSourceString(nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID(), nodeData.getDeviceID(), nodeData.getDataSourceName()));
caseNames.put(nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID(), nodeData.getCorrelationAttributeInstance().getCorrelationCase());
} catch (CentralRepoException ex) {
logger.log(Level.WARNING, "Unable to get correlation case for displaying other occurrence for case: " + nodeData.getCaseName(), ex);
}
} else {
try {
dataSources.add(makeDataSourceString(Case.getCurrentCaseThrows().getName(), nodeData.getDeviceID(), nodeData.getDataSourceName()));
caseNames.put(Case.getCurrentCaseThrows().getName(), new CorrelationCase(Case.getCurrentCaseThrows().getName(), Case.getCurrentCaseThrows().getDisplayName()));
} catch (NoCurrentCaseException ex) {
logger.log(Level.WARNING, "No current case open for other occurrences", ex);
}
} }
totalCount++; int caseCount = casesTableModel.getRowCount();
if (correlationAttributes.isEmpty()) {
casesTableModel.addCorrelationCase(NO_ARTIFACTS_CASE);
} else if (caseCount == 0) {
casesTableModel.addCorrelationCase(NO_RESULTS_CASE);
}
String earliestDate = data.getEarliestCaseDate();
earliestCaseDate.setText(earliestDate.isEmpty() ? Bundle.OtherOccurrencesPanel_earliestCaseNotAvailable() : earliestDate);
foundInLabel.setText(String.format(Bundle.OtherOccurrencesPanel_foundIn_text(), data.getTotalCount(), caseCount, data.getDataSourceCount()));
if (caseCount > 0) {
casesTable.setRowSelectionInterval(0, 0);
}
} catch (InterruptedException | ExecutionException ex) {
logger.log(Level.SEVERE, "Failed to update OtherOccurrence panel", ex);
} }
} catch (CorrelationAttributeNormalizationException | CentralRepoException ex) {
logger.log(Level.WARNING, "Error retrieving other occurrences for " + aType.getDisplayName() + ": " + value, ex);
} }
} };
for (CorrelationCase corCase : caseNames.values()) {
casesTableModel.addCorrelationCase(new CorrelationCaseWrapper(corCase)); worker.execute();
} }
int caseCount = casesTableModel.getRowCount();
if (correlationAttributes.isEmpty()) { /**
casesTableModel.addCorrelationCase(NO_ARTIFACTS_CASE); * Makes a loading message appear in the case table.
} else if (caseCount == 0) { */
casesTableModel.addCorrelationCase(NO_RESULTS_CASE); void showPanelLoadingMessage() {
} casesTableModel.addCorrelationCase(NO_ARTIFACTS_CASE);
setEarliestCaseDate();
foundInLabel.setText(String.format(Bundle.OtherOccurrencesPanel_foundIn_text(), totalCount, caseCount, dataSources.size()));
if (caseCount > 0) {
casesTable.setRowSelectionInterval(0, 0);
}
} }
/** /**
* Load the correlatable data into the table model. If there is no data * Load the correlatable data into the table model. If there is no data
* available display the message on the status panel. * available display the message on the status panel.
* *
* @param correlationAttrs The correlationAttributes to correlate on. * @param data A data wrapper object.
* @param dataSourceName The name of the dataSource to ignore results
* from.
* @param deviceId The deviceId of the device to ignore results
* from.
* @param abstractFile The abstract file to ignore files with the same
* location as.
*/ */
@NbBundle.Messages({ @NbBundle.Messages({
"OtherOccurrencesPanel.foundIn.text=Found %d instances in %d cases and %d data sources." "OtherOccurrencesPanel.foundIn.text=Found %d instances in %d cases and %d data sources."
}) })
void populateTable(Collection<CorrelationAttributeInstance> correlationAttrs, String dataSourceName, String deviceId, AbstractFile abstractFile) { void populateTable(OtherOccurrencesData data) {
this.file = abstractFile; this.file = data.getFile();
this.dataSourceName = dataSourceName; this.dataSourceName = data.getDataSourceName();
this.deviceId = deviceId; this.deviceId = data.getDeviceId();
// get the attributes we can correlate on casesTableModel.clearTable();
correlationAttributes.addAll(correlationAttrs);
Map<String, CorrelationCase> caseNames = new HashMap<>(); correlationAttributes.addAll(data.getCorrelationAttributes());
int totalCount = 0;
Set<String> dataSources = new HashSet<>(); for (CorrelationCase corCase : data.getCaseMap().values()) {
for (CorrelationAttributeInstance corAttr : correlationAttributes) {
for (OtherOccurrenceNodeInstanceData nodeData : getCorrelatedInstances(corAttr).values()) {
if (nodeData.isCentralRepoNode()) {
try {
dataSources.add(makeDataSourceString(nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID(), nodeData.getDeviceID(), nodeData.getDataSourceName()));
caseNames.put(nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID(), nodeData.getCorrelationAttributeInstance().getCorrelationCase());
} catch (CentralRepoException ex) {
logger.log(Level.WARNING, "Unable to get correlation case for displaying other occurrence for case: " + nodeData.getCaseName(), ex);
}
} else {
try {
dataSources.add(makeDataSourceString(Case.getCurrentCaseThrows().getName(), nodeData.getDeviceID(), nodeData.getDataSourceName()));
caseNames.put(Case.getCurrentCaseThrows().getName(), new CorrelationCase(Case.getCurrentCaseThrows().getName(), Case.getCurrentCaseThrows().getDisplayName()));
} catch (NoCurrentCaseException ex) {
logger.log(Level.WARNING, "No current case open for other occurrences", ex);
}
}
totalCount++;
}
}
for (CorrelationCase corCase : caseNames.values()) {
casesTableModel.addCorrelationCase(new CorrelationCaseWrapper(corCase)); casesTableModel.addCorrelationCase(new CorrelationCaseWrapper(corCase));
} }
int caseCount = casesTableModel.getRowCount(); int caseCount = casesTableModel.getRowCount();
@ -496,241 +370,142 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel {
} else if (caseCount == 0) { } else if (caseCount == 0) {
casesTableModel.addCorrelationCase(NO_RESULTS_CASE); casesTableModel.addCorrelationCase(NO_RESULTS_CASE);
} }
setEarliestCaseDate(); String earliestDate = data.getEarliestCaseDate();
foundInLabel.setText(String.format(Bundle.OtherOccurrencesPanel_foundIn_text(), totalCount, caseCount, dataSources.size())); earliestCaseDate.setText(earliestDate.isEmpty() ? Bundle.OtherOccurrencesPanel_earliestCaseNotAvailable() : earliestDate);
foundInLabel.setText(String.format(Bundle.OtherOccurrencesPanel_foundIn_text(), data.getInstanceDataCount(), caseCount, data.getDataSourceCount()));
if (caseCount > 0) { if (caseCount > 0) {
casesTable.setRowSelectionInterval(0, 0); casesTable.setRowSelectionInterval(0, 0);
} }
} }
/** /**
* Query the central repo database (if enabled) and the case database to * Updates displayed information to be correct for the current case
* find all artifact instances correlated to the given central repository * selection
* 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
*
* @return A collection of correlated artifact instances
*/
private Map<UniquePathKey, OtherOccurrenceNodeInstanceData> getCorrelatedInstances(CorrelationAttributeInstance corAttr) {
// @@@ Check exception
try {
final Case openCase = Case.getCurrentCaseThrows();
String caseUUID = openCase.getName();
HashMap<UniquePathKey, OtherOccurrenceNodeInstanceData> nodeDataMap = new HashMap<>();
if (CentralRepository.isEnabled()) {
List<CorrelationAttributeInstance> instances = CentralRepository.getInstance().getArtifactInstancesByTypeValue(corAttr.getCorrelationType(), corAttr.getCorrelationValue());
for (CorrelationAttributeInstance artifactInstance : instances) {
// Only add the attribute if it isn't the object the user selected.
// We consider it to be a different object if at least one of the following is true:
// - the case UUID is different
// - the data source name is different
// - 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()))) {
continue;
}
OtherOccurrenceNodeInstanceData newNode = new OtherOccurrenceNodeInstanceData(artifactInstance, corAttr.getCorrelationType(), corAttr.getCorrelationValue());
UniquePathKey uniquePathKey = new UniquePathKey(newNode);
nodeDataMap.put(uniquePathKey, newNode);
}
if (file != null && corAttr.getCorrelationType().getDisplayName().equals("Files")) {
List<AbstractFile> caseDbFiles = getCaseDbMatches(corAttr, openCase, file);
for (AbstractFile caseDbFile : caseDbFiles) {
addOrUpdateNodeData(openCase, nodeDataMap, caseDbFile);
}
}
}
return nodeDataMap;
} catch (CentralRepoException ex) {
logger.log(Level.SEVERE, "Error getting artifact instances from database.", ex); // NON-NLS
} catch (CorrelationAttributeNormalizationException ex) {
logger.log(Level.INFO, "Error getting artifact instances from database.", ex); // NON-NLS
} catch (NoCurrentCaseException ex) {
logger.log(Level.SEVERE, "Exception while getting open case.", ex); // NON-NLS
} catch (TskCoreException ex) {
// do nothing.
// @@@ Review this behavior
logger.log(Level.SEVERE, "Exception while querying open case.", ex); // NON-NLS
}
return new HashMap<>(
0);
}
/**
* Adds the file to the nodeDataMap map if it does not already exist
*
* @param autopsyCase
* @param nodeDataMap
* @param newFile
*
* @throws TskCoreException
* @throws CentralRepoException
*/
private void addOrUpdateNodeData(final Case autopsyCase, Map<UniquePathKey, OtherOccurrenceNodeInstanceData> nodeDataMap, AbstractFile newFile) throws TskCoreException, CentralRepoException {
OtherOccurrenceNodeInstanceData newNode = new OtherOccurrenceNodeInstanceData(newFile, autopsyCase);
// If the caseDB object has a notable tag associated with it, update
// the known status to BAD
if (newNode.getKnown() != TskData.FileKnown.BAD) {
List<ContentTag> fileMatchTags = autopsyCase.getServices().getTagsManager().getContentTagsByContent(newFile);
for (ContentTag tag : fileMatchTags) {
TskData.FileKnown tagKnownStatus = tag.getName().getKnownStatus();
if (tagKnownStatus.equals(TskData.FileKnown.BAD)) {
newNode.updateKnown(TskData.FileKnown.BAD);
break;
}
}
}
// Make a key to see if the file is already in the map
UniquePathKey uniquePathKey = new UniquePathKey(newNode);
// If this node is already in the list, the only thing we need to do is
// update the known status to BAD if the caseDB version had known status BAD.
// Otherwise this is a new node so add the new node to the map.
if (nodeDataMap.containsKey(uniquePathKey)) {
if (newNode.getKnown() == TskData.FileKnown.BAD) {
OtherOccurrenceNodeInstanceData prevInstance = nodeDataMap.get(uniquePathKey);
prevInstance.updateKnown(newNode.getKnown());
}
} else {
nodeDataMap.put(uniquePathKey, newNode);
}
}
/**
* Get all other abstract files in the current case with the same MD5 as the
* selected node.
*
* @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
*
* @throws NoCurrentCaseException
* @throws TskCoreException
* @throws CentralRepoException
*/
private List<AbstractFile> getCaseDbMatches(CorrelationAttributeInstance corAttr, Case openCase, AbstractFile file) throws NoCurrentCaseException, TskCoreException, CentralRepoException {
List<AbstractFile> caseDbArtifactInstances = new ArrayList<>();
if (file != null) {
String md5 = corAttr.getCorrelationValue();
SleuthkitCase tsk = openCase.getSleuthkitCase();
List<AbstractFile> matches = tsk.findAllFilesWhere(String.format("md5 = '%s'", new Object[]{md5}));
for (AbstractFile fileMatch : matches) {
if (file.equals(fileMatch)) {
continue; // If this is the file the user clicked on
}
caseDbArtifactInstances.add(fileMatch);
}
}
return caseDbArtifactInstances;
}
/**
* Create a unique string to be used as a key for deduping data sources as
* best as possible
*/
private String makeDataSourceString(String caseUUID, String deviceId, String dataSourceName) {
return caseUUID + deviceId + dataSourceName;
}
/**
* Updates diplayed information to be correct for the current case selection
*/ */
private void updateOnCaseSelection() { private void updateOnCaseSelection() {
int[] selectedCaseIndexes = casesTable.getSelectedRows(); if (worker != null) {
worker.cancel(true);
worker = null;
}
final int[] selectedCaseIndexes = casesTable.getSelectedRows();
dataSourcesTableModel.clearTable(); dataSourcesTableModel.clearTable();
filesTableModel.clearTable(); filesTableModel.clearTable();
if (selectedCaseIndexes.length == 0) { if (selectedCaseIndexes.length == 0) {
//special case when no cases are selected //special case when no cases are selected
occurrencePanel = new OccurrencePanel(); occurrencePanel = new OccurrencePanel();
occurrencePanel.getPreferredSize(); occurrencePanel.getPreferredSize();
detailsPanelScrollPane.setViewportView(occurrencePanel); detailsPanelScrollPane.setViewportView(occurrencePanel);
} else {
String currentCaseName;
try {
currentCaseName = Case.getCurrentCaseThrows().getName();
} catch (NoCurrentCaseException ex) {
currentCaseName = null;
logger.log(Level.WARNING, "Unable to get current case for other occurrences content viewer", ex);
}
for (CorrelationAttributeInstance corAttr : correlationAttributes) {
Map<UniquePathKey, OtherOccurrenceNodeInstanceData> correlatedNodeDataMap = new HashMap<>(0);
// get correlation and reference set instances from DB return;
correlatedNodeDataMap.putAll(getCorrelatedInstances(corAttr)); }
for (OtherOccurrenceNodeInstanceData nodeData : correlatedNodeDataMap.values()) {
for (int selectedRow : selectedCaseIndexes) { setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
try {
if (nodeData.isCentralRepoNode()) { worker = new SelectionWorker(correlationAttributes, file, deviceId, dataSourceName) {
if (casesTableModel.getCorrelationCase(casesTable.convertRowIndexToModel(selectedRow)) != null @Override
&& casesTableModel.getCorrelationCase(casesTable.convertRowIndexToModel(selectedRow)).getCaseUUID().equals(nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID())) { public void done() {
if (isCancelled()) {
return;
}
try {
Map<UniquePathKey, NodeData> correlatedNodeDataMap = get();
String currentCaseName;
try {
currentCaseName = Case.getCurrentCaseThrows().getName();
} catch (NoCurrentCaseException ex) {
currentCaseName = null;
logger.log(Level.WARNING, "Unable to get current case for other occurrences content viewer", ex);
}
for (NodeData nodeData : correlatedNodeDataMap.values()) {
for (int selectedRow : selectedCaseIndexes) {
try {
if (nodeData.isCentralRepoNode()) {
if (casesTableModel.getCorrelationCase(casesTable.convertRowIndexToModel(selectedRow)) != null
&& casesTableModel.getCorrelationCase(casesTable.convertRowIndexToModel(selectedRow)).getCaseUUID().equals(nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID())) {
dataSourcesTableModel.addNodeData(nodeData);
}
} else if (currentCaseName != null && (casesTableModel.getCorrelationCase(casesTable.convertRowIndexToModel(selectedRow)).getCaseUUID().equals(currentCaseName))) {
dataSourcesTableModel.addNodeData(nodeData); dataSourcesTableModel.addNodeData(nodeData);
} }
} else if (currentCaseName != null && (casesTableModel.getCorrelationCase(casesTable.convertRowIndexToModel(selectedRow)).getCaseUUID().equals(currentCaseName))) { } catch (CentralRepoException ex) {
dataSourcesTableModel.addNodeData(nodeData); logger.log(Level.WARNING, "Unable to get correlation attribute instance from OtherOccurrenceNodeInstanceData for case " + nodeData.getCaseName(), ex);
} }
} catch (CentralRepoException ex) {
logger.log(Level.WARNING, "Unable to get correlation attribute instance from OtherOccurrenceNodeInstanceData for case " + nodeData.getCaseName(), ex);
} }
} }
if (dataSourcesTable.getRowCount() > 0) {
dataSourcesTable.setRowSelectionInterval(0, 0);
}
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
} catch (InterruptedException | ExecutionException ex) {
logger.log(Level.SEVERE, "Failed to update OtherOccurrencesPanel on data source selection", ex);
} }
} }
if (dataSourcesTable.getRowCount() > 0) { };
dataSourcesTable.setRowSelectionInterval(0, 0);
} worker.execute();
}
} }
/** /**
* Updates diplayed information to be correct for the current data source * Updates displayed information to be correct for the current data source
* selection * selection
*/ */
private void updateOnDataSourceSelection() { private void updateOnDataSourceSelection() {
int[] selectedDataSources = dataSourcesTable.getSelectedRows(); if (worker != null) {
filesTableModel.clearTable(); worker.cancel(true);
for (CorrelationAttributeInstance corAttr : correlationAttributes) { worker = null;
Map<UniquePathKey, OtherOccurrenceNodeInstanceData> correlatedNodeDataMap = new HashMap<>(0); }
// get correlation and reference set instances from DB setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
correlatedNodeDataMap.putAll(getCorrelatedInstances(corAttr));
for (OtherOccurrenceNodeInstanceData nodeData : correlatedNodeDataMap.values()) { final int[] selectedDataSources = dataSourcesTable.getSelectedRows();
for (int selectedDataSourceRow : selectedDataSources) { filesTableModel.clearTable();
try {
if (nodeData.isCentralRepoNode()) { worker = new SelectionWorker(correlationAttributes, file, deviceId, dataSourceName) {
if (dataSourcesTableModel.getCaseUUIDForRow(dataSourcesTable.convertRowIndexToModel(selectedDataSourceRow)).equals(nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID()) @Override
&& dataSourcesTableModel.getDeviceIdForRow(dataSourcesTable.convertRowIndexToModel(selectedDataSourceRow)).equals(nodeData.getDeviceID())) { public void done() {
filesTableModel.addNodeData(nodeData); if (isCancelled()) {
} return;
} else { }
if (dataSourcesTableModel.getDeviceIdForRow(dataSourcesTable.convertRowIndexToModel(selectedDataSourceRow)).equals(nodeData.getDeviceID())) {
filesTableModel.addNodeData(nodeData); try {
Map<UniquePathKey, NodeData> correlatedNodeDataMap = get();
for (NodeData nodeData : correlatedNodeDataMap.values()) {
for (int selectedDataSourceRow : selectedDataSources) {
try {
if (nodeData.isCentralRepoNode()) {
if (dataSourcesTableModel.getCaseUUIDForRow(dataSourcesTable.convertRowIndexToModel(selectedDataSourceRow)).equals(nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID())
&& dataSourcesTableModel.getDeviceIdForRow(dataSourcesTable.convertRowIndexToModel(selectedDataSourceRow)).equals(nodeData.getDeviceID())) {
filesTableModel.addNodeData(nodeData);
}
} else {
if (dataSourcesTableModel.getDeviceIdForRow(dataSourcesTable.convertRowIndexToModel(selectedDataSourceRow)).equals(nodeData.getDeviceID())) {
filesTableModel.addNodeData(nodeData);
}
}
} catch (CentralRepoException ex) {
logger.log(Level.WARNING, "Unable to get correlation attribute instance from OtherOccurrenceNodeInstanceData for case " + nodeData.getCaseName(), ex);
} }
} }
} catch (CentralRepoException ex) {
logger.log(Level.WARNING, "Unable to get correlation attribute instance from OtherOccurrenceNodeInstanceData for case " + nodeData.getCaseName(), ex);
} }
if (filesTable.getRowCount() > 0) {
filesTable.setRowSelectionInterval(0, 0);
}
} catch (InterruptedException | ExecutionException ex) {
logger.log(Level.SEVERE, "Failed to update OtherOccurrencesPanel on case selection", ex);
} finally {
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
} }
} }
} };
if (filesTable.getRowCount() > 0) {
filesTable.setRowSelectionInterval(0, 0); worker.execute();
}
} }
/** /**
@ -738,42 +513,47 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel {
* currently selected File * currently selected File
*/ */
private void updateOnFileSelection() { private void updateOnFileSelection() {
if (filesTable.getSelectedRowCount() == 1) { setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
//if there is one file selected update the deatils to show the data for that file try {
occurrencePanel = new OccurrencePanel(filesTableModel.getListOfNodesForFile(filesTable.convertRowIndexToModel(filesTable.getSelectedRow()))); if (filesTable.getSelectedRowCount() == 1) {
} else if (dataSourcesTable.getSelectedRowCount() == 1) { //if there is one file selected update the deatils to show the data for that file
//if no files were selected and only one data source is selected update the information to reflect the data source occurrencePanel = new OccurrencePanel(filesTableModel.getListOfNodesForFile(filesTable.convertRowIndexToModel(filesTable.getSelectedRow())));
String caseName = dataSourcesTableModel.getCaseNameForRow(dataSourcesTable.convertRowIndexToModel(dataSourcesTable.getSelectedRow())); } else if (dataSourcesTable.getSelectedRowCount() == 1) {
String dsName = dataSourcesTableModel.getValueAt(dataSourcesTable.convertRowIndexToModel(dataSourcesTable.getSelectedRow()), 0).toString(); //if no files were selected and only one data source is selected update the information to reflect the data source
String caseCreatedDate = ""; String caseName = dataSourcesTableModel.getCaseNameForRow(dataSourcesTable.convertRowIndexToModel(dataSourcesTable.getSelectedRow()));
for (int row : casesTable.getSelectedRows()) { String dsName = dataSourcesTableModel.getValueAt(dataSourcesTable.convertRowIndexToModel(dataSourcesTable.getSelectedRow()), 0).toString();
if (casesTableModel.getValueAt(casesTable.convertRowIndexToModel(row), 0).toString().equals(caseName)) { String caseCreatedDate = "";
caseCreatedDate = getCaseCreatedDate(row); for (int row : casesTable.getSelectedRows()) {
break; if (casesTableModel.getValueAt(casesTable.convertRowIndexToModel(row), 0).toString().equals(caseName)) {
caseCreatedDate = getCaseCreatedDate(row);
break;
}
}
occurrencePanel = new OccurrencePanel(caseName, caseCreatedDate, dsName);
} else if (casesTable.getSelectedRowCount() == 1) {
//if no files were selected and a number of data source other than 1 are selected
//update the information to reflect the case
String createdDate;
String caseName = "";
if (casesTable.getRowCount() > 0) {
caseName = casesTableModel.getValueAt(casesTable.convertRowIndexToModel(casesTable.getSelectedRow()), 0).toString();
}
if (caseName.isEmpty()) {
occurrencePanel = new OccurrencePanel();
} else {
createdDate = getCaseCreatedDate(casesTable.getSelectedRow());
occurrencePanel = new OccurrencePanel(caseName, createdDate);
} }
}
occurrencePanel = new OccurrencePanel(caseName, caseCreatedDate, dsName);
} else if (casesTable.getSelectedRowCount() == 1) {
//if no files were selected and a number of data source other than 1 are selected
//update the information to reflect the case
String createdDate;
String caseName = "";
if (casesTable.getRowCount() > 0) {
caseName = casesTableModel.getValueAt(casesTable.convertRowIndexToModel(casesTable.getSelectedRow()), 0).toString();
}
if (caseName.isEmpty()) {
occurrencePanel = new OccurrencePanel();
} else { } else {
createdDate = getCaseCreatedDate(casesTable.getSelectedRow()); //else display an empty details area
occurrencePanel = new OccurrencePanel(caseName, createdDate); occurrencePanel = new OccurrencePanel();
} }
} else { //calling getPreferredSize has a side effect of ensuring it has a preferred size which reflects the contents which are visible
//else display an empty details area occurrencePanel.getPreferredSize();
occurrencePanel = new OccurrencePanel(); detailsPanelScrollPane.setViewportView(occurrencePanel);
} finally {
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
} }
//calling getPreferredSize has a side effect of ensuring it has a preferred size which reflects the contents which are visible
occurrencePanel.getPreferredSize();
detailsPanelScrollPane.setViewportView(occurrencePanel);
} }
/** /**
@ -802,6 +582,95 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel {
return ""; return "";
} }
/**
* SwingWorker used by the case and data source selection handler.
*/
private class SelectionWorker extends SwingWorker<Map<UniquePathKey, NodeData>, Void> {
private final Collection<CorrelationAttributeInstance> coAtInstances;
private final AbstractFile abstractFile;
private final String deviceIdStr;
private final String dataSourceNameStr;
/**
* Construct a new SelectionWorker.
*
* @param coAtInstances
* @param abstractFile
* @param deviceIdStr
* @param dataSourceNameStr
*/
SelectionWorker(Collection<CorrelationAttributeInstance> coAtInstances, AbstractFile abstractFile, String deviceIdStr, String dataSourceNameStr) {
this.coAtInstances = coAtInstances;
this.abstractFile = abstractFile;
this.dataSourceNameStr = dataSourceNameStr;
this.deviceIdStr = deviceIdStr;
}
@Override
protected Map<UniquePathKey, NodeData> doInBackground() throws Exception {
Map<UniquePathKey, NodeData> correlatedNodeDataMap = new HashMap<>();
for (CorrelationAttributeInstance corAttr : coAtInstances) {
correlatedNodeDataMap.putAll(OtherOccurrences.getCorrelatedInstances(abstractFile, deviceIdStr, dataSourceNameStr, corAttr));
if(isCancelled()) {
return new HashMap<>();
}
}
return correlatedNodeDataMap;
}
}
/**
* SwingWorker for creating the CSV dump file.
*/
private class CSVWorker extends SwingWorker<Void, Void> {
private final Collection<CorrelationAttributeInstance> correlationAttList;
private final String dataSourceName;
private final String deviceId;
private final File destFile;
private final AbstractFile abstractFile;
/**
* Construct a CSVWorker
*
* @param destFile Output file.
* @param sourceFile Input file.
* @param dataSourceName Name of current dataSource.
* @param deviceId Id of the selected device.
* @param correlationAttList
*/
CSVWorker(File destFile, AbstractFile sourceFile, String dataSourceName, String deviceId, Collection<CorrelationAttributeInstance> correlationAttList) {
this.destFile = destFile;
this.abstractFile = sourceFile;
this.dataSourceName = dataSourceName;
this.deviceId = deviceId;
this.correlationAttList = correlationAttList;
}
@Override
protected Void doInBackground() throws Exception {
OtherOccurrences.writeOtherOccurrencesToFileAsCSV(this.destFile, this.abstractFile, this.correlationAttList, this.dataSourceName, this.deviceId);
return null;
}
@Override
public void done() {
try {
get();
} catch (InterruptedException | ExecutionException ex) {
JOptionPane.showMessageDialog(OtherOccurrencesPanel.this,
"Failed to create csv file for Other Occurrences at\n" + destFile.getAbsolutePath(),
"Error Creating CSV",
JOptionPane.ERROR_MESSAGE);
logger.log(Level.SEVERE, "Error writing selected rows to from OtherOccurrencePanel to " + destFile.getAbsolutePath(), ex);
}
}
}
/** /**
* This method is called from within the constructor to initialize the form. * This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always * WARNING: Do NOT modify this code. The content of this method is always
@ -976,9 +845,9 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel {
boolean enableCentralRepoActions = false; boolean enableCentralRepoActions = false;
if (CentralRepository.isEnabled() && filesTable.getSelectedRowCount() == 1) { if (CentralRepository.isEnabled() && filesTable.getSelectedRowCount() == 1) {
int rowIndex = filesTable.getSelectedRow(); int rowIndex = filesTable.getSelectedRow();
List<OtherOccurrenceNodeData> selectedFile = filesTableModel.getListOfNodesForFile(rowIndex); List<NodeData> selectedFile = filesTableModel.getListOfNodesForFile(rowIndex);
if (!selectedFile.isEmpty() && selectedFile.get(0) instanceof OtherOccurrenceNodeInstanceData) { if (!selectedFile.isEmpty() && selectedFile.get(0) instanceof NodeData) {
OtherOccurrenceNodeInstanceData instanceData = (OtherOccurrenceNodeInstanceData) selectedFile.get(0); NodeData instanceData = selectedFile.get(0);
enableCentralRepoActions = instanceData.isCentralRepoNode(); enableCentralRepoActions = instanceData.isCentralRepoNode();
} }
} }

View File

@ -37,7 +37,6 @@ import org.apache.commons.lang3.StringUtils;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeNormalizationException; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeNormalizationException;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeUtil; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeUtil;
@ -63,12 +62,9 @@ import org.sleuthkit.datamodel.Image;
import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.centralrepository.datamodel.Persona;
import org.sleuthkit.autopsy.centralrepository.datamodel.PersonaAccount;
import org.sleuthkit.datamodel.Account;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT;
import org.sleuthkit.datamodel.CommunicationsUtils;
import org.sleuthkit.datamodel.Score; import org.sleuthkit.datamodel.Score;
import org.sleuthkit.datamodel.Score.Priority;
import org.sleuthkit.datamodel.Score.Significance;
/** /**
* Listen for ingest events and update entries in the Central Repository * Listen for ingest events and update entries in the Central Repository
@ -76,7 +72,7 @@ import org.sleuthkit.datamodel.Score;
*/ */
@NbBundle.Messages({"IngestEventsListener.ingestmodule.name=Central Repository"}) @NbBundle.Messages({"IngestEventsListener.ingestmodule.name=Central Repository"})
public class IngestEventsListener { public class IngestEventsListener {
private static final Score LIKELY_NOTABLE_SCORE = new Score(Significance.LIKELY_NOTABLE, Priority.NORMAL);
private static final Logger LOGGER = Logger.getLogger(CorrelationAttributeInstance.class.getName()); private static final Logger LOGGER = Logger.getLogger(CorrelationAttributeInstance.class.getName());
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.DATA_SOURCE_ANALYSIS_COMPLETED); private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.DATA_SOURCE_ANALYSIS_COMPLETED);
private static final Set<IngestManager.IngestModuleEvent> INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(DATA_ADDED); private static final Set<IngestManager.IngestModuleEvent> INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(DATA_ADDED);
@ -216,17 +212,17 @@ public class IngestEventsListener {
@NbBundle.Messages({"IngestEventsListener.prevTaggedSet.text=Previously Tagged As Notable (Central Repository)", @NbBundle.Messages({"IngestEventsListener.prevTaggedSet.text=Previously Tagged As Notable (Central Repository)",
"IngestEventsListener.prevCaseComment.text=Previous Case: "}) "IngestEventsListener.prevCaseComment.text=Previous Case: "})
static private void makeAndPostPreviousNotableArtifact(BlackboardArtifact originalArtifact, List<String> caseDisplayNames) { static private void makeAndPostPreviousNotableArtifact(BlackboardArtifact originalArtifact, List<String> caseDisplayNames) {
Collection<BlackboardAttribute> attributesForNewArtifact = Arrays.asList(
Collection<BlackboardAttribute> attributesForNewArtifact = Arrays.asList(new BlackboardAttribute( new BlackboardAttribute(
TSK_SET_NAME, MODULE_NAME, TSK_SET_NAME, MODULE_NAME,
Bundle.IngestEventsListener_prevTaggedSet_text()), Bundle.IngestEventsListener_prevTaggedSet_text()),
new BlackboardAttribute( new BlackboardAttribute(
TSK_COMMENT, MODULE_NAME, TSK_COMMENT, MODULE_NAME,
Bundle.IngestEventsListener_prevCaseComment_text() + caseDisplayNames.stream().distinct().collect(Collectors.joining(","))), Bundle.IngestEventsListener_prevCaseComment_text() + caseDisplayNames.stream().distinct().collect(Collectors.joining(","))),
new BlackboardAttribute( new BlackboardAttribute(
TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, TSK_ASSOCIATED_ARTIFACT, MODULE_NAME,
originalArtifact.getArtifactID())); originalArtifact.getArtifactID()));
makeAndPostInterestingArtifact(originalArtifact, attributesForNewArtifact); makeAndPostInterestingArtifact(originalArtifact, attributesForNewArtifact, Bundle.IngestEventsListener_prevTaggedSet_text());
} }
/** /**
@ -251,26 +247,28 @@ public class IngestEventsListener {
new BlackboardAttribute( new BlackboardAttribute(
TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, TSK_ASSOCIATED_ARTIFACT, MODULE_NAME,
originalArtifact.getArtifactID())); originalArtifact.getArtifactID()));
makeAndPostInterestingArtifact(originalArtifact, attributesForNewArtifact); makeAndPostInterestingArtifact(originalArtifact, attributesForNewArtifact, Bundle.IngestEventsListener_prevExists_text());
} }
/** /**
* Make an interesting item artifact to flag the passed in artifact. * Make an interesting item artifact to flag the passed in artifact.
* *
* @param originalArtifact Artifact in current case we want to flag * @param originalArtifact Artifact in current case we want to flag
* @param attributesForNewArtifact Attributes to assign to the new * @param attributesForNewArtifact Attributes to assign to the new
* Interesting items artifact * Interesting items artifact
* @param configuration The configuration to be specified for the new interesting artifact hit
*/ */
private static void makeAndPostInterestingArtifact(BlackboardArtifact originalArtifact, Collection<BlackboardAttribute> attributesForNewArtifact) { private static void makeAndPostInterestingArtifact(BlackboardArtifact originalArtifact, Collection<BlackboardAttribute> attributesForNewArtifact, String configuration) {
try { try {
SleuthkitCase tskCase = originalArtifact.getSleuthkitCase(); SleuthkitCase tskCase = originalArtifact.getSleuthkitCase();
AbstractFile abstractFile = tskCase.getAbstractFileById(originalArtifact.getObjectID()); AbstractFile abstractFile = tskCase.getAbstractFileById(originalArtifact.getObjectID());
Blackboard blackboard = tskCase.getBlackboard(); Blackboard blackboard = tskCase.getBlackboard();
// Create artifact if it doesn't already exist. // Create artifact if it doesn't already exist.
if (!blackboard.artifactExists(abstractFile, TSK_INTERESTING_ARTIFACT_HIT, attributesForNewArtifact)) { if (!blackboard.artifactExists(abstractFile, TSK_INTERESTING_ARTIFACT_HIT, attributesForNewArtifact)) {
BlackboardArtifact newInterestingArtifact = abstractFile.newAnalysisResult( BlackboardArtifact newInterestingArtifact = abstractFile.newAnalysisResult(
new BlackboardArtifact.Type(TSK_INTERESTING_ARTIFACT_HIT), BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT, LIKELY_NOTABLE_SCORE,
Score.SCORE_UNKNOWN, null, null, null, attributesForNewArtifact) null, configuration, null, attributesForNewArtifact)
.getAnalysisResult(); .getAnalysisResult();
try { try {

View File

@ -67,7 +67,7 @@ import org.sleuthkit.datamodel.Score;
final class CentralRepoIngestModule implements FileIngestModule { final class CentralRepoIngestModule implements FileIngestModule {
private static final String MODULE_NAME = CentralRepoIngestModuleFactory.getModuleName(); private static final String MODULE_NAME = CentralRepoIngestModuleFactory.getModuleName();
private static final Score LIKELY_NOTABLE_SCORE = new Score(Score.Significance.LIKELY_NOTABLE, Score.Priority.NORMAL);
static final boolean DEFAULT_FLAG_TAGGED_NOTABLE_ITEMS = false; static final boolean DEFAULT_FLAG_TAGGED_NOTABLE_ITEMS = false;
static final boolean DEFAULT_FLAG_PREVIOUS_DEVICES = false; static final boolean DEFAULT_FLAG_PREVIOUS_DEVICES = false;
static final boolean DEFAULT_CREATE_CR_PROPERTIES = true; static final boolean DEFAULT_CREATE_CR_PROPERTIES = true;
@ -334,7 +334,6 @@ final class CentralRepoIngestModule implements FileIngestModule {
* @param caseDisplayNames Case names to be added to a TSK_COMMON attribute. * @param caseDisplayNames Case names to be added to a TSK_COMMON attribute.
*/ */
private void postCorrelatedBadFileToBlackboard(AbstractFile abstractFile, List<String> caseDisplayNames) { private void postCorrelatedBadFileToBlackboard(AbstractFile abstractFile, List<String> caseDisplayNames) {
Collection<BlackboardAttribute> attributes = Arrays.asList( Collection<BlackboardAttribute> attributes = Arrays.asList(
new BlackboardAttribute( new BlackboardAttribute(
TSK_SET_NAME, MODULE_NAME, TSK_SET_NAME, MODULE_NAME,
@ -347,8 +346,8 @@ final class CentralRepoIngestModule implements FileIngestModule {
// Create artifact if it doesn't already exist. // Create artifact if it doesn't already exist.
if (!blackboard.artifactExists(abstractFile, TSK_INTERESTING_FILE_HIT, attributes)) { if (!blackboard.artifactExists(abstractFile, TSK_INTERESTING_FILE_HIT, attributes)) {
BlackboardArtifact tifArtifact = abstractFile.newAnalysisResult( BlackboardArtifact tifArtifact = abstractFile.newAnalysisResult(
new BlackboardArtifact.Type(TSK_INTERESTING_FILE_HIT), BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT, LIKELY_NOTABLE_SCORE,
Score.SCORE_UNKNOWN, null, null, null, attributes) null, Bundle.CentralRepoIngestModule_prevTaggedSet_text(), null, attributes)
.getAnalysisResult(); .getAnalysisResult();
try { try {
// index the artifact for keyword search // index the artifact for keyword search

View File

@ -20,9 +20,12 @@ package org.sleuthkit.autopsy.communications.relationships;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.util.List; import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level; import java.util.logging.Level;
import javax.swing.SwingWorker;
import org.openide.explorer.ExplorerManager; import org.openide.explorer.ExplorerManager;
import org.openide.nodes.Node; import org.openide.nodes.Node;
import org.openide.util.Exceptions;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.contentviewers.artifactviewers.MessageArtifactViewer; import org.sleuthkit.autopsy.contentviewers.artifactviewers.MessageArtifactViewer;
@ -50,6 +53,8 @@ final class MessageDataContent extends MessageArtifactViewer implements DataCont
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
final private ExplorerManager explorerManager = new ExplorerManager(); final private ExplorerManager explorerManager = new ExplorerManager();
private ArtifactFetcher worker;
@Override @Override
public void propertyChange(final PropertyChangeEvent evt) { public void propertyChange(final PropertyChangeEvent evt) {
@ -63,11 +68,18 @@ final class MessageDataContent extends MessageArtifactViewer implements DataCont
@Override @Override
public void setNode(Node node) { public void setNode(Node node) {
BlackboardArtifact artifact = null; if(worker != null) {
if (node != null) { worker.cancel(true);
artifact = getNodeArtifact(node); worker = null;
} }
setArtifact(artifact);
if(node == null) {
resetComponent();
return;
}
worker = new ArtifactFetcher(node);
worker.execute();
} }
/** /**
@ -109,4 +121,30 @@ final class MessageDataContent extends MessageArtifactViewer implements DataCont
return nodeArtifact; return nodeArtifact;
} }
private class ArtifactFetcher extends SwingWorker<BlackboardArtifact, Void> {
private final Node node;
ArtifactFetcher(Node node) {
this.node = node;
}
@Override
protected BlackboardArtifact doInBackground() throws Exception {
return getNodeArtifact(node);
}
@Override
public void done() {
if(isCancelled()) {
return;
}
try {
setArtifact(get());
} catch (InterruptedException | ExecutionException ex) {
LOGGER.log(Level.SEVERE, "Failed to get node for artifact.", ex); //NON-NLS
}
}
}
} }

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2013-2018 Basis Technology Corp. * Copyright 2013-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -26,18 +26,18 @@ import java.util.logging.Level;
import javax.swing.SwingWorker; import javax.swing.SwingWorker;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.openide.nodes.Node; import org.openide.nodes.Node;
import org.openide.util.Exceptions;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.openide.util.lookup.ServiceProvider; import org.openide.util.lookup.ServiceProvider;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.TimeZoneUtils;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
import org.sleuthkit.datamodel.DataArtifact;
import org.sleuthkit.datamodel.DataSource; import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.Image; import org.sleuthkit.datamodel.Image;
import org.sleuthkit.datamodel.FsContent; import org.sleuthkit.datamodel.FsContent;
@ -49,7 +49,7 @@ import org.sleuthkit.datamodel.TskData.TSK_DB_FILES_TYPE_ENUM;
* shows the same data that can also be found in the ResultViewer table, just a * shows the same data that can also be found in the ResultViewer table, just a
* different order and allows the full path to be visible in the bottom area. * different order and allows the full path to be visible in the bottom area.
*/ */
@ServiceProvider(service = DataContentViewer.class, position = 6) @ServiceProvider(service = DataContentViewer.class, position = 4)
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
public class Metadata extends javax.swing.JPanel implements DataContentViewer { public class Metadata extends javax.swing.JPanel implements DataContentViewer {
@ -229,9 +229,23 @@ public class Metadata extends javax.swing.JPanel implements DataContentViewer {
@Override @Override
public String getTitle() { public String getTitle() {
return NbBundle.getMessage(this.getClass(), "Metadata.title"); return getTitle(null);
} }
@Messages({
"Metadata_dataArtifactTitle=Source File Metadata"
})
@Override
public String getTitle(Node node) {
if (node != null && !node.getLookup().lookupAll(DataArtifact.class).isEmpty()) {
return Bundle.Metadata_dataArtifactTitle();
} else {
return NbBundle.getMessage(this.getClass(), "Metadata.title");
}
}
@Override @Override
public String getToolTip() { public String getToolTip() {
return NbBundle.getMessage(this.getClass(), "Metadata.toolTip"); return NbBundle.getMessage(this.getClass(), "Metadata.toolTip");
@ -299,10 +313,10 @@ public class Metadata extends javax.swing.JPanel implements DataContentViewer {
addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.size"), Long.toString(file.getSize())); addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.size"), Long.toString(file.getSize()));
addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.fileNameAlloc"), file.getDirFlagAsString()); addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.fileNameAlloc"), file.getDirFlagAsString());
addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.metadataAlloc"), file.getMetaFlagsAsString()); addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.metadataAlloc"), file.getMetaFlagsAsString());
addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.modified"), ContentUtils.getStringTime(file.getMtime(), file)); addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.modified"), TimeZoneUtils.getFormattedTime(file.getMtime()));
addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.accessed"), ContentUtils.getStringTime(file.getAtime(), file)); addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.accessed"), TimeZoneUtils.getFormattedTime(file.getAtime()));
addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.created"), ContentUtils.getStringTime(file.getCrtime(), file)); addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.created"), TimeZoneUtils.getFormattedTime(file.getCrtime()));
addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.changed"), ContentUtils.getStringTime(file.getCtime(), file)); addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.changed"), TimeZoneUtils.getFormattedTime(file.getCtime()));
String md5 = file.getMd5Hash(); String md5 = file.getMd5Hash();
if (md5 == null) { if (md5 == null) {

View File

@ -32,11 +32,13 @@ import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.logging.Level; import java.util.logging.Level;
import javax.swing.JComboBox; import javax.swing.JComboBox;
import javax.swing.JFileChooser; import javax.swing.JFileChooser;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
import javax.swing.SwingWorker;
import javax.swing.filechooser.FileNameExtensionFilter; import javax.swing.filechooser.FileNameExtensionFilter;
import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.FilenameUtils;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
@ -71,6 +73,8 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
private int numRows; // num of rows in the selected table private int numRows; // num of rows in the selected table
private int currPage = 0; // curr page of rows being displayed private int currPage = 0; // curr page of rows being displayed
SwingWorker<?, ?> worker;
/** /**
* Constructs a file content viewer for SQLite database files. * Constructs a file content viewer for SQLite database files.
*/ */
@ -326,11 +330,17 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
@Override @Override
public void setFile(AbstractFile file) { public void setFile(AbstractFile file) {
WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); if (worker != null) {
sqliteDbFile = file; worker.cancel(true);
initReader(); worker = null;
processSQLiteFile(); }
WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); resetComponent();
if (file == null) {
return;
}
processSQLiteFile(file);
} }
@Override @Override
@ -344,10 +354,12 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
tablesDropdownList.removeAllItems(); tablesDropdownList.removeAllItems();
numEntriesField.setText(""); numEntriesField.setText("");
try { if(viewReader != null) {
viewReader.close(); try {
} catch (SQLiteTableReaderException ex) { viewReader.close();
//Could not successfully close the reader, nothing we can do to recover. } catch (SQLiteTableReaderException ex) {
//Could not successfully close the reader, nothing we can do to recover.
}
} }
row = new LinkedHashMap<>(); row = new LinkedHashMap<>();
pageOfTableRows = new ArrayList<>(); pageOfTableRows = new ArrayList<>();
@ -367,25 +379,40 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
"SQLiteViewer.errorMessage.failedToQueryDatabase=The database tables in the file could not be read.", "SQLiteViewer.errorMessage.failedToQueryDatabase=The database tables in the file could not be read.",
"SQLiteViewer.errorMessage.failedToinitJDBCDriver=The JDBC driver for SQLite could not be loaded.", "SQLiteViewer.errorMessage.failedToinitJDBCDriver=The JDBC driver for SQLite could not be loaded.",
"# {0} - exception message", "SQLiteViewer.errorMessage.unexpectedError=An unexpected error occurred:\n{0).",}) "# {0} - exception message", "SQLiteViewer.errorMessage.unexpectedError=An unexpected error occurred:\n{0).",})
private void processSQLiteFile() { private void processSQLiteFile(final AbstractFile file) {
try {
tablesDropdownList.removeAllItems();
Collection<String> dbTablesMap = viewReader.getTableNames(); WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
if (dbTablesMap.isEmpty()) { worker = new SQLiteViewerWorker(file) {
tablesDropdownList.addItem(Bundle.SQLiteViewer_comboBox_noTableEntry()); @Override
tablesDropdownList.setEnabled(false); public void done() {
} else { if (isCancelled()) {
dbTablesMap.forEach((tableName) -> { return;
tablesDropdownList.addItem(tableName); }
});
WorkerResults results;
try {
results = get();
sqliteDbFile = file;
viewReader = results.getReader();
tablesDropdownList.removeAllItems();
Collection<String> dbTablesMap = results.getDbTablesMap();
if (dbTablesMap.isEmpty()) {
tablesDropdownList.addItem(Bundle.SQLiteViewer_comboBox_noTableEntry());
tablesDropdownList.setEnabled(false);
} else {
dbTablesMap.forEach((tableName) -> {
tablesDropdownList.addItem(tableName);
});
}
WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
} catch (InterruptedException | ExecutionException ex) {
logger.log(Level.SEVERE, String.format("Failed to display SQL Viewer for file (%d)", file.getId()), ex);
}
} }
} catch (SQLiteTableReaderException ex) { };
logger.log(Level.WARNING, String.format("Unable to get table names "
+ "from sqlite file [%s] with id=[%d].", sqliteDbFile.getName(), worker.execute();
sqliteDbFile.getId(), ex.getMessage()));
MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_errorMessage_failedToQueryDatabase());
}
} }
@NbBundle.Messages({"# {0} - tableName", @NbBundle.Messages({"# {0} - tableName",
@ -455,8 +482,8 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
* data type. For our use, we want to define an action when encountering * data type. For our use, we want to define an action when encountering
* column names and an action for all other data types. * column names and an action for all other data types.
*/ */
private void initReader() { private SQLiteTableReader initReader(AbstractFile sqliteFile) {
viewReader = new SQLiteTableReader.Builder(sqliteDbFile) return new SQLiteTableReader.Builder(sqliteFile)
.forAllColumnNames((columnName) -> { .forAllColumnNames((columnName) -> {
currentTableHeader.add(columnName); currentTableHeader.add(columnName);
}) })
@ -506,21 +533,44 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
"SQLiteViewer.exportTableToCsv.TableName=Table name: " "SQLiteViewer.exportTableToCsv.TableName=Table name: "
}) })
private void exportTableToCsv(File file) { private void exportTableToCsv(File file) {
File csvFile = new File(file.toString() + ".csv"); final File csvFile = new File(file.toString() + ".csv");
String tableName = (String) this.tablesDropdownList.getSelectedItem(); final String tableName = (String) this.tablesDropdownList.getSelectedItem();
try (FileOutputStream out = new FileOutputStream(csvFile, false)) {
try (SQLiteTableReader sqliteStream = new SQLiteTableReader.Builder(sqliteDbFile) SwingWorker<String, Void> csvWorker = new SwingWorker<String, Void>() {
.forAllColumnNames(getColumnNameCSVStrategy(out)) @Override
.forAllTableValues(getForAllCSVStrategy(out)).build()) { protected String doInBackground() throws Exception {
totalColumnCount = sqliteStream.getColumnCount(tableName); try (FileOutputStream out = new FileOutputStream(csvFile, false)) {
sqliteStream.read(tableName); try (SQLiteTableReader sqliteStream = new SQLiteTableReader.Builder(sqliteDbFile)
.forAllColumnNames(getColumnNameCSVStrategy(out))
.forAllTableValues(getForAllCSVStrategy(out)).build()) {
totalColumnCount = sqliteStream.getColumnCount(tableName);
sqliteStream.read(tableName);
}
} catch (IOException | SQLiteTableReaderException | RuntimeException ex) {
logger.log(Level.WARNING, String.format("Failed to export table [%s]"
+ " to CSV in sqlite file '%s' (objId=%d)", tableName, sqliteDbFile.getName(),
sqliteDbFile.getId()), ex.getMessage()); //NON-NLS
return Bundle.SQLiteViewer_exportTableToCsv_write_errText();
}
return "";
} }
} catch (IOException | SQLiteTableReaderException | RuntimeException ex) {
logger.log(Level.WARNING, String.format("Failed to export table [%s]" @Override
+ " to CSV in sqlite file '%s' (objId=%d)", tableName, sqliteDbFile.getName(), public void done() {
sqliteDbFile.getId()), ex.getMessage()); //NON-NLS try {
MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_exportTableToCsv_write_errText()); String message = get();
} if (!message.isEmpty()) {
MessageNotifyUtil.Message.error(message);
}
} catch (InterruptedException | ExecutionException ex) {
logger.log(Level.SEVERE, "Failure occurred writing sql csv file.", ex);
}
}
};
csvWorker.execute();
} }
/** /**
@ -622,4 +672,49 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
public boolean isSupported(AbstractFile file) { public boolean isSupported(AbstractFile file) {
return true; return true;
} }
/**
* SwingWorker that will gather the data needed to display the given
* file in the SQL viewer.
*/
private class SQLiteViewerWorker extends SwingWorker<WorkerResults, Void> {
private final AbstractFile file;
SQLiteViewerWorker(AbstractFile file) {
this.file = file;
}
@Override
protected WorkerResults doInBackground() throws Exception {
SQLiteTableReader reader = initReader(file);
Collection<String> dbTablesMap = reader.getTableNames();
return new WorkerResults(reader, dbTablesMap);
}
}
/*
* Stores the data gather from the
*/
private class WorkerResults {
private final SQLiteTableReader reader;
private final Collection<String> dbTablesMap;
WorkerResults(SQLiteTableReader reader, Collection<String> dbTablesMap) {
this.reader = reader;
this.dbTablesMap = dbTablesMap;
}
SQLiteTableReader getReader() {
return reader;
}
Collection<String> getDbTablesMap() {
return dbTablesMap;
}
}
} }

View File

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<Properties>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[100, 58]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<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"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="scrollPane" alignment="0" pref="907" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="scrollPane" alignment="1" pref="435" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Container class="javax.swing.JScrollPane" name="scrollPane">
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JTextPane" name="textPanel">
<Properties>
<Property name="editable" type="boolean" value="false"/>
<Property name="background" type="java.awt.Color" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="DEFAULT_BACKGROUND" type="code"/>
</Property>
<Property name="name" type="java.lang.String" value="" noResource="true"/>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[600, 52]"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Form>

View File

@ -0,0 +1,266 @@
/*
* 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.contentviewers.analysisresults;
import java.awt.Color;
import java.text.MessageFormat;
import java.util.List;
import java.util.Optional;
import javax.swing.JLabel;
import javax.swing.text.html.HTMLEditorKit;
import org.apache.commons.lang3.tuple.Pair;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.contentviewers.analysisresults.AnalysisResultsViewModel.NodeResults;
import org.sleuthkit.autopsy.contentviewers.analysisresults.AnalysisResultsViewModel.ResultDisplayAttributes;
import org.sleuthkit.datamodel.AnalysisResult;
import org.sleuthkit.datamodel.Score;
/**
* Displays a list of analysis results in a panel.
*/
public class AnalysisResultsContentPanel extends javax.swing.JPanel {
private static final long serialVersionUID = 1L;
private static final String EMPTY_HTML = "<html><head></head><body></body></html>";
private static final String DEFAULT_FONT_FAMILY = new JLabel().getFont().getFamily();
private static final int DEFAULT_FONT_SIZE = new JLabel().getFont().getSize();
private static final Color DEFAULT_BACKGROUND = new JLabel().getBackground();
// html stylesheet classnames for components
private static final String ANALYSIS_RESULTS_CLASS_PREFIX = "analysisResult_";
private static final String SPACED_SECTION_CLASSNAME = ANALYSIS_RESULTS_CLASS_PREFIX + "spacedSection";
private static final String SUBSECTION_CLASSNAME = ANALYSIS_RESULTS_CLASS_PREFIX + "subsection";
private static final String HEADER_CLASSNAME = ANALYSIS_RESULTS_CLASS_PREFIX + "header";
public static final String MESSAGE_CLASSNAME = ANALYSIS_RESULTS_CLASS_PREFIX + "message";
public static final String TD_CLASSNAME = ANALYSIS_RESULTS_CLASS_PREFIX + "td";
// Anchors are inserted into the navigation so that the viewer can navigate to a selection.
// This is the prefix of those anchors.
private static final String RESULT_ANCHOR_PREFIX = "AnalysisResult_";
// how big the header should be
private static final int HEADER_FONT_SIZE = DEFAULT_FONT_SIZE + 2;
// spacing occurring after an item
private static final int DEFAULT_SECTION_SPACING = DEFAULT_FONT_SIZE / 2;
private static final int CELL_SPACING = DEFAULT_FONT_SIZE / 2;
// the subsection indent
private static final int DEFAULT_SUBSECTION_LEFT_PAD = DEFAULT_FONT_SIZE;
// additional styling for components
private static final String STYLE_SHEET_RULE
= String.format(" .%s { font-size: %dpt;font-style:italic; margin: 0px; padding: 0px; } ", MESSAGE_CLASSNAME, DEFAULT_FONT_SIZE)
+ String.format(" .%s { font-family: %s; font-size: %dpt; font-weight: bold; margin: 0px; padding: 0px; } ",
HEADER_CLASSNAME, DEFAULT_FONT_FAMILY, HEADER_FONT_SIZE)
+ String.format(" .%s { vertical-align: top; font-family: %s; font-size: %dpt; text-align: left; margin: 0pt; padding: 0px %dpt 0px 0px;} ",
TD_CLASSNAME, DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE, CELL_SPACING)
+ String.format(" .%s { margin-top: %dpt; } ", SPACED_SECTION_CLASSNAME, DEFAULT_SECTION_SPACING)
+ String.format(" .%s { padding-left: %dpt; }", SUBSECTION_CLASSNAME, DEFAULT_SUBSECTION_LEFT_PAD);
/**
* Creates new form AnalysisResultsContentViewer
*/
public AnalysisResultsContentPanel() {
initComponents();
textPanel.setContentType("text/html;charset=UTF-8"); //NON-NLS
HTMLEditorKit kit = new HTMLEditorKit();
textPanel.setEditorKit(kit);
kit.getStyleSheet().addRule(STYLE_SHEET_RULE);
}
/**
* Clears current text and shows text provided in the message.
*
* @param message The message to be displayed.
*/
void showMessage(String message) {
textPanel.setText("<html><head></head><body>"
+ MessageFormat.format("<p class='{0}'>{1}</p>",
MESSAGE_CLASSNAME,
message == null ? "" : message)
+ "</body></html>");
}
/**
* Resets the current view and displays nothing.
*/
void reset() {
textPanel.setText(EMPTY_HTML);
}
/**
* Displays analysis results for the node in the text pane.
*
* @param nodeResults The analysis results data to display.
*/
@NbBundle.Messages("AnalysisResultsContentPanel_aggregateScore_displayKey=Aggregate Score")
void displayResults(NodeResults nodeResults) {
Document document = Jsoup.parse(EMPTY_HTML);
Element body = document.getElementsByTag("body").first();
// if there is an aggregate score, append a section with the value
Optional<Score> aggregateScore = nodeResults.getAggregateScore();
if (aggregateScore.isPresent()) {
appendSection(body,
MessageFormat.format("{0}: {1}",
Bundle.AnalysisResultsContentPanel_aggregateScore_displayKey(),
aggregateScore.get().getSignificance().getDisplayName()),
Optional.empty());
}
// for each analysis result item, display the data.
List<ResultDisplayAttributes> displayAttributes = nodeResults.getAnalysisResults();
for (int idx = 0; idx < displayAttributes.size(); idx++) {
AnalysisResultsViewModel.ResultDisplayAttributes resultAttrs = displayAttributes.get(idx);
appendResult(body, idx, resultAttrs);
}
// set the body html
textPanel.setText(document.html());
// if there is a selected result scroll to it
Optional<AnalysisResult> selectedResult = nodeResults.getSelectedResult();
if (selectedResult.isPresent()) {
textPanel.scrollToReference(getAnchor(selectedResult.get()));
}
}
/**
* Returns the anchor id to use with the analysis result (based on the id).
* @param analysisResult The analysis result.
* @return The anchor id.
*/
private String getAnchor(AnalysisResult analysisResult) {
return RESULT_ANCHOR_PREFIX + analysisResult.getId();
}
/**
* Appends a result item to the parent element of an html document.
* @param parent The parent element.
* @param index The index of the item in the list of all items.
* @param attrs The attributes of this item.
*/
@NbBundle.Messages({"# {0} - analysisResultsNumber",
"AnalysisResultsContentPanel_result_headerKey=Analysis Result {0}"
})
private void appendResult(Element parent, int index, AnalysisResultsViewModel.ResultDisplayAttributes attrs) {
// create a new section with appropriate header
Element sectionDiv = appendSection(parent,
Bundle.AnalysisResultsContentPanel_result_headerKey(index + 1),
Optional.ofNullable(getAnchor(attrs.getAnalysisResult())));
// create a table
Element table = sectionDiv.appendElement("table");
table.attr("class", SUBSECTION_CLASSNAME);
Element tableBody = table.appendElement("tbody");
// append a row for each item
for (Pair<String, String> keyVal : attrs.getAttributesToDisplay()) {
Element row = tableBody.appendElement("tr");
String keyString = keyVal.getKey() == null ? "" : keyVal.getKey() + ":";
row.appendElement("td")
.text(keyString)
.attr("class", TD_CLASSNAME);
String valueString = keyVal.getValue() == null ? "" : keyVal.getValue();
row.appendElement("td")
.text(valueString)
.attr("class", TD_CLASSNAME);
}
}
/**
* Appends a new section with a section header to the parent element.
*
* @param parent The element to append this section to.
* @param headerText The text for the section.
* @param anchorId The anchor id for this section.
*
* @return The div for the new section.
*/
private Element appendSection(Element parent, String headerText, Optional<String> anchorId) {
Element sectionDiv = parent.appendElement("div");
// append an anchor tag if there is one
if (anchorId.isPresent()) {
Element anchorEl = sectionDiv.appendElement("a");
anchorEl.attr("name", anchorId.get());
}
// set the class for the section
sectionDiv.attr("class", SPACED_SECTION_CLASSNAME);
// append the header
Element header = sectionDiv.appendElement("h1");
header.text(headerText);
header.attr("class", HEADER_CLASSNAME);
// return the section element
return sectionDiv;
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
javax.swing.JScrollPane scrollPane = new javax.swing.JScrollPane();
textPanel = new javax.swing.JTextPane();
setPreferredSize(new java.awt.Dimension(100, 58));
textPanel.setEditable(false);
textPanel.setBackground(DEFAULT_BACKGROUND);
textPanel.setName(""); // NOI18N
textPanel.setPreferredSize(new java.awt.Dimension(600, 52));
scrollPane.setViewportView(textPanel);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(scrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 907, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(scrollPane, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 435, Short.MAX_VALUE)
);
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JTextPane textPanel;
// End of variables declaration//GEN-END:variables
}

View File

@ -0,0 +1,159 @@
/*
* 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.contentviewers.analysisresults;
import java.awt.Component;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.SwingWorker;
import org.openide.nodes.Node;
import org.openide.util.NbBundle;
import org.openide.util.lookup.ServiceProvider;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker;
import org.sleuthkit.datamodel.AnalysisResult;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Displays a list of analysis results as a content viewer.
*/
@ServiceProvider(service = DataContentViewer.class, position = 7)
public class AnalysisResultsContentViewer implements DataContentViewer {
private static final Logger logger = Logger.getLogger(AnalysisResultsContentPanel.class.getName());
// isPreferred value
private static final int PREFERRED_VALUE = 6;
private final AnalysisResultsViewModel viewModel = new AnalysisResultsViewModel();
private final AnalysisResultsContentPanel panel = new AnalysisResultsContentPanel();
private SwingWorker<?, ?> worker = null;
@NbBundle.Messages({
"AnalysisResultsContentViewer_title=Analysis Results"
})
@Override
public String getTitle() {
return Bundle.AnalysisResultsContentViewer_title();
}
@NbBundle.Messages({
"AnalysisResultsContentViewer_tooltip=Viewer for Analysis Results related to the selected node."
})
@Override
public String getToolTip() {
return Bundle.AnalysisResultsContentViewer_tooltip();
}
@Override
public DataContentViewer createInstance() {
return new AnalysisResultsContentViewer();
}
@Override
public Component getComponent() {
return panel;
}
@Override
public void resetComponent() {
panel.reset();
}
@Override
@NbBundle.Messages({
"AnalysisResultsContentViewer_setNode_loadingMessage=Loading...",
"AnalysisResultsContentViewer_setNode_errorMessage=There was an error loading results.",})
public synchronized void setNode(Node node) {
// reset the panel
panel.reset();
// if there is a worker running, cancel it
if (worker != null) {
worker.cancel(true);
worker = null;
}
// if no node, nothing to do
if (node == null) {
return;
}
// show a loading message
panel.showMessage(Bundle.AnalysisResultsContentViewer_setNode_loadingMessage());
// create the worker
worker = new DataFetchWorker<>(
// load a view model from the node
(selectedNode) -> viewModel.getAnalysisResults(selectedNode),
(nodeAnalysisResults) -> {
if (nodeAnalysisResults.getResultType() == DataFetchResult.ResultType.SUCCESS) {
// if successful, display the results
panel.displayResults(nodeAnalysisResults.getData());
} else {
// if there was an error, display an error message
panel.showMessage(Bundle.AnalysisResultsContentViewer_setNode_errorMessage());
}
},
node);
// kick off the swing worker
worker.execute();
}
@Override
public boolean isSupported(Node node) {
if (node == null) {
return false;
}
// There needs to either be a file with an AnalysisResult or an AnalysisResult in the lookup.
for (Content content : node.getLookup().lookupAll(Content.class)) {
if (content instanceof AnalysisResult) {
return true;
}
if (content == null || content instanceof BlackboardArtifact) {
continue;
}
try {
if (Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard().hasAnalysisResults(content.getId())) {
return true;
}
} catch (NoCurrentCaseException | TskCoreException ex) {
logger.log(Level.SEVERE, "Unable to get analysis results for file with obj id " + content.getId(), ex);
}
}
return false;
}
@Override
public int isPreferred(Node node) {
return PREFERRED_VALUE;
}
}

View File

@ -0,0 +1,278 @@
/*
* 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.contentviewers.analysisresults;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.lang3.tuple.Pair;
import org.openide.nodes.Node;
import org.openide.util.NbBundle;
import org.sleuthkit.datamodel.AnalysisResult;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.Score;
import org.sleuthkit.datamodel.TskCoreException;
/**
*
* Creates a representation of a list of analysis results gathered from a node.
*/
public class AnalysisResultsViewModel {
private static final Logger logger = Logger.getLogger(AnalysisResultsViewModel.class.getName());
/**
* The attributes to display for a particular Analysis Result.
*/
static class ResultDisplayAttributes {
private final AnalysisResult analysisResult;
private final List<Pair<String, String>> attributesToDisplay;
/**
* Constructor.
*
* @param analysisResult The analysis result which these attributes
* describe.
* @param attributesToDisplay The attributes to display in the order
* they should be displayed.
*/
ResultDisplayAttributes(AnalysisResult analysisResult, List<Pair<String, String>> attributesToDisplay) {
this.analysisResult = analysisResult;
this.attributesToDisplay = attributesToDisplay;
}
/**
* Returns the attributes to display.
*
* @return The attributes to display.
*/
List<Pair<String, String>> getAttributesToDisplay() {
return attributesToDisplay;
}
/**
* Returns the analysis result which these attributes describe.
*
* @return The analysis result.
*/
AnalysisResult getAnalysisResult() {
return analysisResult;
}
}
/**
* The analysis results relating to a node (i.e. belonging to source content
* or directly in the lookup) to be displayed.
*/
static class NodeResults {
private final List<ResultDisplayAttributes> analysisResults;
private final Optional<AnalysisResult> selectedResult;
private final Optional<Score> aggregateScore;
/**
* Constructor.
*
* @param analysisResults The analysis results to be displayed.
* @param selectedResult The selected analysis result or empty if none
* selected.
* @param aggregateScore The aggregate score or empty if no score.
*/
NodeResults(List<ResultDisplayAttributes> analysisResults, Optional<AnalysisResult> selectedResult, Optional<Score> aggregateScore) {
this.analysisResults = analysisResults;
this.selectedResult = selectedResult;
this.aggregateScore = aggregateScore;
}
/**
* Returns the analysis results to be displayed.
*
* @return The analysis results to be displayed.
*/
List<ResultDisplayAttributes> getAnalysisResults() {
return analysisResults;
}
/**
* Returns the selected analysis result or empty if none selected.
*
* @return The selected analysis result or empty if none selected.
*/
Optional<AnalysisResult> getSelectedResult() {
return selectedResult;
}
/**
* Returns the aggregate score or empty if no score.
*
* @return The aggregate score or empty if no score.
*/
Optional<Score> getAggregateScore() {
return aggregateScore;
}
}
/**
* Normalizes the value of an attribute of an analysis result for display
* purposes.
*
* @param originalAttrStr The original attribute value.
*
* @return The normalized value for display.
*/
private String normalizeAttr(String originalAttrStr) {
return (originalAttrStr == null) ? "" : originalAttrStr.trim();
}
/**
* Returns the attributes to be displayed for an analysis result.
*
* @param analysisResult The analysis result.
*
* @return The attributes to be displayed.
*/
@NbBundle.Messages({
"AnalysisResultsViewModel_displayAttributes_score=Score",
"AnalysisResultsViewModel_displayAttributes_type=Type",
"AnalysisResultsViewModel_displayAttributes_configuration=Configuration",
"AnalysisResultsViewModel_displayAttributes_conclusion=Conclusion"
})
private ResultDisplayAttributes getDisplayAttributes(AnalysisResult analysisResult) {
// The type of BlackboardArtifact.Type of the analysis result.
String type = "";
try {
type = normalizeAttr(analysisResult.getType().getDisplayName());
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Unable to get type for analysis result with id: " + analysisResult.getArtifactID(), ex);
}
// The standard attributes to display (score, type, configuration, conclusion)
Stream<Pair<String, String>> baseAnalysisAttrs = Stream.of(
Pair.of(Bundle.AnalysisResultsViewModel_displayAttributes_score(),
normalizeAttr(analysisResult.getScore().getSignificance().getDisplayName())),
Pair.of(Bundle.AnalysisResultsViewModel_displayAttributes_type(),
normalizeAttr(type)),
Pair.of(Bundle.AnalysisResultsViewModel_displayAttributes_configuration(),
normalizeAttr(analysisResult.getConfiguration())),
Pair.of(Bundle.AnalysisResultsViewModel_displayAttributes_conclusion(),
normalizeAttr(analysisResult.getConclusion()))
);
// The BlackboardAttributes sorted by type display name.
Stream<Pair<String, String>> blackboardAttributes = Stream.empty();
try {
blackboardAttributes = analysisResult.getAttributes().stream()
.filter(attr -> attr != null && attr.getAttributeType() != null && attr.getAttributeType().getDisplayName() != null)
.map(attr -> Pair.of(attr.getAttributeType().getDisplayName(), normalizeAttr(attr.getDisplayString())))
.sorted((a, b) -> a.getKey().compareToIgnoreCase(b.getKey()));
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Unable to get attributes for analysis result with id: " + analysisResult.getArtifactID(), ex);
}
// return the standard attributes along with the key value pairs of the BlackboardAttribute values.
List<Pair<String, String>> allDisplayAttributes = Stream.concat(baseAnalysisAttrs, blackboardAttributes)
.collect(Collectors.toList());
return new ResultDisplayAttributes(analysisResult, allDisplayAttributes);
}
private List<ResultDisplayAttributes> getOrderedDisplayAttributes(Collection<AnalysisResult> analysisResults) {
return analysisResults.stream()
.filter(ar -> ar != null && ar.getScore() != null)
// reverse order to push more important scores to the top
.sorted((a, b) -> -a.getScore().compareTo(b.getScore()))
.map((ar) -> getDisplayAttributes(ar))
.collect(Collectors.toList());
}
/**
* Returns the view model data representing the analysis results to be
* displayed for the node.
*
* @param node The node.
*
* @return The analysis results view model data to display.
*/
NodeResults getAnalysisResults(Node node) {
if (node == null) {
return new NodeResults(Collections.emptyList(), Optional.empty(), Optional.empty());
}
Optional<Score> aggregateScore = Optional.empty();
// maps id of analysis result to analysis result to prevent duplication
Map<Long, AnalysisResult> allAnalysisResults = new HashMap<>();
Optional<AnalysisResult> selectedResult = Optional.empty();
// Find first content that is not an artifact within node
for (Content content : node.getLookup().lookupAll(Content.class)) {
if (content == null || content instanceof BlackboardArtifact) {
continue;
}
try {
// get the aggregate score of that content
aggregateScore = Optional.ofNullable(content.getAggregateScore());
// and add all analysis results to mapping
content.getAllAnalysisResults().stream()
.forEach((ar) -> allAnalysisResults.put(ar.getArtifactID(), ar));
break;
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Unable to get analysis results for content with obj id " + content.getId(), ex);
}
}
// Find any analysis results in the node
Collection<? extends AnalysisResult> analysisResults = node.getLookup().lookupAll(AnalysisResult.class);
if (analysisResults.size() > 0) {
// get any items with a score
List<AnalysisResult> filteredResults = analysisResults.stream()
.collect(Collectors.toList());
// add them to the map to display
filteredResults.forEach((ar) -> allAnalysisResults.put(ar.getArtifactID(), ar));
// the selected result will be the highest scored analysis result in the node.
selectedResult = filteredResults.stream()
.max((a, b) -> a.getScore().compareTo(b.getScore()));
// if no aggregate score determined at this point, use the selected result score.
if (!aggregateScore.isPresent()) {
aggregateScore = selectedResult.flatMap(selectedRes -> Optional.ofNullable(selectedRes.getScore()));
}
}
// get view model representation
List<ResultDisplayAttributes> displayAttributes = getOrderedDisplayAttributes(allAnalysisResults.values());
return new NodeResults(displayAttributes, selectedResult, aggregateScore);
}
}

View File

@ -0,0 +1,11 @@
AnalysisResultsContentPanel_aggregateScore_displayKey=Aggregate Score
# {0} - analysisResultsNumber
AnalysisResultsContentPanel_result_headerKey=Analysis Result {0}
AnalysisResultsContentViewer_setNode_errorMessage=There was an error loading results.
AnalysisResultsContentViewer_setNode_loadingMessage=Loading...
AnalysisResultsContentViewer_title=Analysis Results
AnalysisResultsContentViewer_tooltip=Viewer for Analysis Results related to the selected node.
AnalysisResultsViewModel_displayAttributes_conclusion=Conclusion
AnalysisResultsViewModel_displayAttributes_configuration=Configuration
AnalysisResultsViewModel_displayAttributes_score=Score
AnalysisResultsViewModel_displayAttributes_type=Type

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2020 Basis Technology Corp. * Copyright 2011-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -41,7 +41,6 @@ import javax.swing.text.View;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
@ -54,6 +53,7 @@ import com.google.gson.JsonArray;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import org.sleuthkit.autopsy.coreutils.TimeZoneUtils;
import org.sleuthkit.autopsy.discovery.ui.AbstractArtifactDetailsPanel; import org.sleuthkit.autopsy.discovery.ui.AbstractArtifactDetailsPanel;
//import org.sleuthkit.autopsy.contentviewers.Bundle; //import org.sleuthkit.autopsy.contentviewers.Bundle;
@ -343,7 +343,7 @@ public class DefaultTableArtifactContentViewer extends AbstractArtifactDetailsPa
// Use Autopsy date formatting settings, not TSK defaults // Use Autopsy date formatting settings, not TSK defaults
case DATETIME: case DATETIME:
value = epochTimeToString(attr.getValueLong()); value = TimeZoneUtils.getFormattedTime(attr.getValueLong());
break; break;
case JSON: case JSON:
// Get the attribute's JSON value and convert to indented multiline display string // Get the attribute's JSON value and convert to indented multiline display string
@ -454,7 +454,7 @@ public class DefaultTableArtifactContentViewer extends AbstractArtifactDetailsPa
String attributeName = jsonKey; String attributeName = jsonKey;
String attributeValue; String attributeValue;
if (attributeName.toUpperCase().contains("DATETIME")) { if (attributeName.toUpperCase().contains("DATETIME")) {
attributeValue = epochTimeToString(Long.parseLong(jsonElement.getAsString())); attributeValue = TimeZoneUtils.getFormattedTime(Long.parseLong(jsonElement.getAsString()));
} else { } else {
attributeValue = jsonElement.getAsString(); attributeValue = jsonElement.getAsString();
} }
@ -463,23 +463,6 @@ public class DefaultTableArtifactContentViewer extends AbstractArtifactDetailsPa
sb.append(NEW_LINE).append(String.format("%s%s = null", startIndent, jsonKey)); sb.append(NEW_LINE).append(String.format("%s%s = null", startIndent, jsonKey));
} }
} }
/**
* Converts epoch time to readable string.
*
* @param epochTime epoch time value to be converted to string.
*
* @return String with human readable time.
*/
private String epochTimeToString(long epochTime) {
String dateTimeString = "0000-00-00 00:00:00";
if (null != content && 0 != epochTime) {
dateFormatter.setTimeZone(ContentUtils.getTimeZone(content));
dateTimeString = dateFormatter.format(new java.util.Date(epochTime * 1000));
}
return dateTimeString;
}
} }
/** /**

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy * Autopsy
* *
* Copyright 2020 Basis Technology Corp. * Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -44,11 +44,10 @@ import org.openide.util.NbBundle;
import org.openide.util.lookup.ServiceProvider; import org.openide.util.lookup.ServiceProvider;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.datamodel.ContentUtils; import org.sleuthkit.autopsy.coreutils.TimeZoneUtils;
import org.sleuthkit.autopsy.discovery.ui.AbstractArtifactDetailsPanel; import org.sleuthkit.autopsy.discovery.ui.AbstractArtifactDetailsPanel;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.TimeUtilities;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
/** /**
@ -263,9 +262,9 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i
for (BlackboardAttribute bba : attrList) { for (BlackboardAttribute bba : attrList) {
if (bba.getAttributeType().getTypeName().startsWith("TSK_DATETIME")) { if (bba.getAttributeType().getTypeName().startsWith("TSK_DATETIME")) {
if (artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_SEARCH_QUERY.getTypeID()) { if (artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_SEARCH_QUERY.getTypeID()) {
addNameValueRow(Bundle.GeneralPurposeArtifactViewer_dates_time(), TimeUtilities.epochToTime(bba.getValueLong(), ContentUtils.getTimeZone(artifact))); addNameValueRow(Bundle.GeneralPurposeArtifactViewer_dates_time(), TimeZoneUtils.getFormattedTime(bba.getValueLong()));
} else { } else {
addNameValueRow(bba.getAttributeType().getDisplayName(), TimeUtilities.epochToTime(bba.getValueLong(), ContentUtils.getTimeZone(artifact))); addNameValueRow(bba.getAttributeType().getDisplayName(), TimeZoneUtils.getFormattedTime(bba.getValueLong()));
} }
} else if (bba.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT.getTypeID() && artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_SEARCH_QUERY.getTypeID()) { } else if (bba.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT.getTypeID() && artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_SEARCH_QUERY.getTypeID()) {
addNameValueRow(Bundle.GeneralPurposeArtifactViewer_term_label(), bba.getDisplayString()); addNameValueRow(Bundle.GeneralPurposeArtifactViewer_term_label(), bba.getDisplayString());
@ -293,7 +292,7 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i
for (int key : attributeMap.keySet()) { for (int key : attributeMap.keySet()) {
for (BlackboardAttribute bba : attributeMap.get(key)) { for (BlackboardAttribute bba : attributeMap.get(key)) {
if (bba.getAttributeType().getTypeName().startsWith("TSK_DATETIME")) { if (bba.getAttributeType().getTypeName().startsWith("TSK_DATETIME")) {
addNameValueRow(bba.getAttributeType().getDisplayName(), TimeUtilities.epochToTime(bba.getValueLong(), ContentUtils.getTimeZone(artifact))); addNameValueRow(bba.getAttributeType().getDisplayName(), TimeZoneUtils.getFormattedTime(bba.getValueLong()));
} else { } else {
addNameValueRow(bba.getAttributeType().getDisplayName(), bba.getDisplayString()); addNameValueRow(bba.getAttributeType().getDisplayName(), bba.getDisplayString());
} }

View File

@ -22,18 +22,15 @@ import org.sleuthkit.autopsy.datamodel.AttachmentNode;
import java.awt.Color; import java.awt.Color;
import java.awt.Component; import java.awt.Component;
import java.awt.ComponentOrientation; import java.awt.ComponentOrientation;
import java.awt.Cursor;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level; import java.util.logging.Level;
import javax.swing.JScrollPane; import javax.swing.JScrollPane;
import javax.swing.text.JTextComponent; import javax.swing.text.JTextComponent;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.openide.explorer.ExplorerManager; import org.openide.explorer.ExplorerManager;
import org.openide.nodes.AbstractNode; import org.openide.nodes.AbstractNode;
import org.openide.nodes.Children; import org.openide.nodes.Children;
@ -44,13 +41,13 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.contentviewers.TranslatablePanel; import org.sleuthkit.autopsy.contentviewers.TranslatablePanel;
import org.sleuthkit.autopsy.contentviewers.TranslatablePanel.TranslatablePanelException; import org.sleuthkit.autopsy.contentviewers.TranslatablePanel.TranslatablePanelException;
import org.sleuthkit.autopsy.contentviewers.Utilities; import org.sleuthkit.autopsy.contentviewers.Utilities;
import org.sleuthkit.autopsy.contentviewers.artifactviewers.MessageArtifactWorker.MesssageArtifactData;
import org.sleuthkit.autopsy.corecomponents.AutoWrappingJTextPane; import org.sleuthkit.autopsy.corecomponents.AutoWrappingJTextPane;
import org.sleuthkit.autopsy.corecomponents.DataResultPanel; import org.sleuthkit.autopsy.corecomponents.DataResultPanel;
import org.sleuthkit.autopsy.corecomponents.TableFilterNode; import org.sleuthkit.autopsy.corecomponents.TableFilterNode;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.directorytree.DataResultFilterNode; import org.sleuthkit.autopsy.directorytree.DataResultFilterNode;
import org.sleuthkit.autopsy.directorytree.NewWindowViewAction; import org.sleuthkit.autopsy.directorytree.NewWindowViewAction;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG; import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT; import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT;
@ -71,13 +68,8 @@ import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHO
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO;
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SUBJECT; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SUBJECT;
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.blackboardutils.attributes.BlackboardJsonAttrUtil;
import org.sleuthkit.datamodel.blackboardutils.attributes.MessageAttachments;
import org.sleuthkit.datamodel.blackboardutils.attributes.MessageAttachments.FileAttachment;
import org.sleuthkit.datamodel.blackboardutils.attributes.MessageAttachments.Attachment; import org.sleuthkit.datamodel.blackboardutils.attributes.MessageAttachments.Attachment;
import org.sleuthkit.datamodel.blackboardutils.attributes.MessageAttachments.URLAttachment;
/** /**
* Shows SMS/MMS/EMail messages * Shows SMS/MMS/EMail messages
@ -141,6 +133,8 @@ public class MessageArtifactViewer extends javax.swing.JPanel implements Artifac
private ExplorerManager drpExplorerManager; private ExplorerManager drpExplorerManager;
private MessageAccountPanel accountsPanel; private MessageAccountPanel accountsPanel;
private MessageArtifactWorker worker;
public MessageArtifactViewer(List<JTextComponent> textAreas, DataResultPanel drp) { public MessageArtifactViewer(List<JTextComponent> textAreas, DataResultPanel drp) {
this.textAreas = textAreas; this.textAreas = textAreas;
@ -419,54 +413,47 @@ public class MessageArtifactViewer extends javax.swing.JPanel implements Artifac
@Override @Override
public void setArtifact(BlackboardArtifact artifact) { public void setArtifact(BlackboardArtifact artifact) {
this.artifact = artifact; resetComponent();
if (worker != null) {
worker.cancel(true);
worker = null;
}
if (artifact == null) { if (artifact == null) {
resetComponent();
return; return;
} }
/* worker = new MessageArtifactWorker(artifact) {
* If the artifact is a keyword hit, use the associated artifact as the @Override
* one to show in this viewer public void done() {
*/ if (isCancelled()) {
if (artifact.getArtifactTypeID() == TSK_KEYWORD_HIT.getTypeID()) { return;
try { }
getAssociatedArtifact(artifact).ifPresent(associatedArtifact -> {
this.artifact = associatedArtifact; try {
}); MesssageArtifactData data = get();
} catch (TskCoreException ex) { MessageArtifactViewer.this.artifact = data.getArtifact();
LOGGER.log(Level.SEVERE, "error getting associated artifact", ex); if (data.getArtifact().getArtifactTypeID() == TSK_MESSAGE.getTypeID()) {
displayMsg(data);
} else if (data.getArtifact().getArtifactTypeID() == TSK_EMAIL_MSG.getTypeID()) {
displayEmailMsg(data);
} else {
resetComponent();
}
msgbodyTabbedPane.setEnabledAt(ACCT_TAB_INDEX, true);
accountsPanel.setArtifact(data.getArtifact());
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
} catch (InterruptedException | ExecutionException ex) {
LOGGER.log(Level.SEVERE, String.format("Failed to update message viewer for artifact (%d)", artifact.getId(), ex));
}
} }
} };
if (this.artifact.getArtifactTypeID() == TSK_MESSAGE.getTypeID()) { setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
displayMsg(); worker.execute();
} else if (this.artifact.getArtifactTypeID() == TSK_EMAIL_MSG.getTypeID()) {
displayEmailMsg();
} else {
resetComponent();
}
msgbodyTabbedPane.setEnabledAt(ACCT_TAB_INDEX, true);
accountsPanel.setArtifact(artifact);
}
/**
* Get the artifact associated with the given artifact, if there is one.
*
* @param artifact The artifact to get the associated artifact from. Must
* not be null
*
* @throws TskCoreException If there is a critical error querying the DB.
* @return An optional containing the artifact associated with the given
* artifact, if there is one.
*/
private static Optional<BlackboardArtifact> getAssociatedArtifact(final BlackboardArtifact artifact) throws TskCoreException {
BlackboardAttribute attribute = artifact.getAttribute(TSK_ASSOCIATED_TYPE);
if (attribute != null) {
return Optional.of(artifact.getSleuthkitCase().getArtifactByArtifactId(attribute.getValueLong()));
}
return Optional.empty();
} }
@Override @Override
@ -474,7 +461,7 @@ public class MessageArtifactViewer extends javax.swing.JPanel implements Artifac
return this; return this;
} }
private void resetComponent() { public void resetComponent() {
// reset all fields // reset all fields
fromText.setText(""); fromText.setText("");
fromLabel.setEnabled(false); fromLabel.setEnabled(false);
@ -505,7 +492,7 @@ public class MessageArtifactViewer extends javax.swing.JPanel implements Artifac
//if the artifact is a keyword hit, check if its associated artifact is a message or email. //if the artifact is a keyword hit, check if its associated artifact is a message or email.
if (artifact.getArtifactTypeID() == TSK_KEYWORD_HIT.getTypeID()) { if (artifact.getArtifactTypeID() == TSK_KEYWORD_HIT.getTypeID()) {
try { try {
if (getAssociatedArtifact(artifact).map(MessageArtifactViewer::isMessageArtifact).orElse(false)) { if (MessageArtifactWorker.getAssociatedArtifact(artifact).map(MessageArtifactViewer::isMessageArtifact).orElse(false)) {
return true; return true;
} }
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
@ -534,27 +521,25 @@ public class MessageArtifactViewer extends javax.swing.JPanel implements Artifac
* Configure the text area at the given index to show the content of the * Configure the text area at the given index to show the content of the
* given type. * given type.
* *
* @param type The ATTRIBUT_TYPE to show in the indexed tab. * @param text text to show in the indexed tab.
* @param index The index of the text area to configure. * @param index The index of the text area to configure.
* *
* @throws TskCoreException * @throws TskCoreException
*/ */
private void configureTextArea(BlackboardAttribute.ATTRIBUTE_TYPE type, int index) throws TskCoreException { private void configureTextArea(String text, int index) {
String attributeText = getAttributeValueSafe(artifact, type); if (index == HTML_TAB_INDEX && StringUtils.isNotBlank(text)) {
htmlPanel.setHtmlText(text);
if (index == HTML_TAB_INDEX && StringUtils.isNotBlank(attributeText)) { } else if (index == TEXT_TAB_INDEX && StringUtils.isNotBlank(text)) {
htmlPanel.setHtmlText(attributeText); textPanel.setContent(text, artifact.toString());
} else if (index == TEXT_TAB_INDEX && StringUtils.isNotBlank(attributeText)) {
textPanel.setContent(attributeText, artifact.toString());
} else { } else {
JTextComponent textComponent = textAreas.get(index); JTextComponent textComponent = textAreas.get(index);
if (textComponent != null) { if (textComponent != null) {
textComponent.setText(attributeText); textComponent.setText(text);
textComponent.setCaretPosition(0); //make sure we start at the top textComponent.setCaretPosition(0); //make sure we start at the top
} }
} }
final boolean hasText = attributeText.length() > 0; final boolean hasText = text.length() > 0;
msgbodyTabbedPane.setEnabledAt(index, hasText); msgbodyTabbedPane.setEnabledAt(index, hasText);
if (hasText) { if (hasText) {
@ -570,38 +555,8 @@ public class MessageArtifactViewer extends javax.swing.JPanel implements Artifac
datetimeText.setEnabled(true); datetimeText.setEnabled(true);
} }
private void configureAttachments() throws TskCoreException { private void configureAttachments(Set<Attachment> attachments) {
int numberOfAttachments = attachments.size();
final Set<Attachment> attachments;
// Attachments are specified in an attribute TSK_ATTACHMENTS as JSON attribute
BlackboardAttribute attachmentsAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ATTACHMENTS));
if (attachmentsAttr != null) {
attachments = new HashSet<>();
try {
MessageAttachments msgAttachments = BlackboardJsonAttrUtil.fromAttribute(attachmentsAttr, MessageAttachments.class);
Collection<FileAttachment> fileAttachments = msgAttachments.getFileAttachments();
for (FileAttachment fileAttachment : fileAttachments) {
attachments.add(fileAttachment);
}
Collection<URLAttachment> urlAttachments = msgAttachments.getUrlAttachments();
for (URLAttachment urlAttachment : urlAttachments) {
attachments.add(urlAttachment);
}
} catch (BlackboardJsonAttrUtil.InvalidJsonException ex) {
LOGGER.log(Level.WARNING, String.format("Unable to parse json for MessageAttachments object in artifact: %s", artifact.getName()), ex);
}
} else { // For backward compatibility - email attachements are derived files and children of the email message artifact
attachments = new HashSet<>();
for (Content child : artifact.getChildren()) {
if (child instanceof AbstractFile) {
attachments.add(new FileAttachment((AbstractFile) child));
}
}
}
final int numberOfAttachments = attachments.size();
msgbodyTabbedPane.setEnabledAt(ATTM_TAB_INDEX, numberOfAttachments > 0); msgbodyTabbedPane.setEnabledAt(ATTM_TAB_INDEX, numberOfAttachments > 0);
msgbodyTabbedPane.setTitleAt(ATTM_TAB_INDEX, "Attachments (" + numberOfAttachments + ")"); msgbodyTabbedPane.setTitleAt(ATTM_TAB_INDEX, "Attachments (" + numberOfAttachments + ")");
@ -609,79 +564,48 @@ public class MessageArtifactViewer extends javax.swing.JPanel implements Artifac
new AttachmentsChildren(attachments))), true)); new AttachmentsChildren(attachments))), true));
} }
private void displayEmailMsg() { private void displayEmailMsg(MesssageArtifactData artifactData) {
enableCommonFields(); enableCommonFields();
directionText.setEnabled(false); directionText.setEnabled(false);
ccLabel.setEnabled(true); ccLabel.setEnabled(true);
try { this.fromText.setText(artifactData.getAttributeDisplayString( TSK_EMAIL_FROM));
this.fromText.setText(getAttributeValueSafe(artifact, TSK_EMAIL_FROM)); this.fromText.setToolTipText(artifactData.getAttributeDisplayString(TSK_EMAIL_FROM));
this.fromText.setToolTipText(getAttributeValueSafe(artifact, TSK_EMAIL_FROM)); this.toText.setText(artifactData.getAttributeDisplayString(TSK_EMAIL_TO));
this.toText.setText(getAttributeValueSafe(artifact, TSK_EMAIL_TO)); this.toText.setToolTipText(artifactData.getAttributeDisplayString(TSK_EMAIL_TO));
this.toText.setToolTipText(getAttributeValueSafe(artifact, TSK_EMAIL_TO)); this.directionText.setText("");
this.directionText.setText(""); this.ccText.setText(artifactData.getAttributeDisplayString(TSK_EMAIL_CC));
this.ccText.setText(getAttributeValueSafe(artifact, TSK_EMAIL_CC)); this.ccText.setToolTipText(artifactData.getAttributeDisplayString(TSK_EMAIL_CC));
this.ccText.setToolTipText(getAttributeValueSafe(artifact, TSK_EMAIL_CC)); this.subjectText.setText(artifactData.getAttributeDisplayString(TSK_SUBJECT));
this.subjectText.setText(getAttributeValueSafe(artifact, TSK_SUBJECT)); this.datetimeText.setText(artifactData.getAttributeDisplayString(TSK_DATETIME_RCVD));
this.datetimeText.setText(getAttributeValueSafe(artifact, TSK_DATETIME_RCVD));
configureTextArea(TSK_HEADERS, HDR_TAB_INDEX); configureTextArea(artifactData.getAttributeDisplayString(TSK_HEADERS), HDR_TAB_INDEX);
configureTextArea(TSK_EMAIL_CONTENT_PLAIN, TEXT_TAB_INDEX); configureTextArea(artifactData.getAttributeDisplayString(TSK_EMAIL_CONTENT_PLAIN), TEXT_TAB_INDEX);
configureTextArea(TSK_EMAIL_CONTENT_HTML, HTML_TAB_INDEX); configureTextArea(artifactData.getAttributeDisplayString(TSK_EMAIL_CONTENT_HTML), HTML_TAB_INDEX);
configureTextArea(TSK_EMAIL_CONTENT_RTF, RTF_TAB_INDEX); configureTextArea(artifactData.getAttributeDisplayString(TSK_EMAIL_CONTENT_RTF), RTF_TAB_INDEX);
configureAttachments(); configureAttachments(artifactData.getAttachements());
} catch (TskCoreException ex) {
LOGGER.log(Level.WARNING, "Failed to get attributes for email message.", ex); //NON-NLS
}
} }
private void displayMsg() { private void displayMsg(MesssageArtifactData artifactData) {
enableCommonFields(); enableCommonFields();
directionText.setEnabled(true); directionText.setEnabled(true);
ccLabel.setEnabled(false); ccLabel.setEnabled(false);
try { this.fromText.setText(artifactData.getAttributeDisplayString(TSK_PHONE_NUMBER_FROM));
this.fromText.setText(getAttributeValueSafe(artifact, TSK_PHONE_NUMBER_FROM)); this.toText.setText(artifactData.getAttributeDisplayString(TSK_PHONE_NUMBER_TO));
this.toText.setText(getAttributeValueSafe(artifact, TSK_PHONE_NUMBER_TO)); this.directionText.setText(artifactData.getAttributeDisplayString(TSK_DIRECTION));
this.directionText.setText(getAttributeValueSafe(artifact, TSK_DIRECTION)); this.ccText.setText("");
this.ccText.setText(""); this.subjectText.setText(artifactData.getAttributeDisplayString(TSK_SUBJECT));
this.subjectText.setText(getAttributeValueSafe(artifact, TSK_SUBJECT)); this.datetimeText.setText(artifactData.getAttributeDisplayString(TSK_DATETIME));
this.datetimeText.setText(getAttributeValueSafe(artifact, TSK_DATETIME));
msgbodyTabbedPane.setEnabledAt(HTML_TAB_INDEX, false); msgbodyTabbedPane.setEnabledAt(HTML_TAB_INDEX, false);
msgbodyTabbedPane.setEnabledAt(RTF_TAB_INDEX, false); msgbodyTabbedPane.setEnabledAt(RTF_TAB_INDEX, false);
msgbodyTabbedPane.setEnabledAt(HDR_TAB_INDEX, false); msgbodyTabbedPane.setEnabledAt(HDR_TAB_INDEX, false);
msgbodyTabbedPane.setEnabledAt(HDR_TAB_INDEX, false); msgbodyTabbedPane.setEnabledAt(HDR_TAB_INDEX, false);
configureTextArea(TSK_TEXT, TEXT_TAB_INDEX); configureTextArea(artifactData.getAttributeDisplayString(TSK_TEXT), TEXT_TAB_INDEX);
configureAttachments(); configureAttachments(artifactData.getAttachements());
} catch (TskCoreException ex) {
LOGGER.log(Level.WARNING, "Failed to get attributes for message.", ex); //NON-NLS
}
}
private static String getAttributeValueSafe(BlackboardArtifact artifact, BlackboardAttribute.ATTRIBUTE_TYPE type) throws TskCoreException {
return Optional.ofNullable(artifact.getAttribute(new BlackboardAttribute.Type(type)))
.map(BlackboardAttribute::getDisplayString)
.orElse("");
}
/**
* Cleans out input HTML string
*
* @param htmlInString The HTML string to cleanse
*
* @return The cleansed HTML String
*/
static private String cleanseHTML(String htmlInString) {
Document doc = Jsoup.parse(htmlInString);
//fix all img tags
doc.select("img[src]").forEach(img -> img.attr("src", ""));
return doc.html();
} }
/** /**

View File

@ -0,0 +1,201 @@
/*
* 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.contentviewers.artifactviewers;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.logging.Level;
import javax.swing.SwingWorker;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT;
import org.sleuthkit.datamodel.BlackboardAttribute;
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.blackboardutils.attributes.BlackboardJsonAttrUtil;
import org.sleuthkit.datamodel.blackboardutils.attributes.MessageAttachments;
/**
* SwingWork for gather the data from the DB needed for showing messages.
*
*/
class MessageArtifactWorker extends SwingWorker<MessageArtifactWorker.MesssageArtifactData, Void> {
private BlackboardArtifact artifact;
private static final Logger logger = Logger.getLogger(MessageArtifactWorker.class.getName());
private static final BlackboardAttribute.Type TSK_ASSOCIATED_TYPE = new BlackboardAttribute.Type(TSK_ASSOCIATED_ARTIFACT);
MessageArtifactWorker(BlackboardArtifact artifact) {
this.artifact = artifact;
}
@Override
protected MesssageArtifactData doInBackground() throws Exception {
/*
* If the artifact is a keyword hit, use the associated artifact as the
* one to show in this viewer
*/
if (artifact.getArtifactTypeID() == TSK_KEYWORD_HIT.getTypeID()) {
try {
getAssociatedArtifact(artifact).ifPresent(associatedArtifact -> {
this.artifact = associatedArtifact;
});
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "error getting associated artifact", ex);
}
}
Map<BlackboardAttribute.Type, BlackboardAttribute> map = getAttributesForArtifact(artifact);
Set<MessageAttachments.Attachment> attachements = getAttachments(artifact);
if (isCancelled()) {
return null;
}
return new MesssageArtifactData(artifact, map, attachements);
}
/**
* Returns a map containing all of the attributes for the given artifact.
*
* @param artifact Artifact to get the attributes for.
*
* @return A map of all the attributes available for the given artifact.
*
* @throws TskCoreException
*/
static private Map<BlackboardAttribute.Type, BlackboardAttribute> getAttributesForArtifact(BlackboardArtifact artifact) throws TskCoreException {
Map<BlackboardAttribute.Type, BlackboardAttribute> attributeMap = new HashMap<>();
for (BlackboardAttribute attribute : artifact.getAttributes()) {
attributeMap.put(attribute.getAttributeType(), attribute);
}
return attributeMap;
}
/**
* Returns the set of for the given artifact.
*
* @param art Message artifact to get attachements from.
*
* @return A set of attachments objects, or empty list if none were found;
*
* @throws TskCoreException
*/
private Set<MessageAttachments.Attachment> getAttachments(BlackboardArtifact art) throws TskCoreException {
final Set<MessageAttachments.Attachment> attachments = new HashSet<>();
// Attachments are specified in an attribute TSK_ATTACHMENTS as JSON attribute
BlackboardAttribute attachmentsAttr = art.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ATTACHMENTS));
if (attachmentsAttr != null) {
try {
MessageAttachments msgAttachments = BlackboardJsonAttrUtil.fromAttribute(attachmentsAttr, MessageAttachments.class);
Collection<MessageAttachments.FileAttachment> fileAttachments = msgAttachments.getFileAttachments();
for (MessageAttachments.FileAttachment fileAttachment : fileAttachments) {
attachments.add(fileAttachment);
}
Collection<MessageAttachments.URLAttachment> urlAttachments = msgAttachments.getUrlAttachments();
for (MessageAttachments.URLAttachment urlAttachment : urlAttachments) {
attachments.add(urlAttachment);
}
} catch (BlackboardJsonAttrUtil.InvalidJsonException ex) {
logger.log(Level.WARNING, String.format("Unable to parse json for MessageAttachments object in artifact: %s", art.getName()), ex);
}
} else { // For backward compatibility - email attachements are derived files and children of the email message artifact
for (Content child : art.getChildren()) {
if (child instanceof AbstractFile) {
attachments.add(new MessageAttachments.FileAttachment((AbstractFile) child));
}
}
}
return attachments;
}
/**
* Get the artifact associated with the given artifact, if there is one.
*
* @param artifact The artifact to get the associated artifact from. Must
* not be null
*
* @throws TskCoreException If there is a critical error querying the DB.
* @return An optional containing the artifact associated with the given
* artifact, if there is one.
*/
static Optional<BlackboardArtifact> getAssociatedArtifact(final BlackboardArtifact artifact) throws TskCoreException {
BlackboardAttribute attribute = artifact.getAttribute(TSK_ASSOCIATED_TYPE);
if (attribute != null) {
return Optional.of(artifact.getSleuthkitCase().getArtifactByArtifactId(attribute.getValueLong()));
}
return Optional.empty();
}
/**
* Object to store the data gathered by the worker thread.
*/
static class MesssageArtifactData {
private final BlackboardArtifact artifact;
private final Map<BlackboardAttribute.Type, BlackboardAttribute> attributeMap;
private final Set<MessageAttachments.Attachment> attachements;
MesssageArtifactData(BlackboardArtifact artifact, Map<BlackboardAttribute.Type, BlackboardAttribute> attributeMap, Set<MessageAttachments.Attachment> attachements) {
this.artifact = artifact;
this.attributeMap = attributeMap;
this.attachements = attachements;
}
BlackboardArtifact getArtifact() {
return artifact;
}
Map<BlackboardAttribute.Type, BlackboardAttribute> getAttributeMap() {
return attributeMap;
}
Set<MessageAttachments.Attachment> getAttachements() {
return attachements;
}
/**
* Returns the display string for the given attribute.
*
* @param attributeType Desired attribute.
*
* @return Display string for attribute or empty string if the attribute
* was not found.
*/
String getAttributeDisplayString(BlackboardAttribute.ATTRIBUTE_TYPE attributeType) {
BlackboardAttribute attribute = attributeMap.get(new BlackboardAttribute.Type(attributeType));
if (attribute != null) {
return attribute.getDisplayString();
}
return "";
}
}
}

View File

@ -37,7 +37,7 @@ import org.sleuthkit.datamodel.TskCoreException;
/** /**
* DataContentViewer for OsAccounts. * DataContentViewer for OsAccounts.
*/ */
@ServiceProvider(service = DataContentViewer.class, position = 7) @ServiceProvider(service = DataContentViewer.class, position = 5)
public class OsAccountViewer extends javax.swing.JPanel implements DataContentViewer { public class OsAccountViewer extends javax.swing.JPanel implements DataContentViewer {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;

View File

@ -3,13 +3,7 @@ Installer.closing.confirmationDialog.title=Ingest is Running
# {0} - exception message # {0} - exception message
Installer.closing.messageBox.caseCloseExceptionMessage=Error closing case: {0} Installer.closing.messageBox.caseCloseExceptionMessage=Error closing case: {0}
OpenIDE-Module-Display-Category=Infrastructure OpenIDE-Module-Display-Category=Infrastructure
OpenIDE-Module-Long-Description=\ OpenIDE-Module-Long-Description=This is the core Autopsy module.\n\nThe module contains the core components needed for the bare application to run; the RCP platform, windowing GUI, sleuthkit bindings, datamodel / storage, explorer, result viewers, content viewers, ingest framework, reporting, and core tools, such as the file search.\n\nThe framework included in the module contains APIs for developing modules for ingest, viewers and reporting. The modules can be deployed as Plugins using the Autopsy plugin installer.\nThis module should not be uninstalled - without it, Autopsy will not run.\n\nFor more information, see http://www.sleuthkit.org/autopsy/
This is the core Autopsy module.\n\n\
The module contains the core components needed for the bare application to run; the RCP platform, windowing GUI, sleuthkit bindings, datamodel / storage, explorer, result viewers, content viewers, ingest framework, reporting, and core tools, such as the file search.\n\n\
The framework included in the module contains APIs for developing modules for ingest, viewers and reporting. \
The modules can be deployed as Plugins using the Autopsy plugin installer.\n\
This module should not be uninstalled - without it, Autopsy will not run.\n\n\
For more information, see http://www.sleuthkit.org/autopsy/
OpenIDE-Module-Name=Autopsy-Core OpenIDE-Module-Name=Autopsy-Core
OpenIDE-Module-Short-Description=Autopsy Core Module OpenIDE-Module-Short-Description=Autopsy Core Module
org_sleuthkit_autopsy_core_update_center=http://sleuthkit.org/autopsy/updates.xml org_sleuthkit_autopsy_core_update_center=http://sleuthkit.org/autopsy/updates.xml

View File

@ -51,6 +51,16 @@ public interface DataContentViewer {
* *
*/ */
public String getTitle(); public String getTitle();
/**
* Returns the title of this viewer to display in the tab.
*
* @param node The node to be viewed in the DataContentViewer.
* @return the title of DataContentViewer.
*/
public default String getTitle(Node node) {
return getTitle();
}
/** /**
* Returns a short description of this viewer to use as a tool tip for its * Returns a short description of this viewer to use as a tool tip for its

View File

@ -23,13 +23,13 @@ INDEX_FOR_LOCAL_HELP=/docs/index.html
LBL_Close=Close LBL_Close=Close
DataContentViewerHex.copyMenuItem.text=Copy DataContentViewerHex.copyMenuItem.text=Copy
DataContentViewerHex.selectAllMenuItem.text=Select All DataContentViewerHex.selectAllMenuItem.text=Select All
DataContentViewerArtifact.totalPageLabel.text=100 DataArtifactContentViewer.totalPageLabel.text=100
DataContentViewerArtifact.prevPageButton.text= DataArtifactContentViewer.prevPageButton.text=
DataContentViewerArtifact.pageLabel2.text=Result DataArtifactContentViewer.pageLabel2.text=Result
DataContentViewerArtifact.nextPageButton.text= DataArtifactContentViewer.nextPageButton.text=
DataContentViewerArtifact.currentPageLabel.text=1 DataArtifactContentViewer.currentPageLabel.text=1
DataContentViewerArtifact.ofLabel.text=of DataArtifactContentViewer.ofLabel.text=of
DataContentViewerArtifact.pageLabel.text=Result: DataArtifactContentViewer.pageLabel.text=Result:
AdvancedConfigurationDialog.applyButton.text=OK AdvancedConfigurationDialog.applyButton.text=OK
DataContentViewerHex.goToPageTextField.text= DataContentViewerHex.goToPageTextField.text=
DataContentViewerHex.goToPageLabel.text=Go to Page: DataContentViewerHex.goToPageLabel.text=Go to Page:
@ -44,10 +44,10 @@ DataResultViewerThumbnail.filePathLabel.text=\ \ \
DataResultViewerThumbnail.goToPageLabel.text=Go to Page: DataResultViewerThumbnail.goToPageLabel.text=Go to Page:
DataResultViewerThumbnail.goToPageField.text= DataResultViewerThumbnail.goToPageField.text=
AdvancedConfigurationDialog.cancelButton.text=Cancel AdvancedConfigurationDialog.cancelButton.text=Cancel
DataContentViewerArtifact.waitText=Retrieving and preparing data, please wait... DataArtifactContentViewer.waitText=Retrieving and preparing data, please wait...
DataContentViewerArtifact.errorText=Error retrieving result DataArtifactContentViewer.errorText=Error retrieving result
DataContentViewerArtifact.title=Results DataArtifactContentViewer.title=Data Artifacts
DataContentViewerArtifact.toolTip=Displays Results associated with the file DataArtifactContentViewer.toolTip=Displays Results associated with the file
DataContentViewerHex.goToPageTextField.msgDlg=Please enter a valid page number between 1 and {0} DataContentViewerHex.goToPageTextField.msgDlg=Please enter a valid page number between 1 and {0}
DataContentViewerHex.goToPageTextField.err=Invalid page number DataContentViewerHex.goToPageTextField.err=Invalid page number
DataContentViewerHex.setDataView.errorText=(offset {0}-{1} could not be read) DataContentViewerHex.setDataView.errorText=(offset {0}-{1} could not be read)

View File

@ -30,8 +30,8 @@ CTL_DataContentAction=DataContent
CTL_DataContentTopComponent=Data Content CTL_DataContentTopComponent=Data Content
CTL_OfflineHelpAction=Offline Autopsy Documentation CTL_OfflineHelpAction=Offline Autopsy Documentation
CTL_OnlineHelpAction=Online Autopsy Documentation CTL_OnlineHelpAction=Online Autopsy Documentation
DataContentViewerArtifact.failedToGetAttributes.message=Failed to get some or all attributes from case database DataArtifactContentViewer.failedToGetAttributes.message=Failed to get some or all attributes from case database
DataContentViewerArtifact.failedToGetSourcePath.message=Failed to get source file path from case database DataArtifactContentViewer.failedToGetSourcePath.message=Failed to get source file path from case database
DataContentViewerHex.copyingFile=Copying file to open in HxD... DataContentViewerHex.copyingFile=Copying file to open in HxD...
DataContentViewerHex.launchError=Unable to launch HxD Editor. Please specify the HxD install location in Tools -> Options -> External Viewer DataContentViewerHex.launchError=Unable to launch HxD Editor. Please specify the HxD install location in Tools -> Options -> External Viewer
DataContentViewerHex_loading_text=Loading hex from file... DataContentViewerHex_loading_text=Loading hex from file...
@ -75,9 +75,9 @@ DataContentViewerHex.totalPageLabel.text_1=100
DataContentViewerHex.pageLabel2.text=Page DataContentViewerHex.pageLabel2.text=Page
# Product Information panel # Product Information panel
LBL_Description=<div style=\"font-size: 12pt; font-family: Verdana, 'Verdana CE', Arial, 'Arial CE', 'Lucida Grande CE', lucida, 'Helvetica CE', sans-serif;\">\n <b>Product Version:</b> {0} ({9}) <br><b>Sleuth Kit Version:</b> {7} <br><b>Netbeans RCP Build:</b> {8} <br> <b>Java:</b> {1}; {2}<br> <b>System:</b> {3}; {4}; {5}<br><b>Userdir:</b> {6}</div> LBL_Description=<div style="font-size: 12pt; font-family: Verdana, 'Verdana CE', Arial, 'Arial CE', 'Lucida Grande CE', lucida, 'Helvetica CE', sans-serif;">\n <b>Product Version:</b> {0} ({9}) <br><b>Sleuth Kit Version:</b> {7} <br><b>Netbeans RCP Build:</b> {8} <br> <b>Java:</b> {1}; {2}<br> <b>System:</b> {3}; {4}; {5}<br><b>Userdir:</b> {6}</div>
Format_OperatingSystem_Value={0} version {1} running on {2} Format_OperatingSystem_Value={0} version {1} running on {2}
LBL_Copyright=<div style\="font-size: 12pt; font-family: Verdana, 'Verdana CE', Arial, 'Arial CE', 'Lucida Grande CE', lucida, 'Helvetica CE', sans-serif; ">Autopsy&trade; is a digital forensics platform based on The Sleuth Kit&trade; and other tools. <br><ul><li>General Information: <a style\="color: \#1E2A60;" href\="http://www.sleuthkit.org">http://www.sleuthkit.org</a>.</li><li>Training: <a style\="color: \#1E2A60;" href\="https://www.autopsy.com/support/training/">https://www.autopsy.com/support/training/</a></li><li>Support: <a style\="color: \#1E2A60;" href\="https://www.sleuthkit.org/support.php">https://www.sleuthkit.org/support.php</a></li></ul>Copyright &copy; 2003-2020. </div> LBL_Copyright=<div style="font-size: 12pt; font-family: Verdana, 'Verdana CE', Arial, 'Arial CE', 'Lucida Grande CE', lucida, 'Helvetica CE', sans-serif; ">Autopsy&trade; is a digital forensics platform based on The Sleuth Kit&trade; and other tools. <br><ul><li>General Information: <a style="color: #1E2A60;" href="http://www.sleuthkit.org">http://www.sleuthkit.org</a>.</li><li>Training: <a style="color: #1E2A60;" href="https://www.autopsy.com/support/training/">https://www.autopsy.com/support/training/</a></li><li>Support: <a style="color: #1E2A60;" href="https://www.sleuthkit.org/support.php">https://www.sleuthkit.org/support.php</a></li></ul>Copyright &copy; 2003-2020. </div>
SortChooser.dialogTitle=Choose Sort Criteria SortChooser.dialogTitle=Choose Sort Criteria
ThumbnailViewChildren.progress.cancelling=(Cancelling) ThumbnailViewChildren.progress.cancelling=(Cancelling)
# {0} - file name # {0} - file name
@ -88,13 +88,13 @@ INDEX_FOR_LOCAL_HELP=/docs/index.html
LBL_Close=Close LBL_Close=Close
DataContentViewerHex.copyMenuItem.text=Copy DataContentViewerHex.copyMenuItem.text=Copy
DataContentViewerHex.selectAllMenuItem.text=Select All DataContentViewerHex.selectAllMenuItem.text=Select All
DataContentViewerArtifact.totalPageLabel.text=100 DataArtifactContentViewer.totalPageLabel.text=100
DataContentViewerArtifact.prevPageButton.text= DataArtifactContentViewer.prevPageButton.text=
DataContentViewerArtifact.pageLabel2.text=Result DataArtifactContentViewer.pageLabel2.text=Result
DataContentViewerArtifact.nextPageButton.text= DataArtifactContentViewer.nextPageButton.text=
DataContentViewerArtifact.currentPageLabel.text=1 DataArtifactContentViewer.currentPageLabel.text=1
DataContentViewerArtifact.ofLabel.text=of DataArtifactContentViewer.ofLabel.text=of
DataContentViewerArtifact.pageLabel.text=Result: DataArtifactContentViewer.pageLabel.text=Result:
AdvancedConfigurationDialog.applyButton.text=OK AdvancedConfigurationDialog.applyButton.text=OK
DataContentViewerHex.goToPageTextField.text= DataContentViewerHex.goToPageTextField.text=
DataContentViewerHex.goToPageLabel.text=Go to Page: DataContentViewerHex.goToPageLabel.text=Go to Page:
@ -105,14 +105,14 @@ DataResultViewerThumbnail.pageNextButton.text=
DataResultViewerThumbnail.imagesLabel.text=Images: DataResultViewerThumbnail.imagesLabel.text=Images:
DataResultViewerThumbnail.imagesRangeLabel.text=- DataResultViewerThumbnail.imagesRangeLabel.text=-
DataResultViewerThumbnail.pageNumLabel.text=- DataResultViewerThumbnail.pageNumLabel.text=-
DataResultViewerThumbnail.filePathLabel.text=\ \ \ DataResultViewerThumbnail.filePathLabel.text=\
DataResultViewerThumbnail.goToPageLabel.text=Go to Page: DataResultViewerThumbnail.goToPageLabel.text=Go to Page:
DataResultViewerThumbnail.goToPageField.text= DataResultViewerThumbnail.goToPageField.text=
AdvancedConfigurationDialog.cancelButton.text=Cancel AdvancedConfigurationDialog.cancelButton.text=Cancel
DataContentViewerArtifact.waitText=Retrieving and preparing data, please wait... DataArtifactContentViewer.waitText=Retrieving and preparing data, please wait...
DataContentViewerArtifact.errorText=Error retrieving result DataArtifactContentViewer.errorText=Error retrieving result
DataContentViewerArtifact.title=Results DataArtifactContentViewer.title=Data Artifacts
DataContentViewerArtifact.toolTip=Displays Results associated with the file DataArtifactContentViewer.toolTip=Displays Results associated with the file
DataContentViewerHex.goToPageTextField.msgDlg=Please enter a valid page number between 1 and {0} DataContentViewerHex.goToPageTextField.msgDlg=Please enter a valid page number between 1 and {0}
DataContentViewerHex.goToPageTextField.err=Invalid page number DataContentViewerHex.goToPageTextField.err=Invalid page number
DataContentViewerHex.setDataView.errorText=(offset {0}-{1} could not be read) DataContentViewerHex.setDataView.errorText=(offset {0}-{1} could not be read)

View File

@ -59,19 +59,19 @@ CTL_OnlineHelpAction=Autopsy\u30aa\u30f3\u30e9\u30a4\u30f3\u30c9\u30ad\u30e5\u30
CriterionChooser.ascendingRadio.text=\u25b2 \u6607\u9806\n CriterionChooser.ascendingRadio.text=\u25b2 \u6607\u9806\n
CriterionChooser.descendingRadio.text=\u25bc \u964d\u9806 CriterionChooser.descendingRadio.text=\u25bc \u964d\u9806
CriterionChooser.removeButton.text=\u524a\u9664 CriterionChooser.removeButton.text=\u524a\u9664
DataContentViewerArtifact.currentPageLabel.text=1 DataArtifactContentViewer.currentPageLabel.text=1
DataContentViewerArtifact.errorText=\u7d50\u679c\u306e\u691c\u7d22\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f DataArtifactContentViewer.errorText=\u7d50\u679c\u306e\u691c\u7d22\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f
DataContentViewerArtifact.failedToGetAttributes.message=\u30b1\u30fc\u30b9\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u304b\u3089\u4e00\u90e8\u307e\u305f\u306f\u3059\u3079\u3066\u306e\u5c5e\u6027\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f DataArtifactContentViewer.failedToGetAttributes.message=\u30b1\u30fc\u30b9\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u304b\u3089\u4e00\u90e8\u307e\u305f\u306f\u3059\u3079\u3066\u306e\u5c5e\u6027\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f
DataContentViewerArtifact.failedToGetSourcePath.message=\u30b1\u30fc\u30b9\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u304b\u3089\u30bd\u30fc\u30b9\u30d5\u30a1\u30a4\u30eb\u30d1\u30b9\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f DataArtifactContentViewer.failedToGetSourcePath.message=\u30b1\u30fc\u30b9\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u304b\u3089\u30bd\u30fc\u30b9\u30d5\u30a1\u30a4\u30eb\u30d1\u30b9\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f
DataContentViewerArtifact.nextPageButton.text= DataArtifactContentViewer.nextPageButton.text=
DataContentViewerArtifact.ofLabel.text=/ DataArtifactContentViewer.ofLabel.text=/
DataContentViewerArtifact.pageLabel.text=\u7d50\u679c\: DataArtifactContentViewer.pageLabel.text=\u7d50\u679c\:
DataContentViewerArtifact.pageLabel2.text=\u7d50\u679c DataArtifactContentViewer.pageLabel2.text=\u7d50\u679c
DataContentViewerArtifact.prevPageButton.text= DataArtifactContentViewer.prevPageButton.text=
DataContentViewerArtifact.title=\u7d50\u679c DataArtifactContentViewer.title=\u7d50\u679c
DataContentViewerArtifact.toolTip=\u30d5\u30a1\u30a4\u30eb\u3068\u95a2\u9023\u4ed8\u3051\u3089\u308c\u3066\u3044\u308b\u7d50\u679c\u3092\u8868\u793a DataArtifactContentViewer.toolTip=\u30d5\u30a1\u30a4\u30eb\u3068\u95a2\u9023\u4ed8\u3051\u3089\u308c\u3066\u3044\u308b\u7d50\u679c\u3092\u8868\u793a
DataContentViewerArtifact.totalPageLabel.text=100 DataArtifactContentViewer.totalPageLabel.text=100
DataContentViewerArtifact.waitText=\u30c7\u30fc\u30bf\u3092\u691c\u7d22\u3057\u3066\u6e96\u5099\u4e2d\u3067\u3059\u3002\u304a\u5f85\u3061\u304f\u3060\u3055\u3044... DataArtifactContentViewer.waitText=\u30c7\u30fc\u30bf\u3092\u691c\u7d22\u3057\u3066\u6e96\u5099\u4e2d\u3067\u3059\u3002\u304a\u5f85\u3061\u304f\u3060\u3055\u3044...
DataContentViewerHex.copyMenuItem.text=\u30b3\u30d4\u30fc DataContentViewerHex.copyMenuItem.text=\u30b3\u30d4\u30fc
DataContentViewerHex.copyingFile=HxD\u3067\u958b\u304f\u30d5\u30a1\u30a4\u30eb\u3092\u30b3\u30d4\u30fc\u4e2d\u3067\u3059... DataContentViewerHex.copyingFile=HxD\u3067\u958b\u304f\u30d5\u30a1\u30a4\u30eb\u3092\u30b3\u30d4\u30fc\u4e2d\u3067\u3059...
DataContentViewerHex.currentPageLabel.text_1=1 DataContentViewerHex.currentPageLabel.text_1=1

View File

@ -2,8 +2,11 @@
<Form version="1.9" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo"> <Form version="1.9" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<Properties> <Properties>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[300, 60]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[100, 58]"/> <Dimension value="[300, 60]"/>
</Property> </Property>
</Properties> </Properties>
<AuxValues> <AuxValues>
@ -21,33 +24,42 @@
<Layout> <Layout>
<DimensionLayout dim="0"> <DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Component id="jScrollPane1" pref="561" max="32767" attributes="0"/> <Component id="scrollPane" pref="300" max="32767" attributes="0"/>
<Component id="artifactContentPanel" alignment="0" max="32767" attributes="0"/> <Component id="artifactContentPanel" alignment="0" max="32767" attributes="0"/>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
<DimensionLayout dim="1"> <DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0">
<Component id="jScrollPane1" min="-2" pref="24" max="-2" attributes="0"/> <Component id="scrollPane" min="-2" pref="24" max="-2" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/> <EmptySpace min="-2" max="-2" attributes="0"/>
<Component id="artifactContentPanel" pref="397" max="32767" attributes="0"/> <Component id="artifactContentPanel" pref="30" max="32767" attributes="0"/>
</Group> </Group>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
</Layout> </Layout>
<SubComponents> <SubComponents>
<Container class="javax.swing.JScrollPane" name="jScrollPane1"> <Container class="javax.swing.JScrollPane" name="scrollPane">
<Properties> <Properties>
<Property name="horizontalScrollBarPolicy" type="int" value="31"/> <Property name="horizontalScrollBarPolicy" type="int" value="31"/>
<Property name="verticalScrollBarPolicy" type="int" value="21"/> <Property name="verticalScrollBarPolicy" type="int" value="21"/>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[6, 60]"/>
</Property>
</Properties> </Properties>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/> <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents> <SubComponents>
<Container class="javax.swing.JPanel" name="jPanel1"> <Container class="javax.swing.JPanel" name="menuBar">
<Properties> <Properties>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="null"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="null"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[620, 58]"/> <Dimension value="null"/>
</Property> </Property>
</Properties> </Properties>
@ -56,13 +68,13 @@
<Component class="javax.swing.JLabel" name="totalPageLabel"> <Component class="javax.swing.JLabel" name="totalPageLabel">
<Properties> <Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataContentViewerArtifact.totalPageLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataArtifactContentViewer.totalPageLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> <Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[40, 16]"/> <Dimension value="null"/>
</Property> </Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[25, 16]"/> <Dimension value="null"/>
</Property> </Property>
</Properties> </Properties>
<Constraints> <Constraints>
@ -74,7 +86,7 @@
<Component class="javax.swing.JLabel" name="ofLabel"> <Component class="javax.swing.JLabel" name="ofLabel">
<Properties> <Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataContentViewerArtifact.ofLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataArtifactContentViewer.ofLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
<Constraints> <Constraints>
@ -86,28 +98,25 @@
<Component class="javax.swing.JLabel" name="currentPageLabel"> <Component class="javax.swing.JLabel" name="currentPageLabel">
<Properties> <Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataContentViewerArtifact.currentPageLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataArtifactContentViewer.currentPageLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> <Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[38, 14]"/> <Dimension value="null"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[18, 14]"/>
</Property> </Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[20, 14]"/> <Dimension value="null"/>
</Property> </Property>
</Properties> </Properties>
<Constraints> <Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription"> <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="1" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="4" insetsLeft="7" insetsBottom="0" insetsRight="0" anchor="18" weightX="0.0" weightY="0.0"/> <GridBagConstraints gridX="1" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="3" insetsLeft="7" insetsBottom="0" insetsRight="0" anchor="18" weightX="0.0" weightY="0.0"/>
</Constraint> </Constraint>
</Constraints> </Constraints>
</Component> </Component>
<Component class="javax.swing.JLabel" name="pageLabel"> <Component class="javax.swing.JLabel" name="pageLabel">
<Properties> <Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataContentViewerArtifact.pageLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataArtifactContentViewer.pageLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
<Constraints> <Constraints>
@ -122,7 +131,7 @@
<Image iconType="3" name="/org/sleuthkit/autopsy/corecomponents/btn_step_forward.png"/> <Image iconType="3" name="/org/sleuthkit/autopsy/corecomponents/btn_step_forward.png"/>
</Property> </Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataContentViewerArtifact.nextPageButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataArtifactContentViewer.nextPageButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
<Property name="borderPainted" type="boolean" value="false"/> <Property name="borderPainted" type="boolean" value="false"/>
<Property name="contentAreaFilled" type="boolean" value="false"/> <Property name="contentAreaFilled" type="boolean" value="false"/>
@ -151,7 +160,7 @@
<Component class="javax.swing.JLabel" name="pageLabel2"> <Component class="javax.swing.JLabel" name="pageLabel2">
<Properties> <Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataContentViewerArtifact.pageLabel2.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataArtifactContentViewer.pageLabel2.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> <Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[29, 14]"/> <Dimension value="[29, 14]"/>
@ -162,7 +171,7 @@
</Properties> </Properties>
<Constraints> <Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription"> <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="4" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="3" insetsLeft="41" insetsBottom="0" insetsRight="0" anchor="18" weightX="0.0" weightY="0.0"/> <GridBagConstraints gridX="4" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="3" insetsLeft="30" insetsBottom="0" insetsRight="0" anchor="18" weightX="0.0" weightY="0.0"/>
</Constraint> </Constraint>
</Constraints> </Constraints>
</Component> </Component>
@ -172,7 +181,7 @@
<Image iconType="3" name="/org/sleuthkit/autopsy/corecomponents/btn_step_back.png"/> <Image iconType="3" name="/org/sleuthkit/autopsy/corecomponents/btn_step_back.png"/>
</Property> </Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataContentViewerArtifact.prevPageButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataArtifactContentViewer.prevPageButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
<Property name="borderPainted" type="boolean" value="false"/> <Property name="borderPainted" type="boolean" value="false"/>
<Property name="contentAreaFilled" type="boolean" value="false"/> <Property name="contentAreaFilled" type="boolean" value="false"/>

View File

@ -32,36 +32,40 @@ import org.openide.util.NbBundle;
import org.openide.util.lookup.ServiceProvider; import org.openide.util.lookup.ServiceProvider;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskException; import org.sleuthkit.datamodel.TskException;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.contentviewers.artifactviewers.ArtifactContentViewer; import org.sleuthkit.autopsy.contentviewers.artifactviewers.ArtifactContentViewer;
import org.sleuthkit.autopsy.contentviewers.artifactviewers.DefaultTableArtifactContentViewer; import org.sleuthkit.autopsy.contentviewers.artifactviewers.DefaultTableArtifactContentViewer;
import org.sleuthkit.datamodel.AnalysisResult;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.DataArtifact;
/** /**
* Instances of this class display the BlackboardArtifacts associated with the * Instances of this class display the DataArtifact associated with the
* Content represented by a Node. * Content represented by a Node.
* *
* It goes through a list of known ArtifactContentViewer to find a viewer that * It goes through a list of known ArtifactContentViewer to find a viewer that
* supports a given artifact and then hands it the artifact to display. * supports a given artifact and then hands it the artifact to display.
*/ */
@ServiceProvider(service = DataContentViewer.class, position = 7) @ServiceProvider(service = DataContentViewer.class, position = 6)
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
public class DataContentViewerArtifact extends javax.swing.JPanel implements DataContentViewer { public class DataArtifactContentViewer extends javax.swing.JPanel implements DataContentViewer {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@NbBundle.Messages({ @NbBundle.Messages({
"DataContentViewerArtifact.failedToGetSourcePath.message=Failed to get source file path from case database", "DataArtifactContentViewer.failedToGetSourcePath.message=Failed to get source file path from case database",
"DataContentViewerArtifact.failedToGetAttributes.message=Failed to get some or all attributes from case database" "DataArtifactContentViewer.failedToGetAttributes.message=Failed to get some or all attributes from case database"
}) })
private final static Logger logger = Logger.getLogger(DataContentViewerArtifact.class.getName()); private final static Logger logger = Logger.getLogger(DataArtifactContentViewer.class.getName());
private final static String WAIT_TEXT = NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.waitText"); private final static String WAIT_TEXT = NbBundle.getMessage(DataArtifactContentViewer.class, "DataArtifactContentViewer.waitText");
private final static String ERROR_TEXT = NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.errorText"); private final static String ERROR_TEXT = NbBundle.getMessage(DataArtifactContentViewer.class, "DataArtifactContentViewer.errorText");
// Value to return in isPreferred if this viewer is less preferred. // Value to return in isPreferred if this viewer is less preferred.
private static final int LESS_PREFERRED = 3; private static final int LESS_PREFERRED = 3;
@ -71,12 +75,12 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
private Node currentNode; // @@@ Remove this when the redundant setNode() calls problem is fixed. private Node currentNode; // @@@ Remove this when the redundant setNode() calls problem is fixed.
private int currentPage = 1; private int currentPage = 1;
private final Object lock = new Object(); private final Object lock = new Object();
private List<BlackboardArtifact> artifactTableContents; // Accessed by multiple threads, use getArtifactContents() and setArtifactContents() private List<DataArtifact> artifactTableContents; // Accessed by multiple threads, use getArtifactContents() and setArtifactContents()
private SwingWorker<ViewUpdate, Void> currentTask; // Accessed by multiple threads, use startNewTask() private SwingWorker<ViewUpdate, Void> currentTask; // Accessed by multiple threads, use startNewTask()
private final Collection<ArtifactContentViewer> knowArtifactViewers = new HashSet<>(Lookup.getDefault().lookupAll(ArtifactContentViewer.class)); private final Collection<ArtifactContentViewer> knowArtifactViewers = new HashSet<>(Lookup.getDefault().lookupAll(ArtifactContentViewer.class));
public DataContentViewerArtifact() { public DataArtifactContentViewer() {
initComponents(); initComponents();
@ -93,8 +97,8 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
private void initComponents() { private void initComponents() {
java.awt.GridBagConstraints gridBagConstraints; java.awt.GridBagConstraints gridBagConstraints;
jScrollPane1 = new javax.swing.JScrollPane(); scrollPane = new javax.swing.JScrollPane();
jPanel1 = new javax.swing.JPanel(); menuBar = new javax.swing.JPanel();
totalPageLabel = new javax.swing.JLabel(); totalPageLabel = new javax.swing.JLabel();
ofLabel = new javax.swing.JLabel(); ofLabel = new javax.swing.JLabel();
currentPageLabel = new javax.swing.JLabel(); currentPageLabel = new javax.swing.JLabel();
@ -106,57 +110,60 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(32767, 0)); filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(32767, 0));
artifactContentPanel = new javax.swing.JPanel(); artifactContentPanel = new javax.swing.JPanel();
setPreferredSize(new java.awt.Dimension(100, 58)); setMinimumSize(new java.awt.Dimension(300, 60));
setPreferredSize(new java.awt.Dimension(300, 60));
jScrollPane1.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); scrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
jScrollPane1.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER); scrollPane.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
scrollPane.setPreferredSize(new java.awt.Dimension(6, 60));
jPanel1.setPreferredSize(new java.awt.Dimension(620, 58)); menuBar.setMaximumSize(null);
jPanel1.setLayout(new java.awt.GridBagLayout()); menuBar.setMinimumSize(null);
menuBar.setPreferredSize(null);
menuBar.setLayout(new java.awt.GridBagLayout());
totalPageLabel.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.totalPageLabel.text")); // NOI18N totalPageLabel.setText(org.openide.util.NbBundle.getMessage(DataArtifactContentViewer.class, "DataArtifactContentViewer.totalPageLabel.text")); // NOI18N
totalPageLabel.setMaximumSize(new java.awt.Dimension(40, 16)); totalPageLabel.setMaximumSize(null);
totalPageLabel.setPreferredSize(new java.awt.Dimension(25, 16)); totalPageLabel.setPreferredSize(null);
gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 3; gridBagConstraints.gridx = 3;
gridBagConstraints.gridy = 0; gridBagConstraints.gridy = 0;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new java.awt.Insets(3, 12, 0, 0); gridBagConstraints.insets = new java.awt.Insets(3, 12, 0, 0);
jPanel1.add(totalPageLabel, gridBagConstraints); menuBar.add(totalPageLabel, gridBagConstraints);
ofLabel.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.ofLabel.text")); // NOI18N ofLabel.setText(org.openide.util.NbBundle.getMessage(DataArtifactContentViewer.class, "DataArtifactContentViewer.ofLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 2; gridBagConstraints.gridx = 2;
gridBagConstraints.gridy = 0; gridBagConstraints.gridy = 0;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new java.awt.Insets(3, 12, 0, 0); gridBagConstraints.insets = new java.awt.Insets(3, 12, 0, 0);
jPanel1.add(ofLabel, gridBagConstraints); menuBar.add(ofLabel, gridBagConstraints);
currentPageLabel.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.currentPageLabel.text")); // NOI18N currentPageLabel.setText(org.openide.util.NbBundle.getMessage(DataArtifactContentViewer.class, "DataArtifactContentViewer.currentPageLabel.text")); // NOI18N
currentPageLabel.setMaximumSize(new java.awt.Dimension(38, 14)); currentPageLabel.setMaximumSize(null);
currentPageLabel.setMinimumSize(new java.awt.Dimension(18, 14)); currentPageLabel.setPreferredSize(null);
currentPageLabel.setPreferredSize(new java.awt.Dimension(20, 14));
gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1; gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 0; gridBagConstraints.gridy = 0;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new java.awt.Insets(4, 7, 0, 0); gridBagConstraints.insets = new java.awt.Insets(3, 7, 0, 0);
jPanel1.add(currentPageLabel, gridBagConstraints); menuBar.add(currentPageLabel, gridBagConstraints);
pageLabel.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.pageLabel.text")); // NOI18N pageLabel.setText(org.openide.util.NbBundle.getMessage(DataArtifactContentViewer.class, "DataArtifactContentViewer.pageLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0; gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0; gridBagConstraints.gridy = 0;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new java.awt.Insets(3, 12, 0, 0); gridBagConstraints.insets = new java.awt.Insets(3, 12, 0, 0);
jPanel1.add(pageLabel, gridBagConstraints); menuBar.add(pageLabel, gridBagConstraints);
nextPageButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward.png"))); // NOI18N nextPageButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward.png"))); // NOI18N
nextPageButton.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.nextPageButton.text")); // NOI18N nextPageButton.setText(org.openide.util.NbBundle.getMessage(DataArtifactContentViewer.class, "DataArtifactContentViewer.nextPageButton.text")); // NOI18N
nextPageButton.setBorderPainted(false); nextPageButton.setBorderPainted(false);
nextPageButton.setContentAreaFilled(false); nextPageButton.setContentAreaFilled(false);
nextPageButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward_disabled.png"))); // NOI18N nextPageButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward_disabled.png"))); // NOI18N
@ -173,9 +180,9 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
gridBagConstraints.gridy = 0; gridBagConstraints.gridy = 0;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new java.awt.Insets(0, 0, 35, 0); gridBagConstraints.insets = new java.awt.Insets(0, 0, 35, 0);
jPanel1.add(nextPageButton, gridBagConstraints); menuBar.add(nextPageButton, gridBagConstraints);
pageLabel2.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.pageLabel2.text")); // NOI18N pageLabel2.setText(org.openide.util.NbBundle.getMessage(DataArtifactContentViewer.class, "DataArtifactContentViewer.pageLabel2.text")); // NOI18N
pageLabel2.setMaximumSize(new java.awt.Dimension(29, 14)); pageLabel2.setMaximumSize(new java.awt.Dimension(29, 14));
pageLabel2.setMinimumSize(new java.awt.Dimension(29, 14)); pageLabel2.setMinimumSize(new java.awt.Dimension(29, 14));
gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints = new java.awt.GridBagConstraints();
@ -183,11 +190,11 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
gridBagConstraints.gridy = 0; gridBagConstraints.gridy = 0;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new java.awt.Insets(3, 41, 0, 0); gridBagConstraints.insets = new java.awt.Insets(3, 30, 0, 0);
jPanel1.add(pageLabel2, gridBagConstraints); menuBar.add(pageLabel2, gridBagConstraints);
prevPageButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back.png"))); // NOI18N prevPageButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back.png"))); // NOI18N
prevPageButton.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.prevPageButton.text")); // NOI18N prevPageButton.setText(org.openide.util.NbBundle.getMessage(DataArtifactContentViewer.class, "DataArtifactContentViewer.prevPageButton.text")); // NOI18N
prevPageButton.setBorderPainted(false); prevPageButton.setBorderPainted(false);
prevPageButton.setContentAreaFilled(false); prevPageButton.setContentAreaFilled(false);
prevPageButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back_disabled.png"))); // NOI18N prevPageButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back_disabled.png"))); // NOI18N
@ -204,22 +211,22 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
gridBagConstraints.gridy = 0; gridBagConstraints.gridy = 0;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new java.awt.Insets(0, 5, 35, 0); gridBagConstraints.insets = new java.awt.Insets(0, 5, 35, 0);
jPanel1.add(prevPageButton, gridBagConstraints); menuBar.add(prevPageButton, gridBagConstraints);
gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 8; gridBagConstraints.gridx = 8;
gridBagConstraints.gridy = 0; gridBagConstraints.gridy = 0;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHEAST; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHEAST;
gridBagConstraints.insets = new java.awt.Insets(3, 0, 0, 8); gridBagConstraints.insets = new java.awt.Insets(3, 0, 0, 8);
jPanel1.add(artifactLabel, gridBagConstraints); menuBar.add(artifactLabel, gridBagConstraints);
gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 7; gridBagConstraints.gridx = 7;
gridBagConstraints.gridy = 0; gridBagConstraints.gridy = 0;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.weightx = 0.1; gridBagConstraints.weightx = 0.1;
jPanel1.add(filler1, gridBagConstraints); menuBar.add(filler1, gridBagConstraints);
jScrollPane1.setViewportView(jPanel1); scrollPane.setViewportView(menuBar);
artifactContentPanel.setLayout(new javax.swing.OverlayLayout(artifactContentPanel)); artifactContentPanel.setLayout(new javax.swing.OverlayLayout(artifactContentPanel));
@ -227,15 +234,15 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
this.setLayout(layout); this.setLayout(layout);
layout.setHorizontalGroup( layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 561, Short.MAX_VALUE) .addComponent(scrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 300, Short.MAX_VALUE)
.addComponent(artifactContentPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(artifactContentPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
); );
layout.setVerticalGroup( layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 24, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(scrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 24, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(artifactContentPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 397, Short.MAX_VALUE)) .addComponent(artifactContentPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 30, Short.MAX_VALUE))
); );
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents
@ -258,13 +265,13 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
private javax.swing.JLabel artifactLabel; private javax.swing.JLabel artifactLabel;
private javax.swing.JLabel currentPageLabel; private javax.swing.JLabel currentPageLabel;
private javax.swing.Box.Filler filler1; private javax.swing.Box.Filler filler1;
private javax.swing.JPanel jPanel1; private javax.swing.JPanel menuBar;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JButton nextPageButton; private javax.swing.JButton nextPageButton;
private javax.swing.JLabel ofLabel; private javax.swing.JLabel ofLabel;
private javax.swing.JLabel pageLabel; private javax.swing.JLabel pageLabel;
private javax.swing.JLabel pageLabel2; private javax.swing.JLabel pageLabel2;
private javax.swing.JButton prevPageButton; private javax.swing.JButton prevPageButton;
private javax.swing.JScrollPane scrollPane;
private javax.swing.JLabel totalPageLabel; private javax.swing.JLabel totalPageLabel;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
@ -308,17 +315,17 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
@Override @Override
public String getTitle() { public String getTitle() {
return NbBundle.getMessage(this.getClass(), "DataContentViewerArtifact.title"); return NbBundle.getMessage(this.getClass(), "DataArtifactContentViewer.title");
} }
@Override @Override
public String getToolTip() { public String getToolTip() {
return NbBundle.getMessage(this.getClass(), "DataContentViewerArtifact.toolTip"); return NbBundle.getMessage(this.getClass(), "DataArtifactContentViewer.toolTip");
} }
@Override @Override
public DataContentViewer createInstance() { public DataContentViewer createInstance() {
return new DataContentViewerArtifact(); return new DataArtifactContentViewer();
} }
@Override @Override
@ -338,21 +345,22 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
} }
for (Content content : node.getLookup().lookupAll(Content.class)) { for (Content content : node.getLookup().lookupAll(Content.class)) {
if ((content != null) && (!(content instanceof BlackboardArtifact))) { if ((content != null) && (!(content instanceof DataArtifact)) && (!(content instanceof AnalysisResult))) {
try { try {
return content.getAllArtifactsCount() > 0; return Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard().hasDataArtifacts(content.getId());
} catch (TskException ex) { } catch (NoCurrentCaseException | TskException ex) {
logger.log(Level.SEVERE, "Couldn't get count of BlackboardArtifacts for content", ex); //NON-NLS logger.log(Level.SEVERE, "Couldn't get count of DataArtifacts for content", ex); //NON-NLS
} }
} }
} }
return false; return false;
} }
@Override @Override
public int isPreferred(Node node) { public int isPreferred(Node node) {
// get the artifact from the lookup // get the artifact from the lookup
BlackboardArtifact artifact = node.getLookup().lookup(BlackboardArtifact.class); DataArtifact artifact = node.getLookup().lookup(DataArtifact.class);
if (artifact == null) { if (artifact == null) {
return LESS_PREFERRED; return LESS_PREFERRED;
} }
@ -386,7 +394,7 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
} }
} }
private ArtifactContentViewer getSupportingViewer(BlackboardArtifact artifact) { private ArtifactContentViewer getSupportingViewer(DataArtifact artifact) {
for (ArtifactContentViewer viewer : knowArtifactViewers) { for (ArtifactContentViewer viewer : knowArtifactViewers) {
if (viewer.isSupported(artifact)) { if (viewer.isSupported(artifact)) {
return viewer; return viewer;
@ -403,10 +411,10 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
int numberOfPages; int numberOfPages;
int currentPage; int currentPage;
BlackboardArtifact artifact; DataArtifact artifact;
String errorMsg; String errorMsg;
ViewUpdate(int numberOfPages, int currentPage, BlackboardArtifact artifact) { ViewUpdate(int numberOfPages, int currentPage, DataArtifact artifact) {
this.currentPage = currentPage; this.currentPage = currentPage;
this.numberOfPages = numberOfPages; this.numberOfPages = numberOfPages;
this.artifact = artifact; this.artifact = artifact;
@ -442,7 +450,7 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
if (viewUpdate.artifact != null) { if (viewUpdate.artifact != null) {
artifactLabel.setText(viewUpdate.artifact.getDisplayName()); artifactLabel.setText(viewUpdate.artifact.getDisplayName());
BlackboardArtifact artifact = viewUpdate.artifact; DataArtifact artifact = viewUpdate.artifact;
ArtifactContentViewer viewer = this.getSupportingViewer(artifact); ArtifactContentViewer viewer = this.getSupportingViewer(artifact);
viewer.setArtifact(artifact); viewer.setArtifact(artifact);
@ -484,7 +492,7 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
* @param artifactList A list of ResultsTableArtifact representations of * @param artifactList A list of ResultsTableArtifact representations of
* artifacts. * artifacts.
*/ */
private void setArtifactContents(List<BlackboardArtifact> artifactList) { private void setArtifactContents(List<DataArtifact> artifactList) {
synchronized (lock) { synchronized (lock) {
this.artifactTableContents = artifactList; this.artifactTableContents = artifactList;
} }
@ -495,11 +503,22 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
* *
* @return A list of artifacts. * @return A list of artifacts.
*/ */
private List<BlackboardArtifact> getArtifactContents() { private List<DataArtifact> getArtifactContents() {
synchronized (lock) { synchronized (lock) {
return Collections.unmodifiableList(artifactTableContents); return Collections.unmodifiableList(artifactTableContents);
} }
} }
/**
* Metric for determining if content is parent source content.
* @param content The content.
* @return True if this content should be used for source content.
*/
private static boolean isSourceContent(Content content) {
return (content != null) &&
(!(content instanceof DataArtifact)) &&
(!(content instanceof AnalysisResult));
}
/** /**
* Instances of this class use a background thread to generate a ViewUpdate * Instances of this class use a background thread to generate a ViewUpdate
@ -520,20 +539,18 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
// blackboard artifact, if any. // blackboard artifact, if any.
Lookup lookup = selectedNode.getLookup(); Lookup lookup = selectedNode.getLookup();
// Get the content. We may get BlackboardArtifacts, ignore those here. // Get the content. We may get DataArtifacts, ignore those here.
ArrayList<BlackboardArtifact> artifacts = new ArrayList<>(); List<DataArtifact> artifacts = Collections.emptyList();
Collection<? extends Content> contents = lookup.lookupAll(Content.class); Collection<? extends Content> contents = lookup.lookupAll(Content.class);
if (contents.isEmpty()) { if (contents.isEmpty()) {
return new ViewUpdate(getArtifactContents().size(), currentPage, ERROR_TEXT); return new ViewUpdate(getArtifactContents().size(), currentPage, ERROR_TEXT);
} }
Content underlyingContent = null;
for (Content content : contents) { for (Content content : contents) {
if ((content != null) && (!(content instanceof BlackboardArtifact))) { if (isSourceContent(content)) {
// Get all of the blackboard artifacts associated with the content. These are what this // Get all of the blackboard artifacts associated with the content. These are what this
// viewer displays. // viewer displays.
try { try {
artifacts = content.getAllArtifacts(); artifacts = content.getAllDataArtifacts();
underlyingContent = content;
break; break;
} catch (TskException ex) { } catch (TskException ex) {
logger.log(Level.SEVERE, "Couldn't get artifacts", ex); //NON-NLS logger.log(Level.SEVERE, "Couldn't get artifacts", ex); //NON-NLS
@ -547,15 +564,15 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
} }
// Build the new artifact contents cache. // Build the new artifact contents cache.
ArrayList<BlackboardArtifact> artifactContents = new ArrayList<>(); ArrayList<DataArtifact> artifactContents = new ArrayList<>();
for (BlackboardArtifact artifact : artifacts) { for (DataArtifact artifact : artifacts) {
artifactContents.add(artifact); artifactContents.add(artifact);
} }
// If the node has an underlying blackboard artifact, show it. If not, // If the node has an underlying data artifact, show it. If not,
// show the first artifact. // show the first artifact.
int index = 0; int index = 0;
BlackboardArtifact artifact = lookup.lookup(BlackboardArtifact.class); DataArtifact artifact = lookup.lookup(DataArtifact.class);
if (artifact != null) { if (artifact != null) {
index = artifacts.indexOf(artifact); index = artifacts.indexOf(artifact);
if (index == -1) { if (index == -1) {
@ -567,7 +584,7 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
if (attr.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT.getTypeID()) { if (attr.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT.getTypeID()) {
long assocArtifactId = attr.getValueLong(); long assocArtifactId = attr.getValueLong();
int assocArtifactIndex = -1; int assocArtifactIndex = -1;
for (BlackboardArtifact art : artifacts) { for (DataArtifact art : artifacts) {
if (assocArtifactId == art.getArtifactID()) { if (assocArtifactId == art.getArtifactID()) {
assocArtifactIndex = artifacts.indexOf(art); assocArtifactIndex = artifacts.indexOf(art);
break; break;
@ -636,14 +653,14 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
protected ViewUpdate doInBackground() { protected ViewUpdate doInBackground() {
// Get the artifact content to display from the cache. Note that one must be subtracted from the // Get the artifact content to display from the cache. Note that one must be subtracted from the
// page index to get the corresponding artifact content index. // page index to get the corresponding artifact content index.
List<BlackboardArtifact> artifactContents = getArtifactContents(); List<DataArtifact> artifactContents = getArtifactContents();
// It may take a considerable amount of time to fetch the attributes of the selected artifact so check for cancellation. // It may take a considerable amount of time to fetch the attributes of the selected artifact so check for cancellation.
if (isCancelled()) { if (isCancelled()) {
return null; return null;
} }
BlackboardArtifact artifactContent = artifactContents.get(pageIndex - 1); DataArtifact artifactContent = artifactContents.get(pageIndex - 1);
return new ViewUpdate(artifactContents.size(), pageIndex, artifactContent); return new ViewUpdate(artifactContents.size(), pageIndex, artifactContent);
} }

View File

@ -134,43 +134,28 @@ public class DataContentPanel extends javax.swing.JPanel implements DataContent,
@Override @Override
public void setNode(Node selectedNode) { public void setNode(Node selectedNode) {
// change the cursor to "waiting cursor" for this operation
this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); if (workerThread != null) {
workerThread.cancel(true);
workerThread = null;
}
currentNode = null;
// Reset everything // Reset everything
for (int index = 0; index < jTabbedPane1.getTabCount(); index++) { for (int index = 0; index < jTabbedPane1.getTabCount(); index++) {
jTabbedPane1.setEnabledAt(index, false); jTabbedPane1.setEnabledAt(index, false);
String tabTitle = viewers.get(index).getTitle(selectedNode);
tabTitle = tabTitle == null ? "" : tabTitle;
if (!tabTitle.equals(jTabbedPane1.getTitleAt(index))) {
jTabbedPane1.setTitleAt(index, tabTitle);
}
viewers.get(index).resetComponent(); viewers.get(index).resetComponent();
} }
String defaultName = NbBundle.getMessage(DataContentTopComponent.class, "CTL_DataContentTopComponent");
// set the file path
if (selectedNode == null) {
setName(defaultName);
} else {
Content content = selectedNode.getLookup().lookup(Content.class);
if (content != null) {
//String path = DataConversion.getformattedPath(ContentUtils.getDisplayPath(selectedNode.getLookup().lookup(Content.class)), 0);
String path = defaultName;
try {
path = content.getUniquePath();
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Exception while calling Content.getUniquePath() for {0}", content); //NON-NLS
}
setName(path);
} else {
setName(defaultName);
}
}
currentNode = selectedNode;
if (workerThread != null) {
workerThread.cancel(true);
}
if (selectedNode != null) { if (selectedNode != null) {
workerThread = new DataContentPanelWorker(currentNode); workerThread = new DataContentPanelWorker(selectedNode);
workerThread.execute(); workerThread.execute();
} }
} }
@ -266,6 +251,10 @@ public class DataContentPanel extends javax.swing.JPanel implements DataContent,
int isPreferred(Node node) { int isPreferred(Node node) {
return this.wrapped.isPreferred(node); return this.wrapped.isPreferred(node);
} }
String getTitle(Node node) {
return this.wrapped.getTitle(node);
}
} }
/** /**
@ -287,9 +276,6 @@ public class DataContentPanel extends javax.swing.JPanel implements DataContent,
@Override @Override
protected WorkerResults doInBackground() throws Exception { protected WorkerResults doInBackground() throws Exception {
if (node == null) {
return null;
}
List<Integer> supportedViewers = new ArrayList<>(); List<Integer> supportedViewers = new ArrayList<>();
int preferredViewerIndex = 0; int preferredViewerIndex = 0;
@ -312,7 +298,7 @@ public class DataContentPanel extends javax.swing.JPanel implements DataContent,
} }
} }
return new WorkerResults(node, supportedViewers, preferredViewerIndex); return new WorkerResults(node, supportedViewers, preferredViewerIndex);
} }
@ -325,7 +311,7 @@ public class DataContentPanel extends javax.swing.JPanel implements DataContent,
try { try {
WorkerResults results = get(); WorkerResults results = get();
currentNode = node;
if (results != null) { if (results != null) {
updateTabs(results.getNode(), results.getSupportedIndices(), results.getPreferredViewerIndex()); updateTabs(results.getNode(), results.getSupportedIndices(), results.getPreferredViewerIndex());
} }

View File

@ -87,6 +87,7 @@ import org.sleuthkit.autopsy.datamodel.BaseChildFactory;
import org.sleuthkit.autopsy.datamodel.BaseChildFactory.PageChangeEvent; import org.sleuthkit.autopsy.datamodel.BaseChildFactory.PageChangeEvent;
import org.sleuthkit.autopsy.datamodel.BaseChildFactory.PageCountChangeEvent; import org.sleuthkit.autopsy.datamodel.BaseChildFactory.PageCountChangeEvent;
import org.sleuthkit.autopsy.datamodel.BaseChildFactory.PageSizeChangeEvent; import org.sleuthkit.autopsy.datamodel.BaseChildFactory.PageSizeChangeEvent;
import org.sleuthkit.datamodel.Score.Significance;
/** /**
* A tabular result viewer that displays the children of the given root node * A tabular result viewer that displays the children of the given root node
@ -1263,6 +1264,29 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/**
* Returns the icon denoted by the Score's Significance.
* @param significance The Score's Significance.
* @return The icon (or null) related to that significance.
*/
private ImageIcon getIcon(Significance significance) {
if (significance == null) {
return null;
}
switch (significance) {
case NOTABLE:
return NOTABLE_ICON_SCORE;
case LIKELY_NOTABLE:
return INTERESTING_SCORE_ICON;
case LIKELY_NONE:
case NONE:
case UNKNOWN:
default:
return null;
}
}
@Override @Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
@ -1283,19 +1307,8 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
switchValue = value; switchValue = value;
} }
setText(""); setText("");
if ((switchValue instanceof Score)) { if ((switchValue instanceof org.sleuthkit.datamodel.Score)) {
setIcon(getIcon(((org.sleuthkit.datamodel.Score) switchValue).getSignificance()));
switch ((Score) switchValue) {
case INTERESTING_SCORE:
setIcon(INTERESTING_SCORE_ICON);
break;
case NOTABLE_SCORE:
setIcon(NOTABLE_ICON_SCORE);
break;
case NO_SCORE:
default:
setIcon(null);
}
} else { } else {
setIcon(null); setIcon(null);
} }

View File

@ -30,9 +30,7 @@ PlatformUtil.getProcVmUsed.sigarNotInit.msg=Cannot get virt mem used, sigar not
PlatformUtil.getProcVmUsed.gen.msg=Cannot get virt mem used, {0} PlatformUtil.getProcVmUsed.gen.msg=Cannot get virt mem used, {0}
PlatformUtil.getJvmMemInfo.usageText=JVM heap usage: {0}, JVM non-heap usage: {1} PlatformUtil.getJvmMemInfo.usageText=JVM heap usage: {0}, JVM non-heap usage: {1}
PlatformUtil.getPhysicalMemInfo.usageText=Physical memory usage (max, total, free): {0}, {1}, {2} PlatformUtil.getPhysicalMemInfo.usageText=Physical memory usage (max, total, free): {0}, {1}, {2}
PlatformUtil.getAllMemUsageInfo.usageText={0}\n\ PlatformUtil.getAllMemUsageInfo.usageText={0}\n{1}\nProcess Virtual Memory: {2}
{1}\n\
Process Virtual Memory: {2}
# {0} - file name # {0} - file name
ReadImageTask.mesageText=Reading image: {0} ReadImageTask.mesageText=Reading image: {0}
StringExtract.illegalStateException.cannotInit.msg=Unicode table not properly initialized, cannot instantiate StringExtract StringExtract.illegalStateException.cannotInit.msg=Unicode table not properly initialized, cannot instantiate StringExtract

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2018 Basis Technology Corp. * Copyright 2011-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -27,6 +27,8 @@ import java.util.GregorianCalendar;
import java.util.List; import java.util.List;
import java.util.SimpleTimeZone; import java.util.SimpleTimeZone;
import java.util.TimeZone; import java.util.TimeZone;
import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.datamodel.TimeUtilities;
/** /**
* Utility methods for workig with time zones. * Utility methods for workig with time zones.
@ -52,7 +54,7 @@ public class TimeZoneUtils {
DateFormat dfm = new SimpleDateFormat("z"); DateFormat dfm = new SimpleDateFormat("z");
dfm.setTimeZone(zone); dfm.setTimeZone(zone);
boolean hasDaylight = zone.useDaylightTime(); boolean hasDaylight = zone.useDaylightTime();
String first = dfm.format(new GregorianCalendar(2010, 1, 1).getTime()).substring(0, 3); String first = dfm.format(new GregorianCalendar(2010, 1, 1).getTime()).substring(0, 3);
String second = dfm.format(new GregorianCalendar(2011, 6, 6).getTime()).substring(0, 3); String second = dfm.format(new GregorianCalendar(2011, 6, 6).getTime()).substring(0, 3);
int mid = hour * -1; int mid = hour * -1;
String result = first + Integer.toString(mid); String result = first + Integer.toString(mid);
@ -65,19 +67,19 @@ public class TimeZoneUtils {
return result; return result;
} }
/** /**
* Generate a time zone string containing the GMT offset and ID. * Generate a time zone string containing the GMT offset and ID.
* *
* @param timeZone The time zone. * @param timeZone The time zone.
* *
* @return The time zone string. * @return The time zone string.
*/ */
public static String createTimeZoneString(TimeZone timeZone) { public static String createTimeZoneString(TimeZone timeZone) {
int offset = timeZone.getRawOffset() / 1000; int offset = timeZone.getRawOffset() / 1000;
int hour = offset / 3600; int hour = offset / 3600;
int minutes = Math.abs((offset % 3600) / 60); int minutes = Math.abs((offset % 3600) / 60);
return String.format("(GMT%+d:%02d) %s", hour, minutes, timeZone.getID()); //NON-NLS return String.format("(GMT%+d:%02d) %s", hour, minutes, timeZone.getID()); //NON-NLS
} }
@ -89,7 +91,7 @@ public class TimeZoneUtils {
* Create a list of time zones. * Create a list of time zones.
*/ */
List<TimeZone> timeZoneList = new ArrayList<>(); List<TimeZone> timeZoneList = new ArrayList<>();
String[] ids = SimpleTimeZone.getAvailableIDs(); String[] ids = SimpleTimeZone.getAvailableIDs();
for (String id : ids) { for (String id : ids) {
/* /*
@ -103,36 +105,72 @@ public class TimeZoneUtils {
*/ */
timeZoneList.add(TimeZone.getTimeZone(id)); timeZoneList.add(TimeZone.getTimeZone(id));
} }
/* /*
* Sort the list of time zones first by offset, then by ID. * Sort the list of time zones first by offset, then by ID.
*/ */
Collections.sort(timeZoneList, new Comparator<TimeZone>(){ Collections.sort(timeZoneList, new Comparator<TimeZone>() {
@Override @Override
public int compare(TimeZone o1, TimeZone o2){ public int compare(TimeZone o1, TimeZone o2) {
int offsetDelta = Integer.compare(o1.getRawOffset(), o2.getRawOffset()); int offsetDelta = Integer.compare(o1.getRawOffset(), o2.getRawOffset());
if (offsetDelta == 0) { if (offsetDelta == 0) {
return o1.getID().compareToIgnoreCase(o2.getID()); return o1.getID().compareToIgnoreCase(o2.getID());
} }
return offsetDelta; return offsetDelta;
} }
}); });
/* /*
* Create a list of Strings encompassing both the GMT offset and the * Create a list of Strings encompassing both the GMT offset and the
* time zone ID. * time zone ID.
*/ */
List<String> outputList = new ArrayList<>(); List<String> outputList = new ArrayList<>();
for (TimeZone timeZone : timeZoneList) { for (TimeZone timeZone : timeZoneList) {
outputList.add(createTimeZoneString(timeZone)); outputList.add(createTimeZoneString(timeZone));
} }
return outputList; return outputList;
} }
/**
* Returns the time formatted in the user selected time zone.
*
* @param epochTime
*
* @return
*/
public static String getFormattedTime(long epochTime) {
return TimeUtilities.epochToTime(epochTime, getTimeZone());
}
/**
* Returns the formatted time in the user selected time zone in ISO8601
* format.
*
* @param epochTime Seconds from java epoch
*
* @return Formatted date time string in ISO8601
*/
public static String getFormattedTimeISO8601(long epochTime) {
return TimeUtilities.epochToTimeISO8601(epochTime, getTimeZone());
}
/**
* Returns the user preferred timezone.
*
* @return TimeZone to use when formatting time values.
*/
public static TimeZone getTimeZone() {
if (UserPreferences.displayTimesInLocalTime()) {
return TimeZone.getDefault();
}
return TimeZone.getTimeZone(UserPreferences.getTimeZoneForDisplays());
}
/** /**
* Prevents instantiation. * Prevents instantiation.
*/ */

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2012-2020 Basis Technology Corp. * Copyright 2012-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -46,7 +46,6 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable; import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable.HasCommentStatus; import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable.HasCommentStatus;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable.Score;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import static org.sleuthkit.autopsy.datamodel.Bundle.*; import static org.sleuthkit.autopsy.datamodel.Bundle.*;
import static org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode.AbstractFilePropertyType.*; import static org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode.AbstractFilePropertyType.*;
@ -59,14 +58,14 @@ import org.sleuthkit.autopsy.texttranslation.NoServiceProviderException;
import org.sleuthkit.autopsy.texttranslation.TextTranslationService; import org.sleuthkit.autopsy.texttranslation.TextTranslationService;
import org.sleuthkit.autopsy.texttranslation.TranslationException; import org.sleuthkit.autopsy.texttranslation.TranslationException;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.ContentTag; import org.sleuthkit.datamodel.ContentTag;
import org.sleuthkit.datamodel.Tag; import org.sleuthkit.datamodel.Tag;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.coreutils.TimeZoneUtils;
import org.sleuthkit.autopsy.texttranslation.utils.FileNameTranslationUtil; import org.sleuthkit.autopsy.texttranslation.utils.FileNameTranslationUtil;
import org.sleuthkit.datamodel.Score;
/** /**
* An abstract node that encapsulates AbstractFile data * An abstract node that encapsulates AbstractFile data
@ -350,10 +349,10 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
new WeakReference<>(this), weakPcl)); new WeakReference<>(this), weakPcl));
} }
properties.add(new NodeProperty<>(MOD_TIME.toString(), MOD_TIME.toString(), NO_DESCR, ContentUtils.getStringTime(content.getMtime(), content))); properties.add(new NodeProperty<>(MOD_TIME.toString(), MOD_TIME.toString(), NO_DESCR, TimeZoneUtils.getFormattedTime(content.getMtime())));
properties.add(new NodeProperty<>(CHANGED_TIME.toString(), CHANGED_TIME.toString(), NO_DESCR, ContentUtils.getStringTime(content.getCtime(), content))); properties.add(new NodeProperty<>(CHANGED_TIME.toString(), CHANGED_TIME.toString(), NO_DESCR, TimeZoneUtils.getFormattedTime(content.getCtime())));
properties.add(new NodeProperty<>(ACCESS_TIME.toString(), ACCESS_TIME.toString(), NO_DESCR, ContentUtils.getStringTime(content.getAtime(), content))); properties.add(new NodeProperty<>(ACCESS_TIME.toString(), ACCESS_TIME.toString(), NO_DESCR, TimeZoneUtils.getFormattedTime(content.getAtime())));
properties.add(new NodeProperty<>(CREATED_TIME.toString(), CREATED_TIME.toString(), NO_DESCR, ContentUtils.getStringTime(content.getCrtime(), content))); properties.add(new NodeProperty<>(CREATED_TIME.toString(), CREATED_TIME.toString(), NO_DESCR, TimeZoneUtils.getFormattedTime(content.getCrtime())));
properties.add(new NodeProperty<>(SIZE.toString(), SIZE.toString(), NO_DESCR, content.getSize())); properties.add(new NodeProperty<>(SIZE.toString(), SIZE.toString(), NO_DESCR, content.getSize()));
properties.add(new NodeProperty<>(FLAGS_DIR.toString(), FLAGS_DIR.toString(), NO_DESCR, content.getDirFlagAsString())); properties.add(new NodeProperty<>(FLAGS_DIR.toString(), FLAGS_DIR.toString(), NO_DESCR, content.getDirFlagAsString()));
properties.add(new NodeProperty<>(FLAGS_META.toString(), FLAGS_META.toString(), NO_DESCR, content.getMetaFlagsAsString())); properties.add(new NodeProperty<>(FLAGS_META.toString(), FLAGS_META.toString(), NO_DESCR, content.getMetaFlagsAsString()));
@ -432,43 +431,6 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
return Pair.of(count, description); return Pair.of(count, description);
} }
@NbBundle.Messages({
"AbstractAbstractFileNode.createSheet.score.displayName=S",
"AbstractAbstractFileNode.createSheet.notableFile.description=File recognized as notable.",
"AbstractAbstractFileNode.createSheet.interestingResult.description=File has interesting result associated with it.",
"AbstractAbstractFileNode.createSheet.taggedFile.description=File has been tagged.",
"AbstractAbstractFileNode.createSheet.notableTaggedFile.description=File tagged with notable tag.",
"AbstractAbstractFileNode.createSheet.noScore.description=No score"})
@Override
protected Pair<DataResultViewerTable.Score, String> getScorePropertyAndDescription(List<Tag> tags) {
DataResultViewerTable.Score score = DataResultViewerTable.Score.NO_SCORE;
String description = Bundle.AbstractAbstractFileNode_createSheet_noScore_description();
if (content.getKnown() == TskData.FileKnown.BAD) {
score = DataResultViewerTable.Score.NOTABLE_SCORE;
description = Bundle.AbstractAbstractFileNode_createSheet_notableFile_description();
}
try {
if (score == DataResultViewerTable.Score.NO_SCORE && !content.getArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT).isEmpty()) {
score = DataResultViewerTable.Score.INTERESTING_SCORE;
description = Bundle.AbstractAbstractFileNode_createSheet_interestingResult_description();
}
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Error getting artifacts for file: " + content.getName(), ex);
}
if (!tags.isEmpty() && (score == DataResultViewerTable.Score.NO_SCORE || score == DataResultViewerTable.Score.INTERESTING_SCORE)) {
score = DataResultViewerTable.Score.INTERESTING_SCORE;
description = Bundle.AbstractAbstractFileNode_createSheet_taggedFile_description();
for (Tag tag : tags) {
if (tag.getName().getKnownStatus() == TskData.FileKnown.BAD) {
score = DataResultViewerTable.Score.NOTABLE_SCORE;
description = Bundle.AbstractAbstractFileNode_createSheet_notableTaggedFile_description();
break;
}
}
}
return Pair.of(score, description);
}
@NbBundle.Messages({ @NbBundle.Messages({
"AbstractAbstractFileNode.createSheet.comment.displayName=C"}) "AbstractAbstractFileNode.createSheet.comment.displayName=C"})
@Override @Override
@ -571,10 +533,10 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
static public void fillPropertyMap(Map<String, Object> map, AbstractFile content) { static public void fillPropertyMap(Map<String, Object> map, AbstractFile content) {
map.put(NAME.toString(), getContentDisplayName(content)); map.put(NAME.toString(), getContentDisplayName(content));
map.put(LOCATION.toString(), getContentPath(content)); map.put(LOCATION.toString(), getContentPath(content));
map.put(MOD_TIME.toString(), ContentUtils.getStringTime(content.getMtime(), content)); map.put(MOD_TIME.toString(), TimeZoneUtils.getFormattedTime(content.getMtime()));
map.put(CHANGED_TIME.toString(), ContentUtils.getStringTime(content.getCtime(), content)); map.put(CHANGED_TIME.toString(), TimeZoneUtils.getFormattedTime(content.getCtime()));
map.put(ACCESS_TIME.toString(), ContentUtils.getStringTime(content.getAtime(), content)); map.put(ACCESS_TIME.toString(), TimeZoneUtils.getFormattedTime(content.getAtime()));
map.put(CREATED_TIME.toString(), ContentUtils.getStringTime(content.getCrtime(), content)); map.put(CREATED_TIME.toString(), TimeZoneUtils.getFormattedTime(content.getCrtime()));
map.put(SIZE.toString(), content.getSize()); map.put(SIZE.toString(), content.getSize());
map.put(FLAGS_DIR.toString(), content.getDirFlagAsString()); map.put(FLAGS_DIR.toString(), content.getDirFlagAsString());
map.put(FLAGS_META.toString(), content.getMetaFlagsAsString()); map.put(FLAGS_META.toString(), content.getMetaFlagsAsString());

View File

@ -32,14 +32,17 @@ import org.openide.nodes.Sheet;
import org.openide.util.lookup.Lookups; import org.openide.util.lookup.Lookups;
import org.openide.util.Lookup; import org.openide.util.Lookup;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance.Type; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance.Type;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable; import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.AnalysisResult;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.Score;
import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.Tag; import org.sleuthkit.datamodel.Tag;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
@ -57,7 +60,7 @@ public abstract class AbstractContentNode<T extends Content> extends ContentNode
/** /**
* Underlying Sleuth Kit Content object * Underlying Sleuth Kit Content object
*/ */
T content; protected final T content;
private static final Logger logger = Logger.getLogger(AbstractContentNode.class.getName()); private static final Logger logger = Logger.getLogger(AbstractContentNode.class.getName());
/** /**
@ -339,7 +342,26 @@ public abstract class AbstractContentNode<T extends Content> extends ContentNode
* *
* @return Score property for the underlying content of the node. * @return Score property for the underlying content of the node.
*/ */
abstract protected Pair<DataResultViewerTable.Score, String> getScorePropertyAndDescription(List<Tag> tags); @Messages({
"# {0} - significanceDisplayName",
"AbstractContentNode_getScorePropertyAndDescription_description=Has an {0} analysis result score"
})
protected Pair<Score, String> getScorePropertyAndDescription(List<Tag> tags) {
Score score = Score.SCORE_UNKNOWN;
try {
if (content instanceof AnalysisResult) {
score = ((AnalysisResult) content).getScore();
} else {
score = this.content.getAggregateScore();
}
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Unable to get aggregate score for content with id: " + this.content.getId(), ex);
}
String significanceDisplay = score.getSignificance().getDisplayName();
String description = Bundle.AbstractContentNode_getScorePropertyAndDescription_description(significanceDisplay);
return Pair.of(score, description);
}
/** /**
* Returns comment property for the node. * Returns comment property for the node.

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2017 Basis Technology Corp. * Copyright 2011-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -18,12 +18,12 @@
*/ */
package org.sleuthkit.autopsy.datamodel; package org.sleuthkit.autopsy.datamodel;
import java.text.SimpleDateFormat;
import java.util.logging.Level; import java.util.logging.Level;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.TimeZoneUtils;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
@ -39,8 +39,7 @@ import org.sleuthkit.datamodel.TskCoreException;
*/ */
@Deprecated @Deprecated
public class ArtifactStringContent implements StringContent { public class ArtifactStringContent implements StringContent {
private final static SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
private final static Logger logger = Logger.getLogger(ArtifactStringContent.class.getName()); private final static Logger logger = Logger.getLogger(ArtifactStringContent.class.getName());
private final BlackboardArtifact artifact; private final BlackboardArtifact artifact;
private String stringContent = ""; private String stringContent = "";
@ -130,11 +129,7 @@ public class ArtifactStringContent implements StringContent {
// Use Autopsy date formatting settings, not TSK defaults // Use Autopsy date formatting settings, not TSK defaults
case DATETIME: case DATETIME:
long epoch = attr.getValueLong(); long epoch = attr.getValueLong();
value = "0000-00-00 00:00:00"; value = TimeZoneUtils.getFormattedTime(epoch * 1000);
if (null != content && 0 != epoch) {
dateFormatter.setTimeZone(ContentUtils.getTimeZone(content));
value = dateFormatter.format(new java.util.Date(epoch * 1000));
}
break; break;
} }

View File

@ -50,6 +50,7 @@ import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.autopsy.guiutils.RefreshThrottler; import org.sleuthkit.autopsy.guiutils.RefreshThrottler;
import org.sleuthkit.datamodel.BlackboardArtifact.Category; import org.sleuthkit.datamodel.BlackboardArtifact.Category;
import org.python.google.common.collect.Sets; import org.python.google.common.collect.Sets;
import org.sleuthkit.datamodel.Blackboard;
import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_ACCOUNT; 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_DATA_SOURCE_USAGE;
import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_EMAIL_MSG; import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_EMAIL_MSG;
@ -644,17 +645,32 @@ public class Artifacts {
@Override @Override
protected List<BlackboardArtifact> makeKeys() { protected List<BlackboardArtifact> makeKeys() {
try { try {
List<BlackboardArtifact> arts; List<? extends BlackboardArtifact> arts;
arts = (filteringDSObjId > 0) Blackboard blackboard = Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard();
? Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard().getArtifacts(type.getTypeID(), filteringDSObjId) switch (this.type.getCategory()) {
: Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboardArtifacts(type.getTypeID());
case ANALYSIS_RESULT:
arts = (filteringDSObjId > 0)
? blackboard.getAnalysisResultsByType(type.getTypeID(), filteringDSObjId)
: blackboard.getAnalysisResultsByType(type.getTypeID());
break;
case DATA_ARTIFACT:
default:
arts = (filteringDSObjId > 0)
? blackboard.getDataArtifacts(type.getTypeID(), filteringDSObjId)
: blackboard.getDataArtifacts(type.getTypeID());
break;
}
for (BlackboardArtifact art : arts) { for (BlackboardArtifact art : arts) {
//Cache attributes while we are off the EDT. //Cache attributes while we are off the EDT.
//See JIRA-5969 //See JIRA-5969
art.getAttributes(); art.getAttributes();
} }
return arts;
@SuppressWarnings("unchecked")
List<BlackboardArtifact> toRet = (List<BlackboardArtifact>)(List<?>)arts;
return toRet;
} catch (NoCurrentCaseException ex) { } catch (NoCurrentCaseException ex) {
logger.log(Level.WARNING, "Trying to access case when no case is open.", ex); //NON-NLS logger.log(Level.WARNING, "Trying to access case when no case is open.", ex); //NON-NLS
} catch (TskCoreException ex) { } catch (TskCoreException ex) {

View File

@ -1,15 +1,15 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2018 Basis Technology Corp. * Copyright 2018-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -43,34 +43,40 @@ import org.sleuthkit.datamodel.SleuthkitVisitableItem;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
/** /**
* Child factory to create the top level children of the autopsy tree * A child factory to create the top level nodes in the main tree view. These
* * nodes are the child nodes of the invisible root node of the tree. The child
* nodes that are created vary with the view option selected by the user: group
* by data type or group by person/host.
*/ */
public final class AutopsyTreeChildFactory extends ChildFactory.Detachable<Object> { public final class AutopsyTreeChildFactory extends ChildFactory.Detachable<Object> {
private static final Set<Case.Events> LISTENING_EVENTS = EnumSet.of( private static final Set<Case.Events> EVENTS_OF_INTEREST = EnumSet.of(Case.Events.DATA_SOURCE_ADDED,
Case.Events.DATA_SOURCE_ADDED,
Case.Events.HOSTS_ADDED, Case.Events.HOSTS_ADDED,
Case.Events.HOSTS_DELETED, Case.Events.HOSTS_DELETED,
Case.Events.PERSONS_ADDED, Case.Events.PERSONS_ADDED,
Case.Events.PERSONS_DELETED, Case.Events.PERSONS_DELETED,
Case.Events.PERSONS_CHANGED Case.Events.HOSTS_ADDED_TO_PERSON,
Case.Events.HOSTS_REMOVED_FROM_PERSON
); );
private static final Set<String> LISTENING_EVENT_NAMES = LISTENING_EVENTS.stream() private static final Set<String> EVENTS_OF_INTEREST_NAMES = EVENTS_OF_INTEREST.stream()
.map(evt -> evt.name()) .map(evt -> evt.name())
.collect(Collectors.toSet()); .collect(Collectors.toSet());
private static final Logger logger = Logger.getLogger(AutopsyTreeChildFactory.class.getName()); private static final Logger logger = Logger.getLogger(AutopsyTreeChildFactory.class.getName());
/** /**
* Listener for handling DATA_SOURCE_ADDED events. * Listener for application events published when persons and/or hosts are
* added to or deleted from the data model for the current case. If the user
* has selected the group by person/host option for the tree, these events
* mean that the top-level person/host nodes in the tree need to be
* refreshed to reflect the changes.
*/ */
private final PropertyChangeListener pcl = new PropertyChangeListener() { private final PropertyChangeListener pcl = new PropertyChangeListener() {
@Override @Override
public void propertyChange(PropertyChangeEvent evt) { public void propertyChange(PropertyChangeEvent evt) {
String eventType = evt.getPropertyName(); String eventType = evt.getPropertyName();
if (LISTENING_EVENT_NAMES.contains(eventType) if (EVENTS_OF_INTEREST_NAMES.contains(eventType)
&& Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) { && Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) {
refreshChildren(); refreshChildren();
} }
@ -80,20 +86,28 @@ public final class AutopsyTreeChildFactory extends ChildFactory.Detachable<Objec
@Override @Override
protected void addNotify() { protected void addNotify() {
super.addNotify(); super.addNotify();
Case.addEventTypeSubscriber(LISTENING_EVENTS, pcl); Case.addEventTypeSubscriber(EVENTS_OF_INTEREST, pcl);
} }
@Override @Override
protected void removeNotify() { protected void removeNotify() {
super.removeNotify(); super.removeNotify();
Case.removeEventTypeSubscriber(LISTENING_EVENTS, pcl); Case.removeEventTypeSubscriber(EVENTS_OF_INTEREST, pcl);
} }
/** /**
* Creates keys for the top level children. * Creates the keys for the top level nodes in the main tree view. These
* nodes are the child nodes of the invisible root node of the tree. The
* child nodes that are created vary with the view option selected by the
* user: group by data type or group by person/host.
* *
* @param list list of keys created * IMPORTANT: Every time a key is added to the keys list, the NetBeans
* @return true, indicating that the key list is complete * framework reacts. To avoid significant performance hits, all of the keys
* need to be added at once.
*
* @param keys A list to contain the keys.
*
* @return True, indicating that the list of keys is complete.
*/ */
@Override @Override
protected boolean createKeys(List<Object> list) { protected boolean createKeys(List<Object> list) {
@ -101,6 +115,10 @@ public final class AutopsyTreeChildFactory extends ChildFactory.Detachable<Objec
try { try {
SleuthkitCase tskCase = Case.getCurrentCaseThrows().getSleuthkitCase(); SleuthkitCase tskCase = Case.getCurrentCaseThrows().getSleuthkitCase();
if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) { if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) {
/*
* The user has selected the group by person/host tree view
* option.
*/
PersonManager personManager = tskCase.getPersonManager(); PersonManager personManager = tskCase.getPersonManager();
List<Person> persons = personManager.getPersons(); List<Person> persons = personManager.getPersons();
// show persons level if there are persons to be shown // show persons level if there are persons to be shown
@ -118,8 +136,7 @@ public final class AutopsyTreeChildFactory extends ChildFactory.Detachable<Objec
nodes = tskCase.getHostManager().getAllHosts().stream() nodes = tskCase.getHostManager().getAllHosts().stream()
.map(HostGrouping::new) .map(HostGrouping::new)
.sorted() .sorted()
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
// either way, add in reports node // either way, add in reports node
@ -137,9 +154,9 @@ public final class AutopsyTreeChildFactory extends ChildFactory.Detachable<Objec
); );
} }
} catch (NoCurrentCaseException ex) { } catch (NoCurrentCaseException ex) {
logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS logger.log(Level.SEVERE, "Failed to create tree because there is no current case", ex); //NON-NLS
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Exception while getting data from case.", ex); //NON-NLS logger.log(Level.SEVERE, "Failed to create tree because of an error querying the case database", ex); //NON-NLS
} }
// add all nodes to the netbeans node list // add all nodes to the netbeans node list
@ -148,28 +165,33 @@ public final class AutopsyTreeChildFactory extends ChildFactory.Detachable<Objec
} }
/** /**
* Creates nodes for the top level Key * Creates a node for a given key for the top level nodes in the main tree
* view.
* *
* @param key * @param key The key.
* *
* @return Node for the key, null if key is unknown. * @return A node for the key.
*/ */
@Override @Override
protected Node createNodeForKey(Object key) { protected Node createNodeForKey(Object key) {
if (key instanceof SleuthkitVisitableItem) { Node node = null;
return ((SleuthkitVisitableItem) key).accept(new CreateSleuthkitNodeVisitor()); if (key != null) {
} else if (key instanceof AutopsyVisitableItem) { if (key instanceof SleuthkitVisitableItem) {
return ((AutopsyVisitableItem) key).accept(new RootContentChildren.CreateAutopsyNodeVisitor()); node = ((SleuthkitVisitableItem) key).accept(new CreateSleuthkitNodeVisitor());
} else { } else if (key instanceof AutopsyVisitableItem) {
logger.log(Level.SEVERE, "Unknown key type ", key.getClass().getName()); node = ((AutopsyVisitableItem) key).accept(new RootContentChildren.CreateAutopsyNodeVisitor());
return null; } else {
logger.log(Level.SEVERE, "Unknown key type: ", key.getClass().getName());
}
} }
return node;
} }
/** /**
* Refresh the children * Refreshes the top level nodes in the main tree view.
*/ */
public void refreshChildren() { public void refreshChildren() {
refresh(true); refresh(true);
} }
} }

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2012-2020 Basis Technology Corp. * Copyright 2012-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -59,12 +59,10 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeUti
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable; import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable.Score;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import static org.sleuthkit.autopsy.datamodel.DisplayableItemNode.findLinked; import static org.sleuthkit.autopsy.datamodel.DisplayableItemNode.findLinked;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable.HasCommentStatus; import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable.HasCommentStatus;
import static org.sleuthkit.autopsy.datamodel.AbstractContentNode.backgroundTasksPool; import static org.sleuthkit.autopsy.datamodel.AbstractContentNode.backgroundTasksPool;
import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager;
import org.sleuthkit.autopsy.timeline.actions.ViewArtifactInTimelineAction; import org.sleuthkit.autopsy.timeline.actions.ViewArtifactInTimelineAction;
import org.sleuthkit.autopsy.timeline.actions.ViewFileInTimelineAction; import org.sleuthkit.autopsy.timeline.actions.ViewFileInTimelineAction;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
@ -75,12 +73,14 @@ import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.Tag; import org.sleuthkit.datamodel.Tag;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.autopsy.datamodel.utils.IconsUtil; import org.sleuthkit.autopsy.datamodel.utils.IconsUtil;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.coreutils.TimeZoneUtils;
import static org.sleuthkit.autopsy.datamodel.AbstractContentNode.NO_DESCR; import static org.sleuthkit.autopsy.datamodel.AbstractContentNode.NO_DESCR;
import org.sleuthkit.autopsy.texttranslation.TextTranslationService; import org.sleuthkit.autopsy.texttranslation.TextTranslationService;
import org.sleuthkit.autopsy.datamodel.utils.FileNameTransTask; import org.sleuthkit.autopsy.datamodel.utils.FileNameTransTask;
import org.sleuthkit.datamodel.AnalysisResult;
import org.sleuthkit.datamodel.Score;
/** /**
* A BlackboardArtifactNode is an AbstractNode implementation that can be used * A BlackboardArtifactNode is an AbstractNode implementation that can be used
@ -438,7 +438,10 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
* action to view it in the timeline. * action to view it in the timeline.
*/ */
try { try {
if (ViewArtifactInTimelineAction.hasSupportedTimeStamp(artifact)) { if (ViewArtifactInTimelineAction.hasSupportedTimeStamp(artifact) &&
// don't show ViewArtifactInTimelineAction for AnalysisResults.
(!(this.artifact instanceof AnalysisResult))) {
actionsList.add(new ViewArtifactInTimelineAction(artifact)); actionsList.add(new ViewArtifactInTimelineAction(artifact));
} }
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
@ -680,22 +683,22 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileModifiedTime.name"), NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileModifiedTime.name"),
NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileModifiedTime.displayName"), NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileModifiedTime.displayName"),
"", "",
file == null ? "" : ContentUtils.getStringTime(file.getMtime(), file))); file == null ? "" : TimeZoneUtils.getFormattedTime(file.getMtime())));
sheetSet.put(new NodeProperty<>( sheetSet.put(new NodeProperty<>(
NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileChangedTime.name"), NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileChangedTime.name"),
NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileChangedTime.displayName"), NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileChangedTime.displayName"),
"", "",
file == null ? "" : ContentUtils.getStringTime(file.getCtime(), file))); file == null ? "" : TimeZoneUtils.getFormattedTime(file.getCtime())));
sheetSet.put(new NodeProperty<>( sheetSet.put(new NodeProperty<>(
NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileAccessedTime.name"), NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileAccessedTime.name"),
NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileAccessedTime.displayName"), NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileAccessedTime.displayName"),
"", "",
file == null ? "" : ContentUtils.getStringTime(file.getAtime(), file))); file == null ? "" : TimeZoneUtils.getFormattedTime(file.getAtime())));
sheetSet.put(new NodeProperty<>( sheetSet.put(new NodeProperty<>(
NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileCreatedTime.name"), NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileCreatedTime.name"),
NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileCreatedTime.displayName"), NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileCreatedTime.displayName"),
"", "",
file == null ? "" : ContentUtils.getStringTime(file.getCrtime(), file))); file == null ? "" : TimeZoneUtils.getFormattedTime(file.getCrtime())));
sheetSet.put(new NodeProperty<>( sheetSet.put(new NodeProperty<>(
NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileSize.name"), NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileSize.name"),
NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileSize.displayName"), NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileSize.displayName"),
@ -843,94 +846,6 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
return status; return status;
} }
/**
* Computes the value of the score property ("S" in S, C, O) for the
* artifact represented by this node. The score property indicates whether
* the artifact or its source content is notable or interesting.
*
* IMPORTANT: Notability takes precedence when computing the score.
*
* A red icon will be displayed in the property sheet if the hash of the
* source file has been found in a notable hash set or if either the
* artifact or its source content has been tagged with a notable tag. A
* yellow icon will be displayed if the source file belongs to an
* interesting file set or either the artifact or its source content has
* been tagged with a non-notable tag.
*
* @param tags The tags that have been applied to the artifact and its
* source content.
*
* @return The value of the score property as an enum element and a
* description string for dislpay in a tool tip.
*/
@Override
protected Pair<DataResultViewerTable.Score, String> getScorePropertyAndDescription(List<Tag> tags) {
/*
* Is the artifact's source content marked as notable?
*/
Score score = Score.NO_SCORE;
String description = Bundle.BlackboardArtifactNode_createSheet_noScore_description();
if (srcContent instanceof AbstractFile) {
if (((AbstractFile) srcContent).getKnown() == TskData.FileKnown.BAD) {
score = Score.NOTABLE_SCORE;
description = Bundle.BlackboardArtifactNode_createSheet_notableFile_description();
}
}
/*
* If the artifact is a hash set hit, is the hash set a notable hashes
* hash set?
*/
if (score == Score.NO_SCORE && artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID()) {
try {
BlackboardAttribute attr = artifact.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_SET_NAME));
List<HashDbManager.HashDb> notableHashsets = HashDbManager.getInstance().getKnownBadFileHashSets();
for (HashDbManager.HashDb hashDb : notableHashsets) {
if (hashDb.getHashSetName().equals(attr.getValueString())) {
score = Score.NOTABLE_SCORE;
description = Bundle.BlackboardArtifactNode_createSheet_notableFile_description();
break;
}
}
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, MessageFormat.format("Error getting TSK_SET_NAME attribute for TSK_HASHSET_HIT artifact (artifact objID={0})", artifact.getId()), ex);
}
}
/*
* Is the artifact's source content notable?
*/
if (score == Score.NO_SCORE) {
try {
if (!srcContent.getArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT).isEmpty()) {
score = Score.INTERESTING_SCORE;
description = Bundle.BlackboardArtifactNode_createSheet_interestingResult_description();
}
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, MessageFormat.format("Error getting TSK_INTERESTING_ARTIFACT_HIT artifacts for source content (artifact objID={0})", artifact.getId()), ex);
}
}
/*
* Analyze any tags applied to the artifact or its source content. If
* there are tags, tha artifact is at least interesting. If one of the
* tags is a notable tag, the artifact is notable.
*/
if (tags.size() > 0 && (score == Score.NO_SCORE || score == Score.INTERESTING_SCORE)) {
score = Score.INTERESTING_SCORE;
description = Bundle.BlackboardArtifactNode_createSheet_taggedItem_description();
for (Tag tag : tags) {
if (tag.getName().getKnownStatus() == TskData.FileKnown.BAD) {
score = Score.NOTABLE_SCORE;
description = Bundle.BlackboardArtifactNode_createSheet_notableTaggedItem_description();
break;
}
}
}
return Pair.of(score, description);
}
/** /**
* Computes the value of the other occurrences property ("O" in S, C, O) for * Computes the value of the other occurrences property ("O" in S, C, O) for
* the artifact represented by this node. The value of the other occurrences * the artifact represented by this node. The value of the other occurrences
@ -1033,7 +948,7 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
} else if (artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID()) { } else if (artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID()) {
addEmailMsgProperty(map, attribute); addEmailMsgProperty(map, attribute);
} else if (attribute.getAttributeType().getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME) { } else if (attribute.getAttributeType().getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME) {
map.put(attribute.getAttributeType().getDisplayName(), ContentUtils.getStringTime(attribute.getValueLong(), srcContent)); map.put(attribute.getAttributeType().getDisplayName(), TimeZoneUtils.getFormattedTime(attribute.getValueLong()));
} else if (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_TOOL_OUTPUT.getTypeID() } else if (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_TOOL_OUTPUT.getTypeID()
&& attributeTypeID == ATTRIBUTE_TYPE.TSK_TEXT.getTypeID()) { && attributeTypeID == ATTRIBUTE_TYPE.TSK_TEXT.getTypeID()) {
/* /*
@ -1099,7 +1014,7 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
} }
map.put(attribute.getAttributeType().getDisplayName(), value); map.put(attribute.getAttributeType().getDisplayName(), value);
} else if (attribute.getAttributeType().getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME) { } else if (attribute.getAttributeType().getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME) {
map.put(attribute.getAttributeType().getDisplayName(), ContentUtils.getStringTime(attribute.getValueLong(), srcContent)); map.put(attribute.getAttributeType().getDisplayName(), TimeZoneUtils.getFormattedTime(attribute.getValueLong()));
} else { } else {
map.put(attribute.getAttributeType().getDisplayName(), attribute.getDisplayString()); map.put(attribute.getAttributeType().getDisplayName(), attribute.getDisplayString());
} }
@ -1146,7 +1061,7 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
"BlackboardArtifactNode.createSheet.noScore.description=No score"}) "BlackboardArtifactNode.createSheet.noScore.description=No score"})
@Deprecated @Deprecated
protected final void addScorePropertyAndDescription(Sheet.Set sheetSet, List<Tag> tags) { protected final void addScorePropertyAndDescription(Sheet.Set sheetSet, List<Tag> tags) {
Pair<DataResultViewerTable.Score, String> scoreAndDescription = getScorePropertyAndDescription(tags); Pair<Score, String> scoreAndDescription = getScorePropertyAndDescription(tags);
sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_score_name(), Bundle.BlackboardArtifactNode_createSheet_score_displayName(), scoreAndDescription.getRight(), scoreAndDescription.getLeft())); sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_score_name(), Bundle.BlackboardArtifactNode_createSheet_score_displayName(), scoreAndDescription.getRight(), scoreAndDescription.getLeft()));
} }

View File

@ -9,13 +9,7 @@ AbstractAbstractFileNode.createSheet.count.description=There were {0} datasource
AbstractAbstractFileNode.createSheet.count.displayName=O AbstractAbstractFileNode.createSheet.count.displayName=O
AbstractAbstractFileNode.createSheet.count.hashLookupNotRun.description=Hash lookup had not been run on this file when the column was populated AbstractAbstractFileNode.createSheet.count.hashLookupNotRun.description=Hash lookup had not been run on this file when the column was populated
AbstractAbstractFileNode.createSheet.count.name=O AbstractAbstractFileNode.createSheet.count.name=O
AbstractAbstractFileNode.createSheet.interestingResult.description=File has interesting result associated with it.
AbstractAbstractFileNode.createSheet.noScore.description=No score
AbstractAbstractFileNode.createSheet.notableFile.description=File recognized as notable.
AbstractAbstractFileNode.createSheet.notableTaggedFile.description=File tagged with notable tag.
AbstractAbstractFileNode.createSheet.score.displayName=S
AbstractAbstractFileNode.createSheet.score.name=S AbstractAbstractFileNode.createSheet.score.name=S
AbstractAbstractFileNode.createSheet.taggedFile.description=File has been tagged.
AbstractAbstractFileNode.extensionColLbl=Extension AbstractAbstractFileNode.extensionColLbl=Extension
AbstractAbstractFileNode.flagsDirColLbl=Flags(Dir) AbstractAbstractFileNode.flagsDirColLbl=Flags(Dir)
AbstractAbstractFileNode.flagsMetaColLbl=Flags(Meta) AbstractAbstractFileNode.flagsMetaColLbl=Flags(Meta)
@ -38,6 +32,8 @@ AbstractAbstractFileNode.typeMetaColLbl=Type(Meta)
AbstractAbstractFileNode.useridColLbl=UserID AbstractAbstractFileNode.useridColLbl=UserID
AbstractContentNode.nodescription=no description AbstractContentNode.nodescription=no description
AbstractContentNode.valueLoading=value loading AbstractContentNode.valueLoading=value loading
# {0} - significanceDisplayName
AbstractContentNode_getScorePropertyAndDescription_description=Has an {0} analysis result score
AbstractFsContentNode.noDesc.text=no description AbstractFsContentNode.noDesc.text=no description
AnalysisResults_name=Analysis Results AnalysisResults_name=Analysis Results
ArtifactStringContent.attrsTableHeader.sources=Source(s) ArtifactStringContent.attrsTableHeader.sources=Source(s)
@ -49,7 +45,6 @@ AttachmentNode.getActions.openInExtViewer.text=Open in External Viewer Ctrl+E
AttachmentNode.getActions.searchFilesSameMD5.text=Search for files with the same MD5 hash AttachmentNode.getActions.searchFilesSameMD5.text=Search for files with the same MD5 hash
AttachmentNode.getActions.viewFileInDir.text=View File in Directory AttachmentNode.getActions.viewFileInDir.text=View File in Directory
AttachmentNode.getActions.viewInNewWin.text=View in New Window AttachmentNode.getActions.viewInNewWin.text=View in New Window
# {0} - node name
BaseChildFactory.NoSuchEventBusException.message=No event bus for node: {0} BaseChildFactory.NoSuchEventBusException.message=No event bus for node: {0}
BlackboardArtifactNode.createSheet.artifactDetails.displayName=Result Details BlackboardArtifactNode.createSheet.artifactDetails.displayName=Result Details
BlackboardArtifactNode.createSheet.artifactDetails.name=Result Details BlackboardArtifactNode.createSheet.artifactDetails.name=Result Details
@ -276,10 +271,10 @@ ImageNode.getActions.viewInNewWin.text=View in New Window
ImageNode.createSheet.name.name=Name ImageNode.createSheet.name.name=Name
ImageNode.createSheet.name.displayName=Name ImageNode.createSheet.name.displayName=Name
ImageNode.createSheet.name.desc=no description ImageNode.createSheet.name.desc=no description
Installer.exception.tskVerStringNull.msg=Sleuth Kit JNI test call returned without error, but version string was null\! Installer.exception.tskVerStringNull.msg=Sleuth Kit JNI test call returned without error, but version string was null!
Installer.exception.taskVerStringBang.msg=Sleuth Kit JNI test call returned without error, but version string was ""\! Installer.exception.taskVerStringBang.msg=Sleuth Kit JNI test call returned without error, but version string was ""!
Installer.tskLibErr.msg=Problem with Sleuth Kit JNI. Test call failed\!\n\nDetails: {0} Installer.tskLibErr.msg=Problem with Sleuth Kit JNI. Test call failed!\n\nDetails: {0}
Installer.tskLibErr.err=Fatal Error\! Installer.tskLibErr.err=Fatal Error!
InterestingHits.interestingItems.text=INTERESTING ITEMS InterestingHits.interestingItems.text=INTERESTING ITEMS
InterestingHits.displayName.text=Interesting Items InterestingHits.displayName.text=Interesting Items
InterestingHits.createSheet.name.name=Name InterestingHits.createSheet.name.name=Name

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2013-2020 Basis Technology Corp. * Copyright 2013-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -29,6 +29,7 @@ import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.openide.util.lookup.Lookups; import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.TimeZoneUtils;
import org.sleuthkit.autopsy.timeline.actions.ViewFileInTimelineAction; import org.sleuthkit.autopsy.timeline.actions.ViewFileInTimelineAction;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
@ -96,22 +97,22 @@ class ContentTagNode extends TagNode {
properties.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileModifiedTime.name"), properties.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileModifiedTime.name"),
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileModifiedTime.displayName"), NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileModifiedTime.displayName"),
"", "",
file != null ? ContentUtils.getStringTime(file.getMtime(), file) : "")); file != null ? TimeZoneUtils.getFormattedTime(file.getMtime()) : ""));
properties.put(new NodeProperty<>( properties.put(new NodeProperty<>(
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileChangedTime.name"), NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileChangedTime.name"),
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileChangedTime.displayName"), NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileChangedTime.displayName"),
"", "",
file != null ? ContentUtils.getStringTime(file.getCtime(), file) : "")); file != null ? TimeZoneUtils.getFormattedTime(file.getCtime()) : ""));
properties.put(new NodeProperty<>( properties.put(new NodeProperty<>(
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileAccessedTime.name"), NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileAccessedTime.name"),
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileAccessedTime.displayName"), NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileAccessedTime.displayName"),
"", "",
file != null ? ContentUtils.getStringTime(file.getAtime(), file) : "")); file != null ? TimeZoneUtils.getFormattedTime(file.getAtime()) : ""));
properties.put(new NodeProperty<>( properties.put(new NodeProperty<>(
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileCreatedTime.name"), NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileCreatedTime.name"),
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileCreatedTime.displayName"), NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileCreatedTime.displayName"),
"", "",
file != null ? ContentUtils.getStringTime(file.getCrtime(), file) : "")); file != null ? TimeZoneUtils.getFormattedTime(file.getCrtime()) : ""));
properties.put(new NodeProperty<>( properties.put(new NodeProperty<>(
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileSize.name"), NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileSize.name"),
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileSize.displayName"), NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileSize.displayName"),

View File

@ -26,8 +26,6 @@ import java.util.TimeZone;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.prefs.PreferenceChangeEvent;
import java.util.prefs.PreferenceChangeListener;
import javax.swing.SwingWorker; import javax.swing.SwingWorker;
import org.netbeans.api.progress.ProgressHandle; import org.netbeans.api.progress.ProgressHandle;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
@ -39,7 +37,6 @@ import org.sleuthkit.datamodel.ContentVisitor;
import org.sleuthkit.datamodel.DerivedFile; import org.sleuthkit.datamodel.DerivedFile;
import org.sleuthkit.datamodel.Directory; import org.sleuthkit.datamodel.Directory;
import org.sleuthkit.datamodel.File; import org.sleuthkit.datamodel.File;
import org.sleuthkit.datamodel.Image;
import org.sleuthkit.datamodel.LayoutFile; import org.sleuthkit.datamodel.LayoutFile;
import org.sleuthkit.datamodel.LocalFile; import org.sleuthkit.datamodel.LocalFile;
import org.sleuthkit.datamodel.LocalDirectory; import org.sleuthkit.datamodel.LocalDirectory;
@ -55,21 +52,9 @@ import org.sleuthkit.datamodel.VirtualDirectory;
public final class ContentUtils { public final class ContentUtils {
private final static Logger logger = Logger.getLogger(ContentUtils.class.getName()); private final static Logger logger = Logger.getLogger(ContentUtils.class.getName());
private static boolean displayTimesInLocalTime = UserPreferences.displayTimesInLocalTime();
private static final SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z"); private static final SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
private static final SimpleDateFormat dateFormatterISO8601 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); private static final SimpleDateFormat dateFormatterISO8601 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
static {
UserPreferences.addChangeListener(new PreferenceChangeListener() {
@Override
public void preferenceChange(PreferenceChangeEvent evt) {
if (evt.getKey().equals(UserPreferences.DISPLAY_TIMES_IN_LOCAL_TIME)) {
displayTimesInLocalTime = UserPreferences.displayTimesInLocalTime();
}
}
});
}
/** /**
* Don't instantiate * Don't instantiate
*/ */
@ -85,6 +70,7 @@ public final class ContentUtils {
* *
* @return The time * @return The time
*/ */
@Deprecated
public static String getStringTime(long epochSeconds, TimeZone tzone) { public static String getStringTime(long epochSeconds, TimeZone tzone) {
String time = "0000-00-00 00:00:00"; String time = "0000-00-00 00:00:00";
if (epochSeconds != 0) { if (epochSeconds != 0) {
@ -104,6 +90,7 @@ public final class ContentUtils {
* *
* @return The time * @return The time
*/ */
@Deprecated
public static String getStringTimeISO8601(long epochSeconds, TimeZone tzone) { public static String getStringTimeISO8601(long epochSeconds, TimeZone tzone) {
String time = "0000-00-00T00:00:00Z"; //NON-NLS String time = "0000-00-00T00:00:00Z"; //NON-NLS
if (epochSeconds != 0) { if (epochSeconds != 0) {
@ -123,7 +110,10 @@ public final class ContentUtils {
* @param content * @param content
* *
* @return * @return
*
* @deprecated Use org.sleuthkit.autopsy.coreutils.TimeZoneUtils.getFormattedTime instead
*/ */
@Deprecated
public static String getStringTime(long epochSeconds, Content content) { public static String getStringTime(long epochSeconds, Content content) {
return getStringTime(epochSeconds, getTimeZone(content)); return getStringTime(epochSeconds, getTimeZone(content));
} }
@ -136,29 +126,30 @@ public final class ContentUtils {
* @param c * @param c
* *
* @return * @return
*
* @deprecated Use org.sleuthkit.autopsy.coreutils.TimeZoneUtils.getFormattedTimeISO8601 instead
*/ */
@Deprecated
public static String getStringTimeISO8601(long epochSeconds, Content c) { public static String getStringTimeISO8601(long epochSeconds, Content c) {
return getStringTimeISO8601(epochSeconds, getTimeZone(c)); return getStringTimeISO8601(epochSeconds, getTimeZone(c));
} }
/**
* Returns either the user selected time zone or the system time zone.
*
* @param content
*
* @return
*
* @deprecated Use org.sleuthkit.autopsy.coreutils.TimeZoneUtils.getTimeZone instead
*/
@Deprecated
public static TimeZone getTimeZone(Content content) { public static TimeZone getTimeZone(Content content) {
if (!shouldDisplayTimesInLocalTime()) {
try { return TimeZone.getTimeZone(UserPreferences.getTimeZoneForDisplays());
if (!shouldDisplayTimesInLocalTime()) { } else {
return TimeZone.getTimeZone(UserPreferences.getTimeZoneForDisplays());
} else {
final Content dataSource = content.getDataSource();
if ((dataSource != null) && (dataSource instanceof Image)) {
Image image = (Image) dataSource;
return TimeZone.getTimeZone(image.getTimeZone());
} else {
//case such as top level VirtualDirectory
return TimeZone.getDefault();
}
}
} catch (TskCoreException ex) {
return TimeZone.getDefault(); return TimeZone.getDefault();
} }
} }
private static final SystemNameVisitor systemName = new SystemNameVisitor(); private static final SystemNameVisitor systemName = new SystemNameVisitor();
@ -553,9 +544,12 @@ public final class ContentUtils {
* Indicates whether or not times should be displayed using local time. * Indicates whether or not times should be displayed using local time.
* *
* @return True or false. * @return True or false.
*
* @deprecated Call UserPreferences.displayTimesInLocalTime instead.
*/ */
@Deprecated
public static boolean shouldDisplayTimesInLocalTime() { public static boolean shouldDisplayTimesInLocalTime() {
return displayTimesInLocalTime; return UserPreferences.displayTimesInLocalTime();
} }
} }

View File

@ -0,0 +1,182 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2012-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.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.EnumSet;
import java.util.List;
import java.util.logging.Level;
import org.openide.nodes.Children;
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.datamodel.Content;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskDataException;
/**
* A structural node in the main tree view when the user has selected the group
* by persons/hosts option. Instances of this node appear as children of a node
* representing a data source association with a host, and as a parent of a data
* source node. For example: "Host X" -> "Data Source Y" -> "Data Source Files"
* -> "Data Source Y", where "Data Source Files" is an instance of this node.
*/
public class DataSourceFilesNode extends DisplayableItemNode {
private static final String NAME = NbBundle.getMessage(DataSourceFilesNode.class, "DataSourcesNode.name");
/**
* @return The name used to identify the node of this type with a lookup.
*/
public static String getNameIdentifier() {
return NAME;
}
private final String displayName;
// NOTE: The images passed in via argument will be ignored.
@Deprecated
public DataSourceFilesNode(List<Content> images) {
this(0);
}
public DataSourceFilesNode() {
this(0);
}
public DataSourceFilesNode(long dsObjId) {
super(Children.create(new DataSourcesNodeChildren(dsObjId), false), Lookups.singleton(NAME));
displayName = (dsObjId > 0) ? NbBundle.getMessage(DataSourceFilesNode.class, "DataSourcesNode.group_by_datasource.name") : NAME;
init();
}
private void init() {
setName(NAME);
setDisplayName(displayName);
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/image.png"); //NON-NLS
}
@Override
public String getItemType() {
return getClass().getName();
}
/*
* Custom Keys implementation that listens for new data sources being added.
*/
public static class DataSourcesNodeChildren extends AbstractContentChildren<Content> {
private static final Logger logger = Logger.getLogger(DataSourcesNodeChildren.class.getName());
private final long datasourceObjId;
List<Content> currentKeys;
public DataSourcesNodeChildren() {
this(0);
}
public DataSourcesNodeChildren(long dsObjId) {
super("ds_" + Long.toString(dsObjId));
this.currentKeys = new ArrayList<>();
this.datasourceObjId = dsObjId;
}
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())) {
refresh(true);
}
}
};
@Override
protected void onAdd() {
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED), pcl);
}
@Override
protected void onRemove() {
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED), pcl);
currentKeys.clear();
}
@Override
protected List<Content> makeKeys() {
try {
if (datasourceObjId == 0) {
currentKeys = Case.getCurrentCaseThrows().getDataSources();
} else {
Content content = Case.getCurrentCaseThrows().getSleuthkitCase().getDataSource(datasourceObjId);
currentKeys = new ArrayList<>(Arrays.asList(content));
}
Collections.sort(currentKeys, new Comparator<Content>() {
@Override
public int compare(Content content1, Content content2) {
String content1Name = content1.getName().toLowerCase();
String content2Name = content2.getName().toLowerCase();
return content1Name.compareTo(content2Name);
}
});
} catch (TskCoreException | NoCurrentCaseException | TskDataException ex) {
logger.log(Level.SEVERE, "Error getting data sources: {0}", ex.getMessage()); // NON-NLS
}
return currentKeys;
}
}
@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(), "DataSourcesNode.createSheet.name.name"),
NbBundle.getMessage(this.getClass(), "DataSourcesNode.createSheet.name.displayName"),
NbBundle.getMessage(this.getClass(), "DataSourcesNode.createSheet.name.desc"),
NAME));
return sheet;
}
}

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011 Basis Technology Corp. * Copyright 2011-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -19,7 +19,7 @@
package org.sleuthkit.autopsy.datamodel; package org.sleuthkit.autopsy.datamodel;
/** /**
* Root node to store the data sources in a case * An "Autopsy visitable item" that supplies a
*/ */
public class DataSources implements AutopsyVisitableItem { public class DataSources implements AutopsyVisitableItem {

View File

@ -1,156 +0,0 @@
/*
* 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.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;
import org.openide.nodes.Sheet;
import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
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.TskCoreException;
/**
* Root node for hosts displaying only data sources (no results, reports, etc.).
*/
@Messages({
"DataSourcesHostsNode_name=Data Sources"
})
public class DataSourcesByTypeNode extends DisplayableItemNode {
/*
* Custom Keys implementation that listens for new data sources being added.
*/
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 (UPDATE_EVT_STRS.contains(eventType)) {
refresh(true);
}
}
};
@Override
protected void addNotify() {
Case.addEventTypeSubscriber(UPDATE_EVTS, pcl);
}
@Override
protected void removeNotify() {
Case.removeEventTypeSubscriber(UPDATE_EVTS, pcl);
}
@Override
protected boolean createKeys(List<HostDataSources> toPopulate) {
try {
Case.getCurrentCaseThrows().getSleuthkitCase().getHostManager().getAllHosts().stream()
.map(HostDataSources::new)
.sorted()
.forEach(toPopulate::add);
} catch (TskCoreException | NoCurrentCaseException ex) {
logger.log(Level.SEVERE, "Error getting data sources: {0}", ex.getMessage()); // NON-NLS
}
return true;
}
@Override
protected Node createNodeForKey(HostDataSources key) {
return new HostNode(key);
}
}
private static final String NAME = Bundle.DataSourcesHostsNode_name();
/**
* @return The name used to identify the node of this type with a lookup.
*/
public static String getNameIdentifier() {
return NAME;
}
/**
* Main constructor.
*/
DataSourcesByTypeNode() {
super(Children.create(new DataSourcesByTypeChildren(), false), Lookups.singleton(NAME));
setName(NAME);
setDisplayName(NAME);
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/image.png");
}
@Override
public String getItemType() {
return getClass().getName();
}
@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(), "DataSourcesNode.createSheet.name.name"),
NbBundle.getMessage(this.getClass(), "DataSourcesNode.createSheet.name.displayName"),
NbBundle.getMessage(this.getClass(), "DataSourcesNode.createSheet.name.desc"),
NAME));
return sheet;
}
}

View File

@ -1,15 +1,15 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2018 Basis Technology Corp. * Copyright 2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -20,135 +20,117 @@ package org.sleuthkit.autopsy.datamodel;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.stream.Collectors;
import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children; import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.openide.nodes.Sheet; import org.openide.nodes.Sheet;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
import org.openide.util.lookup.Lookups; import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskDataException;
/** /**
* Nodes for the images * A top-level structural node (child of the invisible root node) in the main
* tree view when the user has selected the group by data type option. It
* appears as the parent node of the "directory tree" nodes that are the roots
* of the file trees for the individual data sources in a case. For example:
* "Data Sources" -> "Data Source X", "Data Source Y", where "Data Sources" is
* an instance of this node. The siblings of this node are the "Views, "Analysis
* Results," "Os Accounts," "Tags," and "Reports" nodes.
*/ */
@Messages({
"DataSourcesHostsNode_name=Data Sources"
})
public class DataSourcesNode extends DisplayableItemNode { public class DataSourcesNode extends DisplayableItemNode {
private static final String NAME = NbBundle.getMessage(DataSourcesNode.class, "DataSourcesNode.name");
/**
* @return The name used to identify the node of this type with a lookup.
*/
public static String getNameIdentifier() {
return NAME;
}
private final String displayName;
// NOTE: The images passed in via argument will be ignored.
@Deprecated
public DataSourcesNode(List<Content> images) {
this(0);
}
public DataSourcesNode() {
this(0);
}
public DataSourcesNode(long dsObjId) {
super(Children.create(new DataSourcesNodeChildren(dsObjId), false), Lookups.singleton(NAME));
displayName = (dsObjId > 0) ? NbBundle.getMessage(DataSourcesNode.class, "DataSourcesNode.group_by_datasource.name") : NAME;
init();
}
private void init() {
setName(NAME);
setDisplayName(displayName);
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/image.png"); //NON-NLS
}
@Override
public String getItemType() {
return getClass().getName();
}
/* /*
* Custom Keys implementation that listens for new data sources being added. * Custom Keys implementation that listens for new data sources being added.
*/ */
public static class DataSourcesNodeChildren extends AbstractContentChildren<Content> { public static class DataSourcesByTypeChildren extends ChildFactory.Detachable<HostDataSources> {
private static final Logger logger = Logger.getLogger(DataSourcesNodeChildren.class.getName()); private static final Set<Case.Events> UPDATE_EVTS = EnumSet.of(Case.Events.DATA_SOURCE_ADDED,
private final long datasourceObjId; Case.Events.HOSTS_ADDED,
Case.Events.HOSTS_DELETED,
List<Content> currentKeys; Case.Events.HOSTS_UPDATED);
public DataSourcesNodeChildren() { private static final Set<String> UPDATE_EVT_STRS = UPDATE_EVTS.stream()
this(0); .map(evt -> evt.name())
} .collect(Collectors.toSet());
private static final Logger logger = Logger.getLogger(DataSourcesByTypeChildren.class.getName());
public DataSourcesNodeChildren(long dsObjId) {
super("ds_" + Long.toString(dsObjId));
this.currentKeys = new ArrayList<>();
this.datasourceObjId = dsObjId;
}
private final PropertyChangeListener pcl = new PropertyChangeListener() { private final PropertyChangeListener pcl = new PropertyChangeListener() {
@Override @Override
public void propertyChange(PropertyChangeEvent evt) { public void propertyChange(PropertyChangeEvent evt) {
String eventType = evt.getPropertyName(); String eventType = evt.getPropertyName();
if (eventType.equals(Case.Events.DATA_SOURCE_ADDED.toString())) { if (UPDATE_EVT_STRS.contains(eventType)) {
refresh(true); refresh(true);
} }
} }
}; };
@Override @Override
protected void onAdd() { protected void addNotify() {
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED), pcl); Case.addEventTypeSubscriber(UPDATE_EVTS, pcl);
} }
@Override @Override
protected void onRemove() { protected void removeNotify() {
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED), pcl); Case.removeEventTypeSubscriber(UPDATE_EVTS, pcl);
currentKeys.clear();
} }
@Override @Override
protected List<Content> makeKeys() { protected boolean createKeys(List<HostDataSources> toPopulate) {
try { try {
if (datasourceObjId == 0) { Case.getCurrentCaseThrows().getSleuthkitCase().getHostManager().getAllHosts().stream()
currentKeys = Case.getCurrentCaseThrows().getDataSources(); .map(HostDataSources::new)
} .sorted()
else { .forEach(toPopulate::add);
Content content = Case.getCurrentCaseThrows().getSleuthkitCase().getDataSource(datasourceObjId);
currentKeys = new ArrayList<>(Arrays.asList(content));
}
Collections.sort(currentKeys, new Comparator<Content>() {
@Override
public int compare(Content content1, Content content2) {
String content1Name = content1.getName().toLowerCase();
String content2Name = content2.getName().toLowerCase();
return content1Name.compareTo(content2Name);
}
}); } catch (TskCoreException | NoCurrentCaseException ex) {
} catch (TskCoreException | NoCurrentCaseException | TskDataException ex) {
logger.log(Level.SEVERE, "Error getting data sources: {0}", ex.getMessage()); // NON-NLS logger.log(Level.SEVERE, "Error getting data sources: {0}", ex.getMessage()); // NON-NLS
} }
return currentKeys; return true;
} }
@Override
protected Node createNodeForKey(HostDataSources key) {
return new HostNode(key);
}
}
private static final String NAME = Bundle.DataSourcesHostsNode_name();
/**
* @return The name used to identify the node of this type with a lookup.
*/
public static String getNameIdentifier() {
return NAME;
}
/**
* Main constructor.
*/
DataSourcesNode() {
super(Children.create(new DataSourcesByTypeChildren(), false), Lookups.singleton(NAME));
setName(NAME);
setDisplayName(NAME);
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/image.png");
}
@Override
public String getItemType() {
return getClass().getName();
} }
@Override @Override
@ -176,4 +158,4 @@ public class DataSourcesNode extends DisplayableItemNode {
NAME)); NAME));
return sheet; return sheet;
} }
} }

View File

@ -42,7 +42,7 @@ public interface DisplayableItemNodeVisitor<T> {
/* /*
* Data Sources Area * Data Sources Area
*/ */
T visit(DataSourcesNode in); T visit(DataSourceFilesNode in);
T visit(LayoutFileNode lfn); T visit(LayoutFileNode lfn);
@ -192,11 +192,11 @@ public interface DisplayableItemNodeVisitor<T> {
T visit(OsAccounts.OsAccountListNode node); T visit(OsAccounts.OsAccountListNode node);
T visit(PersonGroupingNode node); T visit(PersonNode node);
T visit(HostNode node); T visit(HostNode node);
T visit(DataSourcesByTypeNode node); T visit(DataSourcesNode node);
/* /*
* Unsupported node * Unsupported node
@ -406,7 +406,7 @@ public interface DisplayableItemNodeVisitor<T> {
} }
@Override @Override
public T visit(DataSourcesNode in) { public T visit(DataSourceFilesNode in) {
return defaultVisit(in); return defaultVisit(in);
} }
@ -561,12 +561,12 @@ public interface DisplayableItemNodeVisitor<T> {
} }
@Override @Override
public T visit(DataSourcesByTypeNode node) { public T visit(DataSourcesNode node) {
return defaultVisit(node); return defaultVisit(node);
} }
@Override @Override
public T visit(PersonGroupingNode node) { public T visit(PersonNode node) {
return defaultVisit(node); return defaultVisit(node);
} }

View File

@ -50,6 +50,7 @@ import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery; import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.autopsy.datamodel.Artifacts.UpdatableCountTypeNode; import org.sleuthkit.autopsy.datamodel.Artifacts.UpdatableCountTypeNode;
import org.sleuthkit.datamodel.DataArtifact;
/** /**
* Support for TSK_EMAIL_MSG nodes and displaying emails in the directory tree. * Support for TSK_EMAIL_MSG nodes and displaying emails in the directory tree.
@ -161,7 +162,7 @@ public class EmailExtracted implements AutopsyVisitableItem {
int pathAttrId = BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH.getTypeID(); int pathAttrId = BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH.getTypeID();
String query = "SELECT \n" String query = "SELECT \n"
+ " art.artifact_id AS artifact_id,\n" + " art.artifact_obj_id AS artifact_obj_id,\n"
+ " (SELECT value_text FROM blackboard_attributes attr\n" + " (SELECT value_text FROM blackboard_attributes attr\n"
+ " WHERE attr.artifact_id = art.artifact_id AND attr.attribute_type_id = " + pathAttrId + "\n" + " WHERE attr.artifact_id = art.artifact_id AND attr.attribute_type_id = " + pathAttrId + "\n"
+ " LIMIT 1) AS value_text\n" + " LIMIT 1) AS value_text\n"
@ -176,14 +177,14 @@ public class EmailExtracted implements AutopsyVisitableItem {
try (CaseDbQuery dbQuery = skCase.executeQuery(query)) { try (CaseDbQuery dbQuery = skCase.executeQuery(query)) {
ResultSet resultSet = dbQuery.getResultSet(); ResultSet resultSet = dbQuery.getResultSet();
while (resultSet.next()) { while (resultSet.next()) {
Long artifactId = resultSet.getLong("artifact_id"); Long artifactObjId = resultSet.getLong("artifact_obj_id");
Map<String, String> accountFolderMap = parsePath(resultSet.getString("value_text")); Map<String, String> accountFolderMap = parsePath(resultSet.getString("value_text"));
String account = accountFolderMap.get(MAIL_ACCOUNT); String account = accountFolderMap.get(MAIL_ACCOUNT);
String folder = accountFolderMap.get(MAIL_FOLDER); String folder = accountFolderMap.get(MAIL_FOLDER);
Map<String, List<Long>> folders = newMapping.computeIfAbsent(account, (str) -> new LinkedHashMap<>()); Map<String, List<Long>> folders = newMapping.computeIfAbsent(account, (str) -> new LinkedHashMap<>());
List<Long> messages = folders.computeIfAbsent(folder, (str) -> new ArrayList<>()); List<Long> messages = folders.computeIfAbsent(folder, (str) -> new ArrayList<>());
messages.add(artifactId); messages.add(artifactObjId);
} }
} catch (TskCoreException | SQLException ex) { } catch (TskCoreException | SQLException ex) {
logger.log(Level.WARNING, "Cannot initialize email extraction: ", ex); //NON-NLS logger.log(Level.WARNING, "Cannot initialize email extraction: ", ex); //NON-NLS
@ -499,7 +500,7 @@ public class EmailExtracted implements AutopsyVisitableItem {
/** /**
* Node representing mail folder content (mail messages) * Node representing mail folder content (mail messages)
*/ */
private class MessageFactory extends BaseChildFactory<BlackboardArtifact> implements Observer { private class MessageFactory extends BaseChildFactory<DataArtifact> implements Observer {
private final String accountName; private final String accountName;
private final String folderName; private final String folderName;
@ -512,7 +513,7 @@ public class EmailExtracted implements AutopsyVisitableItem {
} }
@Override @Override
protected Node createNodeForKey(BlackboardArtifact art) { protected Node createNodeForKey(DataArtifact art) {
return new BlackboardArtifactNode(art); return new BlackboardArtifactNode(art);
} }
@ -522,13 +523,13 @@ public class EmailExtracted implements AutopsyVisitableItem {
} }
@Override @Override
protected List<BlackboardArtifact> makeKeys() { protected List<DataArtifact> makeKeys() {
List<BlackboardArtifact> keys = new ArrayList<>(); List<DataArtifact> keys = new ArrayList<>();
if (skCase != null) { if (skCase != null) {
emailResults.getArtifactIds(accountName, folderName).forEach((id) -> { emailResults.getArtifactIds(accountName, folderName).forEach((id) -> {
try { try {
BlackboardArtifact art = skCase.getBlackboardArtifact(id); DataArtifact art = skCase.getBlackboard().getDataArtifactById(id);
//Cache attributes while we are off the EDT. //Cache attributes while we are off the EDT.
//See JIRA-5969 //See JIRA-5969
art.getAttributes(); art.getAttributes();

View File

@ -481,5 +481,10 @@ public final class FileTypes implements AutopsyVisitableItem {
public List<AnalysisResult> getAllAnalysisResults() throws TskCoreException { public List<AnalysisResult> getAllAnalysisResults() throws TskCoreException {
return content.getAllAnalysisResults(); return content.getAllAnalysisResults();
} }
@Override
public List<DataArtifact> getAllDataArtifacts() throws TskCoreException {
return content.getAllDataArtifacts();
}
} }
} }

View File

@ -52,6 +52,7 @@ import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery; import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.autopsy.datamodel.Artifacts.UpdatableCountTypeNode; import org.sleuthkit.autopsy.datamodel.Artifacts.UpdatableCountTypeNode;
import org.sleuthkit.datamodel.AnalysisResult;
/** /**
* Hash set hits node support. Inner classes have all of the nodes in the tree. * Hash set hits node support. Inner classes have all of the nodes in the tree.
@ -136,7 +137,7 @@ public class HashsetHits implements AutopsyVisitableItem {
int setNameId = ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID(); int setNameId = ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID();
int artId = TSK_HASHSET_HIT.getTypeID(); int artId = TSK_HASHSET_HIT.getTypeID();
String query = "SELECT value_text,blackboard_attributes.artifact_id,attribute_type_id " //NON-NLS String query = "SELECT value_text,blackboard_artifacts.artifact_obj_id,attribute_type_id " //NON-NLS
+ "FROM blackboard_attributes,blackboard_artifacts WHERE " //NON-NLS + "FROM blackboard_attributes,blackboard_artifacts WHERE " //NON-NLS
+ "attribute_type_id=" + setNameId //NON-NLS + "attribute_type_id=" + setNameId //NON-NLS
+ " AND blackboard_attributes.artifact_id=blackboard_artifacts.artifact_id" //NON-NLS + " AND blackboard_attributes.artifact_id=blackboard_artifacts.artifact_id" //NON-NLS
@ -150,11 +151,11 @@ public class HashsetHits implements AutopsyVisitableItem {
synchronized (hashSetHitsMap) { synchronized (hashSetHitsMap) {
while (resultSet.next()) { while (resultSet.next()) {
String setName = resultSet.getString("value_text"); //NON-NLS String setName = resultSet.getString("value_text"); //NON-NLS
long artifactId = resultSet.getLong("artifact_id"); //NON-NLS long artifactObjId = resultSet.getLong("artifact_obj_id"); //NON-NLS
if (!hashSetHitsMap.containsKey(setName)) { if (!hashSetHitsMap.containsKey(setName)) {
hashSetHitsMap.put(setName, new HashSet<>()); hashSetHitsMap.put(setName, new HashSet<>());
} }
hashSetHitsMap.get(setName).add(artifactId); hashSetHitsMap.get(setName).add(artifactObjId);
} }
} }
} catch (TskCoreException | SQLException ex) { } catch (TskCoreException | SQLException ex) {
@ -380,10 +381,10 @@ public class HashsetHits implements AutopsyVisitableItem {
/** /**
* Creates the nodes for the hits in a given set. * Creates the nodes for the hits in a given set.
*/ */
private class HitFactory extends BaseChildFactory<BlackboardArtifact> implements Observer { private class HitFactory extends BaseChildFactory<AnalysisResult> implements Observer {
private final String hashsetName; private final String hashsetName;
private final Map<Long, BlackboardArtifact> artifactHits = new HashMap<>(); private final Map<Long, AnalysisResult> artifactHits = new HashMap<>();
private HitFactory(String hashsetName) { private HitFactory(String hashsetName) {
super(hashsetName); super(hashsetName);
@ -401,7 +402,7 @@ public class HashsetHits implements AutopsyVisitableItem {
} }
@Override @Override
protected Node createNodeForKey(BlackboardArtifact key) { protected Node createNodeForKey(AnalysisResult key) {
return new BlackboardArtifactNode(key); return new BlackboardArtifactNode(key);
} }
@ -411,13 +412,13 @@ public class HashsetHits implements AutopsyVisitableItem {
} }
@Override @Override
protected List<BlackboardArtifact> makeKeys() { protected List<AnalysisResult> makeKeys() {
if (skCase != null) { if (skCase != null) {
hashsetResults.getArtifactIds(hashsetName).forEach((id) -> { hashsetResults.getArtifactIds(hashsetName).forEach((id) -> {
try { try {
if (!artifactHits.containsKey(id)) { if (!artifactHits.containsKey(id)) {
BlackboardArtifact art = skCase.getBlackboardArtifact(id); AnalysisResult art = skCase.getBlackboard().getAnalysisResultById(id);
//Cache attributes while we are off the EDT. //Cache attributes while we are off the EDT.
//See JIRA-5969 //See JIRA-5969
art.getAttributes(); art.getAttributes();

View File

@ -39,7 +39,7 @@ import org.openide.util.WeakListeners;
import org.openide.util.lookup.Lookups; import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.casemodule.events.HostsChangedEvent; import org.sleuthkit.autopsy.casemodule.events.HostsUpdatedEvent;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.hosts.AssociatePersonsMenuAction; import org.sleuthkit.autopsy.datamodel.hosts.AssociatePersonsMenuAction;
import org.sleuthkit.autopsy.datamodel.hosts.MergeHostMenuAction; import org.sleuthkit.autopsy.datamodel.hosts.MergeHostMenuAction;
@ -178,8 +178,8 @@ public class HostNode extends DisplayableItemNode {
@Override @Override
public void propertyChange(PropertyChangeEvent evt) { public void propertyChange(PropertyChangeEvent evt) {
String eventType = evt.getPropertyName(); String eventType = evt.getPropertyName();
if (hostId != null && eventType.equals(Case.Events.HOSTS_CHANGED.toString()) && evt instanceof HostsChangedEvent) { if (hostId != null && eventType.equals(Case.Events.HOSTS_UPDATED.toString()) && evt instanceof HostsUpdatedEvent) {
((HostsChangedEvent) evt).getNewValue().stream() ((HostsUpdatedEvent) evt).getHosts().stream()
.filter(h -> h != null && h.getHostId() == hostId) .filter(h -> h != null && h.getHostId() == hostId)
.findFirst() .findFirst()
.ifPresent((newHost) -> { .ifPresent((newHost) -> {
@ -247,7 +247,7 @@ public class HostNode extends DisplayableItemNode {
host == null ? Lookups.fixed(displayName) : Lookups.fixed(host, displayName)); host == null ? Lookups.fixed(displayName) : Lookups.fixed(host, displayName));
hostId = host == null ? null : host.getHostId(); hostId = host == null ? null : host.getHostId();
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.HOSTS_CHANGED), Case.addEventTypeSubscriber(EnumSet.of(Case.Events.HOSTS_UPDATED),
WeakListeners.propertyChange(hostChangePcl, this)); WeakListeners.propertyChange(hostChangePcl, this));
super.setName(displayName); super.setName(displayName);
super.setDisplayName(displayName); super.setDisplayName(displayName);

View File

@ -282,20 +282,6 @@ public class ImageNode extends AbstractContentNode<Image> {
return null; 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. * Returns comment property for the node.
* *

View File

@ -51,6 +51,7 @@ import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery; import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.autopsy.datamodel.Artifacts.UpdatableCountTypeNode; import org.sleuthkit.autopsy.datamodel.Artifacts.UpdatableCountTypeNode;
import org.sleuthkit.datamodel.AnalysisResult;
public class InterestingHits implements AutopsyVisitableItem { public class InterestingHits implements AutopsyVisitableItem {
@ -129,7 +130,7 @@ public class InterestingHits implements AutopsyVisitableItem {
int setNameId = BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID(); int setNameId = BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID();
int artId = artType.getTypeID(); int artId = artType.getTypeID();
String query = "SELECT value_text,blackboard_attributes.artifact_id,attribute_type_id " //NON-NLS String query = "SELECT value_text,blackboard_artifacts.artifact_obj_id,attribute_type_id " //NON-NLS
+ "FROM blackboard_attributes,blackboard_artifacts WHERE " //NON-NLS + "FROM blackboard_attributes,blackboard_artifacts WHERE " //NON-NLS
+ "attribute_type_id=" + setNameId //NON-NLS + "attribute_type_id=" + setNameId //NON-NLS
+ " AND blackboard_attributes.artifact_id=blackboard_artifacts.artifact_id" //NON-NLS + " AND blackboard_attributes.artifact_id=blackboard_artifacts.artifact_id" //NON-NLS
@ -143,13 +144,13 @@ public class InterestingHits implements AutopsyVisitableItem {
ResultSet resultSet = dbQuery.getResultSet(); ResultSet resultSet = dbQuery.getResultSet();
while (resultSet.next()) { while (resultSet.next()) {
String value = resultSet.getString("value_text"); //NON-NLS String value = resultSet.getString("value_text"); //NON-NLS
long artifactId = resultSet.getLong("artifact_id"); //NON-NLS long artifactObjId = resultSet.getLong("artifact_obj_id"); //NON-NLS
if (!interestingItemsMap.containsKey(value)) { if (!interestingItemsMap.containsKey(value)) {
interestingItemsMap.put(value, new LinkedHashMap<>()); interestingItemsMap.put(value, new LinkedHashMap<>());
interestingItemsMap.get(value).put(BlackboardArtifact.Type.TSK_INTERESTING_FILE_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).put(BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT.getDisplayName(), new HashSet<>());
} }
interestingItemsMap.get(value).get(artType.getDisplayName()).add(artifactId); interestingItemsMap.get(value).get(artType.getDisplayName()).add(artifactObjId);
} }
} }
} catch (TskCoreException | SQLException ex) { } catch (TskCoreException | SQLException ex) {
@ -459,11 +460,11 @@ public class InterestingHits implements AutopsyVisitableItem {
} }
} }
private class HitFactory extends BaseChildFactory<BlackboardArtifact> implements Observer { private class HitFactory extends BaseChildFactory<AnalysisResult> implements Observer {
private final String setName; private final String setName;
private final String typeName; private final String typeName;
private final Map<Long, BlackboardArtifact> artifactHits = new HashMap<>(); private final Map<Long, AnalysisResult> artifactHits = new HashMap<>();
private HitFactory(String setName, String typeName) { private HitFactory(String setName, String typeName) {
/** /**
@ -478,13 +479,13 @@ public class InterestingHits implements AutopsyVisitableItem {
} }
@Override @Override
protected List<BlackboardArtifact> makeKeys() { protected List<AnalysisResult> makeKeys() {
if (skCase != null) { if (skCase != null) {
interestingResults.getArtifactIds(setName, typeName).forEach((id) -> { interestingResults.getArtifactIds(setName, typeName).forEach((id) -> {
try { try {
if (!artifactHits.containsKey(id)) { if (!artifactHits.containsKey(id)) {
BlackboardArtifact art = skCase.getBlackboardArtifact(id); AnalysisResult art = skCase.getBlackboard().getAnalysisResultById(id);
//Cache attributes while we are off the EDT. //Cache attributes while we are off the EDT.
//See JIRA-5969 //See JIRA-5969
art.getAttributes(); art.getAttributes();
@ -501,7 +502,7 @@ public class InterestingHits implements AutopsyVisitableItem {
} }
@Override @Override
protected Node createNodeForKey(BlackboardArtifact art) { protected Node createNodeForKey(AnalysisResult art) {
return new BlackboardArtifactNode(art); return new BlackboardArtifactNode(art);
} }

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2020 Basis Technology Corp. * Copyright 2011-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -46,6 +46,7 @@ import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.TimeZoneUtils;
import static org.sleuthkit.autopsy.datamodel.Bundle.*; import static org.sleuthkit.autopsy.datamodel.Bundle.*;
import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.ModuleDataEvent; import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
@ -57,6 +58,7 @@ import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_KEYWORD_HIT; import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_KEYWORD_HIT;
import org.sleuthkit.autopsy.datamodel.Artifacts.UpdatableCountTypeNode; import org.sleuthkit.autopsy.datamodel.Artifacts.UpdatableCountTypeNode;
import org.sleuthkit.datamodel.AnalysisResult;
/** /**
* Keyword hits node support * Keyword hits node support
@ -91,7 +93,7 @@ public class KeywordHits implements AutopsyVisitableItem {
*/ */
private static final String KEYWORD_HIT_ATTRIBUTES_QUERY = "SELECT blackboard_attributes.value_text, "//NON-NLS private static final String KEYWORD_HIT_ATTRIBUTES_QUERY = "SELECT blackboard_attributes.value_text, "//NON-NLS
+ "blackboard_attributes.value_int32, "//NON-NLS + "blackboard_attributes.value_int32, "//NON-NLS
+ "blackboard_attributes.artifact_id, " //NON-NLS + "blackboard_artifacts.artifact_obj_id, " //NON-NLS
+ "blackboard_attributes.attribute_type_id "//NON-NLS + "blackboard_attributes.attribute_type_id "//NON-NLS
+ "FROM blackboard_attributes, blackboard_artifacts "//NON-NLS + "FROM blackboard_attributes, blackboard_artifacts "//NON-NLS
+ "WHERE blackboard_attributes.artifact_id = blackboard_artifacts.artifact_id "//NON-NLS + "WHERE blackboard_attributes.artifact_id = blackboard_artifacts.artifact_id "//NON-NLS
@ -349,12 +351,12 @@ public class KeywordHits implements AutopsyVisitableItem {
try (CaseDbQuery dbQuery = skCase.executeQuery(queryStr)) { try (CaseDbQuery dbQuery = skCase.executeQuery(queryStr)) {
ResultSet resultSet = dbQuery.getResultSet(); ResultSet resultSet = dbQuery.getResultSet();
while (resultSet.next()) { while (resultSet.next()) {
long artifactId = resultSet.getLong("artifact_id"); //NON-NLS long artifactObjId = resultSet.getLong("artifact_obj_id"); //NON-NLS
long typeId = resultSet.getLong("attribute_type_id"); //NON-NLS long typeId = resultSet.getLong("attribute_type_id"); //NON-NLS
String valueStr = resultSet.getString("value_text"); //NON-NLS String valueStr = resultSet.getString("value_text"); //NON-NLS
//get the map of attributes for this artifact //get the map of attributes for this artifact
Map<Long, String> attributesByTypeMap = artifactIds.computeIfAbsent(artifactId, ai -> new LinkedHashMap<>()); Map<Long, String> attributesByTypeMap = artifactIds.computeIfAbsent(artifactObjId, ai -> new LinkedHashMap<>());
if (StringUtils.isNotEmpty(valueStr)) { if (StringUtils.isNotEmpty(valueStr)) {
attributesByTypeMap.put(typeId, valueStr); attributesByTypeMap.put(typeId, valueStr);
} else { } else {
@ -858,7 +860,7 @@ public class KeywordHits implements AutopsyVisitableItem {
"KeywordHits.createNodeForKey.chgTime.name=ChangeTime", "KeywordHits.createNodeForKey.chgTime.name=ChangeTime",
"KeywordHits.createNodeForKey.chgTime.displayName=Change Time", "KeywordHits.createNodeForKey.chgTime.displayName=Change Time",
"KeywordHits.createNodeForKey.chgTime.desc=Change Time"}) "KeywordHits.createNodeForKey.chgTime.desc=Change Time"})
private BlackboardArtifactNode createBlackboardArtifactNode(BlackboardArtifact art) { private BlackboardArtifactNode createBlackboardArtifactNode(AnalysisResult art) {
if (skCase == null) { if (skCase == null) {
return null; return null;
} }
@ -888,29 +890,29 @@ public class KeywordHits implements AutopsyVisitableItem {
KeywordHits_createNodeForKey_modTime_name(), KeywordHits_createNodeForKey_modTime_name(),
KeywordHits_createNodeForKey_modTime_displayName(), KeywordHits_createNodeForKey_modTime_displayName(),
KeywordHits_createNodeForKey_modTime_desc(), KeywordHits_createNodeForKey_modTime_desc(),
ContentUtils.getStringTime(file.getMtime(), file))); TimeZoneUtils.getFormattedTime(file.getMtime())));
n.addNodeProperty(new NodeProperty<>( n.addNodeProperty(new NodeProperty<>(
KeywordHits_createNodeForKey_accessTime_name(), KeywordHits_createNodeForKey_accessTime_name(),
KeywordHits_createNodeForKey_accessTime_displayName(), KeywordHits_createNodeForKey_accessTime_displayName(),
KeywordHits_createNodeForKey_accessTime_desc(), KeywordHits_createNodeForKey_accessTime_desc(),
ContentUtils.getStringTime(file.getAtime(), file))); TimeZoneUtils.getFormattedTime(file.getAtime())));
n.addNodeProperty(new NodeProperty<>( n.addNodeProperty(new NodeProperty<>(
KeywordHits_createNodeForKey_chgTime_name(), KeywordHits_createNodeForKey_chgTime_name(),
KeywordHits_createNodeForKey_chgTime_displayName(), KeywordHits_createNodeForKey_chgTime_displayName(),
KeywordHits_createNodeForKey_chgTime_desc(), KeywordHits_createNodeForKey_chgTime_desc(),
ContentUtils.getStringTime(file.getCtime(), file))); TimeZoneUtils.getFormattedTime(file.getCtime())));
return n; return n;
} }
/** /**
* Creates nodes for individual files that had hits * Creates nodes for individual files that had hits
*/ */
private class HitsFactory extends BaseChildFactory<BlackboardArtifact> implements Observer { private class HitsFactory extends BaseChildFactory<AnalysisResult> implements Observer {
private final String keyword; private final String keyword;
private final String setName; private final String setName;
private final String instance; private final String instance;
private final Map<Long, BlackboardArtifact> artifactHits = new HashMap<>(); private final Map<Long, AnalysisResult> artifactHits = new HashMap<>();
private HitsFactory(String setName, String keyword, String instance) { private HitsFactory(String setName, String keyword, String instance) {
/** /**
@ -926,12 +928,12 @@ public class KeywordHits implements AutopsyVisitableItem {
} }
@Override @Override
protected List<BlackboardArtifact> makeKeys() { protected List<AnalysisResult> makeKeys() {
if (skCase != null) { if (skCase != null) {
keywordResults.getArtifactIds(setName, keyword, instance).forEach((id) -> { keywordResults.getArtifactIds(setName, keyword, instance).forEach((id) -> {
try { try {
if (!artifactHits.containsKey(id)) { if (!artifactHits.containsKey(id)) {
BlackboardArtifact art = skCase.getBlackboardArtifact(id); AnalysisResult art = skCase.getBlackboard().getAnalysisResultById(id);
//Cache attributes while we are off the EDT. //Cache attributes while we are off the EDT.
//See JIRA-5969 //See JIRA-5969
art.getAttributes(); art.getAttributes();
@ -948,7 +950,7 @@ public class KeywordHits implements AutopsyVisitableItem {
} }
@Override @Override
protected Node createNodeForKey(BlackboardArtifact art) { protected Node createNodeForKey(AnalysisResult art) {
return createBlackboardArtifactNode(art); return createBlackboardArtifactNode(art);
} }

View File

@ -39,10 +39,11 @@ import org.openide.util.Exceptions;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.openide.util.WeakListeners; import org.openide.util.WeakListeners;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.events.OsAccountChangedEvent; import org.sleuthkit.autopsy.casemodule.events.OsAccountsUpdatedEvent;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable; import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.TimeZoneUtils;
import static org.sleuthkit.autopsy.datamodel.AbstractContentNode.backgroundTasksPool; import static org.sleuthkit.autopsy.datamodel.AbstractContentNode.backgroundTasksPool;
import org.sleuthkit.autopsy.events.AutopsyEvent; import org.sleuthkit.autopsy.events.AutopsyEvent;
import org.sleuthkit.datamodel.Host; import org.sleuthkit.datamodel.Host;
@ -124,8 +125,8 @@ public final class OsAccounts implements AutopsyVisitableItem {
@Override @Override
public void propertyChange(PropertyChangeEvent evt) { public void propertyChange(PropertyChangeEvent evt) {
String eventType = evt.getPropertyName(); String eventType = evt.getPropertyName();
if (eventType.equals(Case.Events.OS_ACCOUNT_ADDED.toString()) if (eventType.equals(Case.Events.OS_ACCOUNTS_ADDED.toString())
|| eventType.equals(Case.Events.OS_ACCOUNT_REMOVED.toString())) { || eventType.equals(Case.Events.OS_ACCOUNTS_DELETED.toString())) {
refresh(true); refresh(true);
} else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) { } 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 // case was closed. Remove listeners so that we don't get called with a stale case handle
@ -139,13 +140,13 @@ public final class OsAccounts implements AutopsyVisitableItem {
@Override @Override
protected void addNotify() { protected void addNotify() {
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.OS_ACCOUNT_ADDED, Case.Events.OS_ACCOUNT_REMOVED), listener); Case.addEventTypeSubscriber(EnumSet.of(Case.Events.OS_ACCOUNTS_ADDED, Case.Events.OS_ACCOUNTS_DELETED), listener);
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), listener); Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), listener);
} }
@Override @Override
protected void removeNotify() { protected void removeNotify() {
Case.removeEventTypeSubscriber(Collections.singleton(Case.Events.OS_ACCOUNT_ADDED), listener); Case.removeEventTypeSubscriber(Collections.singleton(Case.Events.OS_ACCOUNTS_ADDED), listener);
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), listener); Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), listener);
} }
@ -183,11 +184,14 @@ public final class OsAccounts implements AutopsyVisitableItem {
private final PropertyChangeListener listener = new PropertyChangeListener() { private final PropertyChangeListener listener = new PropertyChangeListener() {
@Override @Override
public void propertyChange(PropertyChangeEvent evt) { public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals(Case.Events.OS_ACCOUNT_CHANGED.name())) { if (evt.getPropertyName().equals(Case.Events.OS_ACCOUNTS_UPDATED.name())) {
if (((OsAccountChangedEvent) evt).getOsAccount().getId() == account.getId()) { OsAccountsUpdatedEvent updateEvent = (OsAccountsUpdatedEvent) evt;
// Update the account node to the new one for (OsAccount acct : updateEvent.getOsAccounts()) {
account = ((OsAccountChangedEvent) evt).getOsAccount(); if (acct.getId() == account.getId()) {
updateSheet(); account = acct;
updateSheet();
break;
}
} }
} else if (evt.getPropertyName().equals(REALM_DATA_AVAILABLE_EVENT)) { } else if (evt.getPropertyName().equals(REALM_DATA_AVAILABLE_EVENT)) {
OsAccountRealm realm = (OsAccountRealm) evt.getNewValue(); OsAccountRealm realm = (OsAccountRealm) evt.getNewValue();
@ -221,7 +225,7 @@ public final class OsAccounts implements AutopsyVisitableItem {
setDisplayName(account.getName()); setDisplayName(account.getName());
setIconBaseWithExtension(ICON_PATH); setIconBaseWithExtension(ICON_PATH);
Case.addEventTypeSubscriber(Collections.singleton(Case.Events.OS_ACCOUNT_CHANGED), weakListener); Case.addEventTypeSubscriber(Collections.singleton(Case.Events.OS_ACCOUNTS_UPDATED), weakListener);
} }
@Override @Override
@ -301,7 +305,7 @@ public final class OsAccounts implements AutopsyVisitableItem {
Optional<Long> creationTimeValue = account.getCreationTime(); Optional<Long> creationTimeValue = account.getCreationTime();
String timeDisplayStr String timeDisplayStr
= creationTimeValue.isPresent() ? DATE_FORMATTER.format(new java.util.Date(creationTimeValue.get() * 1000)) : ""; = creationTimeValue.isPresent() ? TimeZoneUtils.getFormattedTime(creationTimeValue.get() * 1000) : "";
propertiesSet.put(new NodeProperty<>( propertiesSet.put(new NodeProperty<>(
Bundle.OsAccounts_createdTimeProperty_name(), Bundle.OsAccounts_createdTimeProperty_name(),
@ -333,11 +337,6 @@ public final class OsAccounts implements AutopsyVisitableItem {
return null; return null;
} }
@Override
protected Pair<DataResultViewerTable.Score, String> getScorePropertyAndDescription(List<Tag> tags) {
return null;
}
@Override @Override
protected DataResultViewerTable.HasCommentStatus getCommentProperty(List<Tag> tags, CorrelationAttributeInstance attribute) { protected DataResultViewerTable.HasCommentStatus getCommentProperty(List<Tag> tags, CorrelationAttributeInstance attribute) {
return DataResultViewerTable.HasCommentStatus.NO_COMMENT; return DataResultViewerTable.HasCommentStatus.NO_COMMENT;

View File

@ -1,15 +1,15 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2021 Basis Technology Corp. * Copyright 2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -36,7 +36,7 @@ import org.openide.util.WeakListeners;
import org.openide.util.lookup.Lookups; import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.casemodule.events.PersonsChangedEvent; import org.sleuthkit.autopsy.casemodule.events.PersonsUpdatedEvent;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.persons.DeletePersonAction; import org.sleuthkit.autopsy.datamodel.persons.DeletePersonAction;
import org.sleuthkit.autopsy.datamodel.persons.EditPersonAction; import org.sleuthkit.autopsy.datamodel.persons.EditPersonAction;
@ -45,16 +45,21 @@ import org.sleuthkit.datamodel.Person;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
/** /**
* A node to be displayed in the UI tree for a person and persons grouped in * A main tree view node that represents a person in a case. Its child nodes, if
* this host. * any, represent hosts in the case. There must be at least one person in a case
* for the person nodes layer to appear. If the persons layer is present, any
* hosts that are not associated with a person are grouped under an "Unknown
* Persons" person node.
*/ */
@NbBundle.Messages(value = {"PersonNode_unknownPersonNode_title=Unknown Persons"}) @NbBundle.Messages(value = {"PersonNode_unknownPersonNode_title=Unknown Persons"})
public class PersonGroupingNode extends DisplayableItemNode { public class PersonNode extends DisplayableItemNode {
private static final String ICON_PATH = "org/sleuthkit/autopsy/images/person.png"; private static final String ICON_PATH = "org/sleuthkit/autopsy/images/person.png";
/** /**
* Returns the id of an unknown persons node. This can be used with a node lookup. * Returns the id of an unknown persons node. This can be used with a node
* lookup.
*
* @return The id of an unknown persons node. * @return The id of an unknown persons node.
*/ */
public static String getUnknownPersonId() { public static String getUnknownPersonId() {
@ -68,12 +73,13 @@ public class PersonGroupingNode extends DisplayableItemNode {
private static final Logger logger = Logger.getLogger(PersonChildren.class.getName()); private static final Logger logger = Logger.getLogger(PersonChildren.class.getName());
private static final Set<Case.Events> CHILD_EVENTS = EnumSet.of( private static final Set<Case.Events> HOST_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.HOSTS_ADDED,
Case.Events.HOSTS_ADDED, Case.Events.HOSTS_ADDED,
Case.Events.HOSTS_DELETED, Case.Events.HOSTS_DELETED,
Case.Events.PERSONS_CHANGED); Case.Events.HOSTS_ADDED_TO_PERSON,
Case.Events.HOSTS_REMOVED_FROM_PERSON);
private static final Set<String> CHILD_EVENTS_STR = CHILD_EVENTS.stream()
private static final Set<String> HOST_EVENTS_OF_INTEREST_NAMES = HOST_EVENTS_OF_INTEREST.stream()
.map(ev -> ev.name()) .map(ev -> ev.name())
.collect(Collectors.toSet()); .collect(Collectors.toSet());
@ -89,13 +95,18 @@ public class PersonGroupingNode extends DisplayableItemNode {
} }
/** /**
* Listener for handling adding and removing host events. * Listener for application events that are published when hosts are
* added to or deleted from a case, and for events published when the
* associations between persons and hosts change. If the user has
* selected the group by person/host option for the main tree view,
* these events mean that person nodes in the tree need to be refreshed
* to reflect the structural changes.
*/ */
private final PropertyChangeListener hostAddedDeletedPcl = new PropertyChangeListener() { private final PropertyChangeListener hostAddedDeletedPcl = new PropertyChangeListener() {
@Override @Override
public void propertyChange(PropertyChangeEvent evt) { public void propertyChange(PropertyChangeEvent evt) {
String eventType = evt.getPropertyName(); String eventType = evt.getPropertyName();
if (eventType != null && CHILD_EVENTS_STR.contains(eventType)) { if (eventType != null && HOST_EVENTS_OF_INTEREST_NAMES.contains(eventType)) {
refresh(true); refresh(true);
} }
} }
@ -103,12 +114,12 @@ public class PersonGroupingNode extends DisplayableItemNode {
@Override @Override
protected void addNotify() { protected void addNotify() {
Case.addEventTypeSubscriber(CHILD_EVENTS, hostAddedDeletedPcl); Case.addEventTypeSubscriber(HOST_EVENTS_OF_INTEREST, hostAddedDeletedPcl);
} }
@Override @Override
protected void removeNotify() { protected void removeNotify() {
Case.removeEventTypeSubscriber(CHILD_EVENTS, hostAddedDeletedPcl); Case.removeEventTypeSubscriber(HOST_EVENTS_OF_INTEREST, hostAddedDeletedPcl);
} }
@Override @Override
@ -120,7 +131,12 @@ public class PersonGroupingNode extends DisplayableItemNode {
protected boolean createKeys(List<HostGrouping> toPopulate) { protected boolean createKeys(List<HostGrouping> toPopulate) {
List<Host> hosts = Collections.emptyList(); List<Host> hosts = Collections.emptyList();
try { try {
hosts = Case.getCurrentCaseThrows().getSleuthkitCase().getPersonManager().getHostsForPerson(this.person); if (person != null) {
hosts = Case.getCurrentCaseThrows().getSleuthkitCase().getPersonManager().getHostsForPerson(person);
} else {
// This is the "Unknown Persons" node, get the hosts that are not associated with a person.
hosts = Case.getCurrentCaseThrows().getSleuthkitCase().getPersonManager().getHostsWithoutPersons();
}
} catch (NoCurrentCaseException | TskCoreException ex) { } catch (NoCurrentCaseException | TskCoreException ex) {
String personName = person == null || person.getName() == null ? "<unknown>" : person.getName(); String personName = person == null || person.getName() == null ? "<unknown>" : person.getName();
logger.log(Level.WARNING, String.format("Unable to get data sources for host: %s", personName), ex); logger.log(Level.WARNING, String.format("Unable to get data sources for host: %s", personName), ex);
@ -139,14 +155,15 @@ public class PersonGroupingNode extends DisplayableItemNode {
private final Long personId; private final Long personId;
/** /**
* Listener for handling person change events. * Listener for application events that are published when the properties of
* persons in the case change.
*/ */
private final PropertyChangeListener personChangePcl = new PropertyChangeListener() { private final PropertyChangeListener personChangePcl = new PropertyChangeListener() {
@Override @Override
public void propertyChange(PropertyChangeEvent evt) { public void propertyChange(PropertyChangeEvent evt) {
String eventType = evt.getPropertyName(); String eventType = evt.getPropertyName();
if (personId != null && eventType.equals(Case.Events.PERSONS_CHANGED.toString()) && evt instanceof PersonsChangedEvent) { if (personId != null && eventType.equals(Case.Events.PERSONS_UPDATED.toString()) && evt instanceof PersonsUpdatedEvent) {
((PersonsChangedEvent) evt).getNewValue().stream() ((PersonsUpdatedEvent) evt).getNewValue().stream()
.filter(p -> p != null && p.getPersonId() == personId) .filter(p -> p != null && p.getPersonId() == personId)
.findFirst() .findFirst()
.ifPresent((newPerson) -> { .ifPresent((newPerson) -> {
@ -161,6 +178,7 @@ public class PersonGroupingNode extends DisplayableItemNode {
* Gets the display name for this person or "Unknown Persons". * Gets the display name for this person or "Unknown Persons".
* *
* @param person The person. * @param person The person.
*
* @return The non-empty string for the display name. * @return The non-empty string for the display name.
*/ */
private static String getDisplayName(Person person) { private static String getDisplayName(Person person) {
@ -174,17 +192,17 @@ public class PersonGroupingNode extends DisplayableItemNode {
* *
* @param person The person record to be represented. * @param person The person record to be represented.
*/ */
PersonGroupingNode(Person person) { PersonNode(Person person) {
this(person, getDisplayName(person)); this(person, getDisplayName(person));
} }
/** /**
* Constructor. * Constructor.
* *
* @param person The person. * @param person The person.
* @param displayName The display name for the person. * @param displayName The display name for the person.
*/ */
private PersonGroupingNode(Person person, String displayName) { private PersonNode(Person person, String displayName) {
super(Children.create(new PersonChildren(person), false), super(Children.create(new PersonChildren(person), false),
person == null ? Lookups.fixed(displayName) : Lookups.fixed(person, displayName)); person == null ? Lookups.fixed(displayName) : Lookups.fixed(person, displayName));
super.setName(displayName); super.setName(displayName);
@ -192,7 +210,7 @@ public class PersonGroupingNode extends DisplayableItemNode {
this.setIconBaseWithExtension(ICON_PATH); this.setIconBaseWithExtension(ICON_PATH);
this.person = person; this.person = person;
this.personId = person == null ? null : person.getPersonId(); this.personId = person == null ? null : person.getPersonId();
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.PERSONS_CHANGED), Case.addEventTypeSubscriber(EnumSet.of(Case.Events.PERSONS_UPDATED),
WeakListeners.propertyChange(personChangePcl, this)); WeakListeners.propertyChange(personChangePcl, this));
} }

View File

@ -156,20 +156,6 @@ public class PoolNode extends AbstractContentNode<Pool> {
return null; 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. * Returns comment property for the node.
* *

View File

@ -130,7 +130,7 @@ public class RootContentChildren extends Children.Keys<Object> {
@Override @Override
public AbstractNode visit(DataSources i) { public AbstractNode visit(DataSources i) {
return new DataSourcesNode(i.filteringDataSourceObjId()); return new DataSourceFilesNode(i.filteringDataSourceObjId());
} }
@Override @Override
@ -177,7 +177,7 @@ public class RootContentChildren extends Children.Keys<Object> {
@Override @Override
public AbstractNode visit(PersonGrouping personGrouping) { public AbstractNode visit(PersonGrouping personGrouping) {
return new PersonGroupingNode(personGrouping.getPerson()); return new PersonNode(personGrouping.getPerson());
} }
@Override @Override
@ -192,7 +192,7 @@ public class RootContentChildren extends Children.Keys<Object> {
@Override @Override
public AbstractNode visit(DataSourcesByType dataSourceHosts) { public AbstractNode visit(DataSourcesByType dataSourceHosts) {
return new DataSourcesByTypeNode(); return new DataSourcesNode();
} }
@Override @Override

View File

@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.datamodel;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable; import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
import org.sleuthkit.datamodel.Score;
/** /**
* Container to bag the S C & O data for an abstract file node. * Container to bag the S C & O data for an abstract file node.
@ -27,11 +28,11 @@ import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
*/ */
class SCOData { class SCOData {
private Pair<DataResultViewerTable.Score, String> scoreAndDescription = null; private Pair<Score, String> scoreAndDescription = null;
private DataResultViewerTable.HasCommentStatus comment = null; private DataResultViewerTable.HasCommentStatus comment = null;
private Pair<Long, String> countAndDescription = null; private Pair<Long, String> countAndDescription = null;
Pair<DataResultViewerTable.Score, String> getScoreAndDescription() { Pair<Score, String> getScoreAndDescription() {
return scoreAndDescription; return scoreAndDescription;
} }
@ -43,7 +44,7 @@ class SCOData {
return countAndDescription; return countAndDescription;
} }
void setScoreAndDescription(Pair<DataResultViewerTable.Score, String> scoreAndDescription) { void setScoreAndDescription(Pair<Score, String> scoreAndDescription) {
this.scoreAndDescription = scoreAndDescription; this.scoreAndDescription = scoreAndDescription;
} }
void setComment(DataResultViewerTable.HasCommentStatus comment) { void setComment(DataResultViewerTable.HasCommentStatus comment) {

View File

@ -138,20 +138,6 @@ public class UnsupportedContentNode extends AbstractContentNode<UnsupportedConte
return null; 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. * Returns comment property for the node.
* *

View File

@ -258,20 +258,6 @@ public class VolumeNode extends AbstractContentNode<Volume> {
return null; 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. * Returns comment property for the node.
* *

View File

@ -81,6 +81,7 @@ import org.sleuthkit.datamodel.BlackboardArtifact.Type;
import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_ACCOUNT; import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_ACCOUNT;
import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.DataArtifact;
import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData.DbType; import org.sleuthkit.datamodel.TskData.DbType;
@ -97,7 +98,7 @@ final public class Accounts implements AutopsyVisitableItem {
private static final Set<IngestManager.IngestModuleEvent> INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestModuleEvent.DATA_ADDED); 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(); private static final String DISPLAY_NAME = Bundle.Accounts_RootNode_displayName();
@NbBundle.Messages("AccountsRootNode.name=Accounts") @NbBundle.Messages("AccountsRootNode.name=Accounts") //used for the viewArtifact navigation
final public static String NAME = Bundle.AccountsRootNode_name(); final public static String NAME = Bundle.AccountsRootNode_name();
private SleuthkitCase skCase; private SleuthkitCase skCase;
@ -233,7 +234,7 @@ final public class Accounts implements AutopsyVisitableItem {
/** /**
* Top-level node for the accounts tree * Top-level node for the accounts tree
*/ */
@NbBundle.Messages({"Accounts.RootNode.displayName=Accounts"}) @NbBundle.Messages({"Accounts.RootNode.displayName=Communication Accounts"})
final public class AccountsRootNode extends UpdatableCountTypeNode { final public class AccountsRootNode extends UpdatableCountTypeNode {
public AccountsRootNode() { public AccountsRootNode() {
@ -569,7 +570,7 @@ final public class Accounts implements AutopsyVisitableItem {
@Override @Override
protected boolean createKeys(List<Long> list) { protected boolean createKeys(List<Long> list) {
String query String query
= "SELECT blackboard_artifacts.artifact_id " //NON-NLS = "SELECT blackboard_artifacts.artifact_obj_id " //NON-NLS
+ " FROM blackboard_artifacts " //NON-NLS + " FROM blackboard_artifacts " //NON-NLS
+ " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS + " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.Type.TSK_ACCOUNT.getTypeID() //NON-NLS + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.Type.TSK_ACCOUNT.getTypeID() //NON-NLS
@ -581,7 +582,7 @@ final public class Accounts implements AutopsyVisitableItem {
ResultSet rs = results.getResultSet();) { ResultSet rs = results.getResultSet();) {
List<Long> tempList = new ArrayList<>(); List<Long> tempList = new ArrayList<>();
while (rs.next()) { while (rs.next()) {
tempList.add(rs.getLong("artifact_id")); // NON-NLS tempList.add(rs.getLong("artifact_obj_id")); // NON-NLS
} }
list.addAll(tempList); list.addAll(tempList);
} catch (TskCoreException | SQLException ex) { } catch (TskCoreException | SQLException ex) {
@ -594,7 +595,7 @@ final public class Accounts implements AutopsyVisitableItem {
@Override @Override
protected Node[] createNodesForKey(Long t) { protected Node[] createNodesForKey(Long t) {
try { try {
return new Node[]{new BlackboardArtifactNode(skCase.getBlackboardArtifact(t))}; return new Node[]{new BlackboardArtifactNode(skCase.getBlackboard().getDataArtifactById(t))};
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, "Error get black board artifact with id " + t, ex); LOGGER.log(Level.SEVERE, "Error get black board artifact with id " + t, ex);
return new Node[0]; return new Node[0];
@ -1520,7 +1521,7 @@ final public class Accounts implements AutopsyVisitableItem {
} }
try { try {
BlackboardArtifact art = skCase.getBlackboardArtifact(artifactID); DataArtifact art = skCase.getBlackboard().getDataArtifactById(artifactID);
return new Node[]{new AccountArtifactNode(art)}; return new Node[]{new AccountArtifactNode(art)};
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, "Error creating BlackboardArtifactNode for artifact with ID " + artifactID, ex); //NON-NLS LOGGER.log(Level.SEVERE, "Error creating BlackboardArtifactNode for artifact with ID " + artifactID, ex); //NON-NLS

View File

@ -21,7 +21,7 @@ Accounts.FileWithCCNNode.statusProperty.displayName=Status
# {0} - raw file name # {0} - raw file name
# {1} - solr chunk id # {1} - solr chunk id
Accounts.FileWithCCNNode.unallocatedSpaceFile.displayName={0}_chunk_{1} Accounts.FileWithCCNNode.unallocatedSpaceFile.displayName={0}_chunk_{1}
Accounts.RootNode.displayName=Accounts Accounts.RootNode.displayName=Communication Accounts
AccountsRootNode.name=Accounts AccountsRootNode.name=Accounts
ApproveAccountsAction.name=Approve Accounts ApproveAccountsAction.name=Approve Accounts
RejectAccountsAction.name=Reject Accounts RejectAccountsAction.name=Reject Accounts

View File

@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.datamodel.hosts;
import java.awt.Frame; import java.awt.Frame;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.util.Collections;
import java.util.logging.Level; import java.util.logging.Level;
import javax.swing.AbstractAction; import javax.swing.AbstractAction;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
@ -66,7 +67,7 @@ public class AssociateNewPersonAction extends AbstractAction {
newPersonName = getAddDialogName(); newPersonName = getAddDialogName();
if (StringUtils.isNotBlank(newPersonName)) { if (StringUtils.isNotBlank(newPersonName)) {
Person person = Case.getCurrentCaseThrows().getSleuthkitCase().getPersonManager().newPerson(newPersonName); Person person = Case.getCurrentCaseThrows().getSleuthkitCase().getPersonManager().newPerson(newPersonName);
Case.getCurrentCaseThrows().getSleuthkitCase().getPersonManager().setPerson(host, person); Case.getCurrentCaseThrows().getSleuthkitCase().getPersonManager().addHostsToPerson(person, Collections.singletonList(host));
} }
} catch (NoCurrentCaseException | TskCoreException ex) { } catch (NoCurrentCaseException | TskCoreException ex) {
String hostName = this.host == null || this.host.getName() == null ? "" : this.host.getName(); String hostName = this.host == null || this.host.getName() == null ? "" : this.host.getName();

View File

@ -19,6 +19,7 @@
package org.sleuthkit.autopsy.datamodel.hosts; package org.sleuthkit.autopsy.datamodel.hosts;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.util.Collections;
import java.util.logging.Level; import java.util.logging.Level;
import javax.swing.AbstractAction; import javax.swing.AbstractAction;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
@ -65,7 +66,7 @@ public class AssociatePersonAction extends AbstractAction {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
try { try {
Case.getCurrentCaseThrows().getSleuthkitCase().getPersonManager().setPerson(host, person); Case.getCurrentCaseThrows().getSleuthkitCase().getPersonManager().addHostsToPerson(person, Collections.singletonList(host));
} catch (NoCurrentCaseException | TskCoreException ex) { } catch (NoCurrentCaseException | TskCoreException ex) {
String hostName = this.host == null || this.host.getName() == null ? "" : this.host.getName(); String hostName = this.host == null || this.host.getName() == null ? "" : this.host.getName();
String personName = this.person == null || this.person.getName() == null ? "" : this.person.getName(); String personName = this.person == null || this.person.getName() == null ? "" : this.person.getName();

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