1
0
mirror of https://github.com/elisspace/autopsy.git synced 2026-09-06 02:24:30 +00:00

Merge branch 'release-4.5.0' into 2978-db-version-compatibility

This commit is contained in:
Richard Cordovano
2017-09-20 12:02:13 -04:00
committed by GitHub
36 changed files with 457 additions and 363 deletions

View File

@@ -127,7 +127,6 @@ public class Case {
private static final String LOG_FOLDER = "Log"; //NON-NLS
private static final String REPORTS_FOLDER = "Reports"; //NON-NLS
private static final String TEMP_FOLDER = "Temp"; //NON-NLS
private static final int MIN_SECS_BETWEEN_TSK_ERROR_REPORTS = 60;
private static final String MODULE_FOLDER = "ModuleOutput"; //NON-NLS
private static final long EXECUTOR_AWAIT_TIMEOUT_SECS = 5;
private static final String CASE_ACTION_THREAD_NAME = "%s-case-action";

View File

@@ -66,8 +66,8 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
* View correlation results from other cases
*/
@ServiceProvider(service = DataContentViewer.class, position = 8)
@Messages({"DataContentViewerOtherCases.title=Other Data Sources",
"DataContentViewerOtherCases.toolTip=Displays instances of the selected file/artifact from other data sources.",})
@Messages({"DataContentViewerOtherCases.title=Other Occurrences",
"DataContentViewerOtherCases.toolTip=Displays instances of the selected file/artifact from other occurrences.",})
public class DataContentViewerOtherCases extends javax.swing.JPanel implements DataContentViewer {
private final static Logger LOGGER = Logger.getLogger(DataContentViewerOtherCases.class.getName());
@@ -370,7 +370,7 @@ public class DataContentViewerOtherCases extends javax.swing.JPanel implements D
*
* @param node The node to view
*
* @return A collection of Central Repository artifacts to display
* @return A collection of central repository artifacts to display
*/
private Collection<EamArtifact> getArtifactsFromCorrelatableAttributes(Node node) {
Collection<EamArtifact> ret = new ArrayList<>();
@@ -450,7 +450,7 @@ public class DataContentViewerOtherCases extends javax.swing.JPanel implements D
/**
* Query the db for artifact instances from other cases correlated to the
* given Central Repository artifact.
* given central repository artifact.
*
* @param eamArtifact The artifact to correlate against
*
@@ -474,12 +474,12 @@ public class DataContentViewerOtherCases extends javax.swing.JPanel implements D
}
/**
* Get the Global File Instances matching the given eamArtifact and convert
* them to Central Repository Artifact Instancess.
* Get the global file instances matching the given eamArtifact and convert
* them to central repository artifact instancess.
*
* @param eamArtifact Artifact to use for ArtifactTypeEnum matching
*
* @return List of Central Repository Artifact Instances, empty list if none
* @return List of central repository artifact instances, empty list if none
* found
*/
public Collection<EamArtifactInstance> getReferenceInstancesAsArtifactInstances(EamArtifact eamArtifact) {
@@ -530,8 +530,8 @@ public class DataContentViewerOtherCases extends javax.swing.JPanel implements D
*
* @param node The node being viewed.
*/
@Messages({"DataContentViewerOtherCases.table.isempty=There are no associated artifacts or files from other cases to display.",
"DataContentViewerOtherCases.table.noArtifacts=Correlation cannot be performed on the selected file; likely missing MD5 hash."})
@Messages({"DataContentViewerOtherCases.table.isempty=There are no associated artifacts or files from other occurrences to display.",
"DataContentViewerOtherCases.table.noArtifacts=Correlation cannot be performed on the selected file."})
private void populateTable(Node node) {
String dataSourceName = getDataSourceNameFromNode(node);
String deviceId = getDeviceIdFromNode(node);

View File

@@ -190,7 +190,8 @@ public class IngestEventsListener {
}
}
@NbBundle.Messages({"IngestEventsListener.prevcases.text=Previous Cases",
@NbBundle.Messages({"IngestEventsListener.prevTaggedSet.text=Previously Tagged As Notable (Central Repository)",
"IngestEventsListener.prevCaseComment.text=Previous Case: ",
"IngestEventsListener.ingestmodule.name=Correlation Engine"})
private void postCorrelatedBadArtifactToBlackboard(BlackboardArtifact bbArtifact, List<String> caseDisplayNames) {
@@ -200,9 +201,9 @@ public class IngestEventsListener {
String MODULE_NAME = Bundle.IngestEventsListener_ingestmodule_name();
BlackboardArtifact tifArtifact = af.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT);
BlackboardAttribute att = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME,
Bundle.IngestEventsListener_prevcases_text());
Bundle.IngestEventsListener_prevTaggedSet_text());
BlackboardAttribute att2 = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT, MODULE_NAME,
"Previous Case: " + caseDisplayNames.stream().distinct().collect(Collectors.joining(",", "", "")));
Bundle.IngestEventsListener_prevCaseComment_text() + caseDisplayNames.stream().distinct().collect(Collectors.joining(",", "", "")));
tifArtifact.addAttribute(att);
tifArtifact.addAttribute(att2);
tifArtifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, bbArtifact.getArtifactID()));

View File

@@ -54,7 +54,8 @@ import org.sleuthkit.datamodel.TskDataException;
* Ingest module for inserting entries into the Central Repository database on
* ingest of a data source
*/
@Messages({"IngestModule.prevcases.text=Previous Cases"})
@Messages({"IngestModule.prevTaggedSet.text=Previously Tagged As Notable (Central Repository)",
"IngestModule.prevCaseComment.text=Previous Case: "})
class IngestModule implements FileIngestModule {
private final static Logger LOGGER = Logger.getLogger(IngestModule.class.getName());
@@ -289,9 +290,9 @@ class IngestModule implements FileIngestModule {
String MODULE_NAME = IngestModuleFactory.getModuleName();
BlackboardArtifact tifArtifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT);
BlackboardAttribute att = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME,
Bundle.IngestModule_prevcases_text());
Bundle.IngestModule_prevTaggedSet_text());
BlackboardAttribute att2 = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT, MODULE_NAME,
"Previous Case: " + caseDisplayNames.stream().distinct().collect(Collectors.joining(",", "", "")));
Bundle.IngestModule_prevCaseComment_text() + caseDisplayNames.stream().distinct().collect(Collectors.joining(",", "", "")));
tifArtifact.addAttribute(att);
tifArtifact.addAttribute(att2);
@@ -319,7 +320,7 @@ class IngestModule implements FileIngestModule {
String MODULE_NAME = IngestModuleFactory.getModuleName();
BlackboardArtifact tifArtifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT);
BlackboardAttribute att = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME,
Bundle.IngestModule_prevcases_text());
Bundle.IngestModule_prevCaseComment_text());
tifArtifact.addAttribute(att);
try {

View File

@@ -100,8 +100,8 @@
<Properties>
<Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.editors2.TableModelEditor">
<Table columnCount="2" rowCount="0">
<Column editable="false" title="Tag" type="java.lang.Object"/>
<Column editable="true" title="Implies Known Bad" type="java.lang.Boolean"/>
<Column editable="false" title="" type="java.lang.Object"/>
<Column editable="true" title="" type="java.lang.Boolean"/>
</Table>
</Property>
</Properties>

View File

@@ -72,12 +72,15 @@ final class ManageTagsDialog extends javax.swing.JDialog {
display();
}
@Messages({"ManageTagsDialog.init.failedConnection.msg=Cannot connect to central cepository.",
"ManageTagsDialog.init.failedGettingTags.msg=Unable to retrieve list of tags.",
"ManageTagsDialog.tagColumn.header.text=Tags",
"ManageTagsDialog.notableColumn.header.text=Notable"})
private void setupHelpTextArea() {
helpTextArea.setText(Bundle.ManageTagDialog_tagInfo_text());
}
@Messages({"ManageTagsDialog.init.failedConnection.msg=Cannot connect to central repository.",
"ManageTagsDialog.init.failedGettingTags.msg=Unable to retrieve list of tags."})
private void customizeComponents() {
lbWarnings.setText("");
EamDb dbManager;
@@ -105,6 +108,7 @@ final class ManageTagsDialog extends javax.swing.JDialog {
Collections.sort(tagNames);
DefaultTableModel model = (DefaultTableModel) tblTagNames.getModel();
model.setColumnIdentifiers(new String[] {Bundle.ManageTagsDialog_tagColumn_header_text(), Bundle.ManageTagsDialog_notableColumn_header_text()});
for (String tagName : tagNames) {
boolean enabled = badTags.contains(tagName);
model.addRow(new Object[]{tagName, enabled});
@@ -158,7 +162,7 @@ final class ManageTagsDialog extends javax.swing.JDialog {
},
new String [] {
"Tag", "Implies Known Bad"
"", ""
}
) {
Class[] types = new Class [] {

View File

@@ -19,8 +19,6 @@
package org.sleuthkit.autopsy.corecomponents;
import java.awt.Insets;
import java.io.File;
import java.util.Collection;
import java.util.Map;
import java.util.TreeMap;
import java.util.logging.Level;
@@ -28,15 +26,9 @@ import javax.swing.BorderFactory;
import javax.swing.UIManager;
import javax.swing.UIManager.LookAndFeelInfo;
import javax.swing.UnsupportedLookAndFeelException;
import org.netbeans.spi.sendopts.OptionProcessor;
import org.netbeans.swing.tabcontrol.plaf.DefaultTabbedContainerUI;
import org.openide.modules.ModuleInstall;
import org.openide.util.Lookup;
import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.CaseActionException;
import org.sleuthkit.autopsy.casemodule.CaseMetadata;
import org.sleuthkit.autopsy.casemodule.OpenFromArguments;
import org.sleuthkit.autopsy.casemodule.StartupWindowProvider;
import org.sleuthkit.autopsy.coreutils.Logger;
@@ -63,35 +55,11 @@ public class Installer extends ModuleInstall {
@Override
public void restored() {
super.restored();
setLookAndFeel();
UIManager.put("ViewTabDisplayerUI", "org.sleuthkit.autopsy.corecomponents.NoTabsTabDisplayerUI");
UIManager.put(DefaultTabbedContainerUI.KEY_VIEW_CONTENT_BORDER, BorderFactory.createEmptyBorder());
UIManager.put("TabbedPane.contentBorderInsets", new Insets(0, 0, 0, 0));
/*
* Open the case if a case metadata file was double-clicked. This only
* works if the user has associated files with ".aut" extensions with
* Autopsy.
*/
WindowManager.getDefault().invokeWhenUIReady(() -> {
Collection<? extends OptionProcessor> processors = Lookup.getDefault().lookupAll(OptionProcessor.class);
for (OptionProcessor processor : processors) {
if (processor instanceof OpenFromArguments) {
OpenFromArguments argsProcessor = (OpenFromArguments) processor;
final String caseFile = argsProcessor.getDefaultArg();
if (caseFile != null && !caseFile.isEmpty() && caseFile.endsWith(CaseMetadata.getFileExtension()) && new File(caseFile).exists()) { //NON-NLS
try {
Case.openAsCurrentCase(caseFile);
} catch (CaseActionException ex) {
logger.log(Level.SEVERE, String.format("Error opening case with metadata file path %s", caseFile), ex); //NON-NLS
//JIRA-3035: notify user!
}
return;
}
}
}
StartupWindowProvider.getInstance().open();
});
}

View File

@@ -28,8 +28,8 @@ import org.sleuthkit.autopsy.directorytree.ExtractAction;
import org.sleuthkit.autopsy.directorytree.FileSearchAction;
import org.sleuthkit.autopsy.directorytree.NewWindowViewAction;
import org.sleuthkit.autopsy.ingest.runIngestModuleWizard.RunIngestModulesAction;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.SpecialDirectory;
import org.sleuthkit.datamodel.Content;
/**
* Parent class for special directory types (Local and Virtual)
@@ -48,8 +48,7 @@ public abstract class SpecialDirectoryNode extends AbstractAbstractFileNode<Spec
* @return
*/
@Override
@NbBundle.Messages({"SpecialDirectoryNode.action.runIngestMods.text=Run Ingest Modules",
"SpecialDirectoryNode.getActions.viewInNewWin.text=View in New Window"
@NbBundle.Messages({"SpecialDirectoryNode.getActions.viewInNewWin.text=View in New Window"
})
public Action[] getActions(boolean popup) {
List<Action> actions = new ArrayList<>();
@@ -58,7 +57,7 @@ public abstract class SpecialDirectoryNode extends AbstractAbstractFileNode<Spec
}
actions.add(new NewWindowViewAction(
Bundle.SpecialDirectoryNode_action_runIngestMods_text(), this));
Bundle.SpecialDirectoryNode_getActions_viewInNewWin_text(), this));
actions.add(null); // creates a menu separator
actions.add(ExtractAction.getInstance());
actions.add(null); // creates a menu separator

View File

@@ -1143,7 +1143,7 @@ public final class AutoIngestControlPanel extends JPanel implements Observer {
}
tableModel.setRowCount(0);
for (AutoIngestJob job : jobs) {
AutoIngestJob.StageDetails status = job.getStageDetails();
AutoIngestJob.StageDetails status = job.getProcessingStageDetails();
tableModel.addRow(new Object[]{
job.getManifest().getCaseName(), // CASE
job.getManifest().getDataSourcePath().getFileName(), // DATA_SOURCE
@@ -1152,7 +1152,7 @@ public final class AutoIngestControlPanel extends JPanel implements Observer {
job.getProcessingStageStartDate(), // STARTED_TIME
job.getCompletedDate(), // COMPLETED_TIME
status.getDescription(), // ACTIVITY
job.getErrorsOccurred(), // STATUS
job.getErrorsOccurred(), // STATUS
((Date.from(Instant.now()).getTime()) - (status.getStartDate().getTime())), // ACTIVITY_TIME
job.getCaseDirectoryPath(), // CASE_DIRECTORY_PATH
job.getProcessingHostName().equals(LOCAL_HOST_NAME), // IS_LOCAL_JOB
@@ -1701,7 +1701,7 @@ job.getErrorsOccurred(), // STATUS
*
* @param evt The button click event.
*/
@Messages({"AutoIngestControlPanel.casePrioritization.errorMessage=An error occurred when prioritizing the case. Some or all jobs may not have been prioritized."})
@Messages({"AutoIngestControlPanel.casePrioritization.errorMessage=An error occurred when prioritizing the case. Some or all jobs may not have been prioritized."})
private void bnPrioritizeCaseActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnPrioritizeCaseActionPerformed
if (pendingTableModel.getRowCount() > 0 && pendingTable.getSelectedRow() >= 0) {
this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
@@ -1759,8 +1759,8 @@ job.getErrorsOccurred(), // STATUS
options[0]);
}
}//GEN-LAST:event_bnShowCaseLogActionPerformed
@Messages({"AutoIngestControlPanel.jobPrioritization.errorMessage=An error occurred when prioritizing the job."})
@Messages({"AutoIngestControlPanel.jobPrioritization.errorMessage=An error occurred when prioritizing the job."})
private void bnPrioritizeJobActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnPrioritizeJobActionPerformed
if (pendingTableModel.getRowCount() > 0 && pendingTable.getSelectedRow() >= 0) {
this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));

View File

@@ -18,6 +18,7 @@
*/
package org.sleuthkit.autopsy.experimental.autoingest;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.awt.Cursor;
import java.awt.EventQueue;
import java.nio.file.Path;
@@ -44,6 +45,8 @@ import org.sleuthkit.autopsy.core.ServicesMonitor;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestMonitor.JobsSnapshot;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* A dashboard for monitoring an automated ingest cluster.
@@ -55,28 +58,30 @@ public final class AutoIngestDashboard extends JPanel implements Observer {
private static final int GENERIC_COL_MAX_WIDTH = 2000;
private static final int PENDING_TABLE_COL_PREFERRED_WIDTH = 280;
private static final int RUNNING_TABLE_COL_PREFERRED_WIDTH = 175;
private static final int ACTIVITY_TIME_COL_MIN_WIDTH = 250;
private static final int ACTIVITY_TIME_COL_MAX_WIDTH = 450;
private static final int STAGE_TIME_COL_MIN_WIDTH = 250;
private static final int STAGE_TIME_COL_MAX_WIDTH = 450;
private static final int TIME_COL_MIN_WIDTH = 30;
private static final int TIME_COL_MAX_WIDTH = 250;
private static final int TIME_COL_PREFERRED_WIDTH = 140;
private static final int NAME_COL_MIN_WIDTH = 100;
private static final int NAME_COL_MAX_WIDTH = 250;
private static final int NAME_COL_PREFERRED_WIDTH = 140;
private static final int ACTIVITY_COL_MIN_WIDTH = 70;
private static final int ACTIVITY_COL_MAX_WIDTH = 2000;
private static final int ACTIVITY_COL_PREFERRED_WIDTH = 300;
private static final int STAGE_COL_MIN_WIDTH = 70;
private static final int STAGE_COL_MAX_WIDTH = 2000;
private static final int STAGE_COL_PREFERRED_WIDTH = 300;
private static final int STATUS_COL_MIN_WIDTH = 55;
private static final int STATUS_COL_MAX_WIDTH = 250;
private static final int STATUS_COL_PREFERRED_WIDTH = 55;
private static final int COMPLETED_TIME_COL_MIN_WIDTH = 30;
private static final int COMPLETED_TIME_COL_MAX_WIDTH = 2000;
private static final int COMPLETED_TIME_COL_PREFERRED_WIDTH = 280;
private static final String UPDATE_TASKS_THREAD_NAME = "AID-update-tasks-%d";
private static final Logger logger = Logger.getLogger(AutoIngestDashboard.class.getName());
private final DefaultTableModel pendingTableModel;
private final DefaultTableModel runningTableModel;
private final DefaultTableModel completedTableModel;
private AutoIngestMonitor autoIngestMonitor;
private ExecutorService updateExecutor;
/**
* Creates a dashboard for monitoring an automated ingest cluster.
@@ -213,6 +218,7 @@ public final class AutoIngestDashboard extends JPanel implements Observer {
pendingTable.removeColumn(pendingTable.getColumn(JobsTableModelColumns.CASE_DIRECTORY_PATH.getColumnHeader()));
pendingTable.removeColumn(pendingTable.getColumn(JobsTableModelColumns.STATUS.getColumnHeader()));
pendingTable.removeColumn(pendingTable.getColumn(JobsTableModelColumns.MANIFEST_FILE_PATH.getColumnHeader()));
pendingTable.removeColumn(pendingTable.getColumn(JobsTableModelColumns.JOB.getColumnHeader()));
/*
* Set up a column to display the cases associated with the jobs.
@@ -275,6 +281,7 @@ public final class AutoIngestDashboard extends JPanel implements Observer {
runningTable.removeColumn(runningTable.getColumn(JobsTableModelColumns.STATUS.getColumnHeader()));
runningTable.removeColumn(runningTable.getColumn(JobsTableModelColumns.CASE_DIRECTORY_PATH.getColumnHeader()));
runningTable.removeColumn(runningTable.getColumn(JobsTableModelColumns.MANIFEST_FILE_PATH.getColumnHeader()));
runningTable.removeColumn(runningTable.getColumn(JobsTableModelColumns.JOB.getColumnHeader()));
/*
* Set up a column to display the cases associated with the jobs.
@@ -311,10 +318,10 @@ public final class AutoIngestDashboard extends JPanel implements Observer {
* jobs.
*/
column = runningTable.getColumn(JobsTableModelColumns.STAGE.getColumnHeader());
column.setMinWidth(ACTIVITY_COL_MIN_WIDTH);
column.setMaxWidth(ACTIVITY_COL_MAX_WIDTH);
column.setPreferredWidth(ACTIVITY_COL_PREFERRED_WIDTH);
column.setWidth(ACTIVITY_COL_PREFERRED_WIDTH);
column.setMinWidth(STAGE_COL_MIN_WIDTH);
column.setMaxWidth(STAGE_COL_MAX_WIDTH);
column.setPreferredWidth(STAGE_COL_PREFERRED_WIDTH);
column.setWidth(STAGE_COL_PREFERRED_WIDTH);
/*
* Set up a column to display the ingest activity times associated with
@@ -323,9 +330,9 @@ public final class AutoIngestDashboard extends JPanel implements Observer {
column = runningTable.getColumn(JobsTableModelColumns.STAGE_TIME.getColumnHeader());
column.setCellRenderer(new DurationCellRenderer());
column.setMinWidth(GENERIC_COL_MIN_WIDTH);
column.setMaxWidth(ACTIVITY_TIME_COL_MAX_WIDTH);
column.setPreferredWidth(ACTIVITY_TIME_COL_MIN_WIDTH);
column.setWidth(ACTIVITY_TIME_COL_MIN_WIDTH);
column.setMaxWidth(STAGE_TIME_COL_MAX_WIDTH);
column.setPreferredWidth(STAGE_TIME_COL_MIN_WIDTH);
column.setWidth(STAGE_TIME_COL_MIN_WIDTH);
/*
* Prevent sorting when a column header is clicked.
@@ -348,6 +355,7 @@ public final class AutoIngestDashboard extends JPanel implements Observer {
completedTable.removeColumn(completedTable.getColumn(JobsTableModelColumns.HOST_NAME.getColumnHeader()));
completedTable.removeColumn(completedTable.getColumn(JobsTableModelColumns.CASE_DIRECTORY_PATH.getColumnHeader()));
completedTable.removeColumn(completedTable.getColumn(JobsTableModelColumns.MANIFEST_FILE_PATH.getColumnHeader()));
completedTable.removeColumn(completedTable.getColumn(JobsTableModelColumns.JOB.getColumnHeader()));
/*
* Set up a column to display the cases associated with the jobs.
@@ -419,12 +427,20 @@ public final class AutoIngestDashboard extends JPanel implements Observer {
autoIngestMonitor = new AutoIngestMonitor();
autoIngestMonitor.addObserver(this);
autoIngestMonitor.startUp();
updateExecutor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat(UPDATE_TASKS_THREAD_NAME).build());
updateExecutor.submit(new UpdateJobsSnapshotTask());
}
@Override
public void update(Observable observable, Object argument) {
JobsSnapshot jobsSnapshot = (JobsSnapshot) argument;
EventQueue.invokeLater(new RefreshComponentsTask(jobsSnapshot));
public void update(Observable observable, Object arg) {
/*
* By creating a task to get the latest jobs snapshot, the possibility
* of queuing a refresh task for the EDT with snaphot rendered stale by
* the handling of a user prioritization action, etc. on the EDT is
* avoided. This is why the snapshot pushed ny the auto ingest jobs
* monitor is ignored.
*/
updateExecutor.submit(new UpdateJobsSnapshotTask());
}
/**
@@ -438,7 +454,6 @@ public final class AutoIngestDashboard extends JPanel implements Observer {
List<AutoIngestJob> runningJobs = jobsSnapshot.getRunningJobs();
List<AutoIngestJob> completedJobs = jobsSnapshot.getCompletedJobs();
pendingJobs.sort(new AutoIngestJob.PriorityComparator());
runningJobs.sort(new AutoIngestJob.CaseNameAndProcessingHostComparator());
completedJobs.sort(new AutoIngestJob.ReverseCompletedDateComparator());
refreshTable(pendingJobs, pendingTable, pendingTableModel);
refreshTable(runningJobs, runningTable, runningTableModel);
@@ -459,24 +474,19 @@ public final class AutoIngestDashboard extends JPanel implements Observer {
Path currentRow = getSelectedEntry(table, tableModel);
tableModel.setRowCount(0);
for (AutoIngestJob job : jobs) {
if (job.getVersion() < 1) {
// Ignore version '0' nodes since they don't carry enough
// data to populate the table.
continue;
}
AutoIngestJob.StageDetails status = job.getStageDetails();
AutoIngestJob.StageDetails status = job.getProcessingStageDetails();
tableModel.addRow(new Object[]{
job.getManifest().getCaseName(), // CASE
job.getManifest().getDataSourcePath().getFileName(), job.getProcessingHostName(), // HOST_NAME
job.getManifest().getDateFileCreated(), // CREATED_TIME
job.getProcessingStageStartDate(), // STARTED_TIME
job.getCompletedDate(), // COMPLETED_TIME
status.getDescription(), // ACTIVITY
status.getDescription(), // STAGE
job.getErrorsOccurred(), // STATUS
((Date.from(Instant.now()).getTime()) - (status.getStartDate().getTime())), // ACTIVITY_TIME
((Date.from(Instant.now()).getTime()) - (status.getStartDate().getTime())), // STAGE_TIME
job.getCaseDirectoryPath(), // CASE_DIRECTORY_PATH
job.getManifest().getFilePath() // MANIFEST_FILE_PATH
//DLG: Put job object in the table
job.getManifest().getFilePath(), // MANIFEST_FILE_PATH
job
});
}
setSelectedEntry(table, tableModel, currentRow);
@@ -542,7 +552,6 @@ public final class AutoIngestDashboard extends JPanel implements Observer {
*/
private enum JobsTableModelColumns {
// DLG: Go through the bundle.properties file and delete any unused key-value pairs.
CASE(NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.JobsTableModel.ColumnHeader.Case")),
DATA_SOURCE(NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.JobsTableModel.ColumnHeader.ImageFolder")),
HOST_NAME(NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.JobsTableModel.ColumnHeader.HostName")),
@@ -553,8 +562,8 @@ public final class AutoIngestDashboard extends JPanel implements Observer {
STAGE_TIME(NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.JobsTableModel.ColumnHeader.StageTime")),
STATUS(NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.JobsTableModel.ColumnHeader.Status")),
CASE_DIRECTORY_PATH(NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.JobsTableModel.ColumnHeader.CaseFolder")),
MANIFEST_FILE_PATH(NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.JobsTableModel.ColumnHeader.ManifestFilePath")); //DLG:,
//DLG: JOB("");
MANIFEST_FILE_PATH(NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.JobsTableModel.ColumnHeader.ManifestFilePath")),
JOB(NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.JobsTableModel.ColumnHeader.Job"));
private final String header;
@@ -566,15 +575,6 @@ public final class AutoIngestDashboard extends JPanel implements Observer {
return header;
}
/*
* DLG: We need to add the AutoIngestJob object for the row to the
* table. As a model you can look in AutoIngestControlPanel to see how a
* boolean is stored in a hidden IS_LOCAL_JOB column and do something
* similar for the job. Once youy hjave done that, you can change the
* button event handler for the Prioritize button to make it pass the
* AutoIngestJob to the AutoIngestMonitor instead of the manifest file
* path.
*/
private static final String[] headers = {
CASE.getColumnHeader(),
DATA_SOURCE.getColumnHeader(),
@@ -586,11 +586,28 @@ public final class AutoIngestDashboard extends JPanel implements Observer {
STATUS.getColumnHeader(),
STAGE_TIME.getColumnHeader(),
CASE_DIRECTORY_PATH.getColumnHeader(),
MANIFEST_FILE_PATH.getColumnHeader() //DLG: ,
//DLG: JOB.getColumnHeader()
MANIFEST_FILE_PATH.getColumnHeader(),
JOB.getColumnHeader()
};
};
/**
* A task that gets the latest auto ingest jobs snapshot from the autop
* ingest monitor and queues a components refresh task for execution in the
* EDT.
*/
private class UpdateJobsSnapshotTask implements Runnable {
/**
* @inheritDoc
*/
@Override
public void run() {
JobsSnapshot jobsSnapshot = AutoIngestDashboard.this.autoIngestMonitor.getJobsSnapshot();
EventQueue.invokeLater(new RefreshComponentsTask(jobsSnapshot));
}
}
/**
* A task that refreshes the UI components on this panel to reflect a
* snapshot of the pending, running and completed auto ingest jobs lists of
@@ -835,13 +852,13 @@ public final class AutoIngestDashboard extends JPanel implements Observer {
private void prioritizeButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_prioritizeButtonActionPerformed
if (pendingTableModel.getRowCount() > 0 && pendingTable.getSelectedRow() >= 0) {
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
Path manifestFilePath = (Path) (pendingTableModel.getValueAt(pendingTable.getSelectedRow(), JobsTableModelColumns.MANIFEST_FILE_PATH.ordinal()));
AutoIngestJob job = (AutoIngestJob) (pendingTableModel.getValueAt(pendingTable.getSelectedRow(), JobsTableModelColumns.JOB.ordinal()));
JobsSnapshot jobsSnapshot;
try {
jobsSnapshot = autoIngestMonitor.prioritizeJob(manifestFilePath);
jobsSnapshot = autoIngestMonitor.prioritizeJob(job);
refreshTables(jobsSnapshot);
} catch (AutoIngestMonitor.AutoIngestMonitorException ex) {
String errorMessage = String.format(NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.PrioritizeError"), manifestFilePath);
String errorMessage = String.format(NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.PrioritizeError"), job.getManifest().getFilePath());
logger.log(Level.SEVERE, errorMessage, ex);
MessageNotifyUtil.Message.error(errorMessage);
}

View File

@@ -50,10 +50,6 @@ public final class AutoIngestDashboardTopComponent extends TopComponent {
@Messages({
"AutoIngestDashboardTopComponent.exceptionMessage.failedToCreateDashboard=Failed to create Auto Ingest Dashboard.",})
public static void openTopComponent() {
/*
* DLG: Please make the top component initial size big enough to show
* the whole dashboard.
*/
final AutoIngestDashboardTopComponent tc = (AutoIngestDashboardTopComponent) WindowManager.getDefault().findTopComponent(PREFERRED_ID);
if (tc != null) {
topComponentInitialized = true;
@@ -67,7 +63,7 @@ public final class AutoIngestDashboardTopComponent extends TopComponent {
try {
dashboard = AutoIngestDashboard.createDashboard();
tc.add(dashboard);
dashboard.setSize(992, 744);
dashboard.setSize(dashboard.getPreferredSize());
if (tc.isOpened() == false) {
tc.open();
}

View File

@@ -34,8 +34,8 @@ import org.sleuthkit.autopsy.coreutils.NetworkUtils;
import org.sleuthkit.autopsy.ingest.IngestJob;
/**
* An automated ingest job for a manifest. The manifest specifies a co-located
* data source and a case to which the data source is to be added.
* An automated ingest job, which is an ingest job performed by the automated
* ingest service.
*/
@ThreadSafe
public final class AutoIngestJob implements Comparable<AutoIngestJob>, Serializable {
@@ -44,9 +44,13 @@ public final class AutoIngestJob implements Comparable<AutoIngestJob>, Serializa
private static final int CURRENT_VERSION = 1;
private static final int DEFAULT_PRIORITY = 0;
private static final String LOCAL_HOST_NAME = NetworkUtils.getLocalHostName();
private final int version;
/*
* Version 0 fields.
*/
private final Manifest manifest;
private final String nodeName;
@GuardedBy("this")
private String nodeName;
@GuardedBy("this")
private String caseDirectoryPath;
@GuardedBy("this")
@@ -56,8 +60,6 @@ public final class AutoIngestJob implements Comparable<AutoIngestJob>, Serializa
@GuardedBy("this")
private Date stageStartDate;
@GuardedBy("this")
private StageDetails stageDetails;
@GuardedBy("this")
transient private DataSourceProcessor dataSourceProcessor;
@GuardedBy("this")
transient private IngestJob ingestJob;
@@ -69,96 +71,116 @@ public final class AutoIngestJob implements Comparable<AutoIngestJob>, Serializa
private Date completedDate;
@GuardedBy("this")
private boolean errorsOccurred;
/*
* Version 1 fields.
*/
private final int version; // For possible future use.
@GuardedBy("this")
private ProcessingStatus processingStatus;
@GuardedBy("this")
private int numberOfCrashes;
@GuardedBy("this")
private StageDetails stageDetails;
/**
* Constructs a new automated ingest job for a manifest. All job state not
* specified in the manifest is set to the default state for a new job.
* Constructs a new automated ingest job. All job state not specified in the
* job manifest is set to the default state for a new job.
*
* @param manifest The manifest.
* @param manifest The manifest for an automated ingest job.
*/
AutoIngestJob(Manifest manifest) {
this.version = CURRENT_VERSION;
/*
* Version 0 fields.
*/
this.manifest = manifest;
this.nodeName = AutoIngestJob.LOCAL_HOST_NAME;
this.nodeName = "";
this.caseDirectoryPath = "";
this.priority = DEFAULT_PRIORITY;
this.stage = Stage.PENDING;
this.stageStartDate = manifest.getDateFileCreated();
this.stageDetails = this.getStageDetails();
this.dataSourceProcessor = null;
this.ingestJob = null;
this.cancelled = false;
this.completed = false;
this.completedDate = new Date(0);
this.errorsOccurred = false;
/*
* Version 1 fields.
*/
this.version = CURRENT_VERSION;
this.processingStatus = ProcessingStatus.PENDING;
this.numberOfCrashes = 0;
this.stageDetails = this.getProcessingStageDetails();
}
/**
* Constructs an automated ingest job for a manifest. The manifest specifies
* a co-located data source and a case to which the data source is to be
* added.
* Constructs an automated ingest job using the coordination service node
* data for the job.
*
* Note: Manifest objects will be phased out and no longer be part of the
* AutoIngestJob class.
*
* @param nodeData The node data.
* @param nodeData The coordination service node data for an automated
* ingest job.
*/
AutoIngestJob(AutoIngestJobNodeData nodeData) {
this.version = nodeData.getVersion();
/*
* Version 0 fields.
*/
this.manifest = new Manifest(nodeData.getManifestFilePath(), nodeData.getManifestFileDate(), nodeData.getCaseName(), nodeData.getDeviceId(), nodeData.getDataSourcePath(), Collections.emptyMap());
this.nodeName = nodeData.getProcessingHostName();
this.caseDirectoryPath = nodeData.getCaseDirectoryPath().toString();
this.priority = nodeData.getPriority();
this.stage = nodeData.getProcessingStage();
this.stageStartDate = nodeData.getProcessingStageStartDate();
this.stageDetails = this.getStageDetails();
this.dataSourceProcessor = null;
this.ingestJob = null;
this.cancelled = false;
this.completed = false;
this.dataSourceProcessor = null; // Transient data not in node data.
this.ingestJob = null; // Transient data not in node data.
this.cancelled = false; // Transient data not in node data.
this.completed = false; // Transient data not in node data.
this.completedDate = nodeData.getCompletedDate();
this.errorsOccurred = nodeData.getErrorsOccurred();
/*
* Version 1 fields.
*/
this.version = CURRENT_VERSION;
this.processingStatus = nodeData.getProcessingStatus();
this.numberOfCrashes = nodeData.getNumberOfCrashes();
this.stageDetails = this.getProcessingStageDetails();
}
/**
* Gets the auto ingest job manifest.
* Gets the job manifest.
*
* @return The manifest.
* @return The job manifest.
*/
Manifest getManifest() {
return this.manifest;
}
/**
* Sets the path to the case directory of the case associated with this job.
* Sets the path to the case directory for the job.
*
* @param caseDirectoryPath The path to the case directory.
* @param caseDirectoryPath The path to the case directory. Can be the empty
* path if the case directory has not been created
* yet.
*/
synchronized void setCaseDirectoryPath(Path caseDirectoryPath) {
this.caseDirectoryPath = caseDirectoryPath.toString();
if (null != caseDirectoryPath) {
this.caseDirectoryPath = caseDirectoryPath.toString();
} else {
this.caseDirectoryPath = "";
}
}
/**
* Gets the path to the case directory of the case associated with this job,
* may be null.
* Gets the path to the case directory for job, may be the empty path if the
* case directory has not been created yet.
*
* @return The case directory path or null if the case directory has not
* been created yet.
* @return The case directory path. Will be the empty path if the case
* directory has not been created yet.
*/
synchronized Path getCaseDirectoryPath() {
if (!caseDirectoryPath.isEmpty()) {
return Paths.get(caseDirectoryPath);
} else {
return null;
}
return Paths.get(caseDirectoryPath);
}
/**
@@ -181,7 +203,13 @@ public final class AutoIngestJob implements Comparable<AutoIngestJob>, Serializa
return this.priority;
}
synchronized void setStage(Stage newStage) {
/**
* Sets the processing stage of the job. The start date/time for the stage
* is set when the stage is set.
*
* @param newStage The processing stage.
*/
synchronized void setProcessingStage(Stage newStage) {
if (Stage.CANCELLING == this.stage && Stage.COMPLETED != newStage) {
return;
}
@@ -189,15 +217,35 @@ public final class AutoIngestJob implements Comparable<AutoIngestJob>, Serializa
this.stageStartDate = Date.from(Instant.now());
}
/**
* Gets the processing stage of the job.
*
* @return The processing stage.
*/
synchronized Stage getProcessingStage() {
return this.stage;
}
/**
* Gets the date/time the current processing stage of the job started.
*
* @return The current processing stage start date/time.
*/
synchronized Date getProcessingStageStartDate() {
return new Date(this.stageStartDate.getTime());
}
synchronized StageDetails getStageDetails() {
/**
* Gets any available details associated with the current processing stage
* of the job, e.g., the currently running data source level ingest module,
* an ingest module in the process of being cancelled, etc. If no additional
* details are available, the stage details will be the same as the
* processing stage.
*
* @return A stage details object consisting of a descrition and associated
* date/time.
*/
synchronized StageDetails getProcessingStageDetails() {
String description;
Date startDate;
if (Stage.CANCELLING != this.stage && null != this.ingestJob) {
@@ -235,24 +283,43 @@ public final class AutoIngestJob implements Comparable<AutoIngestJob>, Serializa
return this.stageDetails;
}
synchronized void setStageDetails(StageDetails stageDetails) {
this.stageDetails = stageDetails;
}
/**
* Sets the data source processor for the job. Used for job cancellation.
*
* @param dataSourceProcessor A data source processor for the job.
*/
synchronized void setDataSourceProcessor(DataSourceProcessor dataSourceProcessor) {
this.dataSourceProcessor = dataSourceProcessor;
}
/**
* Sets the ingest job for the auto ingest job. Used for obtaining
* processing stage details, cancelling the currently running data source
* ingest module, and cancelling the job.
*
* @param ingestJob The ingest job for the auto ingest job.
*/
synchronized void setIngestJob(IngestJob ingestJob) {
this.ingestJob = ingestJob;
}
/**
* Gets the ingest job for the auto ingest job.
*
* @return The ingest job, may be null.
*
* TODO (JIRA-3059): Provide an AutoIngestJob ingest module cancellation API
* instead.
*/
synchronized IngestJob getIngestJob() {
return this.ingestJob;
}
/**
* Cancels the job.
*/
synchronized void cancel() {
setStage(Stage.CANCELLING);
setProcessingStage(Stage.CANCELLING);
cancelled = true;
errorsOccurred = true;
if (null != dataSourceProcessor) {
@@ -263,15 +330,33 @@ public final class AutoIngestJob implements Comparable<AutoIngestJob>, Serializa
}
}
/**
* Indicates whether or not the job has been cancelled. This is transient
* state used by the auto ingest manager that is not saved as coordination
* service node data for the job.
*
* @return True or false.
*/
synchronized boolean isCanceled() {
return cancelled;
}
/**
* Marks the job as completed. This is transient state used by the auto
* ingest manager that is not saved as coordination service node data for
* the job.
*/
synchronized void setCompleted() {
setStage(Stage.COMPLETED);
setProcessingStage(Stage.COMPLETED);
completed = true;
}
/**
* Indicates whether or not the job has been completed. This is transient
* state that is not saved as coordination service node data for the job.
*
* @return True or false.
*/
synchronized boolean isCompleted() {
return completed;
}
@@ -314,55 +399,108 @@ public final class AutoIngestJob implements Comparable<AutoIngestJob>, Serializa
return this.errorsOccurred;
}
/**
* Gets the processing host name for this job.
*
* @return The processing host name.
*/
synchronized String getProcessingHostName() {
return nodeName;
}
int getVersion() {
return this.version;
/**
* Sets the processing host name for this job.
*
* @param processingHostName The processing host name.
*/
synchronized void setProcessingHostName(String processingHostName) {
this.nodeName = processingHostName;
}
/**
* Gets the processing status of the job.
*
* @return The processing status.
*/
synchronized ProcessingStatus getProcessingStatus() {
return this.processingStatus;
}
/**
* Sets the processing status of the job.
*
* @param processingStatus The processing status.
*/
synchronized void setProcessingStatus(ProcessingStatus processingStatus) {
this.processingStatus = processingStatus;
}
/**
* Gets the number of time this job has "crashed" during processing.
*
* @return The number of crashes.
*/
synchronized int getNumberOfCrashes() {
return this.numberOfCrashes;
}
/**
* Sets the number of time this job has "crashed" during processing.
*
* @param numberOfCrashes The number of crashes.
*/
synchronized void setNumberOfCrashes(int numberOfCrashes) {
this.numberOfCrashes = numberOfCrashes;
}
/**
* Indicates whether some other job is "equal to" this job. Two jobs are
* equal if they have the same manifest file path.
*
* @param otherJob The job to which this job is to be compared.
*
* @return True or false.
*/
@Override
public boolean equals(Object obj) {
if (!(obj instanceof AutoIngestJob)) {
public boolean equals(Object otherJob) {
if (!(otherJob instanceof AutoIngestJob)) {
return false;
}
if (obj == this) {
if (otherJob == this) {
return true;
}
return this.getManifest().getFilePath().equals(((AutoIngestJob) obj).getManifest().getFilePath());
}
@Override
public int hashCode() {
int hash = 71 * (Objects.hashCode(this.caseDirectoryPath));
return hash;
}
@Override
public int compareTo(AutoIngestJob o) {
return -this.getManifest().getDateFileCreated().compareTo(o.getManifest().getDateFileCreated());
return this.getManifest().getFilePath().equals(((AutoIngestJob) otherJob).getManifest().getFilePath());
}
/**
* Custom comparator that allows us to sort List<AutoIngestJob> on reverse
* chronological date modified (descending)
* Returns a hash code value for the job. The hash code is derived from the
* manifest file path.
*
* @return The hash code.
*/
@Override
public int hashCode() {
int hash = 71 * (Objects.hashCode(this.getManifest().getFilePath()));
return hash;
}
/**
* Compares one job to another in a way that orders jobs by manifest
* creation date.
*
* @param otherJob The job to which this job is to be compared.
*
* @return A negative integer, zero, or a positive integer as this job is
* less than, equal to, or greater than the specified job.
*/
@Override
public int compareTo(AutoIngestJob otherJob) {
return -this.getManifest().getDateFileCreated().compareTo(otherJob.getManifest().getDateFileCreated());
}
/**
* Comparator that supports doing a descending sort of jobs based on job
* completion date.
*/
static class ReverseCompletedDateComparator implements Comparator<AutoIngestJob> {
@@ -374,7 +512,8 @@ public final class AutoIngestJob implements Comparable<AutoIngestJob>, Serializa
}
/**
* Comparator that sorts auto ingest jobs by priority in descending order.
* Comparator that supports doing a descending sort of jobs based on job
* priority.
*/
public static class PriorityComparator implements Comparator<AutoIngestJob> {
@@ -386,27 +525,26 @@ public final class AutoIngestJob implements Comparable<AutoIngestJob>, Serializa
}
/**
* Custom comparator that allows us to sort List<AutoIngestJob> on case name
* alphabetically except for jobs for the current host, which are placed at
* the top of the list.
* Comparator that supports doing an alphabetical sort of jobs based on a
* combination of case name and processing host.
*/
static class CaseNameAndProcessingHostComparator implements Comparator<AutoIngestJob> {
@Override
public int compare(AutoIngestJob o1, AutoIngestJob o2) {
if (o1.getProcessingHostName().equalsIgnoreCase(LOCAL_HOST_NAME)) {
return -1; // o1 is for current case, float to top
} else if (o2.getProcessingHostName().equalsIgnoreCase(LOCAL_HOST_NAME)) {
return 1; // o2 is for current case, float to top
public int compare(AutoIngestJob aJob, AutoIngestJob anotherJob) {
if (aJob.getProcessingHostName().equalsIgnoreCase(LOCAL_HOST_NAME)) {
return -1; // aJob is for this, float to top
} else if (anotherJob.getProcessingHostName().equalsIgnoreCase(LOCAL_HOST_NAME)) {
return 1; // anotherJob is for this, float to top
} else {
return o1.getManifest().getCaseName().compareToIgnoreCase(o2.getManifest().getCaseName());
return aJob.getManifest().getCaseName().compareToIgnoreCase(anotherJob.getManifest().getCaseName());
}
}
}
/**
* Processing status for the auto ingest job for the manifest.
* Processing statuses for an auto ingest job.
*/
enum ProcessingStatus {
PENDING,
@@ -415,6 +553,9 @@ public final class AutoIngestJob implements Comparable<AutoIngestJob>, Serializa
DELETED
}
/**
* Processing stages for an auto ingest job.
*/
enum Stage {
PENDING("Pending"),
@@ -443,6 +584,9 @@ public final class AutoIngestJob implements Comparable<AutoIngestJob>, Serializa
}
/**
* Processing stage details for an auto ingest job.
*/
@Immutable
static final class StageDetails implements Serializable {

View File

@@ -32,8 +32,22 @@ import javax.lang.model.type.TypeKind;
final class AutoIngestJobNodeData {
private static final int CURRENT_VERSION = 1;
private static final int MAX_POSSIBLE_NODE_DATA_SIZE = 131629;
private static final int DEFAULT_PRIORITY = 0;
/*
* This number is the sum of each piece of data, based on it's type. For the
* types boolean, int, and long, values 1, 4, and 8 will be added
* respectively. For String objects, the length of the string, plus either a
* byte or short respesenting the length of the string, will be added.
*
* This field is used to set the size of the buffer during the byte array
* creation in the 'toArray()' method. Since the final size of the array
* isn't immediately known at the time of creation, this number is used to
* create an array as large as could possibly be needed to store all the
* data. This avoids the need to continuously enlarge the buffer. Once the
* buffer has all the necessary data, it will be resized as appropriate.
*/
private static final int MAX_POSSIBLE_NODE_DATA_SIZE = 131629;
/*
* Version 0 fields.
@@ -48,16 +62,16 @@ final class AutoIngestJobNodeData {
* Version 1 fields.
*/
private int version;
private String manifestFilePath;
private String manifestFilePath; // 'short' length used in byte array
private long manifestFileDate;
private String caseName;
private String deviceId;
private String dataSourcePath;
private String caseDirectoryPath;
private String processingHostName;
private String caseName; // 'byte' length used in byte array
private String deviceId; // 'byte' length used in byte array
private String dataSourcePath; // 'short' length used in byte array
private String caseDirectoryPath; // 'short' length used in byte array
private String processingHostName; // 'short' length used in byte array
private byte processingStage;
private long processingStageStartDate;
private String processingStageDetailsDescription;
private String processingStageDetailsDescription; // 'byte' length used in byte array
private long processingStageDetailsStartDate;
/**
@@ -84,7 +98,7 @@ final class AutoIngestJobNodeData {
setProcessingHostName(job.getProcessingHostName());
setProcessingStage(job.getProcessingStage());
setProcessingStageStartDate(job.getProcessingStageStartDate());
setProcessingStageDetails(job.getStageDetails());
setProcessingStageDetails(job.getProcessingStageDetails());
}
/**
@@ -107,7 +121,7 @@ final class AutoIngestJobNodeData {
this.numberOfCrashes = 0;
this.completedDate = 0L;
this.errorsOccurred = false;
this.version = CURRENT_VERSION;
this.version = 0;
this.manifestFilePath = "";
this.manifestFileDate = 0L;
this.caseName = "";
@@ -151,7 +165,7 @@ final class AutoIngestJobNodeData {
this.processingStage = buffer.get();
this.processingStageStartDate = buffer.getLong();
this.processingStageDetailsDescription = getStringFromBuffer(buffer, TypeKind.BYTE);
this.processingStageDetailsStartDate = buffer.getLong();;
this.processingStageDetailsStartDate = buffer.getLong();
this.processingHostName = getStringFromBuffer(buffer, TypeKind.SHORT);
}
@@ -318,14 +332,14 @@ final class AutoIngestJobNodeData {
/**
* Gets the path to the case directory of the case associated with the job.
*
* @return The case directory path or null if the case directory has not
* been created yet.
* @return The case directory path or an empty string path if the case
* directory has not been created yet.
*/
synchronized Path getCaseDirectoryPath() {
if (!caseDirectoryPath.isEmpty()) {
return Paths.get(caseDirectoryPath);
} else {
return null;
return Paths.get("");
}
}
@@ -517,7 +531,16 @@ final class AutoIngestJobNodeData {
return array;
}
// DGL: Document what is going on here and how the max buffer sie constant is calculated.
/**
* This method retrieves a string from a given buffer. Depending on the type
* specified, either a 'byte' or a 'short' will first be read out of the
* buffer which gives the length of the string so it can be properly parsed.
*
* @param buffer The buffer from which the string will be read.
* @param lengthType The size of the length data.
*
* @return The string read from the buffer.
*/
private String getStringFromBuffer(ByteBuffer buffer, TypeKind lengthType) {
int length = 0;
String output = "";
@@ -540,7 +563,16 @@ final class AutoIngestJobNodeData {
return output;
}
// DGL: Document what is going on here and how the max buffer sie constant is calculated.
/**
* This method puts a given string into a given buffer. Depending on the
* type specified, either a 'byte' or a 'short' will be inserted prior to
* the string which gives the length of the string so it can be properly
* parsed.
*
* @param stringValue The string to write to the buffer.
* @param buffer The buffer to which the string will be written.
* @param lengthType The size of the length data.
*/
private void putStringIntoBuffer(String stringValue, ByteBuffer buffer, TypeKind lengthType) {
switch (lengthType) {
case BYTE:

View File

@@ -647,10 +647,10 @@ public final class AutoIngestManager extends Observable implements PropertyChang
}
/*
* Add the job to the pending jobs queue and update the coordinatino
* Add the job to the pending jobs queue and update the coordination
* service node data for the job.
*/
if (null != completedJob && null != completedJob.getCaseDirectoryPath()) {
if (null != completedJob && !completedJob.getCaseDirectoryPath().toString().isEmpty()) {
try {
completedJob.setErrorsOccurred(false);
completedJob.setCompletedDate(new Date(0));
@@ -849,7 +849,7 @@ public final class AutoIngestManager extends Observable implements PropertyChang
if (null != ingestJob) {
IngestJob.DataSourceIngestModuleHandle moduleHandle = ingestJob.getSnapshot().runningDataSourceIngestModule();
if (null != moduleHandle) {
currentJob.setStage(AutoIngestJob.Stage.CANCELLING_MODULE);
currentJob.setProcessingStage(AutoIngestJob.Stage.CANCELLING_MODULE);
moduleHandle.cancel();
SYS_LOGGER.log(Level.INFO, "Cancelling {0} module for manifest {1}", new Object[]{moduleHandle.displayName(), currentJob.getManifest().getFilePath()});
}
@@ -1117,7 +1117,9 @@ public final class AutoIngestManager extends Observable implements PropertyChang
AutoIngestJob job = new AutoIngestJob(manifest);
job.setPriority(nodeData.getPriority());
Path caseDirectory = PathUtils.findCaseDirectory(rootOutputDirectory, manifest.getCaseName());
job.setCaseDirectoryPath(caseDirectory);
if (null != caseDirectory) {
job.setCaseDirectoryPath(caseDirectory);
}
newPendingJobsList.add(job);
/*
@@ -1295,7 +1297,7 @@ public final class AutoIngestManager extends Observable implements PropertyChang
AutoIngestJob job = new AutoIngestJob(manifest);
job.setCaseDirectoryPath(caseDirectoryPath);
job.setProcessingStatus(AutoIngestJob.ProcessingStatus.COMPLETED);
job.setStage(AutoIngestJob.Stage.COMPLETED);
job.setProcessingStage(AutoIngestJob.Stage.COMPLETED);
job.setCompletedDate(nodeData.getCompletedDate());
job.setErrorsOccurred(true);
newCompletedJobsList.add(new AutoIngestJob(nodeData));
@@ -1864,10 +1866,11 @@ public final class AutoIngestManager extends Observable implements PropertyChang
*/
private void processJob() throws CoordinationServiceException, SharedConfigurationException, ServicesMonitorException, DatabaseServerDownException, KeywordSearchServerDownException, CaseManagementException, AnalysisStartupException, FileExportException, AutoIngestAlertFileException, AutoIngestJobLoggerException, InterruptedException, AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException, AutoIngestJobNodeData.InvalidDataException {
Path manifestPath = currentJob.getManifest().getFilePath();
currentJob.setProcessingStatus(AutoIngestJob.ProcessingStatus.PROCESSING);
updateCoordinationServiceNode(currentJob);
SYS_LOGGER.log(Level.INFO, "Started processing of {0}", manifestPath);
currentJob.setStage(AutoIngestJob.Stage.STARTING);
currentJob.setProcessingStatus(AutoIngestJob.ProcessingStatus.PROCESSING);
currentJob.setProcessingStage(AutoIngestJob.Stage.STARTING);
currentJob.setProcessingHostName(AutoIngestManager.LOCAL_HOST_NAME);
updateCoordinationServiceNode(currentJob);
setChanged();
notifyObservers(Event.JOB_STARTED);
eventPublisher.publishRemotely(new AutoIngestJobStartedEvent(currentJob));
@@ -1890,6 +1893,7 @@ public final class AutoIngestManager extends Observable implements PropertyChang
// The job may get retried
currentJob.setProcessingStatus(AutoIngestJob.ProcessingStatus.PENDING);
}
currentJob.setProcessingHostName("");
updateCoordinationServiceNode(currentJob);
boolean retry = (!currentJob.isCanceled() && !currentJob.isCompleted());
@@ -1986,7 +1990,7 @@ public final class AutoIngestManager extends Observable implements PropertyChang
if (AutoIngestUserPreferences.getSharedConfigEnabled()) {
Path manifestPath = currentJob.getManifest().getFilePath();
SYS_LOGGER.log(Level.INFO, "Downloading shared configuration for {0}", manifestPath);
currentJob.setStage(AutoIngestJob.Stage.UPDATING_SHARED_CONFIG);
currentJob.setProcessingStage(AutoIngestJob.Stage.UPDATING_SHARED_CONFIG);
new SharedConfiguration().downloadConfiguration();
}
}
@@ -2004,7 +2008,7 @@ public final class AutoIngestManager extends Observable implements PropertyChang
private void verifyRequiredSevicesAreRunning() throws ServicesMonitorException, DatabaseServerDownException, KeywordSearchServerDownException {
Path manifestPath = currentJob.getManifest().getFilePath();
SYS_LOGGER.log(Level.INFO, "Checking services availability for {0}", manifestPath);
currentJob.setStage(AutoIngestJob.Stage.CHECKING_SERVICES);
currentJob.setProcessingStage(AutoIngestJob.Stage.CHECKING_SERVICES);
if (!isServiceUp(ServicesMonitor.Service.REMOTE_CASE_DATABASE.toString())) {
throw new DatabaseServerDownException("Case database server is down");
}
@@ -2051,7 +2055,7 @@ public final class AutoIngestManager extends Observable implements PropertyChang
Manifest manifest = currentJob.getManifest();
String caseName = manifest.getCaseName();
SYS_LOGGER.log(Level.INFO, "Opening case {0} for {1}", new Object[]{caseName, manifest.getFilePath()});
currentJob.setStage(AutoIngestJob.Stage.OPENING_CASE);
currentJob.setProcessingStage(AutoIngestJob.Stage.OPENING_CASE);
/*
* Acquire and hold a case name lock so that only one node at as
* time can scan the output directory at a time. This prevents
@@ -2164,7 +2168,7 @@ public final class AutoIngestManager extends Observable implements PropertyChang
DataSource dataSource = identifyDataSource(caseForJob);
if (null == dataSource) {
currentJob.setStage(AutoIngestJob.Stage.COMPLETED);
currentJob.setProcessingStage(AutoIngestJob.Stage.COMPLETED);
return;
}
@@ -2174,7 +2178,7 @@ public final class AutoIngestManager extends Observable implements PropertyChang
runDataSourceProcessor(caseForJob, dataSource);
if (dataSource.getContent().isEmpty()) {
currentJob.setStage(AutoIngestJob.Stage.COMPLETED);
currentJob.setProcessingStage(AutoIngestJob.Stage.COMPLETED);
return;
}
@@ -2219,7 +2223,7 @@ public final class AutoIngestManager extends Observable implements PropertyChang
Manifest manifest = currentJob.getManifest();
Path manifestPath = manifest.getFilePath();
SYS_LOGGER.log(Level.INFO, "Identifying data source for {0} ", manifestPath);
currentJob.setStage(AutoIngestJob.Stage.IDENTIFYING_DATA_SOURCE);
currentJob.setProcessingStage(AutoIngestJob.Stage.IDENTIFYING_DATA_SOURCE);
Path caseDirectoryPath = currentJob.getCaseDirectoryPath();
AutoIngestJobLogger jobLogger = new AutoIngestJobLogger(manifestPath, manifest.getDataSourceFileName(), caseDirectoryPath);
Path dataSourcePath = manifest.getDataSourcePath();
@@ -2255,7 +2259,7 @@ public final class AutoIngestManager extends Observable implements PropertyChang
Manifest manifest = currentJob.getManifest();
Path manifestPath = manifest.getFilePath();
SYS_LOGGER.log(Level.INFO, "Adding data source for {0} ", manifestPath);
currentJob.setStage(AutoIngestJob.Stage.ADDING_DATA_SOURCE);
currentJob.setProcessingStage(AutoIngestJob.Stage.ADDING_DATA_SOURCE);
UUID taskId = UUID.randomUUID();
DataSourceProcessorCallback callBack = new AddDataSourceCallback(caseForJob, dataSource, taskId);
DataSourceProcessorProgressMonitor progressMonitor = new DoNothingDSPProgressMonitor();
@@ -2420,7 +2424,7 @@ public final class AutoIngestManager extends Observable implements PropertyChang
Manifest manifest = currentJob.getManifest();
Path manifestPath = manifest.getFilePath();
SYS_LOGGER.log(Level.INFO, "Starting ingest modules analysis for {0} ", manifestPath);
currentJob.setStage(AutoIngestJob.Stage.ANALYZING_DATA_SOURCE);
currentJob.setProcessingStage(AutoIngestJob.Stage.ANALYZING_DATA_SOURCE);
Path caseDirectoryPath = currentJob.getCaseDirectoryPath();
AutoIngestJobLogger jobLogger = new AutoIngestJobLogger(manifestPath, manifest.getDataSourceFileName(), caseDirectoryPath);
IngestJobEventListener ingestJobEventListener = new IngestJobEventListener();
@@ -2456,7 +2460,7 @@ public final class AutoIngestManager extends Observable implements PropertyChang
}
jobLogger.logAnalysisCompleted();
} else {
currentJob.setStage(AutoIngestJob.Stage.CANCELLING);
currentJob.setProcessingStage(AutoIngestJob.Stage.CANCELLING);
currentJob.setErrorsOccurred(true);
AutoIngestAlertFile.create(caseDirectoryPath); // Do this first, it is more important than the case log
jobLogger.logAnalysisCancelled();
@@ -2519,7 +2523,7 @@ public final class AutoIngestManager extends Observable implements PropertyChang
Manifest manifest = currentJob.getManifest();
Path manifestPath = manifest.getFilePath();
SYS_LOGGER.log(Level.INFO, "Exporting files for {0}", manifestPath);
currentJob.setStage(AutoIngestJob.Stage.EXPORTING_FILES);
currentJob.setProcessingStage(AutoIngestJob.Stage.EXPORTING_FILES);
Path caseDirectoryPath = currentJob.getCaseDirectoryPath();
AutoIngestJobLogger jobLogger = new AutoIngestJobLogger(manifestPath, manifest.getDataSourceFileName(), caseDirectoryPath);
try {
@@ -2770,9 +2774,10 @@ public final class AutoIngestManager extends Observable implements PropertyChang
try {
synchronized (jobsLock) {
if (currentJob != null) {
currentJob.getStageDetails();
currentJob.getProcessingStageDetails();
setChanged();
notifyObservers(Event.JOB_STATUS_UPDATED);
updateCoordinationServiceNode(currentJob);
eventPublisher.publishRemotely(new AutoIngestJobStatusEvent(currentJob));
}

View File

@@ -19,13 +19,10 @@
package org.sleuthkit.autopsy.experimental.autoingest;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.awt.Cursor;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Observable;
@@ -34,7 +31,6 @@ import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import javax.annotation.concurrent.GuardedBy;
import org.openide.util.Exceptions;
import org.sleuthkit.autopsy.coordinationservice.CoordinationService;
import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CoordinationServiceException;
import org.sleuthkit.autopsy.coreutils.Logger;
@@ -143,8 +139,6 @@ public final class AutoIngestMonitor extends Observable implements PropertyChang
*/
private void handleJobStartedEvent(AutoIngestJobStartedEvent event) {
synchronized (jobsLock) {
// DLG: TEST! Remove job from pending queue, if present
// DLG: TEST! Add job to running jobs list
jobsSnapshot.removePendingJob(event.getJob());
jobsSnapshot.addOrReplaceRunningJob(event.getJob());
setChanged();
@@ -159,8 +153,12 @@ public final class AutoIngestMonitor extends Observable implements PropertyChang
*/
private void handleJobStatusEvent(AutoIngestJobStatusEvent event) {
synchronized (jobsLock) {
// DLG: TEST! Replace job in running list with job from event
jobsSnapshot.addOrReplaceRunningJob(event.getJob());
/*
* Currently this event is only published for running jobs.
*/
AutoIngestJob job = event.getJob();
jobsSnapshot.removePendingJob(job);
jobsSnapshot.addOrReplaceRunningJob(job);
setChanged();
notifyObservers(jobsSnapshot);
}
@@ -173,10 +171,10 @@ public final class AutoIngestMonitor extends Observable implements PropertyChang
*/
private void handleJobCompletedEvent(AutoIngestJobCompletedEvent event) {
synchronized (jobsLock) {
// DLG: TEST! Remove job from event from running list, if present
// DLG: TEST! Add job to completed list
jobsSnapshot.removeRunningJob(event.getJob());
jobsSnapshot.addOrReplaceCompletedJob(event.getJob());
AutoIngestJob job = event.getJob();
jobsSnapshot.removePendingJob(job);
jobsSnapshot.removeRunningJob(job);
jobsSnapshot.addOrReplaceCompletedJob(job);
setChanged();
notifyObservers(jobsSnapshot);
}
@@ -241,6 +239,13 @@ public final class AutoIngestMonitor extends Observable implements PropertyChang
for (String node : nodeList) {
try {
AutoIngestJobNodeData nodeData = new AutoIngestJobNodeData(coordinationService.getNodeData(CoordinationService.CategoryNode.MANIFESTS, node));
if (nodeData.getVersion() < 1) {
/*
* Ignore version '0' nodes that have not been
* "upgraded" since they don't carry enough data.
*/
continue;
}
AutoIngestJob job = new AutoIngestJob(nodeData);
ProcessingStatus processingStatus = nodeData.getProcessingStatus();
switch (processingStatus) {
@@ -275,9 +280,9 @@ public final class AutoIngestMonitor extends Observable implements PropertyChang
/**
* Bumps the priority of an auto ingest job.
*
* @param manifestPath The manifest file path for the job to be prioritized.
* @param job The job to be prioritized.
*/
JobsSnapshot prioritizeJob(Path manifestFilePath) throws AutoIngestMonitorException {
JobsSnapshot prioritizeJob(AutoIngestJob job) throws AutoIngestMonitorException {
int highestPriority = 0;
AutoIngestJob prioritizedJob = null;
synchronized (jobsLock) {
@@ -285,11 +290,11 @@ public final class AutoIngestMonitor extends Observable implements PropertyChang
* Get the highest known priority and make sure the job is still in
* the pending jobs queue.
*/
for (AutoIngestJob job : jobsSnapshot.getPendingJobs()) {
if (job.getPriority() > highestPriority) {
highestPriority = job.getPriority();
for (AutoIngestJob pendingJob : jobsSnapshot.getPendingJobs()) {
if (pendingJob.getPriority() > highestPriority) {
highestPriority = pendingJob.getPriority();
}
if (job.getManifest().getFilePath().equals(manifestFilePath)) {
if (pendingJob.equals(job)) {
prioritizedJob = job;
}
}
@@ -300,27 +305,25 @@ public final class AutoIngestMonitor extends Observable implements PropertyChang
*/
if (null != prioritizedJob) {
++highestPriority;
String manifestNodePath = prioritizedJob.getManifest().getFilePath().toString();
String manifestNodePath = job.getManifest().getFilePath().toString();
try {
AutoIngestJobNodeData nodeData = new AutoIngestJobNodeData(coordinationService.getNodeData(CoordinationService.CategoryNode.MANIFESTS, manifestNodePath));
nodeData.setPriority(highestPriority);
coordinationService.setNodeData(CoordinationService.CategoryNode.MANIFESTS, manifestNodePath, nodeData.toArray());
} catch (AutoIngestJobNodeData.InvalidDataException | CoordinationServiceException | InterruptedException ex) {
throw new AutoIngestMonitorException("Error bumping priority for job " + prioritizedJob.toString(), ex);
throw new AutoIngestMonitorException("Error bumping priority for job " + job.toString(), ex);
}
prioritizedJob.setPriority(highestPriority);
}
/*
* Publish a prioritization event.
*/
if (null != prioritizedJob) {
final String caseName = prioritizedJob.getManifest().getCaseName();
/*
* Publish a prioritization event.
*/
final String caseName = job.getManifest().getCaseName();
new Thread(() -> {
eventPublisher.publishRemotely(new AutoIngestCasePrioritizedEvent(LOCAL_HOST_NAME, caseName));
}).start();
}
}
return jobsSnapshot;
}
}
@@ -344,7 +347,7 @@ public final class AutoIngestMonitor extends Observable implements PropertyChang
synchronized (jobsLock) {
jobsSnapshot = newJobsSnapshot;
setChanged();
notifyObservers(jobsSnapshot);
notifyObservers(null);
}
}
}

View File

@@ -11,45 +11,16 @@ AutoIngestDashboard.JobsTableModel.ColumnHeader.CompletedTime=Job Completed
AutoIngestDashboard.JobsTableModel.ColumnHeader.Stage=Stage
AutoIngestDashboard.JobsTableModel.ColumnHeader.Status=Status
AutoIngestDashboard.JobsTableModel.ColumnHeader.ManifestFilePath= Manifest File Path
AutoIngestDashboard.bnResume.text=Resume
AutoIngestDashboard.bnPause.confirmHeader=Are you sure you want to pause?
AutoIngestDashboard.bnPause.warningText=Pause will occur after the current job completes processing. This could take a long time. Continue?
AutoIngestDashboard.bnPause.toolTipTextResume=Resume processing of Pending Jobs
AutoIngestDashboard.bnPause.pausing=Pausing after current job completes...
AutoIngestDashboard.Cancelling=Cancelling...
AutoIngestDashboard.pendingTable.toolTipText=The Pending table displays the order upcoming Jobs will be processed with the top of the list first
AutoIngestDashboard.runningTable.toolTipText=The Running table displays the currently running Job and information about it
AutoIngestDashboard.completedTable.toolTipText=The Completed table shows all Jobs that have been processed already
AutoIngestDashboard.JobsTableModel.ColumnHeader.StageTime=Time in Stage
AutoIngestDashboard.JobsTableModel.ColumnHeader.CaseFolder=Case Folder
AutoIngestDashboard.JobsTableModel.ColumnHeader.LocalJob= Local Job?
AutoIngestDashboard.DeletionFailed=Deletion failed for job
AutoIngestDashboard.ShowLogFailed.Title=Unable to display case log
AutoIngestDashboard.ShowLogFailed.Message=Case log file does not exist
AutoIngestDashboard.bnPrioritizeCase.toolTipText=Move all images associated with a case to top of Pending queue.
AutoIngestDashboard.ExitConsequences=This will cancel any currently running job on this host. Exiting while a job is running potentially leaves the case in an inconsistent or corrupted state.
AutoIngestDashboard.ExitingStatus=Exiting...
AutoIngestDashboard.OK=OK
AutoIngestDashboard.Cancel=Cancel
AutoIngestDashboard.AutoIngestStartupFailed.Message=Failed to start automated ingest.\nPlease see auto ingest system log for details.
AutoIngestDashboard.AutoIngestStartupFailed.Title=Automated Ingest Error
AutoIngestDashboard.AutoIngestStartupError=Failed to start automated ingest. Verify Multi-user Settings.
AutoIngestDashboard.AutoIngestStartupWarning.Title=Automated Ingest Warning
AutoIngestDashboard.AutoIngestStartupWarning.Message=Failed to establish remote communications with other automated ingest nodes.\nAuto ingest dashboard will only be able to display local ingest job events.\nPlease verify Multi-User settings (Options->Multi-User). See application log for details.
AutoIngestDashboard.UpdatingSharedConfig=Updating shared configuration
AutoIngestDashboard.SharedConfigurationDisabled=Shared configuration disabled
AutoIngestDashboard.EnableConfigurationSettings=Enable shared configuration from the options panel before uploading
AutoIngestDashboard.ErrorUploadingConfiguration=Error uploading configuration
AutoIngestDashboard.UploadSuccessTitle=Success
AutoIngestDashboard.UploadSuccess=Shared configuration successfully uploaded
AutoIngestDashboard.UploadFailedTitle=Failed
AutoIngestDashboard.ConfigLocked=The shared configuration directory is locked because upload from another node is in progress. \nIf this is an error, you can unlock the directory and then retry the upload.
AutoIngestDashboard.ConfigLockedTitle=Configuration directory locked
AutoIngestDashboard.JobsTableModel.ColumnHeader.CaseFolder=Case
AutoIngestDashboard.JobsTableModel.ColumnHeader.Job=Job
AutoIngestDashboard.tbServicesStatusMessage.Message=Case databases {0}, keyword search {1}, coordination {2}, messaging {3}
AutoIngestDashboard.tbServicesStatusMessage.Message.Up=up
AutoIngestDashboard.tbServicesStatusMessage.Message.Down=down
AutoIngestDashboard.tbServicesStatusMessage.Message.Unknown=unknown
AutoIngestDashboard.PauseDueToSystemError=Paused due to system error, please consult the auto ingest system log
ConfirmationDialog.DoNotDelete=Do not delete
ConfirmationDialog.Delete=Permanently delete
ConfirmationDialog.DeleteAreYouSure=The entire case will be removed. Are you sure you want to delete case
@@ -260,7 +231,6 @@ FileExporterSettingsPanel.BrowseReportTooltip_1=Browse for the Reports Folder
FileExporterSettingsPanel.NewRuleTooltip_1=Clear the rule editor to begin a new rule
FileExporterSettingsPanel.DeleteTooltip_1=Delete the selected rule
FileExporterSettingsPanel.SaveTooltip_1=Save the current rule
AutoIngestDashboard.bnPrioritizeFolder.label=<AutoIngestDashboard.bnPrioritizeJob.text>
AutoIngestCasePanel.rbDays.text=Days
AutoIngestCasePanel.rbWeeks.text=Weeks
AutoIngestCasePanel.rbMonths.text=Months

View File

@@ -42,7 +42,7 @@ public final class Manifest implements Serializable {
this.dateFileCreated = dateFileCreated;
this.caseName = caseName;
this.deviceId = deviceId;
if (dataSourcePath != null) {
if (null != dataSourcePath) {
this.dataSourcePath = dataSourcePath.toString();
} else {
this.dataSourcePath = "";
@@ -55,7 +55,7 @@ public final class Manifest implements Serializable {
}
public Date getDateFileCreated() {
return this.dateFileCreated;
return new Date(this.dateFileCreated.getTime());
}
public String getCaseName() {

View File

@@ -3,7 +3,7 @@
<name unique="dashboard"/>
<kind type="editor"/>
<state type="separated"/>
<bounds x="76" y="68" width="996" height="672"/>
<bounds x="76" y="68" width="1005" height="750"/>
<frame state="0"/>
<empty-behavior permanent="false"/>

View File

@@ -1,52 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2013 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.imagegallery.actions;
import java.awt.Desktop;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.net.URI;
import java.util.logging.Level;
import org.openide.awt.ActionID;
import org.openide.awt.ActionReference;
import org.openide.awt.ActionRegistration;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.coreutils.Logger;
@ActionID(
category = "Help",
id = "org.sleuthkit.autopsy.imagegallery.actions.OpenHelpAction"
)
@ActionRegistration(
displayName = "#CTL_OpenHelpAction"
)
@ActionReference(path = "Menu/Help", position = 350)
@Messages("CTL_OpenHelpAction=Image / Video Gallery Help")
public final class OpenHelpAction implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
try {
Desktop.getDesktop().browse(URI.create("http://sleuthkit.org/autopsy/docs/user-docs/4.4.1/image_gallery_page.html")); //NON-NLS
} catch (IOException ex) {
Logger.getLogger(OpenHelpAction.class.getName()).log(Level.SEVERE, "failed to open help page", ex); //NON-NLS
}
}
}

View File

@@ -136,7 +136,15 @@ public class Toolbar extends ToolBar {
tagGroupMenuButton.setText(followUpGroupAction.getText());
tagGroupMenuButton.setGraphic(followUpGroupAction.getGraphic());
} catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, "Could create follow up tag menu item", ex); //NON-NLS
/*
* The problem appears to be a timing issue where a case is closed
* before this initialization is completed, which It appears to be
* harmless, so we are temporarily changing this log message to a
* WARNING.
*
* TODO (JIRA-3010): SEVERE error logged by image Gallery UI
*/
LOGGER.log(Level.WARNING, "Could not create Follow Up tag menu item", ex); //NON-NLS
}
tagGroupMenuButton.showingProperty().addListener(showing -> {
if (tagGroupMenuButton.isShowing()) {

View File

@@ -265,7 +265,6 @@
<unzip src="${nbdist.dir}/${app.name}-${app.version}.zip" dest="${nbdist.dir}/${app.name}-installer"/>
<!-- Disable the Experimental module by default for the installed version -->
<replace file="${nbdist.dir}/${app.name}-installer/autopsy/config/modules/org-sleuthkit-autopsy-experimental.xml" token="&lt;param name=&quot;enabled&quot;&gt;true&lt;/param&gt;" value="&lt;param name=&quot;enabled&quot;&gt;false&lt;/param&gt;"/>
<replace file="${nbdist.dir}/${app.name}-installer/autopsy/config/modules/org-sleuthkit-autopsy-centralrepository.xml" token="&lt;param name=&quot;enabled&quot;&gt;true&lt;/param&gt;" value="&lt;param name=&quot;enabled&quot;&gt;false&lt;/param&gt;"/>
<echo file="${nbdist.dir}/${app.name}-installer/autopsy/.lastModified" message="" />
<antcall target="build-installer-${os.family}" />
</target>

View File

@@ -2,22 +2,22 @@
\section cr_overview Overview
The Central Repository allows a user to find matching artifacts both across cases and across data sources in the same case.
It is a combination of an ingest module that extracts, stores, and compares properties against lists of known or known bad
The central repository allows a user to find matching artifacts both across cases and across data sources in the same case.
It is a combination of an ingest module that extracts, stores, and compares properties against lists of notable
properties, a database that stores these properties, and an additional panel in Autopsy to display other instances of each
property. The Central Repository database can either be SQLite or PostgreSQL.
property. The central repository database can either be SQLite or PostgreSQL.
The following are some use cases for the Central Repository:
- <b>Finding Other Instances of a Property</b>
- If you find a file or Autopsy artifact (such as a Web History item), there is a content viewer in the bottom right that will show you other cases that had this same file or that had items with the same feature (such as Domain name). You will also be able to see what other data sources in the same case had this feature.
- <b>Alerting When Previously Notable Properties Occur</b>
- You can use the Central Repository to record which properties were associated with files and artifacts that were evidence (or notable). Once these properties have been tagged as 'BAD' they will be added to the Interesting Items section of the tree when seen again in any future cases.
- You can use the Central Repository to record which properties were associated with files and artifacts that were evidence (or notable). Once these properties have been tagged as notable they will be added to the Interesting Items section of the tree when seen again in any future cases.
\section cr_terms Terms and Concepts
- <b>Central Repository</b> - The Autopsy feature containing the Central Repository Database and Correlation Engine Ingest Module. Also responsible for displaying correlated properties to the user
- <b>Central Repository</b> - The Autopsy feature containing the central repository database and Correlation Engine Ingest Module. Also responsible for displaying correlated properties to the user
- <b>Central Repository Database</b> - the SQLite or PostgreSQL database that holds all the data
- <b>Correlation Engine Ingest Module</b> - The ingest module responsible for adding new properties to the database and comparing these properties against the Known/Known Bad lists
- <b>Correlation Engine Ingest Module</b> - The ingest module responsible for adding new properties to the database and comparing these properties against existing notable properties
- <b>Property</b> - The data being stored/correlated. These can be file paths/MD5 hashes, email addresses, phone numbers, etc.
\section cr_setup Setup
@@ -62,14 +62,14 @@ Enter or browse to a folder for the database. If the database file does not exis
In Autopsy, you are allowed to define your own Tag names, tag files and artifacts,and add comments when you tag a file or artifact. The purpose of this
feature is to associate one or more of those tags with this module to be used for Correlation. Associating a tag with the Correlation Engine means
that when a file/artifact is tagged by the user, any property created from that file/artifact in the Correlation Database is marked as Bad. After
this point, whenever the Correlation Engine Ingest Module creates a property that matches this Bad one, it is automatically flagged
that when a file/artifact is tagged by the user, any property created from that file/artifact in the Correlation Database is marked as notable. After
this point, whenever the Correlation Engine Ingest Module creates a property that matches this notable one, it is automatically flagged
and added to the list of Interesting Items.
\image html central_repo_manage_tags.png
If a case is open, checking the Implies Known Bad checkbox will give you the option to add the known bad status to anything in the current case
that has already been tagged. For example, if you create a tag named "Alpha", tag a few items and then go into Manage Tags and check the box for the Alpha tag, you can optionally choose to have the status for those tagged items changed in the Central Repository. The effect is the same as if you had checked the box in Manage Tags before tagging the items. Note that data from any previous cases will not be changed.
If a case is open, checking the Notable checkbox will give you the option to add the notable status to anything in the current case
that has already been tagged. For example, if you create a tag named "Alpha", tag a few items and then go into Manage Tags and check the box for the Alpha tag, you can optionally choose to have the status for those tagged items changed in the central repository. The effect is the same as if you had checked the box in Manage Tags before tagging the items. Note that data from any previous cases will not be changed.
By default there is a tag called "Evidence" as the only tag associated with this module. To associate one or more tag(s) with this module, check the Correlate box next to the tag
name(s) and click OK. To create additional tags, use the Tags options panel.
@@ -99,9 +99,9 @@ Descriptions of the property types:
\subsection cr_ingest_module Correlation Engine Module
The Correlation Engine ingest module is responsible for adding properties to the database and comparing each property
against the list of Known/Known Bad property. It is best to run all ingest modules to get the most out of the Correlation
against the list of notable properties. It is best to run all ingest modules to get the most out of the Correlation
Engine. For example, if Hash Lookup is not run then the Correlation Engine module will not put any files into the
database. If the Correlation Engine module is not run on a particular case but the Central Repository is enabled,
database. If the Correlation Engine module is not run on a particular case but a central repository is enabled,
there will still be some limited functionality. The Content Viewer will still display matching properties from
other cases/data sources where the Correlation Engine was run.
@@ -109,13 +109,13 @@ other cases/data sources where the Correlation Engine was run.
Any file or artifact that a user tags with one of the tags associated with the Correlation Engine will be added
to the database as a file or artifact of interest. Any future data source ingest, where this module is enabled,
will use those files or artifacts as if they were part of the Known Bad list, causing matching files from that
will use those files or artifacts in a similar manner as a Known Bad hash set, causing matching files from that
ingest to be added to the Interesting Artifacts list in that currently open case.
\image html central_repo_tag_file.png
If a tag is accidentally added to a file or artifact, it can be removed though the context menu. This will remove its
Known Bad status in the Central Repository.
notable status in the Central Repository.
\subsection cr_case_properties Edit Central Repository Case Properties
@@ -128,17 +128,17 @@ This is where the examiner can store a number of details about the case.
\section cr_viewing_results Viewing Results
Results from enabling the Central Repository and running the Correlation Engine Ingest Module can be seen in two places:
Results from enabling a central repository and running the Correlation Engine Ingest Module can be seen in two places:
- The Content Viewer for each file or artifact will display all matching properties from other cases/data sources
- The Interesting Files node of the result tree will contain any files or results that matched properties previously flagged as BAD
- The Interesting Files node of the result tree will contain any files or results that matched properties previously marked as notable
\subsection cr_content_viewer Content Viewer
The \ref content_viewer_page panel is where previous instances of properties are displayed. This module adds a new tab to the Content Viewer. The tab for this module is called "Other Data Sources". It can display data that is found in other cases, other data sources for the same case, or imported global artifacts.
The \ref content_viewer_page panel is where previous instances of properties are displayed. This module adds a new tab to the Content Viewer. The tab for this module is called "Other Occurrences". It can display data that is found in other cases, other data sources for the same case, or imported global artifacts.
If at least one other case or data source has been ingested with this module enabled, there is a potential that data will be displayed in the Other Data Sources content viewer. Note that the Correlation Engine Ingest Module does not have to have been run on the current data source to see correlated files from other cases/data sources. If the selected file or artifact is associated by one of the supported Correlation Types, to one or more file(s) or artifact(s) in the database, the associated files/artifacts will be displayed. Note: the Content Viewer will display ALL associated files and artifacts available in the database. It ignores the user's enabled/disabled Correlation Properties.
If at least one other case or data source has been ingested with this module enabled, there is a potential that data will be displayed in the Other Occurrences content viewer. Note that the Correlation Engine Ingest Module does not have to have been run on the current data source to see correlated files from other cases/data sources. If the selected file or artifact is associated by one of the supported Correlation Types, to one or more file(s) or artifact(s) in the database, the associated files/artifacts will be displayed. Note: the Content Viewer will display ALL associated files and artifacts available in the database. It ignores the user's enabled/disabled Correlation Properties.
By default, the rows in the content viewer will have background colors to indicate if they are known to be of interest. Files/artifacts that are Known Bad will have a Red background, Unknown will have Yellow background, and Known will have a White background.
By default, the rows in the content viewer will have background colors to indicate if they are known to be of interest. Files/artifacts that are notable will have a Red background, unknown will have Yellow background, and known will have a White background.
\image html central_repo_content_viewer.png
@@ -173,8 +173,8 @@ details will include:
- Case Creation Date
- Case Examiner contact information
- Case Examiner's notes
T
hese details would have been entered by the examiner of the selected case, by visiting
These details would have been entered by the examiner of the selected case, by visiting
the Case -> Central Repository Case Properties menu, when that case was open.
<b>Show Commonality Details</b>
@@ -187,11 +187,11 @@ In the Results tree of an open case is an entry called Interesting Items. When t
\image html central_repo_interesting_items.png
As an example, if the Files Correlatable Property is enabled, and the ingest is currently processing a file, for example "badfile.exe", and the MD5 hash for that file already exists in the database as a KNOWN BAD file, then an entry in the Interesting Items tree will be added for the current instance of "badfile.exe" in the data source currently being ingested.
As an example, if the Files Correlatable Property is enabled, and the ingest is currently processing a file, for example "badfile.exe", and the MD5 hash for that file already exists in the database as a notable file, then an entry in the Interesting Items tree will be added for the current instance of "badfile.exe" in the data source currently being ingested.
The same type of thing will happen for each enabled Correlatable Property.
In the case of the phone number correlatable type, the Interesting Items tree will start a sub-tree for each phone number. The sub-tree will then contain each instance of that Known Bad phone number.
In the case of the phone number correlatable type, the Interesting Items tree will start a sub-tree for each phone number. The sub-tree will then contain each instance of that notable phone number.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 998 KiB

After

Width:  |  Height:  |  Size: 998 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 245 KiB

After

Width:  |  Height:  |  Size: 245 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 118 KiB

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 146 KiB

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 431 KiB

After

Width:  |  Height:  |  Size: 430 KiB