diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties index d6b8aeb280..c0fe06e728 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties @@ -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.updateCaseNum.exception.msg=Error while trying to update the case number. 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.existCantRW=Cannot create case dir, already exists and cannot read/write\: {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 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.cantCreateCaseDir=Could not create case 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.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.Title=Connection Failure Case.GetCaseTypeGivenPath.Failure=Unable to get case type @@ -192,7 +191,6 @@ ReviewModeCasePanel.LastAccessedTimeHeaderText=Last Accessed Time ReviewModeCasePanel.MetadataFileHeaderText=Metadata File OpenMultiUserCasePanel.jLabel1.text=Recent Cases OpenMultiUserCasePanel.openButton.text=Open -OpenMultiUserCasePanel.cancelButton.text=Cancel CueBannerPanel.newCaseLabel.text=New Case CueBannerPanel.openCaseButton.text= CueBannerPanel.openCaseLabel.text=Open Case @@ -237,6 +235,9 @@ ImageFilePanel.md5HashTextField.text= ImageFilePanel.errorLabel.text=Error Label ImageFilePanel.hashValuesNoteLabel.text=NOTE: These values will not be validated when the data source is added. 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... +# 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 diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle_ja.properties index 208c5ed868..e20b33d82a 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle_ja.properties @@ -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.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.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\ \u30b1\u30fc\u30b9\u540d\uff1a {0}\n\ \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 ImageFilePanel.errorLabel.text=\u30a8\u30e9\u30fc\u30e9\u30d9\u30eb 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.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 @@ -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.openAutoIngestCaseLabel.text=\u65e2\u5b58\u30b1\u30fc\u30b9\u3092\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 CueBannerPanel.newCaseLabel.text=\u65b0\u898f\u30b1\u30fc\u30b9\u3092\u4f5c\u6210 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.approveButtonText=\u9078\u629e LocalDiskSelectionDialog.errorLabel.text=\u30a8\u30e9\u30fc\u30e9\u30d9\u30eb -LocalDiskSelectionDialog.selectLocalDiskLabel.text=\u30ed\u30fc\u30ab\u30eb\u30c7\u30a3\u30b9\u30af\u3092\u9078\u629e\uff1a \ No newline at end of file +LocalDiskSelectionDialog.selectLocalDiskLabel.text=\u30ed\u30fc\u30ab\u30eb\u30c7\u30a3\u30b9\u30af\u3092\u9078\u629e\uff1a +OpenMultiUserCasePanel.cancelButton.text=\u30ad\u30e3\u30f3\u30bb\u30eb diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index 44f1a893af..777f1472ef 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -58,7 +58,6 @@ import javax.annotation.concurrent.GuardedBy; import javax.annotation.concurrent.ThreadSafe; import javax.swing.JOptionPane; import javax.swing.SwingUtilities; -import org.openide.util.Exceptions; import org.openide.util.Lookup; import org.openide.util.NbBundle; 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.commonfilesearch.CommonAttributeSearchAction; import org.sleuthkit.autopsy.communications.OpenCommVisualizationToolAction; -import org.sleuthkit.autopsy.coordinationservice.CaseNodeData; import org.sleuthkit.autopsy.coordinationservice.CoordinationService; import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CategoryNode; import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CoordinationServiceException; @@ -892,72 +890,71 @@ public class Case { /** * Creates a case directory and its subdirectories. * - * @param caseDir Path to the case directory (typically base + case name). - * @param caseType The type of case, single-user or multi-user. + * @param caseDirPath Path to the case directory (typically base + case + * name). + * @param caseType The type of case, single-user or multi-user. * * @throws CaseActionException throw if could not create the case dir */ - public static void createCaseDirectory(String caseDir, CaseType caseType) throws CaseActionException { - - File caseDirF = new File(caseDir); - - if (caseDirF.exists()) { - if (caseDirF.isFile()) { - throw new CaseActionException( - NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.existNotDir", caseDir)); - - } else if (!caseDirF.canRead() || !caseDirF.canWrite()) { - throw new CaseActionException( - NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.existCantRW", caseDir)); + public static void createCaseDirectory(String caseDirPath, CaseType caseType) throws CaseActionException { + /* + * Check the case directory path and permissions. The case directory may + * already exist. + */ + File caseDir = new File(caseDirPath); + if (caseDir.exists()) { + if (caseDir.isFile()) { + throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.existNotDir", caseDirPath)); + } else if (!caseDir.canRead() || !caseDir.canWrite()) { + throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.existCantRW", caseDirPath)); } } - 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( - NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreate", caseDir)); - } + /* + * Create the subdirectories of the case directory, if they do not + * 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 - String hostClause = ""; + Path exportDir = Paths.get(caseDirPath, hostPathComponent, EXPORT_FOLDER); + if (!exportDir.toFile().mkdirs()) { + throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreateCaseDir", exportDir)); + } - if (caseType == CaseType.MULTI_USER_CASE) { - hostClause = File.separator + NetworkUtils.getLocalHostName(); - } - 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(); + Path logsDir = Paths.get(caseDirPath, hostPathComponent, LOG_FOLDER); + if (!logsDir.toFile().mkdirs()) { + throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreateCaseDir", logsDir)); + } - if (result == false) { - throw new CaseActionException( - NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreateCaseDir", caseDir)); - } + Path tempDir = Paths.get(caseDirPath, hostPathComponent, TEMP_FOLDER); + if (!tempDir.toFile().mkdirs()) { + throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreateCaseDir", tempDir)); + } - final String modulesOutDir = caseDir + hostClause + File.separator + MODULE_FOLDER; - result = new File(modulesOutDir).mkdir(); + Path cacheDir = Paths.get(caseDirPath, hostPathComponent, CACHE_FOLDER); + if (!cacheDir.toFile().mkdirs()) { + throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreateCaseDir", cacheDir)); + } - if (result == false) { - throw new CaseActionException( - NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreateModDir", - modulesOutDir)); - } + Path moduleOutputDir = Paths.get(caseDirPath, hostPathComponent, MODULE_FOLDER); + if (!moduleOutputDir.toFile().mkdirs()) { + throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreateModDir", moduleOutputDir)); + } - final String reportsOutDir = caseDir + hostClause + File.separator + REPORTS_FOLDER; - result = new File(reportsOutDir).mkdir(); - - 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); + Path reportsDir = Paths.get(caseDirPath, hostPathComponent, REPORTS_FOLDER); + if (!reportsDir.toFile().mkdirs()) { + throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreateReportsDir", reportsDir)); } } @@ -1947,39 +1944,44 @@ public class Case { * @param isNewCase True for a new case, false otherwise. * @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 * and may be a wrapper for a lower-level * exception. */ private void open(boolean isNewCase, ProgressIndicator progressIndicator) throws CaseActionException { try { - if (Thread.currentThread().isInterrupted()) { - throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser()); - } - + checkForUserCancellation(); + createCaseDirectoryIfDoesNotExist(progressIndicator); + checkForUserCancellation(); + switchLoggingToCaseLogsDirectory(progressIndicator); + checkForUserCancellation(); if (isNewCase) { - createCaseData(progressIndicator); // RJCTODO: This name is vague + createCaseNodeData(progressIndicator); } else { - openCaseData(progressIndicator); // RJCTODO: This name is vague + updateCaseNodeData(progressIndicator); } - - if (Thread.currentThread().isInterrupted()) { - throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser()); - } - - openServices(progressIndicator); - - if (Thread.currentThread().isInterrupted()) { - throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser()); + checkForUserCancellation(); + deleteTempfilesFromCaseDirectory(progressIndicator); + checkForUserCancellation(); + if (isNewCase) { + createCaseDatabase(progressIndicator); + } else { + openCaseDataBase(progressIndicator); } + checkForUserCancellation(); + openCaseLevelServices(progressIndicator); + checkForUserCancellation(); + openAppServiceCaseResources(progressIndicator); + checkForUserCancellation(); + openCommunicationChannels(progressIndicator); } catch (CaseActionException ex) { /* - * Cancellation or failure. Clean up. The sleep is a little hack to - * clear the interrupted flag for this thread if this is a - * cancellation scenario, so that the clean up can run to completion - * in this thread. + * Cancellation or failure. Clean up by calling the close method. + * The sleep is a little hack to clear the interrupted flag for this + * thread if this is a cancellation scenario, so that the clean up + * can run to completion in the current thread. */ try { 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 - * database. The exception will have a + * TODO (JIRA-2180): Always create the case directory as part of the case + * 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 * for a lower-level exception. */ @Messages({ - "Case.progressMessage.creatingCaseDirectory=Creating case directory...", - "Case.progressMessage.creatingCaseDatabase=Creating case database...", - "# {0} - exception message", "Case.exceptionMessage.couldNotCreateCaseDatabase=Failed to create case database:\n{0}", - "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. - */ + "Case.progressMessage.creatingCaseDirectory=Creating case directory...",}) + private void createCaseDirectoryIfDoesNotExist(ProgressIndicator progressIndicator) throws CaseActionException { + progressIndicator.progress(Bundle.Case_progressMessage_creatingCaseDirectory()); if (new File(metadata.getCaseDirectory()).exists() == false) { progressIndicator.progress(Bundle.Case_progressMessage_creatingCaseDirectory()); 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()); try { if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) { /* * For single-user cases, the case database is a SQLite database - * with a standard name, physically located in the root of the - * case directory. + * with a standard name, physically located in the case + * directory. */ caseDb = SleuthkitCase.newCase(Paths.get(metadata.getCaseDirectory(), SINGLE_USER_CASE_DB_NAME).toString()); metadata.setCaseDatabaseName(SINGLE_USER_CASE_DB_NAME); @@ -2052,7 +2144,7 @@ public class Case { /* * For multi-user cases, the case database is a PostgreSQL * 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()); metadata.setCaseDatabaseName(caseDb.getDatabaseName()); @@ -2060,161 +2152,81 @@ public class Case { } catch (TskCoreException ex) { throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotCreateCaseDatabase(ex.getLocalizedMessage()), 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) { - 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. * - * @throws CaseActionException if there is a problem opening the case. The - * exception will have a user-friendly message - * and may be a wrapper for a lower-level - * exception. + * @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.openingCaseDatabase=Opening case database...", - "Case.exceptionMessage.couldNotOpenCaseDatabase=Failed to open case database.", - "Case.unsupportedSchemaVersionMessage=Unsupported DB schema version - see log for details", - "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." + "# {0} - exception message", "Case.exceptionMessage.couldNotOpenCaseDatabase=Failed to open case database:\n{0}.", + "# {0} - exception message", "Case.exceptionMessage.unsupportedSchemaVersionMessage=Unsupported case database schema version:\n{0}.", + "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 { - 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(); if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) { caseDb = SleuthkitCase.openCase(Paths.get(metadata.getCaseDirectory(), databaseName).toString()); } else if (UserPreferences.getIsMultiUserModeEnabled()) { - try { - caseDb = SleuthkitCase.openCase(databaseName, UserPreferences.getDatabaseConnectionInfo(), metadata.getCaseDirectory()); - } catch (UserPreferencesException ex) { - throw new CaseActionException(Case_databaseConnectionInfo_error_msg(), ex); - } + caseDb = SleuthkitCase.openCase(databaseName, UserPreferences.getDatabaseConnectionInfo(), metadata.getCaseDirectory()); } else { throw new CaseActionException(Case_open_exception_multiUserCaseNotEnabled()); } } 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) { - 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 - * cases. + * Opens the case-level services: the files manager, tags manager and + * blackboard. * * @param progressIndicator A progress indicator. - * - * @throws CaseActionException */ @Messages({ - "Case.progressMessage.switchingLogDirectory=Switching log directory...", - "Case.progressMessage.clearingTempDirectory=Clearing case temp directory...", - "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. - */ + "Case.progressMessage.openingCaseLevelServices=Opening case-level services...",}) + private void openCaseLevelServices(ProgressIndicator progressIndicator) { progressIndicator.progress(Bundle.Case_progressMessage_openingCaseLevelServices()); 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 * 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({ + "Case.progressMessage.openingApplicationServiceResources=Opening application service 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.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 * task, running in a named thread managed by an executor service, with @@ -2230,20 +2242,20 @@ public class Case { * with a Cancel button. */ CancelButtonListener cancelButtonListener = null; - ProgressIndicator progressIndicator; + ProgressIndicator appServiceProgressIndicator; if (RuntimeProperties.runningWithGUI()) { cancelButtonListener = new CancelButtonListener(Bundle.Case_serviceOpenCaseResourcesProgressIndicator_cancellingMessage(service.getServiceName())); - progressIndicator = new ModalDialogProgressIndicator( + appServiceProgressIndicator = new ModalDialogProgressIndicator( mainFrame, Bundle.Case_serviceOpenCaseResourcesProgressIndicator_title(service.getServiceName()), new String[]{Bundle.Case_progressIndicatorCancelButton_label()}, Bundle.Case_progressIndicatorCancelButton_label(), cancelButtonListener); } else { - progressIndicator = new LoggingProgressIndicator(); + appServiceProgressIndicator = new LoggingProgressIndicator(); } - progressIndicator.start(Bundle.Case_progressMessage_preparing()); - AutopsyService.CaseContext context = new AutopsyService.CaseContext(this, progressIndicator); + appServiceProgressIndicator.start(Bundle.Case_progressMessage_preparing()); + AutopsyService.CaseContext context = new AutopsyService.CaseContext(this, appServiceProgressIndicator); String threadNameSuffix = service.getServiceName().replaceAll("[ ]", "-"); //NON-NLS threadNameSuffix = threadNameSuffix.toLowerCase(); TaskThreadFactory threadFactory = new TaskThreadFactory(String.format(CASE_RESOURCES_THREAD_NAME, threadNameSuffix)); @@ -2299,17 +2311,50 @@ public class Case { * task responded to a cancellation request. */ 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. + * + * @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 { /* diff --git a/Core/src/org/sleuthkit/autopsy/coordinationservice/CaseNodeData.java b/Core/src/org/sleuthkit/autopsy/casemodule/CaseNodeData.java similarity index 66% rename from Core/src/org/sleuthkit/autopsy/coordinationservice/CaseNodeData.java rename to Core/src/org/sleuthkit/autopsy/casemodule/CaseNodeData.java index 7b10f80c55..19b134145e 100644 --- a/Core/src/org/sleuthkit/autopsy/coordinationservice/CaseNodeData.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/CaseNodeData.java @@ -16,13 +16,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.coordinationservice; +package org.sleuthkit.autopsy.casemodule; -import java.nio.BufferUnderflowException; -import java.nio.ByteBuffer; +import java.io.ByteArrayInputStream; +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.Paths; -import java.text.DateFormat; import java.text.ParseException; import java.util.Date; import org.sleuthkit.autopsy.casemodule.CaseMetadata; @@ -45,8 +47,8 @@ public final class CaseNodeData { * Version 1 fields. */ private Path directory; - private long createDate; - private long lastAccessDate; + private Date createDate; + private Date lastAccessDate; private String name; private String displayName; private short deletedItemFlags; @@ -75,8 +77,8 @@ public final class CaseNodeData { this.version = CURRENT_VERSION; this.errorsOccurred = false; this.directory = Paths.get(metadata.getCaseDirectory()); - this.createDate = CaseMetadata.getDateFormat().parse(metadata.getCreatedDate()).getTime(); - this.lastAccessDate = new Date().getTime(); // Don't really know. + this.createDate = CaseMetadata.getDateFormat().parse(metadata.getCreatedDate()); + this.lastAccessDate = new Date(); this.name = metadata.getCaseName(); this.displayName = metadata.getCaseDisplayName(); this.deletedItemFlags = 0; @@ -89,45 +91,27 @@ public final class CaseNodeData { * * @param nodeData The raw bytes received from the coordination service. * - * @throws InvalidDataException If the node data buffer is smaller than - * expected. + * @throws IOException If there is an error reading the node data. */ - public CaseNodeData(byte[] nodeData) throws InvalidDataException { + public CaseNodeData(byte[] nodeData) throws IOException { 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"); } - - /* - * Get the fields from the node data. - */ - ByteBuffer buffer = ByteBuffer.wrap(nodeData); - try { - /* - * Get version 0 fields. - */ - this.version = buffer.getInt(); - - /* - * Flags bit format: 76543210 0-6 --> reserved for future use 7 --> - * errorsOccurred - */ - 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); + DataInputStream inputStream = new DataInputStream(new ByteArrayInputStream(nodeData)); + this.version = inputStream.readInt(); + if (this.version > 0) { + this.errorsOccurred = inputStream.readBoolean(); + } else { + short legacyErrorsOccurred = inputStream.readByte(); + this.errorsOccurred = (legacyErrorsOccurred < 0); + } + if (this.version > 0) { + this.directory = Paths.get(inputStream.readUTF()); + this.createDate = new Date(inputStream.readLong()); + this.lastAccessDate = new Date(inputStream.readLong()); + this.name = inputStream.readUTF(); + this.displayName = inputStream.readUTF(); + this.deletedItemFlags = inputStream.readShort(); } } @@ -186,7 +170,7 @@ public final class CaseNodeData { * @return The create date. */ 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. */ 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. */ 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. */ public void setLastAccessDate(Date lastAccessDate) { - this.lastAccessDate = lastAccessDate.getTime(); + this.lastAccessDate = new Date(lastAccessDate.getTime()); } /** @@ -259,34 +243,23 @@ public final class CaseNodeData { * service. * * @return The node data as a byte array. + * + * @throws IOException If there is an error writing the node data. */ - public byte[] toArray() { - int bufferSize = Integer.BYTES; // version - bufferSize += 1; // errorsOccurred - bufferSize += this.directory.toString().getBytes().length; // directory - bufferSize += Long.BYTES; // createDate - bufferSize += Long.BYTES; // lastAccessDate - bufferSize += this.name.getBytes().length; // name - bufferSize += this.displayName.getBytes().length; // displayName - bufferSize += Short.BYTES; // deletedItemFlags - ByteBuffer buffer = ByteBuffer.allocate(bufferSize); - - buffer.putInt(this.version); - buffer.put((byte) (this.errorsOccurred ? 0x80 : 0)); - - 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 byte[] toArray() throws IOException { + ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); + DataOutputStream outputStream = new DataOutputStream(byteStream); + outputStream.writeInt(this.version); + outputStream.writeBoolean(this.errorsOccurred); + outputStream.writeUTF(this.directory.toString()); + outputStream.writeLong(this.createDate.getTime()); + outputStream.writeLong(this.lastAccessDate.getTime()); + outputStream.writeUTF(this.name); + outputStream.writeUTF(this.displayName); + outputStream.writeShort(this.deletedItemFlags); + outputStream.flush(); + byteStream.flush(); + return byteStream.toByteArray(); } public final static class InvalidDataException extends Exception { diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/MulitUserCaseNodeDataCollector.java b/Core/src/org/sleuthkit/autopsy/casemodule/MulitUserCaseNodeDataCollector.java new file mode 100755 index 0000000000..4ec14d4650 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/MulitUserCaseNodeDataCollector.java @@ -0,0 +1,164 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019-2019 Basis Technology Corp. + * Contact: carrier sleuthkit 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 getNodeData() throws CoordinationService.CoordinationServiceException { + final List cases = new ArrayList<>(); + final CoordinationService coordinationService = CoordinationService.getInstance(); + final List 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() { + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCaseNode.java b/Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCaseNode.java index 0a762f5f3b..5d801e2d38 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCaseNode.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCaseNode.java @@ -33,7 +33,12 @@ import org.openide.nodes.AbstractNode; import org.openide.nodes.Children; import org.openide.nodes.Sheet; 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.MessageNotifyUtil; import org.sleuthkit.autopsy.datamodel.NodeProperty; @@ -66,9 +71,9 @@ final class MultiUserCaseNode extends AbstractNode { } @NbBundle.Messages({ - "CaseNode.column.name=Name", - "CaseNode.column.createTime=Create Time", - "CaseNode.column.path=Path" + "MultiUserCaseNode.column.name=Name", + "MultiUserCaseNode.column.createTime=Create Time", + "MultiUserCaseNode.column.path=Path" }) @Override protected Sheet createSheet() { @@ -78,17 +83,17 @@ final class MultiUserCaseNode extends AbstractNode { sheetSet = Sheet.createPropertiesSet(); sheet.put(sheetSet); } - sheetSet.put(new NodeProperty<>(Bundle.CaseNode_column_name(), - Bundle.CaseNode_column_name(), - Bundle.CaseNode_column_name(), + sheetSet.put(new NodeProperty<>(Bundle.MultiUserCaseNode_column_name(), + Bundle.MultiUserCaseNode_column_name(), + Bundle.MultiUserCaseNode_column_name(), caseNodeData.getDisplayName())); - sheetSet.put(new NodeProperty<>(Bundle.CaseNode_column_createTime(), - Bundle.CaseNode_column_createTime(), - Bundle.CaseNode_column_createTime(), + sheetSet.put(new NodeProperty<>(Bundle.MultiUserCaseNode_column_createTime(), + Bundle.MultiUserCaseNode_column_createTime(), + Bundle.MultiUserCaseNode_column_createTime(), caseNodeData.getCreateDate())); - sheetSet.put(new NodeProperty<>(Bundle.CaseNode_column_path(), - Bundle.CaseNode_column_path(), - Bundle.CaseNode_column_path(), + sheetSet.put(new NodeProperty<>(Bundle.MultiUserCaseNode_column_path(), + Bundle.MultiUserCaseNode_column_path(), + Bundle.MultiUserCaseNode_column_path(), caseNodeData.getDirectory().toString())); return sheet; } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCasesBrowserPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCasesBrowserPanel.java index 2a99346792..b8522c50d7 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCasesBrowserPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCasesBrowserPanel.java @@ -24,25 +24,10 @@ import org.netbeans.swing.etable.ETableColumn; import org.netbeans.swing.etable.ETableColumnModel; import org.netbeans.swing.outline.DefaultOutlineModel; 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.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.sleuthkit.autopsy.casemodule.Bundle; /** * 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. */ @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 Logger logger = Logger.getLogger(MultiUserCasesBrowserPanel.class.getName()); - private ExplorerManager explorerManager; // RJCTODO: COnsider making this final - private final Outline outline; + private final ExplorerManager explorerManager; private final OutlineView outlineView; - private LoadCaseListWorker loadCaseListWorker; + private final Outline outline; /** * 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. */ MultiUserCasesBrowserPanel() { + explorerManager = new ExplorerManager(); outlineView = new org.openide.explorer.view.OutlineView(); initComponents(); outline = outlineView.getOutline(); customizeOutlineView(); + explorerManager.setRootContext(new MultiUserCasesRootNode()); } /** @@ -76,17 +61,17 @@ class MultiUserCasesBrowserPanel extends javax.swing.JPanel implements ExplorerM */ private void customizeOutlineView() { outlineView.setPropertyColumns( - Bundle.CaseNode_column_createTime(), Bundle.CaseNode_column_createTime(), // RJCTODO: Move these into this file? - Bundle.CaseNode_column_path(), Bundle.CaseNode_column_path()); - ((DefaultOutlineModel) outline.getOutlineModel()).setNodesColumnLabel(Bundle.CaseNode_column_name()); + Bundle.MultiUserCaseNode_column_createTime(), Bundle.MultiUserCaseNode_column_createTime(), + Bundle.MultiUserCaseNode_column_path(), Bundle.MultiUserCaseNode_column_path()); + ((DefaultOutlineModel) outline.getOutlineModel()).setNodesColumnLabel(Bundle.MultiUserCaseNode_column_name()); outline.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); TableColumnModel columnModel = outline.getColumnModel(); int pathColumnIndex = 0; int dateColumnIndex = 0; 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; - } 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; } } @@ -103,10 +88,6 @@ class MultiUserCasesBrowserPanel extends javax.swing.JPanel implements ExplorerM */ outline.setColumnSorted(dateColumnIndex, false, 1); - if (null == explorerManager) { - explorerManager = new ExplorerManager(); - } - caseTableScrollPane.setViewportView(outlineView); this.setVisible(true); } @@ -124,17 +105,7 @@ class MultiUserCasesBrowserPanel extends javax.swing.JPanel implements ExplorerM "MultiUserCasesBrowserPanel.waitNode.message=Please Wait..." }) void refreshCases() { - if (loadCaseListWorker == null || loadCaseListWorker.isDone()) { - /* - * 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(); - } + explorerManager.setRootContext(new MultiUserCasesRootNode()); } /** @@ -162,98 +133,4 @@ class MultiUserCasesBrowserPanel extends javax.swing.JPanel implements ExplorerM private javax.swing.JScrollPane caseTableScrollPane; // 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, 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 doInBackground() throws Exception { - final List cases = new ArrayList<>(); - final CoordinationService coordinationService = CoordinationService.getInstance(); - final List 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 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)); - } - - } - } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCasesRootNode.java b/Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCasesRootNode.java index 3ed3f15492..c4272927b7 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCasesRootNode.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCasesRootNode.java @@ -19,12 +19,12 @@ package org.sleuthkit.autopsy.casemodule; import java.util.List; +import java.util.logging.Level; import org.openide.nodes.AbstractNode; import org.openide.nodes.ChildFactory; import org.openide.nodes.Children; import org.openide.nodes.Node; -import org.openide.util.NbBundle.Messages; -import org.sleuthkit.autopsy.coordinationservice.CaseNodeData; +import org.sleuthkit.autopsy.coordinationservice.CoordinationService; import org.sleuthkit.autopsy.coreutils.Logger; /** @@ -32,6 +32,8 @@ import org.sleuthkit.autopsy.coreutils.Logger; */ 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 * Explorer View. @@ -39,22 +41,19 @@ final class MultiUserCasesRootNode extends AbstractNode { * @param case A list of coordination service node data objects representing * multi-user cases. */ - MultiUserCasesRootNode(List cases) { - super(Children.create(new MultiUserCasesRootNodeChildren(cases), true)); + MultiUserCasesRootNode() { + super(Children.create(new MultiUserCasesRootNodeChildren(), true)); } private static class MultiUserCasesRootNodeChildren extends ChildFactory { - private final List cases; - - MultiUserCasesRootNodeChildren(List cases) { - this.cases = cases; - } - @Override protected boolean createKeys(List keys) { - if (cases != null && cases.size() > 0) { - keys.addAll(cases); + try { + List 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; } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/OpenMultiUserCaseDialog.java b/Core/src/org/sleuthkit/autopsy/casemodule/OpenMultiUserCaseDialog.java index 6fdf6044ae..b7039689ec 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/OpenMultiUserCaseDialog.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/OpenMultiUserCaseDialog.java @@ -25,6 +25,7 @@ import javax.swing.JDialog; import javax.swing.KeyStroke; import org.openide.util.NbBundle; import org.openide.windows.WindowManager; +import org.sleuthkit.autopsy.casemodule.Bundle; /** * A singleton JDialog that allows a user to open a multi-user case. diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/OpenMultiUserCasePanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/OpenMultiUserCasePanel.java index 6c533a6ff6..d3e43c736a 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/OpenMultiUserCasePanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/OpenMultiUserCasePanel.java @@ -46,7 +46,7 @@ final class OpenMultiUserCasePanel extends JPanel { */ OpenMultiUserCasePanel(JDialog parentDialog) { this.parentDialog = parentDialog; - initComponents(); + initComponents(); // Machine generated code caseBrowserPanel = new MultiUserCasesBrowserPanel(); caseExplorerScrollPane.add(caseBrowserPanel); caseExplorerScrollPane.setViewportView(caseBrowserPanel); diff --git a/Core/src/org/sleuthkit/autopsy/coordinationservice/InvalidNodeDataException.java b/Core/src/org/sleuthkit/autopsy/coordinationservice/InvalidNodeDataException.java deleted file mode 100755 index f13c2be677..0000000000 --- a/Core/src/org/sleuthkit/autopsy/coordinationservice/InvalidNodeDataException.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2019-2019 Basis Technology Corp. - * Contact: carrier sleuthkit 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); - } -} diff --git a/Core/src/org/sleuthkit/autopsy/coordinationservice/NodeDataUtils.java b/Core/src/org/sleuthkit/autopsy/coordinationservice/NodeDataUtils.java deleted file mode 100755 index cfdb5a5d51..0000000000 --- a/Core/src/org/sleuthkit/autopsy/coordinationservice/NodeDataUtils.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2019-2019 Basis Technology Corp. - * Contact: carrier sleuthkit 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() { - } - -} diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.java index ea4f4d3136..f03c7b01cf 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.java @@ -380,7 +380,7 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer { public void setNode(Node givenNode) { setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); if (selectionListener == null) { - this.getExplorerManager().addPropertyChangeListener(new NodeSelectionListener()); // RJCTODO: remove listener on cleanup + this.getExplorerManager().addPropertyChangeListener(new NodeSelectionListener()); } if (rootNodeChildren != null) { rootNodeChildren.cancelLoadingThumbnails(); diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java index d2a9c40dc0..49945e2383 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java @@ -63,7 +63,7 @@ import org.sleuthkit.autopsy.casemodule.Case.CaseType; import org.sleuthkit.autopsy.casemodule.CaseActionException; import org.sleuthkit.autopsy.casemodule.CaseDetails; 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.CoordinationServiceException; import org.sleuthkit.autopsy.coordinationservice.CoordinationService.Lock; @@ -1135,9 +1135,9 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen * * @throws CoordinationService.CoordinationServiceException * @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.setErrorsOccurred(true); byte[] rawData = caseNodeData.toArray(); @@ -1517,7 +1517,7 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen job.setErrorsOccurred(true); try { 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); } } else { @@ -2020,7 +2020,7 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen * for an auto ingest * 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"); Lock manifestLock = JobProcessingTask.this.dequeueAndLockNextJob(); while (null != manifestLock) { @@ -2221,7 +2221,7 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen * for an auto ingest * 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(); sysLogger.log(Level.INFO, "Started processing of {0}", manifestPath); currentJob.setProcessingStatus(AutoIngestJob.ProcessingStatus.PROCESSING); @@ -2309,7 +2309,7 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen * to collect metrics for an * 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(); if (currentJob.isCanceled() || jobProcessingTaskFuture.isCancelled()) { return; @@ -2489,7 +2489,7 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen * collect metrics for an auto * 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 { if (currentJob.isCanceled() || jobProcessingTaskFuture.isCancelled()) { return; @@ -2528,7 +2528,7 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen * collect metrics for an auto * 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()) { return; } @@ -2585,7 +2585,7 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen * interrupted while blocked, i.e., * 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(); Path manifestPath = manifest.getFilePath(); 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 * 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(); Path manifestPath = manifest.getFilePath(); 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 * 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(); Path manifestPath = manifest.getFilePath(); Path caseDirectoryPath = currentJob.getCaseDirectoryPath(); @@ -2763,7 +2763,7 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen * while blocked, i.e., if auto * 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(); Path manifestPath = manifest.getFilePath(); 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 * 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(); Path manifestPath = manifest.getFilePath(); sysLogger.log(Level.INFO, "Exporting files for {0}", manifestPath);