Interim checkin for open multi-user dialog, case node data, case changes

This commit is contained in:
Richard Cordovano 2019-01-24 17:26:26 -05:00
parent a307f20e52
commit 6c112cbfca
14 changed files with 554 additions and 590 deletions

View File

@ -78,13 +78,12 @@ Case.updateCaseName.exception.msg=Error while trying to update the case name.
Case.updateExaminer.exception.msg=Error while trying to update the examiner. Case.updateExaminer.exception.msg=Error while trying to update the examiner.
Case.updateCaseNum.exception.msg=Error while trying to update the case number. Case.updateCaseNum.exception.msg=Error while trying to update the case number.
Case.exception.errGetRootObj=Error getting root objects. Case.exception.errGetRootObj=Error getting root objects.
Case.createCaseDir.exception.existNotDir=Cannot create case dir, already exists and is not a directory\: {0} Case.createCaseDir.exception.existNotDir=Cannot create case directory, it already exists and is not a directory\: {0}
Case.createCaseDir.exception.existCantRW=Cannot create case dir, already exists and cannot read/write\: {0} Case.createCaseDir.exception.existCantRW=Cannot create case directory, it already exists and cannot read/write\: {0}
Case.createCaseDir.exception.cantCreate=Cannot create case directory or it already exists\: {0} Case.createCaseDir.exception.cantCreate=Cannot create case directory or it already exists\: {0}
Case.createCaseDir.exception.cantCreateCaseDir=Could not create case directory\: {0} Case.createCaseDir.exception.cantCreateCaseDir=Could not create case directory\: {0}
Case.createCaseDir.exception.cantCreateModDir=Could not create modules output directory\: {0} Case.createCaseDir.exception.cantCreateModDir=Could not create modules output directory\: {0}
Case.createCaseDir.exception.cantCreateReportsDir=Could not create reports output directory\: {0} Case.createCaseDir.exception.cantCreateReportsDir=Could not create reports output directory\: {0}
Case.createCaseDir.exception.gen=Could not create case directory\: {0}
Case.CollaborationSetup.FailNotify.ErrMsg=Failed to connect to any other nodes that may be collaborating on this case. Case.CollaborationSetup.FailNotify.ErrMsg=Failed to connect to any other nodes that may be collaborating on this case.
Case.CollaborationSetup.FailNotify.Title=Connection Failure Case.CollaborationSetup.FailNotify.Title=Connection Failure
Case.GetCaseTypeGivenPath.Failure=Unable to get case type Case.GetCaseTypeGivenPath.Failure=Unable to get case type
@ -192,7 +191,6 @@ ReviewModeCasePanel.LastAccessedTimeHeaderText=Last Accessed Time
ReviewModeCasePanel.MetadataFileHeaderText=Metadata File ReviewModeCasePanel.MetadataFileHeaderText=Metadata File
OpenMultiUserCasePanel.jLabel1.text=Recent Cases OpenMultiUserCasePanel.jLabel1.text=Recent Cases
OpenMultiUserCasePanel.openButton.text=Open OpenMultiUserCasePanel.openButton.text=Open
OpenMultiUserCasePanel.cancelButton.text=Cancel
CueBannerPanel.newCaseLabel.text=New Case CueBannerPanel.newCaseLabel.text=New Case
CueBannerPanel.openCaseButton.text= CueBannerPanel.openCaseButton.text=
CueBannerPanel.openCaseLabel.text=Open Case CueBannerPanel.openCaseLabel.text=Open Case
@ -237,6 +235,9 @@ ImageFilePanel.md5HashTextField.text=
ImageFilePanel.errorLabel.text=Error Label ImageFilePanel.errorLabel.text=Error Label
ImageFilePanel.hashValuesNoteLabel.text=NOTE: These values will not be validated when the data source is added. ImageFilePanel.hashValuesNoteLabel.text=NOTE: These values will not be validated when the data source is added.
ImageFilePanel.hashValuesLabel.text=Hash Values (optional): ImageFilePanel.hashValuesLabel.text=Hash Values (optional):
OpenMultiUserCasePanel.searchLabel.text=Select any case and start typing to search by case name
OpenMultiUserCasePanel.bnOpenSingleUserCase.text=Open Single-User Case... OpenMultiUserCasePanel.bnOpenSingleUserCase.text=Open Single-User Case...
# To change this license header, choose License Headers in Project Properties.
# To change this template file, choose Tools | Templates
# and open the template in the editor.
OpenMultiUserCasePanel.searchLabel.text=Select any case and start typing to search by case name
OpenMultiUserCasePanel.cancelButton.text=Cancel

View File

@ -65,7 +65,6 @@ Case.createCaseDir.exception.existNotDir=\u30b1\u30fc\u30b9\u30c7\u30a3\u30ec\u3
Case.createCaseDir.exception.existCantRW=\u30b1\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u65e2\u306b\u5b58\u5728\u3057\u3001\u8aad\u307f\u53d6\u308a\uff0f\u66f8\u304d\u8fbc\u307f\u304c\u3067\u304d\u307e\u305b\u3093\uff1a{0} Case.createCaseDir.exception.existCantRW=\u30b1\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u65e2\u306b\u5b58\u5728\u3057\u3001\u8aad\u307f\u53d6\u308a\uff0f\u66f8\u304d\u8fbc\u307f\u304c\u3067\u304d\u307e\u305b\u3093\uff1a{0}
Case.createCaseDir.exception.cantCreateCaseDir=\u30b1\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\uff1a {0} Case.createCaseDir.exception.cantCreateCaseDir=\u30b1\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\uff1a {0}
Case.createCaseDir.exception.cantCreateModDir=\u30e2\u30b8\u30e5\u30fc\u30eb\u30a2\u30a6\u30c8\u30d7\u30c3\u30c8\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\uff1a{0} Case.createCaseDir.exception.cantCreateModDir=\u30e2\u30b8\u30e5\u30fc\u30eb\u30a2\u30a6\u30c8\u30d7\u30c3\u30c8\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\uff1a{0}
Case.createCaseDir.exception.gen=\u30b1\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\uff1a{0}
CaseDeleteAction.closeConfMsg.text=\u3053\u306e\u30b1\u30fc\u30b9\u3092\u672c\u5f53\u306b\u9589\u3058\u3001\u524a\u9664\u3057\u307e\u3059\u304b\uff1f\n\ CaseDeleteAction.closeConfMsg.text=\u3053\u306e\u30b1\u30fc\u30b9\u3092\u672c\u5f53\u306b\u9589\u3058\u3001\u524a\u9664\u3057\u307e\u3059\u304b\uff1f\n\
\u30b1\u30fc\u30b9\u540d\uff1a {0}\n\ \u30b1\u30fc\u30b9\u540d\uff1a {0}\n\
\u30b1\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\: {1} \u30b1\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\: {1}
@ -132,7 +131,6 @@ AddImageWizardIngestConfigPanel.CANCEL_BUTTON.text=\u30ad\u30e3\u30f3\u30bb\u30e
LocalFilesPanel.errorLabel.text=\u30a8\u30e9\u30fc\u30e9\u30d9\u30eb LocalFilesPanel.errorLabel.text=\u30a8\u30e9\u30fc\u30e9\u30d9\u30eb
ImageFilePanel.errorLabel.text=\u30a8\u30e9\u30fc\u30e9\u30d9\u30eb ImageFilePanel.errorLabel.text=\u30a8\u30e9\u30fc\u30e9\u30d9\u30eb
NewCaseVisualPanel1.caseTypeLabel.text=\u30b1\u30fc\u30b9\u30bf\u30a4\u30d7\uff1a NewCaseVisualPanel1.caseTypeLabel.text=\u30b1\u30fc\u30b9\u30bf\u30a4\u30d7\uff1a
Case.databaseConnectionInfo.error.msg=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30b5\u30fc\u30d0\u30fc\u306e\u63a5\u7d9a\u60c5\u5831\u3092\u5165\u624b\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002\u30c4\u30fc\u30eb\u3001\u30aa\u30d7\u30b7\u30e7\u30f3\u3001\u8907\u6570\u30e6\u30fc\u30b6\u30fc\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002
Case.open.exception.multiUserCaseNotEnabled=\u8907\u6570\u30e6\u30fc\u30b6\u30fc\u306e\u30b1\u30fc\u30b9\u304c\u6709\u52b9\u5316\u3055\u308c\u3066\u3044\u306a\u3044\u3068\u3001\u8907\u6570\u30e6\u30fc\u30b6\u30fc\u306e\u30b1\u30fc\u30b9\u306f\u958b\u3051\u307e\u305b\u3093\u3002\u30c4\u30fc\u30eb\u3001\u30aa\u30d7\u30b7\u30e7\u30f3\u3001\u8907\u6570\u30e6\u30fc\u30b6\u30fc\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002 Case.open.exception.multiUserCaseNotEnabled=\u8907\u6570\u30e6\u30fc\u30b6\u30fc\u306e\u30b1\u30fc\u30b9\u304c\u6709\u52b9\u5316\u3055\u308c\u3066\u3044\u306a\u3044\u3068\u3001\u8907\u6570\u30e6\u30fc\u30b6\u30fc\u306e\u30b1\u30fc\u30b9\u306f\u958b\u3051\u307e\u305b\u3093\u3002\u30c4\u30fc\u30eb\u3001\u30aa\u30d7\u30b7\u30e7\u30f3\u3001\u8907\u6570\u30e6\u30fc\u30b6\u30fc\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002
Case.createCaseDir.exception.cantCreateReportsDir=\u30ec\u30dd\u30fc\u30c8\u30a2\u30a6\u30c8\u30d7\u30c3\u30c8\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\uff1a{0} Case.createCaseDir.exception.cantCreateReportsDir=\u30ec\u30dd\u30fc\u30c8\u30a2\u30a6\u30c8\u30d7\u30c3\u30c8\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\uff1a{0}
Case.CollaborationSetup.FailNotify.ErrMsg=\u3053\u306e\u30b1\u30fc\u30b9\u3067\u4f7f\u308f\u308c\u3066\u3044\u308b\u304b\u3082\u3057\u308c\u306a\u3044\u30ce\u30fc\u30c9\u306b\u63a5\u7d9a\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 Case.CollaborationSetup.FailNotify.ErrMsg=\u3053\u306e\u30b1\u30fc\u30b9\u3067\u4f7f\u308f\u308c\u3066\u3044\u308b\u304b\u3082\u3057\u308c\u306a\u3044\u30ce\u30fc\u30c9\u306b\u63a5\u7d9a\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002
@ -180,7 +178,6 @@ OptionalCasePropertiesPanel.caseDisplayNameLabel.text=\u30b1\u30fc\u30b9\u756a\u
CueBannerPanel.openRecentCaseLabel.text=\u6700\u8fd1\u958b\u3044\u305f\u30b1\u30fc\u30b9\u3092\u958b\u304f CueBannerPanel.openRecentCaseLabel.text=\u6700\u8fd1\u958b\u3044\u305f\u30b1\u30fc\u30b9\u3092\u958b\u304f
CueBannerPanel.openAutoIngestCaseLabel.text=\u65e2\u5b58\u30b1\u30fc\u30b9\u3092\u958b\u304f CueBannerPanel.openAutoIngestCaseLabel.text=\u65e2\u5b58\u30b1\u30fc\u30b9\u3092\u958b\u304f
OpenMultiUserCasePanel.openButton.text=\u958b\u304f OpenMultiUserCasePanel.openButton.text=\u958b\u304f
OpenMultiUserCasePanel.cancelButton.text=\u30ad\u30e3\u30f3\u30bb\u30eb
OpenMultiUserCasePanel.jLabel1.text=\u6700\u8fd1\u958b\u3044\u305f\u30d5\u30a1\u30a4\u30eb OpenMultiUserCasePanel.jLabel1.text=\u6700\u8fd1\u958b\u3044\u305f\u30d5\u30a1\u30a4\u30eb
CueBannerPanel.newCaseLabel.text=\u65b0\u898f\u30b1\u30fc\u30b9\u3092\u4f5c\u6210 CueBannerPanel.newCaseLabel.text=\u65b0\u898f\u30b1\u30fc\u30b9\u3092\u4f5c\u6210
CueBannerPanel.openCaseLabel.text=\u65e2\u5b58\u30b1\u30fc\u30b9\u3092\u958b\u304f CueBannerPanel.openCaseLabel.text=\u65e2\u5b58\u30b1\u30fc\u30b9\u3092\u958b\u304f
@ -200,4 +197,5 @@ LogicalEvidenceFilePanel.errorLabel.text=\u30a8\u30e9\u30fc\u30e9\u30d9\u30eb
LogicalEvidenceFilePanel.logicalEvidenceFileChooser.dialogTitle=\u30ed\u30fc\u30ab\u30eb\u30d5\u30a1\u30a4\u30eb\u307e\u305f\u306f\u30d5\u30a9\u30eb\u30c0\u3092\u9078\u629e LogicalEvidenceFilePanel.logicalEvidenceFileChooser.dialogTitle=\u30ed\u30fc\u30ab\u30eb\u30d5\u30a1\u30a4\u30eb\u307e\u305f\u306f\u30d5\u30a9\u30eb\u30c0\u3092\u9078\u629e
LogicalEvidenceFilePanel.logicalEvidenceFileChooser.approveButtonText=\u9078\u629e LogicalEvidenceFilePanel.logicalEvidenceFileChooser.approveButtonText=\u9078\u629e
LocalDiskSelectionDialog.errorLabel.text=\u30a8\u30e9\u30fc\u30e9\u30d9\u30eb LocalDiskSelectionDialog.errorLabel.text=\u30a8\u30e9\u30fc\u30e9\u30d9\u30eb
LocalDiskSelectionDialog.selectLocalDiskLabel.text=\u30ed\u30fc\u30ab\u30eb\u30c7\u30a3\u30b9\u30af\u3092\u9078\u629e\uff1a LocalDiskSelectionDialog.selectLocalDiskLabel.text=\u30ed\u30fc\u30ab\u30eb\u30c7\u30a3\u30b9\u30af\u3092\u9078\u629e\uff1a
OpenMultiUserCasePanel.cancelButton.text=\u30ad\u30e3\u30f3\u30bb\u30eb

View File

@ -58,7 +58,6 @@ import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe; import javax.annotation.concurrent.ThreadSafe;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import org.openide.util.Exceptions;
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.openide.util.NbBundle.Messages;
@ -82,7 +81,6 @@ import org.sleuthkit.autopsy.casemodule.events.ReportAddedEvent;
import org.sleuthkit.autopsy.casemodule.services.Services; import org.sleuthkit.autopsy.casemodule.services.Services;
import org.sleuthkit.autopsy.commonfilesearch.CommonAttributeSearchAction; import org.sleuthkit.autopsy.commonfilesearch.CommonAttributeSearchAction;
import org.sleuthkit.autopsy.communications.OpenCommVisualizationToolAction; import org.sleuthkit.autopsy.communications.OpenCommVisualizationToolAction;
import org.sleuthkit.autopsy.coordinationservice.CaseNodeData;
import org.sleuthkit.autopsy.coordinationservice.CoordinationService; import org.sleuthkit.autopsy.coordinationservice.CoordinationService;
import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CategoryNode; import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CategoryNode;
import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CoordinationServiceException; import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CoordinationServiceException;
@ -892,72 +890,71 @@ public class Case {
/** /**
* Creates a case directory and its subdirectories. * Creates a case directory and its subdirectories.
* *
* @param caseDir Path to the case directory (typically base + case name). * @param caseDirPath Path to the case directory (typically base + case
* @param caseType The type of case, single-user or multi-user. * name).
* @param caseType The type of case, single-user or multi-user.
* *
* @throws CaseActionException throw if could not create the case dir * @throws CaseActionException throw if could not create the case dir
*/ */
public static void createCaseDirectory(String caseDir, CaseType caseType) throws CaseActionException { public static void createCaseDirectory(String caseDirPath, CaseType caseType) throws CaseActionException {
/*
File caseDirF = new File(caseDir); * Check the case directory path and permissions. The case directory may
* already exist.
if (caseDirF.exists()) { */
if (caseDirF.isFile()) { File caseDir = new File(caseDirPath);
throw new CaseActionException( if (caseDir.exists()) {
NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.existNotDir", caseDir)); if (caseDir.isFile()) {
throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.existNotDir", caseDirPath));
} else if (!caseDirF.canRead() || !caseDirF.canWrite()) { } else if (!caseDir.canRead() || !caseDir.canWrite()) {
throw new CaseActionException( throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.existCantRW", caseDirPath));
NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.existCantRW", caseDir));
} }
} }
try { /*
boolean result = (caseDirF).mkdirs(); // create root case Directory * Create the case directory, if it does not already exist.
*/
if (!caseDir.mkdirs()) {
throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreate", caseDirPath));
}
if (result == false) { /*
throw new CaseActionException( * Create the subdirectories of the case directory, if they do not
NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreate", caseDir)); * already exist. Note that multi-user cases get an extra layer of
} * subdirectories, one subdirectory per application host machine.
*/
String hostPathComponent = "";
if (caseType == CaseType.MULTI_USER_CASE) {
hostPathComponent = File.separator + NetworkUtils.getLocalHostName();
}
// create the folders inside the case directory Path exportDir = Paths.get(caseDirPath, hostPathComponent, EXPORT_FOLDER);
String hostClause = ""; if (!exportDir.toFile().mkdirs()) {
throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreateCaseDir", exportDir));
}
if (caseType == CaseType.MULTI_USER_CASE) { Path logsDir = Paths.get(caseDirPath, hostPathComponent, LOG_FOLDER);
hostClause = File.separator + NetworkUtils.getLocalHostName(); if (!logsDir.toFile().mkdirs()) {
} throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreateCaseDir", logsDir));
result = result && (new File(caseDir + hostClause + File.separator + EXPORT_FOLDER)).mkdirs() }
&& (new File(caseDir + hostClause + File.separator + LOG_FOLDER)).mkdirs()
&& (new File(caseDir + hostClause + File.separator + TEMP_FOLDER)).mkdirs()
&& (new File(caseDir + hostClause + File.separator + CACHE_FOLDER)).mkdirs();
if (result == false) { Path tempDir = Paths.get(caseDirPath, hostPathComponent, TEMP_FOLDER);
throw new CaseActionException( if (!tempDir.toFile().mkdirs()) {
NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreateCaseDir", caseDir)); throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreateCaseDir", tempDir));
} }
final String modulesOutDir = caseDir + hostClause + File.separator + MODULE_FOLDER; Path cacheDir = Paths.get(caseDirPath, hostPathComponent, CACHE_FOLDER);
result = new File(modulesOutDir).mkdir(); if (!cacheDir.toFile().mkdirs()) {
throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreateCaseDir", cacheDir));
}
if (result == false) { Path moduleOutputDir = Paths.get(caseDirPath, hostPathComponent, MODULE_FOLDER);
throw new CaseActionException( if (!moduleOutputDir.toFile().mkdirs()) {
NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreateModDir", throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreateModDir", moduleOutputDir));
modulesOutDir)); }
}
final String reportsOutDir = caseDir + hostClause + File.separator + REPORTS_FOLDER; Path reportsDir = Paths.get(caseDirPath, hostPathComponent, REPORTS_FOLDER);
result = new File(reportsOutDir).mkdir(); if (!reportsDir.toFile().mkdirs()) {
throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreateReportsDir", reportsDir));
if (result == false) {
throw new CaseActionException(
NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreateReportsDir",
modulesOutDir));
}
} catch (MissingResourceException | CaseActionException e) {
throw new CaseActionException(
NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.gen", caseDir), e);
} }
} }
@ -1947,39 +1944,44 @@ public class Case {
* @param isNewCase True for a new case, false otherwise. * @param isNewCase True for a new case, false otherwise.
* @param progressIndicator A progress indicator. * @param progressIndicator A progress indicator.
* *
* @throws CaseActionException if there is a problem creating the case. The * @throws CaseActionException If there is a problem creating the case. The
* exception will have a user-friendly message * exception will have a user-friendly message
* and may be a wrapper for a lower-level * and may be a wrapper for a lower-level
* exception. * exception.
*/ */
private void open(boolean isNewCase, ProgressIndicator progressIndicator) throws CaseActionException { private void open(boolean isNewCase, ProgressIndicator progressIndicator) throws CaseActionException {
try { try {
if (Thread.currentThread().isInterrupted()) { checkForUserCancellation();
throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser()); createCaseDirectoryIfDoesNotExist(progressIndicator);
} checkForUserCancellation();
switchLoggingToCaseLogsDirectory(progressIndicator);
checkForUserCancellation();
if (isNewCase) { if (isNewCase) {
createCaseData(progressIndicator); // RJCTODO: This name is vague createCaseNodeData(progressIndicator);
} else { } else {
openCaseData(progressIndicator); // RJCTODO: This name is vague updateCaseNodeData(progressIndicator);
} }
checkForUserCancellation();
if (Thread.currentThread().isInterrupted()) { deleteTempfilesFromCaseDirectory(progressIndicator);
throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser()); checkForUserCancellation();
} if (isNewCase) {
createCaseDatabase(progressIndicator);
openServices(progressIndicator); } else {
openCaseDataBase(progressIndicator);
if (Thread.currentThread().isInterrupted()) {
throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser());
} }
checkForUserCancellation();
openCaseLevelServices(progressIndicator);
checkForUserCancellation();
openAppServiceCaseResources(progressIndicator);
checkForUserCancellation();
openCommunicationChannels(progressIndicator);
} catch (CaseActionException ex) { } catch (CaseActionException ex) {
/* /*
* Cancellation or failure. Clean up. The sleep is a little hack to * Cancellation or failure. Clean up by calling the close method.
* clear the interrupted flag for this thread if this is a * The sleep is a little hack to clear the interrupted flag for this
* cancellation scenario, so that the clean up can run to completion * thread if this is a cancellation scenario, so that the clean up
* in this thread. * can run to completion in the current thread.
*/ */
try { try {
Thread.sleep(1); Thread.sleep(1);
@ -1991,60 +1993,150 @@ public class Case {
} }
/** /**
* Creates the case directory, case database, and case metadata file. * Checks current thread for an interrupt. Usage: checking for user
* cancellation of a case creation/opening operation, as reflected in the
* exception message.
* *
* @param progressIndicator A progress indicartor. * @throws CaseActionCancelledException If the current thread is
* interrupted, assumes interrupt was
* due to a user action.
*/
private static void checkForUserCancellation() throws CaseActionCancelledException {
if (Thread.currentThread().isInterrupted()) {
throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser());
}
}
/**
* Creates the case directory, if it does not already exist.
* *
* @throws CaseActionException If there is a problem creating the case * TODO (JIRA-2180): Always create the case directory as part of the case
* database. The exception will have a * creation process.
*
* @param progressIndicator A progress indicator.
*
* @throws CaseActionException If there is a problem completing the
* operation. The exception will have a
* user-friendly message and may be a wrapper * user-friendly message and may be a wrapper
* for a lower-level exception. * for a lower-level exception.
*/ */
@Messages({ @Messages({
"Case.progressMessage.creatingCaseDirectory=Creating case directory...", "Case.progressMessage.creatingCaseDirectory=Creating case directory...",})
"Case.progressMessage.creatingCaseDatabase=Creating case database...", private void createCaseDirectoryIfDoesNotExist(ProgressIndicator progressIndicator) throws CaseActionException {
"# {0} - exception message", "Case.exceptionMessage.couldNotCreateCaseDatabase=Failed to create case database:\n{0}", progressIndicator.progress(Bundle.Case_progressMessage_creatingCaseDirectory());
"Case.exceptionMessage.couldNotCreateMetadataFile=Failed to create case metadata file."
})
private void createCaseData(ProgressIndicator progressIndicator) throws CaseActionException {
// RJCTODO: progress
try {
CoordinationService coordinationService = CoordinationService.getInstance();
CaseNodeData nodeData = new CaseNodeData(metadata);
coordinationService.setNodeData(CategoryNode.CASES, metadata.getCaseDirectory(), nodeData.toArray());
} catch (CoordinationServiceException | InterruptedException | ParseException ex) {
// RJCTODO
}
if (Thread.currentThread().isInterrupted()) {
throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser());
}
/*
* Create the case directory, if it does not already exist.
*
* TODO (JIRA-2180): Always create the case directory as part of the
* case creation process.
*/
if (new File(metadata.getCaseDirectory()).exists() == false) { if (new File(metadata.getCaseDirectory()).exists() == false) {
progressIndicator.progress(Bundle.Case_progressMessage_creatingCaseDirectory()); progressIndicator.progress(Bundle.Case_progressMessage_creatingCaseDirectory());
Case.createCaseDirectory(metadata.getCaseDirectory(), metadata.getCaseType()); Case.createCaseDirectory(metadata.getCaseDirectory(), metadata.getCaseType());
} }
}
if (Thread.currentThread().isInterrupted()) { /**
throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser()); * Switches from writing log messages to the application logs to the logs
* subdirectory of the case directory.
*
* @param progressIndicator A progress indicator.
*/
@Messages({
"Case.progressMessage.switchingLogDirectory=Switching log directory..."
})
private void switchLoggingToCaseLogsDirectory(ProgressIndicator progressIndicator) {
progressIndicator.progress(Bundle.Case_progressMessage_switchingLogDirectory());
Logger.setLogDirectory(getLogDirectoryPath());
}
/**
* Creates the node data for the case directory lock coordination service
* node.
*
* @param progressIndicator A progress indicator.
*
* @throws CaseActionException If there is a problem completing the
* operation. The exception will have a
* user-friendly message and may be a wrapper
* for a lower-level exception.
*/
@Messages({
"Case.progressMessage.creatingCaseNodeData=Creating coordination service node data...",
"# {0} - exception message", "Case.exceptionMessage.couldNotCreateCaseNodeData=Failed to create coordination service node data:\n{0}."
})
private void createCaseNodeData(ProgressIndicator progressIndicator) throws CaseActionException {
progressIndicator.progress(Bundle.Case_progressMessage_creatingCaseNodeData());
try {
CoordinationService coordinationService = CoordinationService.getInstance();
CaseNodeData nodeData = new CaseNodeData(metadata);
coordinationService.setNodeData(CategoryNode.CASES, metadata.getCaseDirectory(), nodeData.toArray());
} catch (CoordinationServiceException | InterruptedException | ParseException | IOException ex) {
throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotCreateCaseNodeData(ex.getLocalizedMessage()), ex);
} }
}
/**
* Updates the node data for the case directory lock coordination service
* node.
*
* @param progressIndicator A progress indicator.
*
* @throws CaseActionException If there is a problem completing the
* operation. The exception will have a
* user-friendly message and may be a wrapper
* for a lower-level exception.
*/
@Messages({
"Case.progressMessage.updatingCaseNodeData=Updating coordination service node data...",
"# {0} - exception message", "Case.exceptionMessage.couldNotUpdateCaseNodeData=Failed to update coordination service node data:\n{0}."
})
private void updateCaseNodeData(ProgressIndicator progressIndicator) throws CaseActionException {
progressIndicator.progress(Bundle.Case_progressMessage_updatingCaseNodeData());
try {
CoordinationService coordinationService = CoordinationService.getInstance();
CaseNodeData nodeData = new CaseNodeData(coordinationService.getNodeData(CategoryNode.CASES, metadata.getCaseDirectory()));
nodeData.setLastAccessDate(new Date());
coordinationService.setNodeData(CategoryNode.CASES, metadata.getCaseDirectory(), nodeData.toArray());
} catch (CoordinationServiceException | InterruptedException | IOException ex) {
throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotUpdateCaseNodeData(ex.getLocalizedMessage()), ex);
}
}
/**
* Deletes any files in the temp subdirectory of the case directory.
*
* @param progressIndicator A progress indicator.
*/
@Messages({
"Case.progressMessage.clearingTempDirectory=Clearing case temp directory...",})
private void deleteTempfilesFromCaseDirectory(ProgressIndicator progressIndicator) {
/* /*
* Create the case database. * Clear the temp subdirectory of the case directory.
*/ */
progressIndicator.progress(Bundle.Case_progressMessage_clearingTempDirectory());
Case.clearTempSubDir(this.getTempDirectory());
}
/**
* Creates the node data for the case directory lock coordination service
* node, the case directory, the case database and the case metadata file.
*
* @param progressIndicator A progress indicator.
*
* @throws CaseActionException If there is a problem completing the
* operation. The exception will have a
* user-friendly message and may be a wrapper
* for a lower-level exception.
*/
@Messages({
"Case.progressMessage.creatingCaseDatabase=Creating case database...",
"# {0} - exception message", "Case.exceptionMessage.couldNotGetDbServerConnectionInfo=Failed to get case database server conneciton info:\n{0}.",
"# {0} - exception message", "Case.exceptionMessage.couldNotCreateCaseDatabase=Failed to create case database:\n{0}.",
"# {0} - exception message", "Case.exceptionMessage.couldNotSaveDbNameToMetadataFile=Failed to save case database name to case metadata file:\n{0}."
})
private void createCaseDatabase(ProgressIndicator progressIndicator) throws CaseActionException {
progressIndicator.progress(Bundle.Case_progressMessage_creatingCaseDatabase()); progressIndicator.progress(Bundle.Case_progressMessage_creatingCaseDatabase());
try { try {
if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) { if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) {
/* /*
* For single-user cases, the case database is a SQLite database * For single-user cases, the case database is a SQLite database
* with a standard name, physically located in the root of the * with a standard name, physically located in the case
* case directory. * directory.
*/ */
caseDb = SleuthkitCase.newCase(Paths.get(metadata.getCaseDirectory(), SINGLE_USER_CASE_DB_NAME).toString()); caseDb = SleuthkitCase.newCase(Paths.get(metadata.getCaseDirectory(), SINGLE_USER_CASE_DB_NAME).toString());
metadata.setCaseDatabaseName(SINGLE_USER_CASE_DB_NAME); metadata.setCaseDatabaseName(SINGLE_USER_CASE_DB_NAME);
@ -2052,7 +2144,7 @@ public class Case {
/* /*
* For multi-user cases, the case database is a PostgreSQL * For multi-user cases, the case database is a PostgreSQL
* database with a name derived from the case display name, * database with a name derived from the case display name,
* physically located on a database server. * physically located on the PostgreSQL database server.
*/ */
caseDb = SleuthkitCase.newCase(metadata.getCaseDisplayName(), UserPreferences.getDatabaseConnectionInfo(), metadata.getCaseDirectory()); caseDb = SleuthkitCase.newCase(metadata.getCaseDisplayName(), UserPreferences.getDatabaseConnectionInfo(), metadata.getCaseDirectory());
metadata.setCaseDatabaseName(caseDb.getDatabaseName()); metadata.setCaseDatabaseName(caseDb.getDatabaseName());
@ -2060,161 +2152,81 @@ public class Case {
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotCreateCaseDatabase(ex.getLocalizedMessage()), ex); throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotCreateCaseDatabase(ex.getLocalizedMessage()), ex);
} catch (UserPreferencesException ex) { } catch (UserPreferencesException ex) {
throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.databaseConnectionInfo.error.msg"), ex); throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotGetDbServerConnectionInfo(ex.getLocalizedMessage()), ex);
} catch (CaseMetadataException ex) { } catch (CaseMetadataException ex) {
throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotCreateMetadataFile(), ex); throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotSaveDbNameToMetadataFile(ex.getLocalizedMessage()), ex);
} }
} }
/** /**
* Opens an existing case database. * Updates the node data for an existing case directory lock coordination
* service node and opens an existing case database.
* *
* @param progressIndicator A progress indicator. * @param progressIndicator A progress indicator.
* *
* @throws CaseActionException if there is a problem opening the case. The * @throws CaseActionException If there is a problem completing the
* exception will have a user-friendly message * operation. The exception will have a
* and may be a wrapper for a lower-level * user-friendly message and may be a wrapper
* exception. * for a lower-level exception.
*/ */
@Messages({ @Messages({
"Case.progressMessage.openingCaseDatabase=Opening case database...", "Case.progressMessage.openingCaseDatabase=Opening case database...",
"Case.exceptionMessage.couldNotOpenCaseDatabase=Failed to open case database.", "# {0} - exception message", "Case.exceptionMessage.couldNotOpenCaseDatabase=Failed to open case database:\n{0}.",
"Case.unsupportedSchemaVersionMessage=Unsupported DB schema version - see log for details", "# {0} - exception message", "Case.exceptionMessage.unsupportedSchemaVersionMessage=Unsupported case database schema version:\n{0}.",
"Case.databaseConnectionInfo.error.msg=Error accessing database server connection info. See Tools, Options, Multi-User.", "Case.open.exception.multiUserCaseNotEnabled=Cannot open a multi-user case if multi-user cases are not enabled. See Tools, Options, Multi-User."
"Case.open.exception.multiUserCaseNotEnabled=Cannot open a multi-user case if multi-user cases are not enabled. "
+ "See Tools, Options, Multi-user."
}) })
private void openCaseData(ProgressIndicator progressIndicator) throws CaseActionException { private void openCaseDataBase(ProgressIndicator progressIndicator) throws CaseActionException {
progressIndicator.progress(Bundle.Case_progressMessage_openingCaseDatabase());
try { try {
if (Thread.currentThread().isInterrupted()) {
throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser());
}
// RJCTODO: progress
try {
CoordinationService coordinationService = CoordinationService.getInstance();
CaseNodeData nodeData = new CaseNodeData(coordinationService.getNodeData(CategoryNode.CASES, metadata.getCaseDirectory()));
nodeData.setLastAccessDate(new Date());
coordinationService.setNodeData(CategoryNode.CASES, metadata.getCaseDirectory(), nodeData.toArray());
} catch (CoordinationServiceException | InterruptedException | CaseNodeData.InvalidDataException ex) {
// RJCTODO
}
if (Thread.currentThread().isInterrupted()) {
throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser());
}
progressIndicator.progress(Bundle.Case_progressMessage_openingCaseDatabase());
String databaseName = metadata.getCaseDatabaseName(); String databaseName = metadata.getCaseDatabaseName();
if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) { if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) {
caseDb = SleuthkitCase.openCase(Paths.get(metadata.getCaseDirectory(), databaseName).toString()); caseDb = SleuthkitCase.openCase(Paths.get(metadata.getCaseDirectory(), databaseName).toString());
} else if (UserPreferences.getIsMultiUserModeEnabled()) { } else if (UserPreferences.getIsMultiUserModeEnabled()) {
try { caseDb = SleuthkitCase.openCase(databaseName, UserPreferences.getDatabaseConnectionInfo(), metadata.getCaseDirectory());
caseDb = SleuthkitCase.openCase(databaseName, UserPreferences.getDatabaseConnectionInfo(), metadata.getCaseDirectory());
} catch (UserPreferencesException ex) {
throw new CaseActionException(Case_databaseConnectionInfo_error_msg(), ex);
}
} else { } else {
throw new CaseActionException(Case_open_exception_multiUserCaseNotEnabled()); throw new CaseActionException(Case_open_exception_multiUserCaseNotEnabled());
} }
} catch (TskUnsupportedSchemaVersionException ex) { } catch (TskUnsupportedSchemaVersionException ex) {
throw new CaseActionException(Bundle.Case_unsupportedSchemaVersionMessage(), ex); throw new CaseActionException(Bundle.Case_exceptionMessage_unsupportedSchemaVersionMessage(ex.getLocalizedMessage()), ex);
} catch (UserPreferencesException ex) {
throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotGetDbServerConnectionInfo(ex.getLocalizedMessage()), ex);
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotOpenCaseDatabase(), ex); throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotOpenCaseDatabase(ex.getLocalizedMessage()), ex);
} }
} }
/** /**
* Completes the case opening tasks common to both new cases and existing * Opens the case-level services: the files manager, tags manager and
* cases. * blackboard.
* *
* @param progressIndicator A progress indicator. * @param progressIndicator A progress indicator.
*
* @throws CaseActionException
*/ */
@Messages({ @Messages({
"Case.progressMessage.switchingLogDirectory=Switching log directory...", "Case.progressMessage.openingCaseLevelServices=Opening case-level services...",})
"Case.progressMessage.clearingTempDirectory=Clearing case temp directory...", private void openCaseLevelServices(ProgressIndicator progressIndicator) {
"Case.progressMessage.openingCaseLevelServices=Opening case-level services...",
"Case.progressMessage.openingApplicationServiceResources=Opening application service case resources...",
"Case.progressMessage.settingUpNetworkCommunications=Setting up network communications...",})
private void openServices(ProgressIndicator progressIndicator) throws CaseActionException {
/*
* Switch to writing to the application logs in the logs subdirectory of
* the case directory.
*/
progressIndicator.progress(Bundle.Case_progressMessage_switchingLogDirectory());
Logger.setLogDirectory(getLogDirectoryPath());
if (Thread.currentThread().isInterrupted()) {
throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser());
}
/*
* Clear the temp subdirectory of the case directory.
*/
progressIndicator.progress(Bundle.Case_progressMessage_clearingTempDirectory());
Case.clearTempSubDir(this.getTempDirectory());
if (Thread.currentThread().isInterrupted()) {
throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser());
}
/*
* Open the case-level services.
*/
progressIndicator.progress(Bundle.Case_progressMessage_openingCaseLevelServices()); progressIndicator.progress(Bundle.Case_progressMessage_openingCaseLevelServices());
this.caseServices = new Services(caseDb); this.caseServices = new Services(caseDb);
if (Thread.currentThread().isInterrupted()) {
throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser());
}
/*
* Allow any registered application services to open any resources
* specific to this case.
*/
progressIndicator.progress(Bundle.Case_progressMessage_openingApplicationServiceResources());
openAppServiceCaseResources();
if (Thread.currentThread().isInterrupted()) {
throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser());
}
/*
* If this case is a multi-user case, set up for communication with
* other nodes.
*/
if (CaseType.MULTI_USER_CASE == metadata.getCaseType()) {
progressIndicator.progress(Bundle.Case_progressMessage_settingUpNetworkCommunications());
try {
eventPublisher.openRemoteEventChannel(String.format(EVENT_CHANNEL_NAME, metadata.getCaseName()));
if (Thread.currentThread().isInterrupted()) {
throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser());
}
collaborationMonitor = new CollaborationMonitor(metadata.getCaseName());
} catch (AutopsyEventException | CollaborationMonitor.CollaborationMonitorException ex) {
/*
* The collaboration monitor and event channel are not
* essential. Log an error and notify the user, but do not
* throw.
*/
logger.log(Level.SEVERE, "Failed to setup network communications", ex); //NON-NLS
if (RuntimeProperties.runningWithGUI()) {
SwingUtilities.invokeLater(() -> MessageNotifyUtil.Notify.error(
NbBundle.getMessage(Case.class, "Case.CollaborationSetup.FailNotify.Title"),
NbBundle.getMessage(Case.class, "Case.CollaborationSetup.FailNotify.ErrMsg")));
}
}
}
} }
/** /**
* Allows any registered application-level services to open resources * Allows any registered application-level services to open resources
* specific to this case. * specific to this case.
*
* @param progressIndicator A progress indicator.
*
* @throws CaseActionException If there is a problem completing the
* operation. The exception will have a
* user-friendly message and may be a wrapper
* for a lower-level exception.
*/ */
@NbBundle.Messages({ @NbBundle.Messages({
"Case.progressMessage.openingApplicationServiceResources=Opening application service case resources...",
"# {0} - service name", "Case.serviceOpenCaseResourcesProgressIndicator.title={0} Opening Case Resources", "# {0} - service name", "Case.serviceOpenCaseResourcesProgressIndicator.title={0} Opening Case Resources",
"# {0} - service name", "Case.serviceOpenCaseResourcesProgressIndicator.cancellingMessage=Cancelling opening case resources by {0}...", "# {0} - service name", "Case.serviceOpenCaseResourcesProgressIndicator.cancellingMessage=Cancelling opening case resources by {0}...",
"# {0} - service name", "Case.servicesException.notificationTitle={0} Error" "# {0} - service name", "Case.servicesException.notificationTitle={0} Error"
}) })
private void openAppServiceCaseResources() throws CaseActionException { private void openAppServiceCaseResources(ProgressIndicator progressIndicator) throws CaseActionException {
progressIndicator.progress(Bundle.Case_progressMessage_openingApplicationServiceResources());
/* /*
* Each service gets its own independently cancellable/interruptible * Each service gets its own independently cancellable/interruptible
* task, running in a named thread managed by an executor service, with * task, running in a named thread managed by an executor service, with
@ -2230,20 +2242,20 @@ public class Case {
* with a Cancel button. * with a Cancel button.
*/ */
CancelButtonListener cancelButtonListener = null; CancelButtonListener cancelButtonListener = null;
ProgressIndicator progressIndicator; ProgressIndicator appServiceProgressIndicator;
if (RuntimeProperties.runningWithGUI()) { if (RuntimeProperties.runningWithGUI()) {
cancelButtonListener = new CancelButtonListener(Bundle.Case_serviceOpenCaseResourcesProgressIndicator_cancellingMessage(service.getServiceName())); cancelButtonListener = new CancelButtonListener(Bundle.Case_serviceOpenCaseResourcesProgressIndicator_cancellingMessage(service.getServiceName()));
progressIndicator = new ModalDialogProgressIndicator( appServiceProgressIndicator = new ModalDialogProgressIndicator(
mainFrame, mainFrame,
Bundle.Case_serviceOpenCaseResourcesProgressIndicator_title(service.getServiceName()), Bundle.Case_serviceOpenCaseResourcesProgressIndicator_title(service.getServiceName()),
new String[]{Bundle.Case_progressIndicatorCancelButton_label()}, new String[]{Bundle.Case_progressIndicatorCancelButton_label()},
Bundle.Case_progressIndicatorCancelButton_label(), Bundle.Case_progressIndicatorCancelButton_label(),
cancelButtonListener); cancelButtonListener);
} else { } else {
progressIndicator = new LoggingProgressIndicator(); appServiceProgressIndicator = new LoggingProgressIndicator();
} }
progressIndicator.start(Bundle.Case_progressMessage_preparing()); appServiceProgressIndicator.start(Bundle.Case_progressMessage_preparing());
AutopsyService.CaseContext context = new AutopsyService.CaseContext(this, progressIndicator); AutopsyService.CaseContext context = new AutopsyService.CaseContext(this, appServiceProgressIndicator);
String threadNameSuffix = service.getServiceName().replaceAll("[ ]", "-"); //NON-NLS String threadNameSuffix = service.getServiceName().replaceAll("[ ]", "-"); //NON-NLS
threadNameSuffix = threadNameSuffix.toLowerCase(); threadNameSuffix = threadNameSuffix.toLowerCase();
TaskThreadFactory threadFactory = new TaskThreadFactory(String.format(CASE_RESOURCES_THREAD_NAME, threadNameSuffix)); TaskThreadFactory threadFactory = new TaskThreadFactory(String.format(CASE_RESOURCES_THREAD_NAME, threadNameSuffix));
@ -2299,17 +2311,50 @@ public class Case {
* task responded to a cancellation request. * task responded to a cancellation request.
*/ */
ThreadUtils.shutDownTaskExecutor(executor); ThreadUtils.shutDownTaskExecutor(executor);
progressIndicator.finish(); appServiceProgressIndicator.finish();
} }
checkForUserCancellation();
}
}
if (Thread.currentThread().isInterrupted()) { /**
throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser()); * If this case is a multi-user case, sets up for communication with other
* application nodes.
*
* @param progressIndicator A progress indicator.
*
* @throws CaseActionException If there is a problem completing the
* operation. The exception will have a
* user-friendly message and may be a wrapper
* for a lower-level exception.
*/
@Messages({
"Case.progressMessage.settingUpNetworkCommunications=Setting up network communications...",
"# {0} - exception message", "Case.exceptionMessage.couldNotOpenRemoteEventChannel=Failed to open remote events channel:\n{0}.",
"# {0} - exception message", "Case.exceptionMessage.couldNotCreatCollaborationMonitor=Failed to create collaboration monitor:\n{0}."
})
private void openCommunicationChannels(ProgressIndicator progressIndicator) throws CaseActionException {
if (CaseType.MULTI_USER_CASE == metadata.getCaseType()) {
progressIndicator.progress(Bundle.Case_progressMessage_settingUpNetworkCommunications());
try {
eventPublisher.openRemoteEventChannel(String.format(EVENT_CHANNEL_NAME, metadata.getCaseName()));
checkForUserCancellation();
collaborationMonitor = new CollaborationMonitor(metadata.getCaseName());
} catch (AutopsyEventException ex) {
throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotOpenRemoteEventChannel(ex.getLocalizedMessage()), ex);
} catch (CollaborationMonitor.CollaborationMonitorException ex) {
throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotCreatCollaborationMonitor(ex.getLocalizedMessage()), ex);
} }
} }
} }
/** /**
* Closes the case. * Closes the case.
*
* @throws CaseActionException If there is a problem completing the
* operation. The exception will have a
* user-friendly message and may be a wrapper
* for a lower-level exception.
*/ */
private void close() throws CaseActionException { private void close() throws CaseActionException {
/* /*

View File

@ -16,13 +16,15 @@
* 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.coordinationservice; package org.sleuthkit.autopsy.casemodule;
import java.nio.BufferUnderflowException; import java.io.ByteArrayInputStream;
import java.nio.ByteBuffer; import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.text.DateFormat;
import java.text.ParseException; import java.text.ParseException;
import java.util.Date; import java.util.Date;
import org.sleuthkit.autopsy.casemodule.CaseMetadata; import org.sleuthkit.autopsy.casemodule.CaseMetadata;
@ -45,8 +47,8 @@ public final class CaseNodeData {
* Version 1 fields. * Version 1 fields.
*/ */
private Path directory; private Path directory;
private long createDate; private Date createDate;
private long lastAccessDate; private Date lastAccessDate;
private String name; private String name;
private String displayName; private String displayName;
private short deletedItemFlags; private short deletedItemFlags;
@ -75,8 +77,8 @@ public final class CaseNodeData {
this.version = CURRENT_VERSION; this.version = CURRENT_VERSION;
this.errorsOccurred = false; this.errorsOccurred = false;
this.directory = Paths.get(metadata.getCaseDirectory()); this.directory = Paths.get(metadata.getCaseDirectory());
this.createDate = CaseMetadata.getDateFormat().parse(metadata.getCreatedDate()).getTime(); this.createDate = CaseMetadata.getDateFormat().parse(metadata.getCreatedDate());
this.lastAccessDate = new Date().getTime(); // Don't really know. this.lastAccessDate = new Date();
this.name = metadata.getCaseName(); this.name = metadata.getCaseName();
this.displayName = metadata.getCaseDisplayName(); this.displayName = metadata.getCaseDisplayName();
this.deletedItemFlags = 0; this.deletedItemFlags = 0;
@ -89,45 +91,27 @@ public final class CaseNodeData {
* *
* @param nodeData The raw bytes received from the coordination service. * @param nodeData The raw bytes received from the coordination service.
* *
* @throws InvalidDataException If the node data buffer is smaller than * @throws IOException If there is an error reading the node data.
* expected.
*/ */
public CaseNodeData(byte[] nodeData) throws InvalidDataException { public CaseNodeData(byte[] nodeData) throws IOException {
if (nodeData == null || nodeData.length == 0) { if (nodeData == null || nodeData.length == 0) {
throw new InvalidDataException(null == nodeData ? "Null node data byte array" : "Zero-length node data byte array"); throw new IOException(null == nodeData ? "Null node data byte array" : "Zero-length node data byte array");
} }
DataInputStream inputStream = new DataInputStream(new ByteArrayInputStream(nodeData));
/* this.version = inputStream.readInt();
* Get the fields from the node data. if (this.version > 0) {
*/ this.errorsOccurred = inputStream.readBoolean();
ByteBuffer buffer = ByteBuffer.wrap(nodeData); } else {
try { short legacyErrorsOccurred = inputStream.readByte();
/* this.errorsOccurred = (legacyErrorsOccurred < 0);
* Get version 0 fields. }
*/ if (this.version > 0) {
this.version = buffer.getInt(); this.directory = Paths.get(inputStream.readUTF());
this.createDate = new Date(inputStream.readLong());
/* this.lastAccessDate = new Date(inputStream.readLong());
* Flags bit format: 76543210 0-6 --> reserved for future use 7 --> this.name = inputStream.readUTF();
* errorsOccurred this.displayName = inputStream.readUTF();
*/ this.deletedItemFlags = inputStream.readShort();
byte flags = buffer.get();
this.errorsOccurred = (flags < 0);
if (buffer.hasRemaining()) {
/*
* Get version 1 fields.
*/
this.directory = Paths.get(NodeDataUtils.getStringFromBuffer(buffer));
this.createDate = buffer.getLong();
this.lastAccessDate = buffer.getLong();
this.name = NodeDataUtils.getStringFromBuffer(buffer);
this.displayName = NodeDataUtils.getStringFromBuffer(buffer);
this.deletedItemFlags = buffer.getShort();
}
} catch (BufferUnderflowException ex) {
throw new InvalidDataException("Node data is incomplete", ex);
} }
} }
@ -186,7 +170,7 @@ public final class CaseNodeData {
* @return The create date. * @return The create date.
*/ */
public Date getCreateDate() { public Date getCreateDate() {
return new Date(this.createDate); return new Date(this.createDate.getTime());
} }
/** /**
@ -195,7 +179,7 @@ public final class CaseNodeData {
* @param createDate The create date. * @param createDate The create date.
*/ */
public void setCreateDate(Date createDate) { public void setCreateDate(Date createDate) {
this.createDate = createDate.getTime(); this.createDate = new Date(createDate.getTime());
} }
/** /**
@ -204,7 +188,7 @@ public final class CaseNodeData {
* @return The last access date. * @return The last access date.
*/ */
public Date getLastAccessDate() { public Date getLastAccessDate() {
return new Date(this.lastAccessDate); return new Date(this.lastAccessDate.getTime());
} }
/** /**
@ -213,7 +197,7 @@ public final class CaseNodeData {
* @param lastAccessDate The last access date. * @param lastAccessDate The last access date.
*/ */
public void setLastAccessDate(Date lastAccessDate) { public void setLastAccessDate(Date lastAccessDate) {
this.lastAccessDate = lastAccessDate.getTime(); this.lastAccessDate = new Date(lastAccessDate.getTime());
} }
/** /**
@ -259,34 +243,23 @@ public final class CaseNodeData {
* service. * service.
* *
* @return The node data as a byte array. * @return The node data as a byte array.
*
* @throws IOException If there is an error writing the node data.
*/ */
public byte[] toArray() { public byte[] toArray() throws IOException {
int bufferSize = Integer.BYTES; // version ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
bufferSize += 1; // errorsOccurred DataOutputStream outputStream = new DataOutputStream(byteStream);
bufferSize += this.directory.toString().getBytes().length; // directory outputStream.writeInt(this.version);
bufferSize += Long.BYTES; // createDate outputStream.writeBoolean(this.errorsOccurred);
bufferSize += Long.BYTES; // lastAccessDate outputStream.writeUTF(this.directory.toString());
bufferSize += this.name.getBytes().length; // name outputStream.writeLong(this.createDate.getTime());
bufferSize += this.displayName.getBytes().length; // displayName outputStream.writeLong(this.lastAccessDate.getTime());
bufferSize += Short.BYTES; // deletedItemFlags outputStream.writeUTF(this.name);
ByteBuffer buffer = ByteBuffer.allocate(bufferSize); outputStream.writeUTF(this.displayName);
outputStream.writeShort(this.deletedItemFlags);
buffer.putInt(this.version); outputStream.flush();
buffer.put((byte) (this.errorsOccurred ? 0x80 : 0)); byteStream.flush();
return byteStream.toByteArray();
if (this.version >= 1) {
NodeDataUtils.putStringIntoBuffer(this.directory.toString(), buffer);
buffer.putLong(this.createDate);
buffer.putLong(this.lastAccessDate);
NodeDataUtils.putStringIntoBuffer(name, buffer);
NodeDataUtils.putStringIntoBuffer(displayName, buffer);
buffer.putShort(deletedItemFlags);
}
byte[] array = new byte[buffer.position()];
buffer.rewind();
buffer.get(array, 0, array.length);
return array;
} }
public final static class InvalidDataException extends Exception { public final static class InvalidDataException extends Exception {

View File

@ -0,0 +1,164 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019-2019 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;
import java.io.File;
import java.io.IOException;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import org.sleuthkit.autopsy.casemodule.CaseMetadata;
import org.sleuthkit.autopsy.coordinationservice.CoordinationService;
import org.sleuthkit.autopsy.coreutils.Logger;
/**
* Queries the coordination service to collect the multi-user case node data
* stored in the case directory lock ZooKeeper nodes.
*/
final class MulitUserCaseNodeDataCollector {
private static final Logger logger = Logger.getLogger(MulitUserCaseNodeDataCollector.class.getName());
private static final String CASE_AUTO_INGEST_LOG_NAME = "AUTO_INGEST_LOG.TXT"; //NON-NLS
private static final String RESOURCES_LOCK_SUFFIX = "_RESOURCES"; //NON-NLS
/**
* Queries the coordination service to collect the multi-user case node data
* stored in the case directory lock ZooKeeper nodes.
*
* @return A list of CaseNodedata objects that convert data for a case
* directory lock coordination service node to and from byte arrays.
*
* @throws CoordinationServiceException If there is an error
*/
public static List<CaseNodeData> getNodeData() throws CoordinationService.CoordinationServiceException {
final List<CaseNodeData> cases = new ArrayList<>();
final CoordinationService coordinationService = CoordinationService.getInstance();
final List<String> nodeList = coordinationService.getNodeList(CoordinationService.CategoryNode.CASES);
for (String nodeName : nodeList) {
/*
* Ignore auto ingest case name lock nodes.
*/
final Path nodeNameAsPath = Paths.get(nodeName);
if (!(nodeNameAsPath.toString().contains("\\") || nodeNameAsPath.toString().contains("//"))) {
continue;
}
/*
* Ignore case auto ingest log lock nodes and resource lock nodes.
*/
final String lastNodeNameComponent = nodeNameAsPath.getFileName().toString();
if (lastNodeNameComponent.equals(CASE_AUTO_INGEST_LOG_NAME)) {
continue;
}
/*
* Ignore case resources lock nodes.
*/
if (lastNodeNameComponent.endsWith(RESOURCES_LOCK_SUFFIX)) {
continue;
}
/*
* Get the data from the case directory lock node. This data may not
* exist for "legacy" nodes. If it is missing, create it.
*/
try {
CaseNodeData nodeData;
byte[] nodeBytes = coordinationService.getNodeData(CoordinationService.CategoryNode.CASES, nodeName);
if (nodeBytes != null && nodeBytes.length > 0) {
nodeData = new CaseNodeData(nodeBytes);
if (nodeData.getVersion() == 0) {
/*
* Version 0 case node data was only written if errors
* occurred during an auto ingest job and consisted of
* only the set errors flag.
*/
nodeData = createNodeDataFromCaseMetadata(nodeName, true);
}
} else {
nodeData = createNodeDataFromCaseMetadata(nodeName, false);
}
cases.add(nodeData);
} catch (CoordinationService.CoordinationServiceException | InterruptedException | IOException | ParseException | CaseMetadata.CaseMetadataException ex) {
logger.log(Level.SEVERE, String.format("Error getting coordination service node data for %s", nodeName), ex);
}
}
return cases;
}
/**
* Creates and saves case directory lock coordination service node data from
* the metadata file for the case associated with the node.
*
* @param nodeName The coordination service node name, i.e., the case
* directory path.
* @param errorsOccurred Whether or not errors occurred during an auto
* ingest job for the case.
*
* @return A CaseNodedata object.
*
* @throws IOException If there is an error writing the
* node data to a byte array.
* @throws CaseMetadataException If there is an error reading the
* case metadata file.
* @throws ParseException If there is an error parsing a date
* from the case metadata file.
* @throws CoordinationServiceException If there is an error interacting
* with the coordination service.
* @throws InterruptedException If a coordination service operation
* is interrupted.
*/
private static CaseNodeData createNodeDataFromCaseMetadata(String nodeName, boolean errorsOccurred) throws IOException, CaseMetadata.CaseMetadataException, ParseException, CoordinationService.CoordinationServiceException, InterruptedException {
CaseNodeData nodeData = null;
Path caseDirectoryPath = Paths.get(nodeName).toRealPath(LinkOption.NOFOLLOW_LINKS);
File caseDirectory = caseDirectoryPath.toFile();
if (caseDirectory.exists()) {
File[] files = caseDirectory.listFiles();
for (File file : files) {
String name = file.getName().toLowerCase();
if (name.endsWith(CaseMetadata.getFileExtension())) {
CaseMetadata metadata = new CaseMetadata(Paths.get(file.getAbsolutePath()));
nodeData = new CaseNodeData(metadata);
nodeData.setErrorsOccurred(errorsOccurred);
break;
}
}
}
if (nodeData != null) {
CoordinationService coordinationService = CoordinationService.getInstance();
coordinationService.setNodeData(CoordinationService.CategoryNode.CASES, nodeName, nodeData.toArray());
return nodeData;
} else {
throw new IOException(String.format("Could not find case metadata file for %s", nodeName));
}
}
/**
* Private constructor to prevent instantiation of this utility class.
*/
private MulitUserCaseNodeDataCollector() {
}
}

View File

@ -33,7 +33,12 @@ import org.openide.nodes.AbstractNode;
import org.openide.nodes.Children; import org.openide.nodes.Children;
import org.openide.nodes.Sheet; import org.openide.nodes.Sheet;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coordinationservice.CaseNodeData; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.CaseActionCancelledException;
import org.sleuthkit.autopsy.casemodule.CaseActionException;
import org.sleuthkit.autopsy.casemodule.CaseMetadata;
import org.sleuthkit.autopsy.casemodule.StartupWindowProvider;
import org.sleuthkit.autopsy.casemodule.Bundle;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.datamodel.NodeProperty; import org.sleuthkit.autopsy.datamodel.NodeProperty;
@ -66,9 +71,9 @@ final class MultiUserCaseNode extends AbstractNode {
} }
@NbBundle.Messages({ @NbBundle.Messages({
"CaseNode.column.name=Name", "MultiUserCaseNode.column.name=Name",
"CaseNode.column.createTime=Create Time", "MultiUserCaseNode.column.createTime=Create Time",
"CaseNode.column.path=Path" "MultiUserCaseNode.column.path=Path"
}) })
@Override @Override
protected Sheet createSheet() { protected Sheet createSheet() {
@ -78,17 +83,17 @@ final class MultiUserCaseNode extends AbstractNode {
sheetSet = Sheet.createPropertiesSet(); sheetSet = Sheet.createPropertiesSet();
sheet.put(sheetSet); sheet.put(sheetSet);
} }
sheetSet.put(new NodeProperty<>(Bundle.CaseNode_column_name(), sheetSet.put(new NodeProperty<>(Bundle.MultiUserCaseNode_column_name(),
Bundle.CaseNode_column_name(), Bundle.MultiUserCaseNode_column_name(),
Bundle.CaseNode_column_name(), Bundle.MultiUserCaseNode_column_name(),
caseNodeData.getDisplayName())); caseNodeData.getDisplayName()));
sheetSet.put(new NodeProperty<>(Bundle.CaseNode_column_createTime(), sheetSet.put(new NodeProperty<>(Bundle.MultiUserCaseNode_column_createTime(),
Bundle.CaseNode_column_createTime(), Bundle.MultiUserCaseNode_column_createTime(),
Bundle.CaseNode_column_createTime(), Bundle.MultiUserCaseNode_column_createTime(),
caseNodeData.getCreateDate())); caseNodeData.getCreateDate()));
sheetSet.put(new NodeProperty<>(Bundle.CaseNode_column_path(), sheetSet.put(new NodeProperty<>(Bundle.MultiUserCaseNode_column_path(),
Bundle.CaseNode_column_path(), Bundle.MultiUserCaseNode_column_path(),
Bundle.CaseNode_column_path(), Bundle.MultiUserCaseNode_column_path(),
caseNodeData.getDirectory().toString())); caseNodeData.getDirectory().toString()));
return sheet; return sheet;
} }

View File

@ -24,25 +24,10 @@ import org.netbeans.swing.etable.ETableColumn;
import org.netbeans.swing.etable.ETableColumnModel; import org.netbeans.swing.etable.ETableColumnModel;
import org.netbeans.swing.outline.DefaultOutlineModel; import org.netbeans.swing.outline.DefaultOutlineModel;
import org.netbeans.swing.outline.Outline; import org.netbeans.swing.outline.Outline;
import java.awt.EventQueue;
import java.io.File;
import java.io.IOException;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import javax.swing.SwingWorker;
import org.openide.explorer.ExplorerManager; import org.openide.explorer.ExplorerManager;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coordinationservice.CaseNodeData;
import org.sleuthkit.autopsy.coordinationservice.CoordinationService;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.EmptyNode;
import java.util.concurrent.ExecutionException;
import org.openide.explorer.view.OutlineView; import org.openide.explorer.view.OutlineView;
import org.sleuthkit.autopsy.casemodule.Bundle;
/** /**
* A JPanel with a scroll pane child component that contains a NetBeans * A JPanel with a scroll pane child component that contains a NetBeans
@ -50,14 +35,12 @@ import org.openide.explorer.view.OutlineView;
* to the coordination service. * to the coordination service.
*/ */
@SuppressWarnings("PMD.SingularField") // Matisse-generated UI widgets cause lots of false positives @SuppressWarnings("PMD.SingularField") // Matisse-generated UI widgets cause lots of false positives
class MultiUserCasesBrowserPanel extends javax.swing.JPanel implements ExplorerManager.Provider { final class MultiUserCasesBrowserPanel extends javax.swing.JPanel implements ExplorerManager.Provider {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static final Logger logger = Logger.getLogger(MultiUserCasesBrowserPanel.class.getName()); private final ExplorerManager explorerManager;
private ExplorerManager explorerManager; // RJCTODO: COnsider making this final
private final Outline outline;
private final OutlineView outlineView; private final OutlineView outlineView;
private LoadCaseListWorker loadCaseListWorker; private final Outline outline;
/** /**
* Constructs a JPanel with a scroll pane child component that contains a * Constructs a JPanel with a scroll pane child component that contains a
@ -65,10 +48,12 @@ class MultiUserCasesBrowserPanel extends javax.swing.JPanel implements ExplorerM
* cases known to the coordination service. * cases known to the coordination service.
*/ */
MultiUserCasesBrowserPanel() { MultiUserCasesBrowserPanel() {
explorerManager = new ExplorerManager();
outlineView = new org.openide.explorer.view.OutlineView(); outlineView = new org.openide.explorer.view.OutlineView();
initComponents(); initComponents();
outline = outlineView.getOutline(); outline = outlineView.getOutline();
customizeOutlineView(); customizeOutlineView();
explorerManager.setRootContext(new MultiUserCasesRootNode());
} }
/** /**
@ -76,17 +61,17 @@ class MultiUserCasesBrowserPanel extends javax.swing.JPanel implements ExplorerM
*/ */
private void customizeOutlineView() { private void customizeOutlineView() {
outlineView.setPropertyColumns( outlineView.setPropertyColumns(
Bundle.CaseNode_column_createTime(), Bundle.CaseNode_column_createTime(), // RJCTODO: Move these into this file? Bundle.MultiUserCaseNode_column_createTime(), Bundle.MultiUserCaseNode_column_createTime(),
Bundle.CaseNode_column_path(), Bundle.CaseNode_column_path()); Bundle.MultiUserCaseNode_column_path(), Bundle.MultiUserCaseNode_column_path());
((DefaultOutlineModel) outline.getOutlineModel()).setNodesColumnLabel(Bundle.CaseNode_column_name()); ((DefaultOutlineModel) outline.getOutlineModel()).setNodesColumnLabel(Bundle.MultiUserCaseNode_column_name());
outline.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); outline.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
TableColumnModel columnModel = outline.getColumnModel(); TableColumnModel columnModel = outline.getColumnModel();
int pathColumnIndex = 0; int pathColumnIndex = 0;
int dateColumnIndex = 0; int dateColumnIndex = 0;
for (int index = 0; index < columnModel.getColumnCount(); index++) { for (int index = 0; index < columnModel.getColumnCount(); index++) {
if (columnModel.getColumn(index).getHeaderValue().toString().equals(Bundle.CaseNode_column_path())) { if (columnModel.getColumn(index).getHeaderValue().toString().equals(Bundle.MultiUserCaseNode_column_path())) {
pathColumnIndex = index; pathColumnIndex = index;
} else if (columnModel.getColumn(index).getHeaderValue().toString().equals(Bundle.CaseNode_column_createTime())) { } else if (columnModel.getColumn(index).getHeaderValue().toString().equals(Bundle.MultiUserCaseNode_column_createTime())) {
dateColumnIndex = index; dateColumnIndex = index;
} }
} }
@ -103,10 +88,6 @@ class MultiUserCasesBrowserPanel extends javax.swing.JPanel implements ExplorerM
*/ */
outline.setColumnSorted(dateColumnIndex, false, 1); outline.setColumnSorted(dateColumnIndex, false, 1);
if (null == explorerManager) {
explorerManager = new ExplorerManager();
}
caseTableScrollPane.setViewportView(outlineView); caseTableScrollPane.setViewportView(outlineView);
this.setVisible(true); this.setVisible(true);
} }
@ -124,17 +105,7 @@ class MultiUserCasesBrowserPanel extends javax.swing.JPanel implements ExplorerM
"MultiUserCasesBrowserPanel.waitNode.message=Please Wait..." "MultiUserCasesBrowserPanel.waitNode.message=Please Wait..."
}) })
void refreshCases() { void refreshCases() {
if (loadCaseListWorker == null || loadCaseListWorker.isDone()) { explorerManager.setRootContext(new MultiUserCasesRootNode());
/*
* RJCTODO: Explain this or move the data fetching into the create
* keys method of the nodes...
*/
EmptyNode emptyNode = new EmptyNode(Bundle.MultiUserCasesBrowserPanel_waitNode_message());
explorerManager.setRootContext(emptyNode);
loadCaseListWorker = new LoadCaseListWorker();
loadCaseListWorker.execute();
}
} }
/** /**
@ -162,98 +133,4 @@ class MultiUserCasesBrowserPanel extends javax.swing.JPanel implements ExplorerM
private javax.swing.JScrollPane caseTableScrollPane; private javax.swing.JScrollPane caseTableScrollPane;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
/**
* A background task that gets the multi-user case data from the case
* directory lock coordination service nodes.
*/
private class LoadCaseListWorker extends SwingWorker<List<CaseNodeData>, Void> {
private static final String CASE_AUTO_INGEST_LOG_NAME = "AUTO_INGEST_LOG.TXT"; //NON-NLS
private static final String RESOURCES_LOCK_SUFFIX = "_RESOURCES"; //NON-NLS
@Override
protected List<CaseNodeData> doInBackground() throws Exception {
final List<CaseNodeData> cases = new ArrayList<>();
final CoordinationService coordinationService = CoordinationService.getInstance();
final List<String> nodeList = coordinationService.getNodeList(CoordinationService.CategoryNode.CASES);
for (String nodeName : nodeList) {
/*
* Ignore case name lock nodes.
*/
final Path nodeNameAsPath = Paths.get(nodeName);
if (!(nodeNameAsPath.toString().contains("\\") || nodeNameAsPath.toString().contains("//"))) {
continue;
}
/*
* Ignore case auto ingest log lock nodes and resource lock
* nodes.
*/
final String lastNodeNameComponent = nodeNameAsPath.getFileName().toString();
if (lastNodeNameComponent.equals(CASE_AUTO_INGEST_LOG_NAME) || lastNodeNameComponent.endsWith(RESOURCES_LOCK_SUFFIX)) {
continue;
}
/*
* Get the data from the case directory lock node. This data may not exist
* for "legacy" nodes. If it is missing, create it.
*/
try {
CaseNodeData nodeData;
byte[] nodeBytes = CoordinationService.getInstance().getNodeData(CoordinationService.CategoryNode.CASES, nodeName);
if (nodeBytes != null && nodeBytes.length > 0) {
nodeData = new CaseNodeData(nodeBytes);
if (nodeData.getVersion() > 0) {
cases.add(nodeData);
} else {
nodeData = createNodeDataFromCaseMetadata(nodeName);
}
} else {
nodeData = createNodeDataFromCaseMetadata(nodeName);
}
cases.add(nodeData);
} catch (CoordinationService.CoordinationServiceException | CaseNodeData.InvalidDataException | IOException | CaseMetadata.CaseMetadataException ex) {
logger.log(Level.SEVERE, String.format("Error getting coordination service node data for %s", nodeName), ex);
}
}
return cases;
}
@Override
protected void done() {
try {
final List<CaseNodeData> cases = get();
EventQueue.invokeLater(() -> {
MultiUserCasesRootNode caseListNode = new MultiUserCasesRootNode(cases);
explorerManager.setRootContext(caseListNode);
});
} catch (InterruptedException ex) {
logger.log(Level.SEVERE, "Unexpected interrupt during background processing", ex);
} catch (ExecutionException ex) {
logger.log(Level.SEVERE, "Error during background processing", ex);
}
}
private CaseNodeData createNodeDataFromCaseMetadata(String nodeName) throws IOException, CaseMetadata.CaseMetadataException, ParseException, CoordinationService.CoordinationServiceException, InterruptedException {
Path caseDirectoryPath = Paths.get(nodeName).toRealPath(LinkOption.NOFOLLOW_LINKS);
File caseDirectory = caseDirectoryPath.toFile();
if (caseDirectory.exists()) {
File[] files = caseDirectory.listFiles();
for (File file : files) {
String name = file.getName().toLowerCase();
if (name.endsWith(CaseMetadata.getFileExtension())) {
CaseMetadata metadata = new CaseMetadata(Paths.get(file.getAbsolutePath()));
CaseNodeData nodeData = new CaseNodeData(metadata);
CoordinationService coordinationService = CoordinationService.getInstance();
coordinationService.setNodeData(CoordinationService.CategoryNode.CASES, nodeName, nodeData.toArray());
}
}
}
throw new IOException(String.format("Could not find case metadata file for %s", nodeName));
}
}
} }

View File

@ -19,12 +19,12 @@
package org.sleuthkit.autopsy.casemodule; package org.sleuthkit.autopsy.casemodule;
import java.util.List; import java.util.List;
import java.util.logging.Level;
import org.openide.nodes.AbstractNode; import org.openide.nodes.AbstractNode;
import org.openide.nodes.ChildFactory; import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children; import org.openide.nodes.Children;
import org.openide.nodes.Node; import org.openide.nodes.Node;
import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.coordinationservice.CoordinationService;
import org.sleuthkit.autopsy.coordinationservice.CaseNodeData;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
/** /**
@ -32,6 +32,8 @@ import org.sleuthkit.autopsy.coreutils.Logger;
*/ */
final class MultiUserCasesRootNode extends AbstractNode { final class MultiUserCasesRootNode extends AbstractNode {
private static final Logger logger = Logger.getLogger(MultiUserCasesRootNode.class.getName());
/** /**
* Constructs a root node for displaying MultiUserCaseNodes in a NetBeans * Constructs a root node for displaying MultiUserCaseNodes in a NetBeans
* Explorer View. * Explorer View.
@ -39,22 +41,19 @@ final class MultiUserCasesRootNode extends AbstractNode {
* @param case A list of coordination service node data objects representing * @param case A list of coordination service node data objects representing
* multi-user cases. * multi-user cases.
*/ */
MultiUserCasesRootNode(List<CaseNodeData> cases) { MultiUserCasesRootNode() {
super(Children.create(new MultiUserCasesRootNodeChildren(cases), true)); super(Children.create(new MultiUserCasesRootNodeChildren(), true));
} }
private static class MultiUserCasesRootNodeChildren extends ChildFactory<CaseNodeData> { private static class MultiUserCasesRootNodeChildren extends ChildFactory<CaseNodeData> {
private final List<CaseNodeData> cases;
MultiUserCasesRootNodeChildren(List<CaseNodeData> cases) {
this.cases = cases;
}
@Override @Override
protected boolean createKeys(List<CaseNodeData> keys) { protected boolean createKeys(List<CaseNodeData> keys) {
if (cases != null && cases.size() > 0) { try {
keys.addAll(cases); List<CaseNodeData> caseNodeData = MulitUserCaseNodeDataCollector.getNodeData();
keys.addAll(caseNodeData);
} catch (CoordinationService.CoordinationServiceException ex) {
logger.log(Level.SEVERE, "Failed to get case node data from coodination service", ex);
} }
return true; return true;
} }

View File

@ -25,6 +25,7 @@ import javax.swing.JDialog;
import javax.swing.KeyStroke; import javax.swing.KeyStroke;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.windows.WindowManager; import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.casemodule.Bundle;
/** /**
* A singleton JDialog that allows a user to open a multi-user case. * A singleton JDialog that allows a user to open a multi-user case.

View File

@ -46,7 +46,7 @@ final class OpenMultiUserCasePanel extends JPanel {
*/ */
OpenMultiUserCasePanel(JDialog parentDialog) { OpenMultiUserCasePanel(JDialog parentDialog) {
this.parentDialog = parentDialog; this.parentDialog = parentDialog;
initComponents(); initComponents(); // Machine generated code
caseBrowserPanel = new MultiUserCasesBrowserPanel(); caseBrowserPanel = new MultiUserCasesBrowserPanel();
caseExplorerScrollPane.add(caseBrowserPanel); caseExplorerScrollPane.add(caseBrowserPanel);
caseExplorerScrollPane.setViewportView(caseBrowserPanel); caseExplorerScrollPane.setViewportView(caseBrowserPanel);

View File

@ -1,35 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019-2019 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.coordinationservice;
/**
* Exception thrown when invlaid coordniation service node data is encountered.
*/
public final class InvalidNodeDataException extends Exception {
private static final long serialVersionUID = 1L;
private InvalidNodeDataException(String message) {
super(message);
}
private InvalidNodeDataException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@ -1,64 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019-2019 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.coordinationservice;
import java.nio.ByteBuffer;
import javax.lang.model.type.TypeKind;
/**
* Utilities for reading and writing node data.
*/
public final class NodeDataUtils {
/**
* This method retrieves a string from a given buffer. Assumes the string
* was written using putStringIntoBuffer and first reads the the length of
* the string.
*
* @param buffer The buffer from which the string will be read.
*
* @return The string read from the buffer.
*/
public static String getStringFromBuffer(ByteBuffer buffer) {
String output = "";
int length = buffer.getInt();
if (length > 0) {
byte[] array = new byte[length];
buffer.get(array, 0, length);
output = new String(array);
}
return output;
}
/**
* This method puts a given string into a given buffer. The length of the
* string will be inserted prior to the string.
*
* @param stringValue The string to write to the buffer.
* @param buffer The buffer to which the string will be written.
*/
public static void putStringIntoBuffer(String stringValue, ByteBuffer buffer) {
buffer.putInt(stringValue.getBytes().length);
buffer.put(stringValue.getBytes());
}
private NodeDataUtils() {
}
}

View File

@ -380,7 +380,7 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer {
public void setNode(Node givenNode) { public void setNode(Node givenNode) {
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
if (selectionListener == null) { if (selectionListener == null) {
this.getExplorerManager().addPropertyChangeListener(new NodeSelectionListener()); // RJCTODO: remove listener on cleanup this.getExplorerManager().addPropertyChangeListener(new NodeSelectionListener());
} }
if (rootNodeChildren != null) { if (rootNodeChildren != null) {
rootNodeChildren.cancelLoadingThumbnails(); rootNodeChildren.cancelLoadingThumbnails();

View File

@ -63,7 +63,7 @@ import org.sleuthkit.autopsy.casemodule.Case.CaseType;
import org.sleuthkit.autopsy.casemodule.CaseActionException; import org.sleuthkit.autopsy.casemodule.CaseActionException;
import org.sleuthkit.autopsy.casemodule.CaseDetails; import org.sleuthkit.autopsy.casemodule.CaseDetails;
import org.sleuthkit.autopsy.casemodule.CaseMetadata; import org.sleuthkit.autopsy.casemodule.CaseMetadata;
import org.sleuthkit.autopsy.coordinationservice.CaseNodeData; import org.sleuthkit.autopsy.casemodule.CaseNodeData;
import org.sleuthkit.autopsy.coordinationservice.CoordinationService; import org.sleuthkit.autopsy.coordinationservice.CoordinationService;
import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CoordinationServiceException; import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CoordinationServiceException;
import org.sleuthkit.autopsy.coordinationservice.CoordinationService.Lock; import org.sleuthkit.autopsy.coordinationservice.CoordinationService.Lock;
@ -1135,9 +1135,9 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen
* *
* @throws CoordinationService.CoordinationServiceException * @throws CoordinationService.CoordinationServiceException
* @throws InterruptedException * @throws InterruptedException
* @throws CaseNodeData.InvalidDataException * @throws IOException
*/ */
private void setCaseNodeDataErrorsOccurred(Path caseDirectoryPath) throws CoordinationServiceException, InterruptedException, CaseNodeData.InvalidDataException { private void setCaseNodeDataErrorsOccurred(Path caseDirectoryPath) throws CoordinationServiceException, InterruptedException, IOException {
CaseNodeData caseNodeData = new CaseNodeData(coordinationService.getNodeData(CoordinationService.CategoryNode.CASES, caseDirectoryPath.toString())); CaseNodeData caseNodeData = new CaseNodeData(coordinationService.getNodeData(CoordinationService.CategoryNode.CASES, caseDirectoryPath.toString()));
caseNodeData.setErrorsOccurred(true); caseNodeData.setErrorsOccurred(true);
byte[] rawData = caseNodeData.toArray(); byte[] rawData = caseNodeData.toArray();
@ -1517,7 +1517,7 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen
job.setErrorsOccurred(true); job.setErrorsOccurred(true);
try { try {
setCaseNodeDataErrorsOccurred(caseDirectoryPath); setCaseNodeDataErrorsOccurred(caseDirectoryPath);
} catch (CaseNodeData.InvalidDataException ex) { } catch (IOException ex) {
sysLogger.log(Level.SEVERE, String.format("Error attempting to get case node data for %s", caseDirectoryPath), ex); sysLogger.log(Level.SEVERE, String.format("Error attempting to get case node data for %s", caseDirectoryPath), ex);
} }
} else { } else {
@ -2020,7 +2020,7 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen
* for an auto ingest * for an auto ingest
* job. * job.
*/ */
private void processJobs() throws CoordinationServiceException, SharedConfigurationException, ServicesMonitorException, DatabaseServerDownException, KeywordSearchServerDownException, CaseManagementException, AnalysisStartupException, FileExportException, AutoIngestJobLoggerException, InterruptedException, AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException, AutoIngestJobNodeData.InvalidDataException, CaseNodeData.InvalidDataException, JobMetricsCollectionException { private void processJobs() throws CoordinationServiceException, SharedConfigurationException, ServicesMonitorException, DatabaseServerDownException, KeywordSearchServerDownException, CaseManagementException, AnalysisStartupException, FileExportException, AutoIngestJobLoggerException, InterruptedException, AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException, AutoIngestJobNodeData.InvalidDataException, IOException, JobMetricsCollectionException {
sysLogger.log(Level.INFO, "Started processing pending jobs queue"); sysLogger.log(Level.INFO, "Started processing pending jobs queue");
Lock manifestLock = JobProcessingTask.this.dequeueAndLockNextJob(); Lock manifestLock = JobProcessingTask.this.dequeueAndLockNextJob();
while (null != manifestLock) { while (null != manifestLock) {
@ -2221,7 +2221,7 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen
* for an auto ingest * for an auto ingest
* job. * job.
*/ */
private void processJob() throws CoordinationServiceException, SharedConfigurationException, ServicesMonitorException, DatabaseServerDownException, KeywordSearchServerDownException, CaseManagementException, AnalysisStartupException, FileExportException, AutoIngestJobLoggerException, InterruptedException, AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException, CaseNodeData.InvalidDataException, JobMetricsCollectionException { private void processJob() throws CoordinationServiceException, SharedConfigurationException, ServicesMonitorException, DatabaseServerDownException, KeywordSearchServerDownException, CaseManagementException, AnalysisStartupException, FileExportException, AutoIngestJobLoggerException, InterruptedException, AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException, IOException, JobMetricsCollectionException {
Path manifestPath = currentJob.getManifest().getFilePath(); Path manifestPath = currentJob.getManifest().getFilePath();
sysLogger.log(Level.INFO, "Started processing of {0}", manifestPath); sysLogger.log(Level.INFO, "Started processing of {0}", manifestPath);
currentJob.setProcessingStatus(AutoIngestJob.ProcessingStatus.PROCESSING); currentJob.setProcessingStatus(AutoIngestJob.ProcessingStatus.PROCESSING);
@ -2309,7 +2309,7 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen
* to collect metrics for an * to collect metrics for an
* auto ingest job. * auto ingest job.
*/ */
private void attemptJob() throws CoordinationServiceException, SharedConfigurationException, ServicesMonitorException, DatabaseServerDownException, KeywordSearchServerDownException, CaseManagementException, AnalysisStartupException, FileExportException, AutoIngestJobLoggerException, InterruptedException, AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException, CaseNodeData.InvalidDataException, JobMetricsCollectionException { private void attemptJob() throws CoordinationServiceException, SharedConfigurationException, ServicesMonitorException, DatabaseServerDownException, KeywordSearchServerDownException, CaseManagementException, AnalysisStartupException, FileExportException, AutoIngestJobLoggerException, InterruptedException, AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException, IOException, JobMetricsCollectionException {
updateConfiguration(); updateConfiguration();
if (currentJob.isCanceled() || jobProcessingTaskFuture.isCancelled()) { if (currentJob.isCanceled() || jobProcessingTaskFuture.isCancelled()) {
return; return;
@ -2489,7 +2489,7 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen
* collect metrics for an auto * collect metrics for an auto
* ingest job. * ingest job.
*/ */
private void runIngestForJob(Case caseForJob) throws CoordinationServiceException, AnalysisStartupException, FileExportException, AutoIngestJobLoggerException, InterruptedException, AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException, CaseNodeData.InvalidDataException, JobMetricsCollectionException { private void runIngestForJob(Case caseForJob) throws CoordinationServiceException, AnalysisStartupException, FileExportException, AutoIngestJobLoggerException, InterruptedException, AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException, IOException, JobMetricsCollectionException {
try { try {
if (currentJob.isCanceled() || jobProcessingTaskFuture.isCancelled()) { if (currentJob.isCanceled() || jobProcessingTaskFuture.isCancelled()) {
return; return;
@ -2528,7 +2528,7 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen
* collect metrics for an auto * collect metrics for an auto
* ingest job. * ingest job.
*/ */
private void ingestDataSource(Case caseForJob) throws AnalysisStartupException, FileExportException, AutoIngestJobLoggerException, InterruptedException, AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException, CaseNodeData.InvalidDataException, CoordinationServiceException, JobMetricsCollectionException { private void ingestDataSource(Case caseForJob) throws AnalysisStartupException, FileExportException, AutoIngestJobLoggerException, InterruptedException, AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException, IOException, CoordinationServiceException, JobMetricsCollectionException {
if (currentJob.isCanceled() || jobProcessingTaskFuture.isCancelled()) { if (currentJob.isCanceled() || jobProcessingTaskFuture.isCancelled()) {
return; return;
} }
@ -2585,7 +2585,7 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen
* interrupted while blocked, i.e., * interrupted while blocked, i.e.,
* if auto ingest is shutting down. * if auto ingest is shutting down.
*/ */
private AutoIngestDataSource identifyDataSource() throws AutoIngestJobLoggerException, InterruptedException, CaseNodeData.InvalidDataException, CoordinationServiceException { private AutoIngestDataSource identifyDataSource() throws AutoIngestJobLoggerException, InterruptedException, IOException, CoordinationServiceException {
Manifest manifest = currentJob.getManifest(); Manifest manifest = currentJob.getManifest();
Path manifestPath = manifest.getFilePath(); Path manifestPath = manifest.getFilePath();
sysLogger.log(Level.INFO, "Identifying data source for {0} ", manifestPath); sysLogger.log(Level.INFO, "Identifying data source for {0} ", manifestPath);
@ -2619,7 +2619,7 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen
* while blocked, i.e., if auto * while blocked, i.e., if auto
* ingest is shutting down. * ingest is shutting down.
*/ */
private void runDataSourceProcessor(Case caseForJob, AutoIngestDataSource dataSource) throws InterruptedException, AutoIngestJobLoggerException, AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException, CaseNodeData.InvalidDataException, CoordinationServiceException { private void runDataSourceProcessor(Case caseForJob, AutoIngestDataSource dataSource) throws InterruptedException, AutoIngestJobLoggerException, AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException, IOException, CoordinationServiceException {
Manifest manifest = currentJob.getManifest(); Manifest manifest = currentJob.getManifest();
Path manifestPath = manifest.getFilePath(); Path manifestPath = manifest.getFilePath();
sysLogger.log(Level.INFO, "Adding data source for {0} ", manifestPath); sysLogger.log(Level.INFO, "Adding data source for {0} ", manifestPath);
@ -2701,7 +2701,7 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen
* while blocked, i.e., if auto * while blocked, i.e., if auto
* ingest is shutting down. * ingest is shutting down.
*/ */
private void logDataSourceProcessorResult(AutoIngestDataSource dataSource) throws AutoIngestJobLoggerException, InterruptedException, CaseNodeData.InvalidDataException, CoordinationServiceException { private void logDataSourceProcessorResult(AutoIngestDataSource dataSource) throws AutoIngestJobLoggerException, InterruptedException, IOException, CoordinationServiceException {
Manifest manifest = currentJob.getManifest(); Manifest manifest = currentJob.getManifest();
Path manifestPath = manifest.getFilePath(); Path manifestPath = manifest.getFilePath();
Path caseDirectoryPath = currentJob.getCaseDirectoryPath(); Path caseDirectoryPath = currentJob.getCaseDirectoryPath();
@ -2763,7 +2763,7 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen
* while blocked, i.e., if auto * while blocked, i.e., if auto
* ingest is shutting down. * ingest is shutting down.
*/ */
private void analyze(AutoIngestDataSource dataSource) throws AnalysisStartupException, AutoIngestJobLoggerException, InterruptedException, CaseNodeData.InvalidDataException, CoordinationServiceException { private void analyze(AutoIngestDataSource dataSource) throws AnalysisStartupException, AutoIngestJobLoggerException, InterruptedException, IOException, CoordinationServiceException {
Manifest manifest = currentJob.getManifest(); Manifest manifest = currentJob.getManifest();
Path manifestPath = manifest.getFilePath(); Path manifestPath = manifest.getFilePath();
sysLogger.log(Level.INFO, "Starting ingest modules analysis for {0} ", manifestPath); sysLogger.log(Level.INFO, "Starting ingest modules analysis for {0} ", manifestPath);
@ -2901,7 +2901,7 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen
* while blocked, i.e., if auto * while blocked, i.e., if auto
* ingest is shutting down. * ingest is shutting down.
*/ */
private void exportFiles(AutoIngestDataSource dataSource) throws FileExportException, AutoIngestJobLoggerException, InterruptedException, CaseNodeData.InvalidDataException, CoordinationServiceException { private void exportFiles(AutoIngestDataSource dataSource) throws FileExportException, AutoIngestJobLoggerException, InterruptedException, IOException, CoordinationServiceException {
Manifest manifest = currentJob.getManifest(); Manifest manifest = currentJob.getManifest();
Path manifestPath = manifest.getFilePath(); Path manifestPath = manifest.getFilePath();
sysLogger.log(Level.INFO, "Exporting files for {0}", manifestPath); sysLogger.log(Level.INFO, "Exporting files for {0}", manifestPath);