mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-06 21:00:22 +00:00
Merge pull request #4504 from rcordovano/improve-open-multi-user-case-dialog-performance
Improve open multi user case dialog performance
This commit is contained in:
commit
cdb7aebc99
@ -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
|
||||||
@ -190,17 +189,10 @@ ReviewModeCasePanel.StatusIconHeaderText=Status
|
|||||||
ReviewModeCasePanel.OutputFolderHeaderText=Output Folder
|
ReviewModeCasePanel.OutputFolderHeaderText=Output Folder
|
||||||
ReviewModeCasePanel.LastAccessedTimeHeaderText=Last Accessed Time
|
ReviewModeCasePanel.LastAccessedTimeHeaderText=Last Accessed Time
|
||||||
ReviewModeCasePanel.MetadataFileHeaderText=Metadata File
|
ReviewModeCasePanel.MetadataFileHeaderText=Metadata File
|
||||||
OpenMultiUserCasePanel.jLabel1.text=Recent Cases
|
|
||||||
OpenMultiUserCasePanel.openButton.text=Open
|
|
||||||
OpenMultiUserCasePanel.cancelButton.text=Cancel
|
|
||||||
MultiUserCasesPanel.bnOpen.text=&Open
|
|
||||||
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
|
||||||
MultiUserCasesPanel.bnOpenSingleUserCase.text=Open Single-User Case...
|
|
||||||
CueBannerPanel.newCaseButton.text=
|
CueBannerPanel.newCaseButton.text=
|
||||||
MultiUserCasesPanel.searchLabel.text=Select any case and start typing to search by case name
|
|
||||||
MultiUserCasesPanel.cancelButton.text=Cancel
|
|
||||||
ImageFilePanel.sectorSizeLabel.text=Sector size:
|
ImageFilePanel.sectorSizeLabel.text=Sector size:
|
||||||
LocalDiskPanel.sectorSizeLabel.text=Sector Size:
|
LocalDiskPanel.sectorSizeLabel.text=Sector Size:
|
||||||
LocalFilesPanel.displayNameLabel.text=Logical File Set Display Name: Default
|
LocalFilesPanel.displayNameLabel.text=Logical File Set Display Name: Default
|
||||||
@ -240,4 +232,8 @@ ImageFilePanel.sha1HashTextField.text=
|
|||||||
ImageFilePanel.md5HashTextField.text=
|
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.cancelButton.text=Cancel
|
||||||
|
OpenMultiUserCasePanel.openSelectedCaseButton.text=Open Selected Case
|
||||||
|
OpenMultiUserCasePanel.openSingleUserCaseButton.text=Open Single-User Case...
|
||||||
|
@ -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
|
||||||
@ -179,9 +177,6 @@ OptionalCasePropertiesPanel.examinerLabel.text=\u8abf\u67fb\u62c5\u5f53\u8005\uf
|
|||||||
OptionalCasePropertiesPanel.caseDisplayNameLabel.text=\u30b1\u30fc\u30b9\u756a\u53f7\uff1a
|
OptionalCasePropertiesPanel.caseDisplayNameLabel.text=\u30b1\u30fc\u30b9\u756a\u53f7\uff1a
|
||||||
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.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.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
|
||||||
ImageFilePanel.sectorSizeLabel.text=\u30a4\u30f3\u30d7\u30c3\u30c8\u30bf\u30a4\u30e0\u30be\u30fc\u30f3\u3092\u9078\u629e\u3057\u3066\u4e0b\u3055\u3044\uff1a
|
ImageFilePanel.sectorSizeLabel.text=\u30a4\u30f3\u30d7\u30c3\u30c8\u30bf\u30a4\u30e0\u30be\u30fc\u30f3\u3092\u9078\u629e\u3057\u3066\u4e0b\u3055\u3044\uff1a
|
||||||
@ -200,4 +195,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
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.casemodule;
|
package org.sleuthkit.autopsy.casemodule;
|
||||||
|
|
||||||
|
import org.sleuthkit.autopsy.coordinationservice.CaseNodeData;
|
||||||
import java.awt.Frame;
|
import java.awt.Frame;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.awt.event.ActionListener;
|
import java.awt.event.ActionListener;
|
||||||
@ -32,6 +33,7 @@ import java.sql.Connection;
|
|||||||
import java.sql.DriverManager;
|
import java.sql.DriverManager;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.sql.Statement;
|
import java.sql.Statement;
|
||||||
|
import java.text.ParseException;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
@ -39,7 +41,6 @@ import java.util.HashMap;
|
|||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.MissingResourceException;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
@ -574,7 +575,7 @@ public class Case {
|
|||||||
* exception.
|
* exception.
|
||||||
*/
|
*/
|
||||||
@Messages({
|
@Messages({
|
||||||
"Case.exceptionMessage.failedToReadMetadata=Failed to read case metadata.",
|
"# {0} - exception message", "Case.exceptionMessage.failedToReadMetadata=Failed to read case metadata:\n{0}.",
|
||||||
"Case.exceptionMessage.cannotOpenMultiUserCaseNoSettings=Multi-user settings are missing (see Tools, Options, Multi-user tab), cannot open a multi-user case."
|
"Case.exceptionMessage.cannotOpenMultiUserCaseNoSettings=Multi-user settings are missing (see Tools, Options, Multi-user tab), cannot open a multi-user case."
|
||||||
})
|
})
|
||||||
public static void openAsCurrentCase(String caseMetadataFilePath) throws CaseActionException {
|
public static void openAsCurrentCase(String caseMetadataFilePath) throws CaseActionException {
|
||||||
@ -582,7 +583,7 @@ public class Case {
|
|||||||
try {
|
try {
|
||||||
metadata = new CaseMetadata(Paths.get(caseMetadataFilePath));
|
metadata = new CaseMetadata(Paths.get(caseMetadataFilePath));
|
||||||
} catch (CaseMetadataException ex) {
|
} catch (CaseMetadataException ex) {
|
||||||
throw new CaseActionException(Bundle.Case_exceptionMessage_failedToReadMetadata(), ex);
|
throw new CaseActionException(Bundle.Case_exceptionMessage_failedToReadMetadata(ex.getLocalizedMessage()), ex);
|
||||||
}
|
}
|
||||||
if (CaseType.MULTI_USER_CASE == metadata.getCaseType() && !UserPreferences.getIsMultiUserModeEnabled()) {
|
if (CaseType.MULTI_USER_CASE == metadata.getCaseType() && !UserPreferences.getIsMultiUserModeEnabled()) {
|
||||||
throw new CaseActionException(Bundle.Case_exceptionMessage_cannotOpenMultiUserCaseNoSettings());
|
throw new CaseActionException(Bundle.Case_exceptionMessage_cannotOpenMultiUserCaseNoSettings());
|
||||||
@ -713,7 +714,8 @@ public class Case {
|
|||||||
"Case.exceptionMessage.cannotDeleteCurrentCase=Cannot delete current case, it must be closed first.",
|
"Case.exceptionMessage.cannotDeleteCurrentCase=Cannot delete current case, it must be closed first.",
|
||||||
"Case.progressMessage.checkingForOtherUser=Checking to see if another user has the case open...",
|
"Case.progressMessage.checkingForOtherUser=Checking to see if another user has the case open...",
|
||||||
"Case.exceptionMessage.cannotGetLockToDeleteCase=Cannot delete case because it is open for another user or there is a problem with the coordination service.",
|
"Case.exceptionMessage.cannotGetLockToDeleteCase=Cannot delete case because it is open for another user or there is a problem with the coordination service.",
|
||||||
"Case.exceptionMessage.failedToDeleteCoordinationServiceNodes=Failed to delete the coordination service nodes for the case.",})
|
"Case.exceptionMessage.failedToDeleteCoordinationServiceNodes=Failed to delete the coordination service nodes for the case."
|
||||||
|
})
|
||||||
public static void deleteCase(CaseMetadata metadata) throws CaseActionException {
|
public static void deleteCase(CaseMetadata metadata) throws CaseActionException {
|
||||||
StopWatch stopWatch = new StopWatch();
|
StopWatch stopWatch = new StopWatch();
|
||||||
stopWatch.start();
|
stopWatch.start();
|
||||||
@ -895,72 +897,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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1588,15 +1589,15 @@ public class Case {
|
|||||||
public void notifyDataSourceAdded(Content dataSource, UUID addingDataSourceEventId) {
|
public void notifyDataSourceAdded(Content dataSource, UUID addingDataSourceEventId) {
|
||||||
eventPublisher.publish(new DataSourceAddedEvent(dataSource, addingDataSourceEventId));
|
eventPublisher.publish(new DataSourceAddedEvent(dataSource, addingDataSourceEventId));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notifies case event subscribers that a data source has been added to the
|
* Notifies case event subscribers that a data source has been added to the
|
||||||
* case database.
|
* case database.
|
||||||
*
|
*
|
||||||
* This should not be called from the event dispatch thread (EDT)
|
* This should not be called from the event dispatch thread (EDT)
|
||||||
*
|
*
|
||||||
* @param dataSource The data source.
|
* @param dataSource The data source.
|
||||||
* @param newName The new name for the data source
|
* @param newName The new name for the data source
|
||||||
*/
|
*/
|
||||||
public void notifyDataSourceNameChanged(Content dataSource, String newName) {
|
public void notifyDataSourceNameChanged(Content dataSource, String newName) {
|
||||||
eventPublisher.publish(new DataSourceNameChangedEvent(dataSource, newName));
|
eventPublisher.publish(new DataSourceNameChangedEvent(dataSource, newName));
|
||||||
@ -1759,7 +1760,7 @@ public class Case {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the case display name.
|
* Updates the case details.
|
||||||
*
|
*
|
||||||
* @param newDisplayName the new display name for the case
|
* @param newDisplayName the new display name for the case
|
||||||
*
|
*
|
||||||
@ -1775,6 +1776,16 @@ public class Case {
|
|||||||
} catch (CaseMetadataException ex) {
|
} catch (CaseMetadataException ex) {
|
||||||
throw new CaseActionException(Bundle.Case_exceptionMessage_metadataUpdateError(), ex);
|
throw new CaseActionException(Bundle.Case_exceptionMessage_metadataUpdateError(), ex);
|
||||||
}
|
}
|
||||||
|
if (getCaseType() == CaseType.MULTI_USER_CASE && !oldCaseDetails.getCaseDisplayName().equals(caseDetails.getCaseDisplayName())) {
|
||||||
|
try {
|
||||||
|
CoordinationService coordinationService = CoordinationService.getInstance();
|
||||||
|
CaseNodeData nodeData = new CaseNodeData(coordinationService.getNodeData(CategoryNode.CASES, metadata.getCaseDirectory()));
|
||||||
|
nodeData.setDisplayName(caseDetails.getCaseDisplayName());
|
||||||
|
coordinationService.setNodeData(CategoryNode.CASES, metadata.getCaseDirectory(), nodeData.toArray());
|
||||||
|
} catch (CoordinationServiceException | InterruptedException | IOException ex) {
|
||||||
|
throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotUpdateCaseNodeData(ex.getLocalizedMessage()), ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!oldCaseDetails.getCaseNumber().equals(caseDetails.getCaseNumber())) {
|
if (!oldCaseDetails.getCaseNumber().equals(caseDetails.getCaseNumber())) {
|
||||||
eventPublisher.publish(new AutopsyEvent(Events.NUMBER.toString(), oldCaseDetails.getCaseNumber(), caseDetails.getCaseNumber()));
|
eventPublisher.publish(new AutopsyEvent(Events.NUMBER.toString(), oldCaseDetails.getCaseNumber(), caseDetails.getCaseNumber()));
|
||||||
}
|
}
|
||||||
@ -1963,38 +1974,50 @@ 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);
|
saveCaseMetadataToFile(progressIndicator);
|
||||||
|
}
|
||||||
|
checkForUserCancellation();
|
||||||
|
if (isNewCase) {
|
||||||
|
createCaseNodeData(progressIndicator);
|
||||||
} else {
|
} else {
|
||||||
openCaseData(progressIndicator);
|
updateCaseNodeData(progressIndicator);
|
||||||
}
|
}
|
||||||
|
checkForUserCancellation();
|
||||||
if (Thread.currentThread().isInterrupted()) {
|
if (!isNewCase) {
|
||||||
throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser());
|
deleteTempfilesFromCaseDirectory(progressIndicator);
|
||||||
}
|
}
|
||||||
|
checkForUserCancellation();
|
||||||
openServices(progressIndicator);
|
if (isNewCase) {
|
||||||
|
createCaseDatabase(progressIndicator);
|
||||||
if (Thread.currentThread().isInterrupted()) {
|
} else {
|
||||||
throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser());
|
openCaseDataBase(progressIndicator);
|
||||||
}
|
}
|
||||||
|
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);
|
||||||
@ -2006,43 +2029,180 @@ 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...",
|
|
||||||
"# {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 {
|
private void createCaseDirectoryIfDoesNotExist(ProgressIndicator progressIndicator) throws CaseActionException {
|
||||||
/*
|
progressIndicator.progress(Bundle.Case_progressMessage_creatingCaseDirectory());
|
||||||
* 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());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves teh case metadata to a file.SHould not be called until the case
|
||||||
|
* directory has been created.
|
||||||
|
*
|
||||||
|
* @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.savingCaseMetadata=Saving case metadata to file...",
|
||||||
|
"# {0} - exception message", "Case.exceptionMessage.couldNotSaveCaseMetadata=Failed to save case metadata:\n{0}."
|
||||||
|
})
|
||||||
|
private void saveCaseMetadataToFile(ProgressIndicator progressIndicator) throws CaseActionException {
|
||||||
|
progressIndicator.progress(Bundle.Case_progressMessage_savingCaseMetadata());
|
||||||
|
try {
|
||||||
|
this.metadata.writeToFile();
|
||||||
|
} catch (CaseMetadataException ex) {
|
||||||
|
throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotSaveCaseMetadata(ex.getLocalizedMessage()), ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 {
|
||||||
|
if (getCaseType() == CaseType.MULTI_USER_CASE) {
|
||||||
|
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.
|
||||||
|
*
|
||||||
|
* @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 {
|
||||||
|
if (getCaseType() == CaseType.MULTI_USER_CASE) {
|
||||||
|
progressIndicator.progress(Bundle.Case_progressMessage_updatingCaseNodeData());
|
||||||
|
if (getCaseType() == CaseType.MULTI_USER_CASE) {
|
||||||
|
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);
|
||||||
@ -2050,7 +2210,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());
|
||||||
@ -2058,143 +2218,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 {
|
||||||
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
|
||||||
@ -2210,20 +2308,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));
|
||||||
@ -2279,17 +2377,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 {
|
||||||
/*
|
/*
|
||||||
|
@ -1,265 +0,0 @@
|
|||||||
/*
|
|
||||||
* Autopsy Forensic Browser
|
|
||||||
*
|
|
||||||
* Copyright 2017-2018 Basis Technology Corp.
|
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.sleuthkit.autopsy.casemodule;
|
|
||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import javax.swing.ListSelectionModel;
|
|
||||||
import javax.swing.event.ListSelectionListener;
|
|
||||||
import javax.swing.table.TableColumnModel;
|
|
||||||
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 org.openide.nodes.Node;
|
|
||||||
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.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.CoordinationService;
|
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
|
||||||
import org.sleuthkit.autopsy.datamodel.EmptyNode;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A Swing JPanel with a scroll pane child component. The scroll pane contain
|
|
||||||
* the table of cases.
|
|
||||||
*
|
|
||||||
* Used to display a list of multi user cases and allow the user to open one of
|
|
||||||
* them.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
|
||||||
class CaseBrowser extends javax.swing.JPanel implements ExplorerManager.Provider {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
private final Outline outline;
|
|
||||||
private ExplorerManager em;
|
|
||||||
private final org.openide.explorer.view.OutlineView outlineView;
|
|
||||||
private int originalPathColumnIndex = 0;
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(CaseBrowser.class.getName());
|
|
||||||
private LoadCaseListWorker tableWorker;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ExplorerManager getExplorerManager() {
|
|
||||||
return em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new CaseBrowser
|
|
||||||
*/
|
|
||||||
CaseBrowser() {
|
|
||||||
outlineView = new org.openide.explorer.view.OutlineView();
|
|
||||||
initComponents();
|
|
||||||
|
|
||||||
outline = outlineView.getOutline();
|
|
||||||
outlineView.setPropertyColumns(
|
|
||||||
Bundle.CaseNode_column_createdTime(), Bundle.CaseNode_column_createdTime(),
|
|
||||||
Bundle.CaseNode_column_metadataFilePath(), Bundle.CaseNode_column_metadataFilePath());
|
|
||||||
((DefaultOutlineModel) outline.getOutlineModel()).setNodesColumnLabel(Bundle.CaseNode_column_name());
|
|
||||||
customize();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configures the the table of cases and its columns.
|
|
||||||
*/
|
|
||||||
private void customize() {
|
|
||||||
outline.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
|
||||||
TableColumnModel columnModel = outline.getColumnModel();
|
|
||||||
int dateColumnIndex = 0;
|
|
||||||
for (int index = 0; index < columnModel.getColumnCount(); index++) {
|
|
||||||
//get indexes for created date column and path column
|
|
||||||
if (columnModel.getColumn(index).getHeaderValue().toString().equals(Bundle.CaseNode_column_metadataFilePath())) {
|
|
||||||
originalPathColumnIndex = index;
|
|
||||||
} else if (columnModel.getColumn(index).getHeaderValue().toString().equals(Bundle.CaseNode_column_createdTime())) {
|
|
||||||
dateColumnIndex = index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//Hide path column by default (user can unhide it)
|
|
||||||
ETableColumn column = (ETableColumn) columnModel.getColumn(originalPathColumnIndex);
|
|
||||||
((ETableColumnModel) columnModel).setColumnHidden(column, true);
|
|
||||||
outline.setRootVisible(false);
|
|
||||||
|
|
||||||
//Sort on Created date column in descending order by default
|
|
||||||
outline.setColumnSorted(dateColumnIndex, false, 1);
|
|
||||||
if (null == em) {
|
|
||||||
em = new ExplorerManager();
|
|
||||||
}
|
|
||||||
caseTableScrollPane.setViewportView(outlineView);
|
|
||||||
this.setVisible(true);
|
|
||||||
outline.setRowSelectionAllowed(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a listener to changes in case selections in the table
|
|
||||||
*
|
|
||||||
* @param listener the ListSelectionListener to add
|
|
||||||
*/
|
|
||||||
void addListSelectionListener(ListSelectionListener listener) {
|
|
||||||
outline.getSelectionModel().addListSelectionListener(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the path to the .aut file for the selected case.
|
|
||||||
*
|
|
||||||
* @return the full path to the selected case's .aut file
|
|
||||||
*/
|
|
||||||
String getCasePath() {
|
|
||||||
int[] selectedRows = outline.getSelectedRows();
|
|
||||||
if (selectedRows.length == 1) {
|
|
||||||
try {
|
|
||||||
return ((Node.Property) outline.getModel().getValueAt(outline.convertRowIndexToModel(selectedRows[0]), originalPathColumnIndex)).getValue().toString();
|
|
||||||
} catch (IllegalAccessException | InvocationTargetException ex) {
|
|
||||||
LOGGER.log(Level.SEVERE, "Unable to get case path from table.", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if a row could be and is selected.
|
|
||||||
*
|
|
||||||
* @return true if a row is selected, false if no row is selected
|
|
||||||
*/
|
|
||||||
boolean isRowSelected() {
|
|
||||||
return outline.getRowSelectionAllowed() && outline.getSelectedRows().length > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NbBundle.Messages({"CaseBrowser.caseListLoading.message=Please Wait..."})
|
|
||||||
/**
|
|
||||||
* Gets the list of cases known to the review mode cases manager and
|
|
||||||
* refreshes the cases table.
|
|
||||||
*/
|
|
||||||
void refresh() {
|
|
||||||
if (tableWorker == null || tableWorker.isDone()) {
|
|
||||||
outline.setRowSelectionAllowed(false);
|
|
||||||
//create a new TableWorker to and execute it in a background thread if one is not currently working
|
|
||||||
//set the table to display text informing the user that the list is being retreived and disable case selection
|
|
||||||
EmptyNode emptyNode = new EmptyNode(Bundle.CaseBrowser_caseListLoading_message());
|
|
||||||
em.setRootContext(emptyNode);
|
|
||||||
tableWorker = new LoadCaseListWorker();
|
|
||||||
tableWorker.execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method is called from within the constructor to initialize the form.
|
|
||||||
* WARNING: Do NOT modify this code. The content of this method is always
|
|
||||||
* regenerated by the Form Editor.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
|
||||||
private void initComponents() {
|
|
||||||
|
|
||||||
caseTableScrollPane = new javax.swing.JScrollPane();
|
|
||||||
|
|
||||||
setMinimumSize(new java.awt.Dimension(0, 5));
|
|
||||||
setPreferredSize(new java.awt.Dimension(5, 5));
|
|
||||||
setLayout(new java.awt.BorderLayout());
|
|
||||||
|
|
||||||
caseTableScrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
|
|
||||||
caseTableScrollPane.setMinimumSize(new java.awt.Dimension(0, 5));
|
|
||||||
caseTableScrollPane.setOpaque(false);
|
|
||||||
caseTableScrollPane.setPreferredSize(new java.awt.Dimension(5, 5));
|
|
||||||
add(caseTableScrollPane, java.awt.BorderLayout.CENTER);
|
|
||||||
}// </editor-fold>//GEN-END:initComponents
|
|
||||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
|
||||||
private javax.swing.JScrollPane caseTableScrollPane;
|
|
||||||
// End of variables declaration//GEN-END:variables
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Swingworker to fetch the updated List of cases in a background thread
|
|
||||||
*/
|
|
||||||
private class LoadCaseListWorker extends SwingWorker<Void, Void> {
|
|
||||||
|
|
||||||
private List<CaseMetadata> cases;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a list of the cases in the top level case folder
|
|
||||||
*
|
|
||||||
* @return List of cases.
|
|
||||||
*
|
|
||||||
* @throws CoordinationServiceException
|
|
||||||
*/
|
|
||||||
private List<CaseMetadata> getCases() throws CoordinationService.CoordinationServiceException {
|
|
||||||
List<CaseMetadata> caseList = new ArrayList<>();
|
|
||||||
List<String> nodeList = CoordinationService.getInstance().getNodeList(CoordinationService.CategoryNode.CASES);
|
|
||||||
|
|
||||||
for (String node : nodeList) {
|
|
||||||
Path casePath;
|
|
||||||
try {
|
|
||||||
casePath = Paths.get(node).toRealPath(LinkOption.NOFOLLOW_LINKS);
|
|
||||||
|
|
||||||
File caseFolder = casePath.toFile();
|
|
||||||
if (caseFolder.exists()) {
|
|
||||||
/*
|
|
||||||
* Search for '*.aut' files.
|
|
||||||
*/
|
|
||||||
File[] fileArray = caseFolder.listFiles();
|
|
||||||
if (fileArray == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
String autFilePath = null;
|
|
||||||
for (File file : fileArray) {
|
|
||||||
String name = file.getName().toLowerCase();
|
|
||||||
if (autFilePath == null && name.endsWith(".aut")) {
|
|
||||||
try {
|
|
||||||
caseList.add(new CaseMetadata(Paths.get(file.getAbsolutePath())));
|
|
||||||
} catch (CaseMetadata.CaseMetadataException ex) {
|
|
||||||
LOGGER.log(Level.SEVERE, String.format("Error reading case metadata file '%s'.", autFilePath), ex);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (IOException ignore) {
|
|
||||||
//if a path could not be resolved to a real path do add it to the caseList
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return caseList;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Void doInBackground() throws Exception {
|
|
||||||
|
|
||||||
try {
|
|
||||||
cases = getCases();
|
|
||||||
} catch (CoordinationService.CoordinationServiceException ex) {
|
|
||||||
LOGGER.log(Level.SEVERE, "Unexpected exception while refreshing the table.", ex); //NON-NLS
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void done() {
|
|
||||||
EventQueue.invokeLater(() -> {
|
|
||||||
MultiUserNode caseListNode = new MultiUserNode(cases);
|
|
||||||
em.setRootContext(caseListNode);
|
|
||||||
outline.setRowSelectionAllowed(true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2011-2017 Basis Technology Corp.
|
* Copyright 2011-2019 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -28,6 +28,7 @@ import java.nio.file.Paths;
|
|||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.Locale;
|
||||||
import javax.xml.parsers.DocumentBuilder;
|
import javax.xml.parsers.DocumentBuilder;
|
||||||
import javax.xml.parsers.DocumentBuilderFactory;
|
import javax.xml.parsers.DocumentBuilderFactory;
|
||||||
import javax.xml.parsers.ParserConfigurationException;
|
import javax.xml.parsers.ParserConfigurationException;
|
||||||
@ -52,7 +53,8 @@ import org.xml.sax.SAXException;
|
|||||||
public final class CaseMetadata {
|
public final class CaseMetadata {
|
||||||
|
|
||||||
private static final String FILE_EXTENSION = ".aut";
|
private static final String FILE_EXTENSION = ".aut";
|
||||||
private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss (z)");
|
private static final String DATE_FORMAT_STRING = "yyyy/MM/dd HH:mm:ss (z)";
|
||||||
|
private static final DateFormat DATE_FORMAT = new SimpleDateFormat(DATE_FORMAT_STRING, Locale.US);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Fields from schema version 1
|
* Fields from schema version 1
|
||||||
@ -92,6 +94,7 @@ public final class CaseMetadata {
|
|||||||
private final static String EXAMINER_ELEMENT_PHONE = "ExaminerPhone"; //NON-NLS
|
private final static String EXAMINER_ELEMENT_PHONE = "ExaminerPhone"; //NON-NLS
|
||||||
private final static String EXAMINER_ELEMENT_EMAIL = "ExaminerEmail"; //NON-NLS
|
private final static String EXAMINER_ELEMENT_EMAIL = "ExaminerEmail"; //NON-NLS
|
||||||
private final static String CASE_ELEMENT_NOTES = "CaseNotes"; //NON-NLS
|
private final static String CASE_ELEMENT_NOTES = "CaseNotes"; //NON-NLS
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Unread fields, regenerated on save.
|
* Unread fields, regenerated on save.
|
||||||
*/
|
*/
|
||||||
@ -119,6 +122,15 @@ public final class CaseMetadata {
|
|||||||
return FILE_EXTENSION;
|
return FILE_EXTENSION;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the date format used for dates in case metadata.
|
||||||
|
*
|
||||||
|
* @return The date format.
|
||||||
|
*/
|
||||||
|
public static DateFormat getDateFormat() {
|
||||||
|
return new SimpleDateFormat(DATE_FORMAT_STRING, Locale.US);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a CaseMetadata object for a new case. The metadata is not
|
* Constructs a CaseMetadata object for a new case. The metadata is not
|
||||||
* persisted to the case metadata file until writeFile or a setX method is
|
* persisted to the case metadata file until writeFile or a setX method is
|
||||||
@ -295,7 +307,7 @@ public final class CaseMetadata {
|
|||||||
*
|
*
|
||||||
* @return The date this case was created, as a string.
|
* @return The date this case was created, as a string.
|
||||||
*/
|
*/
|
||||||
String getCreatedDate() {
|
public String getCreatedDate() {
|
||||||
return createdDate;
|
return createdDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -352,7 +364,7 @@ public final class CaseMetadata {
|
|||||||
* @throws CaseMetadataException If there is an error writing to the case
|
* @throws CaseMetadataException If there is an error writing to the case
|
||||||
* metadata file.
|
* metadata file.
|
||||||
*/
|
*/
|
||||||
private void writeToFile() throws CaseMetadataException {
|
void writeToFile() throws CaseMetadataException {
|
||||||
try {
|
try {
|
||||||
/*
|
/*
|
||||||
* Create the XML DOM.
|
* Create the XML DOM.
|
||||||
|
@ -161,7 +161,7 @@ public final class CaseOpenAction extends CallableSystemAction implements Action
|
|||||||
WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
|
WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
|
||||||
|
|
||||||
if (multiUserCaseWindow == null) {
|
if (multiUserCaseWindow == null) {
|
||||||
multiUserCaseWindow = MultiUserCasesDialog.getInstance();
|
multiUserCaseWindow = OpenMultiUserCaseDialog.getInstance();
|
||||||
}
|
}
|
||||||
multiUserCaseWindow.setLocationRelativeTo(WindowManager.getDefault().getMainWindow());
|
multiUserCaseWindow.setLocationRelativeTo(WindowManager.getDefault().getMainWindow());
|
||||||
multiUserCaseWindow.setVisible(true);
|
multiUserCaseWindow.setVisible(true);
|
||||||
|
103
Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCaseNode.java
Executable file
103
Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCaseNode.java
Executable file
@ -0,0 +1,103 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2017-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 org.sleuthkit.autopsy.coordinationservice.CaseNodeData;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.swing.Action;
|
||||||
|
import org.openide.nodes.AbstractNode;
|
||||||
|
import org.openide.nodes.Children;
|
||||||
|
import org.openide.nodes.Sheet;
|
||||||
|
import org.openide.util.NbBundle;
|
||||||
|
import org.sleuthkit.autopsy.datamodel.NodeProperty;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A NetBeans Explorer View node that represents a multi-user case.
|
||||||
|
*/
|
||||||
|
final class MultiUserCaseNode extends AbstractNode {
|
||||||
|
|
||||||
|
private final CaseNodeData caseNodeData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a NetBeans Explorer View node that represents a multi-user
|
||||||
|
* case.
|
||||||
|
*
|
||||||
|
* @param caseNodeData The coordination service node data for the case.
|
||||||
|
*/
|
||||||
|
MultiUserCaseNode(CaseNodeData caseNodeData) {
|
||||||
|
super(Children.LEAF);
|
||||||
|
super.setName(caseNodeData.getDisplayName());
|
||||||
|
setName(caseNodeData.getDisplayName());
|
||||||
|
setDisplayName(caseNodeData.getDisplayName());
|
||||||
|
this.caseNodeData = caseNodeData;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NbBundle.Messages({
|
||||||
|
"MultiUserCaseNode.column.name=Name",
|
||||||
|
"MultiUserCaseNode.column.createTime=Create Time",
|
||||||
|
"MultiUserCaseNode.column.path=Path"
|
||||||
|
})
|
||||||
|
@Override
|
||||||
|
protected Sheet createSheet() {
|
||||||
|
Sheet sheet = super.createSheet();
|
||||||
|
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
|
||||||
|
if (sheetSet == null) {
|
||||||
|
sheetSet = Sheet.createPropertiesSet();
|
||||||
|
sheet.put(sheetSet);
|
||||||
|
}
|
||||||
|
sheetSet.put(new NodeProperty<>(Bundle.MultiUserCaseNode_column_name(),
|
||||||
|
Bundle.MultiUserCaseNode_column_name(),
|
||||||
|
Bundle.MultiUserCaseNode_column_name(),
|
||||||
|
caseNodeData.getDisplayName()));
|
||||||
|
sheetSet.put(new NodeProperty<>(Bundle.MultiUserCaseNode_column_createTime(),
|
||||||
|
Bundle.MultiUserCaseNode_column_createTime(),
|
||||||
|
Bundle.MultiUserCaseNode_column_createTime(),
|
||||||
|
caseNodeData.getCreateDate()));
|
||||||
|
sheetSet.put(new NodeProperty<>(Bundle.MultiUserCaseNode_column_path(),
|
||||||
|
Bundle.MultiUserCaseNode_column_path(),
|
||||||
|
Bundle.MultiUserCaseNode_column_path(),
|
||||||
|
caseNodeData.getDirectory().toString()));
|
||||||
|
return sheet;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Action[] getActions(boolean context) {
|
||||||
|
List<Action> actions = new ArrayList<>();
|
||||||
|
actions.add(new OpenMultiUserCaseAction(this.caseNodeData));
|
||||||
|
actions.add(new OpenCaseAutoIngestLogAction(this.caseNodeData));
|
||||||
|
return actions.toArray(new Action[actions.size()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Action getPreferredAction() {
|
||||||
|
return new OpenMultiUserCaseAction(this.caseNodeData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the coordintaion service case node data this Explorer View node
|
||||||
|
* represents.
|
||||||
|
*
|
||||||
|
* @return The case node data.
|
||||||
|
*/
|
||||||
|
CaseNodeData getCaseNodeData() {
|
||||||
|
return this.caseNodeData;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
164
Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCaseNodeDataCollector.java
Executable file
164
Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCaseNodeDataCollector.java
Executable 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 org.sleuthkit.autopsy.coordinationservice.CaseNodeData;
|
||||||
|
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.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 MultiUserCaseNodeDataCollector {
|
||||||
|
|
||||||
|
private static final Logger logger = Logger.getLogger(MultiUserCaseNodeDataCollector.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 MultiUserCaseNodeDataCollector() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,144 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2017-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 javax.swing.ListSelectionModel;
|
||||||
|
import javax.swing.event.ListSelectionListener;
|
||||||
|
import javax.swing.table.TableColumnModel;
|
||||||
|
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 org.openide.explorer.ExplorerManager;
|
||||||
|
import org.openide.util.NbBundle;
|
||||||
|
import org.openide.explorer.view.OutlineView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A JPanel with a scroll pane child component that contains a NetBeans
|
||||||
|
* OutlineView that can be used to display a list of the multi-user cases known
|
||||||
|
* to the coordination service.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("PMD.SingularField") // Matisse-generated UI widgets cause lots of false positives
|
||||||
|
final class MultiUserCasesBrowserPanel extends javax.swing.JPanel implements ExplorerManager.Provider {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
private final ExplorerManager explorerManager;
|
||||||
|
private final OutlineView outlineView;
|
||||||
|
private final Outline outline;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a JPanel with a scroll pane child component that contains a
|
||||||
|
* NetBeans OutlineView that can be used to display a list of the multi-user
|
||||||
|
* cases known to the coordination service.
|
||||||
|
*/
|
||||||
|
MultiUserCasesBrowserPanel() {
|
||||||
|
explorerManager = new ExplorerManager();
|
||||||
|
outlineView = new org.openide.explorer.view.OutlineView();
|
||||||
|
initComponents();
|
||||||
|
outline = outlineView.getOutline();
|
||||||
|
configureOutlineView();
|
||||||
|
explorerManager.setRootContext(new MultiUserCasesRootNode());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures the child scroll pane component's child OutlineView component.
|
||||||
|
*/
|
||||||
|
private void configureOutlineView() {
|
||||||
|
outlineView.setPropertyColumns(
|
||||||
|
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.MultiUserCaseNode_column_path())) {
|
||||||
|
pathColumnIndex = index;
|
||||||
|
} else if (columnModel.getColumn(index).getHeaderValue().toString().equals(Bundle.MultiUserCaseNode_column_createTime())) {
|
||||||
|
dateColumnIndex = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Hide path column by default (user can unhide it)
|
||||||
|
*/
|
||||||
|
ETableColumn column = (ETableColumn) columnModel.getColumn(pathColumnIndex);
|
||||||
|
((ETableColumnModel) columnModel).setColumnHidden(column, true);
|
||||||
|
outline.setRootVisible(false);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sort on Created date column in descending order by default.
|
||||||
|
*/
|
||||||
|
outline.setColumnSorted(dateColumnIndex, false, 1);
|
||||||
|
|
||||||
|
caseTableScrollPane.setViewportView(outlineView);
|
||||||
|
this.setVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExplorerManager getExplorerManager() {
|
||||||
|
return explorerManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a listener to changes in case selection in this browser.
|
||||||
|
*
|
||||||
|
* @param listener the ListSelectionListener to add
|
||||||
|
*/
|
||||||
|
void addListSelectionListener(ListSelectionListener listener) {
|
||||||
|
outline.getSelectionModel().addListSelectionListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refreshes the list of multi-user cases in this browser.
|
||||||
|
*/
|
||||||
|
@NbBundle.Messages({
|
||||||
|
"MultiUserCasesBrowserPanel.waitNode.message=Please Wait..."
|
||||||
|
})
|
||||||
|
void refreshCases() {
|
||||||
|
explorerManager.setRootContext(new MultiUserCasesRootNode());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called from within the constructor to initialize the form.
|
||||||
|
* WARNING: Do NOT modify this code. The content of this method is always
|
||||||
|
* regenerated by the Form Editor.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||||
|
private void initComponents() {
|
||||||
|
|
||||||
|
caseTableScrollPane = new javax.swing.JScrollPane();
|
||||||
|
|
||||||
|
setMinimumSize(new java.awt.Dimension(0, 5));
|
||||||
|
setPreferredSize(new java.awt.Dimension(5, 5));
|
||||||
|
setLayout(new java.awt.BorderLayout());
|
||||||
|
|
||||||
|
caseTableScrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
|
||||||
|
caseTableScrollPane.setMinimumSize(new java.awt.Dimension(0, 5));
|
||||||
|
caseTableScrollPane.setOpaque(false);
|
||||||
|
caseTableScrollPane.setPreferredSize(new java.awt.Dimension(5, 5));
|
||||||
|
add(caseTableScrollPane, java.awt.BorderLayout.CENTER);
|
||||||
|
}// </editor-fold>//GEN-END:initComponents
|
||||||
|
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||||
|
private javax.swing.JScrollPane caseTableScrollPane;
|
||||||
|
// End of variables declaration//GEN-END:variables
|
||||||
|
|
||||||
|
}
|
@ -1,90 +0,0 @@
|
|||||||
/*
|
|
||||||
* Autopsy Forensic Browser
|
|
||||||
*
|
|
||||||
* Copyright 2011-2018 Basis Technology Corp.
|
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.sleuthkit.autopsy.casemodule;
|
|
||||||
|
|
||||||
import java.awt.Dialog;
|
|
||||||
import java.awt.event.KeyEvent;
|
|
||||||
import javax.swing.JComponent;
|
|
||||||
import javax.swing.JDialog;
|
|
||||||
import javax.swing.KeyStroke;
|
|
||||||
import org.openide.windows.WindowManager;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class extends a JDialog and maintains the MultiUserCasesPanel.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
|
||||||
final class MultiUserCasesDialog extends JDialog {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
private static final String REVIEW_MODE_TITLE = "Open Multi-User Case";
|
|
||||||
private static MultiUserCasesPanel multiUserCasesPanel;
|
|
||||||
private static MultiUserCasesDialog instance;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the instance of the MultiuserCasesDialog.
|
|
||||||
*
|
|
||||||
* @return The instance.
|
|
||||||
*/
|
|
||||||
static public MultiUserCasesDialog getInstance() {
|
|
||||||
if(instance == null) {
|
|
||||||
instance = new MultiUserCasesDialog();
|
|
||||||
instance.init();
|
|
||||||
}
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs a MultiUserCasesDialog object.
|
|
||||||
*/
|
|
||||||
private MultiUserCasesDialog() {
|
|
||||||
super(WindowManager.getDefault().getMainWindow(),
|
|
||||||
REVIEW_MODE_TITLE,
|
|
||||||
Dialog.ModalityType.APPLICATION_MODAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the multi-user cases panel.
|
|
||||||
*/
|
|
||||||
private void init() {
|
|
||||||
getRootPane().registerKeyboardAction(
|
|
||||||
e -> {
|
|
||||||
setVisible(false);
|
|
||||||
},
|
|
||||||
KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_IN_FOCUSED_WINDOW);
|
|
||||||
|
|
||||||
multiUserCasesPanel = new MultiUserCasesPanel(this);
|
|
||||||
add(multiUserCasesPanel);
|
|
||||||
pack();
|
|
||||||
setResizable(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the dialog visibility. When setting it to visible, the contents will
|
|
||||||
* refresh.
|
|
||||||
*
|
|
||||||
* @param value True or false.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void setVisible(boolean value) {
|
|
||||||
if(value) {
|
|
||||||
multiUserCasesPanel.refresh();
|
|
||||||
}
|
|
||||||
super.setVisible(value);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,261 +0,0 @@
|
|||||||
/*
|
|
||||||
* Autopsy Forensic Browser
|
|
||||||
*
|
|
||||||
* Copyright 2011-2018 Basis Technology Corp.
|
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.sleuthkit.autopsy.casemodule;
|
|
||||||
|
|
||||||
import java.awt.Cursor;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import javax.swing.JDialog;
|
|
||||||
import javax.swing.JPanel;
|
|
||||||
import javax.swing.SortOrder;
|
|
||||||
import javax.swing.SwingUtilities;
|
|
||||||
import javax.swing.event.ListSelectionEvent;
|
|
||||||
import javax.swing.table.DefaultTableModel;
|
|
||||||
import javax.swing.table.TableRowSorter;
|
|
||||||
import org.openide.util.Lookup;
|
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
|
||||||
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A panel that allows a user to open cases created by auto ingest.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
|
||||||
final class MultiUserCasesPanel extends JPanel{
|
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(MultiUserCasesPanel.class.getName());
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
private final JDialog parentDialog;
|
|
||||||
private final CaseBrowser caseBrowserPanel;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs a panel that allows a user to open cases created by automated
|
|
||||||
* ingest.
|
|
||||||
*/
|
|
||||||
MultiUserCasesPanel(JDialog parentDialog) {
|
|
||||||
this.parentDialog = parentDialog;
|
|
||||||
initComponents();
|
|
||||||
|
|
||||||
caseBrowserPanel = new CaseBrowser();
|
|
||||||
caseExplorerScrollPane.add(caseBrowserPanel);
|
|
||||||
caseExplorerScrollPane.setViewportView(caseBrowserPanel);
|
|
||||||
/*
|
|
||||||
* Listen for row selection changes and set button state for the current
|
|
||||||
* selection.
|
|
||||||
*/
|
|
||||||
caseBrowserPanel.addListSelectionListener((ListSelectionEvent e) -> {
|
|
||||||
setButtons();
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the list of cases known to the review mode cases manager and
|
|
||||||
* refreshes the cases table.
|
|
||||||
*/
|
|
||||||
void refresh() {
|
|
||||||
caseBrowserPanel.refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enables/disables the Open and Show Log buttons based on the case selected
|
|
||||||
* in the cases table.
|
|
||||||
*/
|
|
||||||
void setButtons() {
|
|
||||||
bnOpen.setEnabled(caseBrowserPanel.isRowSelected());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Open a case.
|
|
||||||
*
|
|
||||||
* @param caseMetadataFilePath The path to the case metadata file.
|
|
||||||
*/
|
|
||||||
private void openCase(String caseMetadataFilePath) {
|
|
||||||
if (caseMetadataFilePath != null) {
|
|
||||||
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
|
|
||||||
|
|
||||||
StartupWindowProvider.getInstance().close();
|
|
||||||
if (parentDialog != null) {
|
|
||||||
parentDialog.setVisible(false);
|
|
||||||
}
|
|
||||||
new Thread(() -> {
|
|
||||||
try {
|
|
||||||
Case.openAsCurrentCase(caseMetadataFilePath);
|
|
||||||
} catch (CaseActionException ex) {
|
|
||||||
if (null != ex.getCause() && !(ex.getCause() instanceof CaseActionCancelledException)) {
|
|
||||||
logger.log(Level.SEVERE, String.format("Error opening case with metadata file path %s", caseMetadataFilePath), ex); //NON-NLS
|
|
||||||
MessageNotifyUtil.Message.error(ex.getCause().getLocalizedMessage());
|
|
||||||
}
|
|
||||||
SwingUtilities.invokeLater(() -> {
|
|
||||||
//GUI changes done back on the EDT
|
|
||||||
StartupWindowProvider.getInstance().open();
|
|
||||||
});
|
|
||||||
} finally {
|
|
||||||
SwingUtilities.invokeLater(() -> {
|
|
||||||
//GUI changes done back on the EDT
|
|
||||||
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}).start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* RowSorter which makes columns whose type is Date to be sorted first in
|
|
||||||
* Descending order then in Ascending order
|
|
||||||
*/
|
|
||||||
private static class RowSorter<M extends DefaultTableModel> extends TableRowSorter<M> {
|
|
||||||
|
|
||||||
RowSorter(M tModel) {
|
|
||||||
super(tModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void toggleSortOrder(int column) {
|
|
||||||
if (!this.getModel().getColumnClass(column).equals(Date.class)) {
|
|
||||||
super.toggleSortOrder(column); //if it isn't a date column perform the regular sorting
|
|
||||||
} else {
|
|
||||||
ArrayList<RowSorter.SortKey> sortKeys = new ArrayList<>(getSortKeys());
|
|
||||||
if (sortKeys.isEmpty() || sortKeys.get(0).getColumn() != column) { //sort descending
|
|
||||||
sortKeys.add(0, new RowSorter.SortKey(column, SortOrder.DESCENDING));
|
|
||||||
} else if (sortKeys.get(0).getSortOrder() == SortOrder.ASCENDING) {
|
|
||||||
sortKeys.removeIf(key -> key.getColumn() == column);
|
|
||||||
sortKeys.add(0, new RowSorter.SortKey(column, SortOrder.DESCENDING));
|
|
||||||
} else {
|
|
||||||
sortKeys.removeIf(key -> key.getColumn() == column);
|
|
||||||
sortKeys.add(0, new RowSorter.SortKey(column, SortOrder.ASCENDING));
|
|
||||||
}
|
|
||||||
setSortKeys(sortKeys);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method is called from within the constructor to initialize the form.
|
|
||||||
* WARNING: Do NOT modify this code. The content of this method is always
|
|
||||||
* regenerated by the Form Editor.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
|
||||||
private void initComponents() {
|
|
||||||
|
|
||||||
bnOpen = new javax.swing.JButton();
|
|
||||||
bnOpenSingleUserCase = new javax.swing.JButton();
|
|
||||||
cancelButton = new javax.swing.JButton();
|
|
||||||
searchLabel = new javax.swing.JLabel();
|
|
||||||
caseExplorerScrollPane = new javax.swing.JScrollPane();
|
|
||||||
|
|
||||||
setName("Completed Cases"); // NOI18N
|
|
||||||
setPreferredSize(new java.awt.Dimension(960, 485));
|
|
||||||
|
|
||||||
org.openide.awt.Mnemonics.setLocalizedText(bnOpen, org.openide.util.NbBundle.getMessage(MultiUserCasesPanel.class, "MultiUserCasesPanel.bnOpen.text")); // NOI18N
|
|
||||||
bnOpen.setEnabled(false);
|
|
||||||
bnOpen.setMaximumSize(new java.awt.Dimension(80, 23));
|
|
||||||
bnOpen.setMinimumSize(new java.awt.Dimension(80, 23));
|
|
||||||
bnOpen.setPreferredSize(new java.awt.Dimension(80, 23));
|
|
||||||
bnOpen.addActionListener(new java.awt.event.ActionListener() {
|
|
||||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
|
||||||
bnOpenActionPerformed(evt);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
org.openide.awt.Mnemonics.setLocalizedText(bnOpenSingleUserCase, org.openide.util.NbBundle.getMessage(MultiUserCasesPanel.class, "MultiUserCasesPanel.bnOpenSingleUserCase.text")); // NOI18N
|
|
||||||
bnOpenSingleUserCase.setMinimumSize(new java.awt.Dimension(156, 23));
|
|
||||||
bnOpenSingleUserCase.setPreferredSize(new java.awt.Dimension(156, 23));
|
|
||||||
bnOpenSingleUserCase.addActionListener(new java.awt.event.ActionListener() {
|
|
||||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
|
||||||
bnOpenSingleUserCaseActionPerformed(evt);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
org.openide.awt.Mnemonics.setLocalizedText(cancelButton, org.openide.util.NbBundle.getMessage(MultiUserCasesPanel.class, "MultiUserCasesPanel.cancelButton.text")); // NOI18N
|
|
||||||
cancelButton.setMaximumSize(new java.awt.Dimension(80, 23));
|
|
||||||
cancelButton.setMinimumSize(new java.awt.Dimension(80, 23));
|
|
||||||
cancelButton.setPreferredSize(new java.awt.Dimension(80, 23));
|
|
||||||
cancelButton.addActionListener(new java.awt.event.ActionListener() {
|
|
||||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
|
||||||
cancelButtonActionPerformed(evt);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
org.openide.awt.Mnemonics.setLocalizedText(searchLabel, org.openide.util.NbBundle.getMessage(MultiUserCasesPanel.class, "MultiUserCasesPanel.searchLabel.text")); // NOI18N
|
|
||||||
|
|
||||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
|
||||||
this.setLayout(layout);
|
|
||||||
layout.setHorizontalGroup(
|
|
||||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
|
||||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
|
|
||||||
.addContainerGap()
|
|
||||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
|
|
||||||
.addComponent(caseExplorerScrollPane)
|
|
||||||
.addGroup(layout.createSequentialGroup()
|
|
||||||
.addComponent(searchLabel)
|
|
||||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
|
||||||
.addComponent(bnOpenSingleUserCase, javax.swing.GroupLayout.PREFERRED_SIZE, 192, javax.swing.GroupLayout.PREFERRED_SIZE)
|
|
||||||
.addGap(190, 190, 190)
|
|
||||||
.addComponent(bnOpen, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
|
||||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
|
||||||
.addComponent(cancelButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
|
|
||||||
.addContainerGap())
|
|
||||||
);
|
|
||||||
|
|
||||||
layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {bnOpen, cancelButton});
|
|
||||||
|
|
||||||
layout.setVerticalGroup(
|
|
||||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
|
||||||
.addGroup(layout.createSequentialGroup()
|
|
||||||
.addGap(6, 6, 6)
|
|
||||||
.addComponent(caseExplorerScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 450, javax.swing.GroupLayout.PREFERRED_SIZE)
|
|
||||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
|
||||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
|
||||||
.addComponent(cancelButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
|
||||||
.addComponent(bnOpen, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
|
||||||
.addComponent(bnOpenSingleUserCase, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
|
||||||
.addComponent(searchLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
|
||||||
.addContainerGap())
|
|
||||||
);
|
|
||||||
}// </editor-fold>//GEN-END:initComponents
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Open button action
|
|
||||||
*
|
|
||||||
* @param evt -- The event that caused this to be called
|
|
||||||
*/
|
|
||||||
private void bnOpenActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnOpenActionPerformed
|
|
||||||
openCase(caseBrowserPanel.getCasePath());
|
|
||||||
}//GEN-LAST:event_bnOpenActionPerformed
|
|
||||||
|
|
||||||
private void bnOpenSingleUserCaseActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnOpenSingleUserCaseActionPerformed
|
|
||||||
Lookup.getDefault().lookup(CaseOpenAction.class).openCaseSelectionWindow();
|
|
||||||
}//GEN-LAST:event_bnOpenSingleUserCaseActionPerformed
|
|
||||||
|
|
||||||
private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed
|
|
||||||
if (parentDialog != null) {
|
|
||||||
parentDialog.setVisible(false);
|
|
||||||
}
|
|
||||||
}//GEN-LAST:event_cancelButtonActionPerformed
|
|
||||||
|
|
||||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
|
||||||
private javax.swing.JButton bnOpen;
|
|
||||||
private javax.swing.JButton bnOpenSingleUserCase;
|
|
||||||
private javax.swing.JButton cancelButton;
|
|
||||||
private javax.swing.JScrollPane caseExplorerScrollPane;
|
|
||||||
private javax.swing.JLabel searchLabel;
|
|
||||||
// End of variables declaration//GEN-END:variables
|
|
||||||
}
|
|
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2017-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 org.sleuthkit.autopsy.coordinationservice.CaseNodeData;
|
||||||
|
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.sleuthkit.autopsy.coordinationservice.CoordinationService;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A root node for displaying MultiUserCaseNodes in a NetBeans Explorer View.
|
||||||
|
*/
|
||||||
|
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.
|
||||||
|
*
|
||||||
|
* @param case A list of coordination service node data objects representing
|
||||||
|
* multi-user cases.
|
||||||
|
*/
|
||||||
|
MultiUserCasesRootNode() {
|
||||||
|
super(Children.create(new MultiUserCasesRootNodeChildren(), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A child factory for creating child nodes for a MultiUserCasesRootNode.
|
||||||
|
* The child nodes are of type MultiUserCaseNode. The node keys are of type
|
||||||
|
* CaseNodeData.
|
||||||
|
*/
|
||||||
|
private static class MultiUserCasesRootNodeChildren extends ChildFactory<CaseNodeData> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean createKeys(List<CaseNodeData> keys) {
|
||||||
|
try {
|
||||||
|
List<CaseNodeData> caseNodeData = MultiUserCaseNodeDataCollector.getNodeData();
|
||||||
|
keys.addAll(caseNodeData);
|
||||||
|
} catch (CoordinationService.CoordinationServiceException ex) {
|
||||||
|
logger.log(Level.SEVERE, "Failed to get case node data from coodination service", ex);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Node createNodeForKey(CaseNodeData key) {
|
||||||
|
return new MultiUserCaseNode(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,232 +0,0 @@
|
|||||||
/*
|
|
||||||
* Autopsy Forensic Browser
|
|
||||||
*
|
|
||||||
* Copyright 2017-2018 Basis Technology Corp.
|
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.sleuthkit.autopsy.casemodule;
|
|
||||||
|
|
||||||
import java.awt.Desktop;
|
|
||||||
import java.awt.event.ActionEvent;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import javax.swing.AbstractAction;
|
|
||||||
import javax.swing.Action;
|
|
||||||
import javax.swing.JOptionPane;
|
|
||||||
import javax.swing.SwingUtilities;
|
|
||||||
import org.openide.nodes.AbstractNode;
|
|
||||||
import org.openide.nodes.ChildFactory;
|
|
||||||
import org.openide.nodes.Children;
|
|
||||||
import org.openide.nodes.Node;
|
|
||||||
import org.openide.nodes.Sheet;
|
|
||||||
import org.openide.util.NbBundle.Messages;
|
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
|
||||||
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
|
||||||
import org.sleuthkit.autopsy.datamodel.NodeProperty;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A root node containing child nodes of the multi user cases
|
|
||||||
*/
|
|
||||||
final class MultiUserNode extends AbstractNode {
|
|
||||||
|
|
||||||
@Messages({"CaseNode.column.name=Name",
|
|
||||||
"CaseNode.column.createdTime=Created Time",
|
|
||||||
"CaseNode.column.metadataFilePath=Path"})
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(MultiUserNode.class.getName());
|
|
||||||
private static final String LOG_FILE_NAME = "auto_ingest_log.txt";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides a root node with children which each represent a case.
|
|
||||||
*
|
|
||||||
* @param caseList the list of CaseMetadata objects representing the cases
|
|
||||||
*/
|
|
||||||
MultiUserNode(List<CaseMetadata> caseList) {
|
|
||||||
super(Children.create(new MultiUserNodeChildren(caseList), true));
|
|
||||||
}
|
|
||||||
|
|
||||||
static class MultiUserNodeChildren extends ChildFactory<CaseMetadata> {
|
|
||||||
|
|
||||||
private final List<CaseMetadata> caseList;
|
|
||||||
|
|
||||||
MultiUserNodeChildren(List<CaseMetadata> caseList) {
|
|
||||||
this.caseList = caseList;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean createKeys(List<CaseMetadata> list) {
|
|
||||||
if (caseList != null && caseList.size() > 0) {
|
|
||||||
list.addAll(caseList);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Node createNodeForKey(CaseMetadata key) {
|
|
||||||
return new MultiUserCaseNode(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A node which represents a single multi user case.
|
|
||||||
*/
|
|
||||||
static final class MultiUserCaseNode extends AbstractNode {
|
|
||||||
|
|
||||||
private final String caseName;
|
|
||||||
private final String caseCreatedDate;
|
|
||||||
private final String caseMetadataFilePath;
|
|
||||||
private final Path caseLogFilePath;
|
|
||||||
|
|
||||||
MultiUserCaseNode(CaseMetadata multiUserCase) {
|
|
||||||
super(Children.LEAF);
|
|
||||||
caseName = multiUserCase.getCaseDisplayName();
|
|
||||||
caseCreatedDate = multiUserCase.getCreatedDate();
|
|
||||||
super.setName(caseName);
|
|
||||||
setName(caseName);
|
|
||||||
setDisplayName(caseName);
|
|
||||||
caseMetadataFilePath = multiUserCase.getFilePath().toString();
|
|
||||||
caseLogFilePath = Paths.get(multiUserCase.getCaseDirectory(), LOG_FILE_NAME);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns action to open the Case represented by this node
|
|
||||||
* @return an action which will open the current case
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Action getPreferredAction() {
|
|
||||||
return new OpenMultiUserCaseAction(caseMetadataFilePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Sheet createSheet() {
|
|
||||||
Sheet sheet = super.createSheet();
|
|
||||||
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
|
|
||||||
if (sheetSet == null) {
|
|
||||||
sheetSet = Sheet.createPropertiesSet();
|
|
||||||
sheet.put(sheetSet);
|
|
||||||
}
|
|
||||||
sheetSet.put(new NodeProperty<>(Bundle.CaseNode_column_name(), Bundle.CaseNode_column_name(), Bundle.CaseNode_column_name(),
|
|
||||||
caseName));
|
|
||||||
sheetSet.put(new NodeProperty<>(Bundle.CaseNode_column_createdTime(), Bundle.CaseNode_column_createdTime(), Bundle.CaseNode_column_createdTime(),
|
|
||||||
caseCreatedDate));
|
|
||||||
sheetSet.put(new NodeProperty<>(Bundle.CaseNode_column_metadataFilePath(), Bundle.CaseNode_column_metadataFilePath(), Bundle.CaseNode_column_metadataFilePath(),
|
|
||||||
caseMetadataFilePath));
|
|
||||||
return sheet;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Action[] getActions(boolean context) {
|
|
||||||
List<Action> actions = new ArrayList<>();
|
|
||||||
actions.add(new OpenMultiUserCaseAction(caseMetadataFilePath)); //open case context menu option
|
|
||||||
actions.add(new OpenCaseLogAction(caseLogFilePath));
|
|
||||||
return actions.toArray(new Action[actions.size()]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Messages({"MultiUserNode.OpenMultiUserCaseAction.text=Open Case"})
|
|
||||||
/**
|
|
||||||
* An action that opens the specified case and hides the multi user case
|
|
||||||
* panel.
|
|
||||||
*/
|
|
||||||
private static final class OpenMultiUserCaseAction extends AbstractAction {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
private final String caseMetadataFilePath;
|
|
||||||
|
|
||||||
OpenMultiUserCaseAction(String path) {
|
|
||||||
super(Bundle.MultiUserNode_OpenMultiUserCaseAction_text());
|
|
||||||
caseMetadataFilePath = path;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
StartupWindowProvider.getInstance().close();
|
|
||||||
MultiUserCasesDialog.getInstance().setVisible(false);
|
|
||||||
new Thread(
|
|
||||||
() -> {
|
|
||||||
try {
|
|
||||||
Case.openAsCurrentCase(caseMetadataFilePath);
|
|
||||||
} catch (CaseActionException ex) {
|
|
||||||
if (null != ex.getCause() && !(ex.getCause() instanceof CaseActionCancelledException)) {
|
|
||||||
LOGGER.log(Level.SEVERE, String.format("Error opening case with metadata file path %s", caseMetadataFilePath), ex); //NON-NLS
|
|
||||||
MessageNotifyUtil.Message.error(ex.getCause().getLocalizedMessage());
|
|
||||||
}
|
|
||||||
SwingUtilities.invokeLater(() -> {
|
|
||||||
//GUI changes done back on the EDT
|
|
||||||
StartupWindowProvider.getInstance().open();
|
|
||||||
MultiUserCasesDialog.getInstance().setVisible(true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
).start();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object clone() throws CloneNotSupportedException {
|
|
||||||
return super.clone(); //To change body of generated methods, choose Tools | Templates.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Messages({"MultiUserNode.OpenCaseLogAction.text=Open Log File"})
|
|
||||||
/**
|
|
||||||
* An action that opens the specified case and hides the multi user case
|
|
||||||
* panel.
|
|
||||||
*/
|
|
||||||
private static final class OpenCaseLogAction extends AbstractAction {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
private final Path pathToLog;
|
|
||||||
|
|
||||||
OpenCaseLogAction(Path caseLogFilePath) {
|
|
||||||
super(Bundle.MultiUserNode_OpenCaseLogAction_text());
|
|
||||||
pathToLog = caseLogFilePath;
|
|
||||||
this.setEnabled(caseLogFilePath != null && caseLogFilePath.toFile().exists());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
|
|
||||||
if (pathToLog != null) {
|
|
||||||
try {
|
|
||||||
if (pathToLog.toFile().exists()) {
|
|
||||||
Desktop.getDesktop().edit(pathToLog.toFile());
|
|
||||||
|
|
||||||
} else {
|
|
||||||
JOptionPane.showMessageDialog(MultiUserCasesDialog.getInstance(), org.openide.util.NbBundle.getMessage(MultiUserNode.class, "DisplayLogDialog.cannotFindLog"),
|
|
||||||
org.openide.util.NbBundle.getMessage(MultiUserNode.class, "DisplayLogDialog.unableToShowLogFile"), JOptionPane.ERROR_MESSAGE);
|
|
||||||
}
|
|
||||||
} catch (IOException ex) {
|
|
||||||
LOGGER.log(Level.SEVERE, String.format("Error attempting to open case auto ingest log file %s", pathToLog), ex);
|
|
||||||
JOptionPane.showMessageDialog(MultiUserCasesDialog.getInstance(),
|
|
||||||
org.openide.util.NbBundle.getMessage(MultiUserNode.class, "DisplayLogDialog.cannotOpenLog"),
|
|
||||||
org.openide.util.NbBundle.getMessage(MultiUserNode.class, "DisplayLogDialog.unableToShowLogFile"),
|
|
||||||
JOptionPane.PLAIN_MESSAGE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object clone() throws CloneNotSupportedException {
|
|
||||||
return super.clone(); //To change body of generated methods, choose Tools | Templates.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
83
Core/src/org/sleuthkit/autopsy/casemodule/OpenCaseAutoIngestLogAction.java
Executable file
83
Core/src/org/sleuthkit/autopsy/casemodule/OpenCaseAutoIngestLogAction.java
Executable file
@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2017-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.awt.Desktop;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import javax.swing.AbstractAction;
|
||||||
|
import org.openide.util.NbBundle;
|
||||||
|
import org.sleuthkit.autopsy.coordinationservice.CaseNodeData;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An action that opens a case auto ingest log given the coordination service
|
||||||
|
* node data for the case.
|
||||||
|
*/
|
||||||
|
final class OpenCaseAutoIngestLogAction extends AbstractAction {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
private static final Logger logger = Logger.getLogger(OpenCaseAutoIngestLogAction.class.getName());
|
||||||
|
private static final String CASE_AUTO_INGEST_LOG_FILE_NAME = "auto_ingest_log.txt";
|
||||||
|
private final Path caseAutoIngestLogFilePath;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an action that opens a case auto ingest log given the
|
||||||
|
* coordination service node data for the case.
|
||||||
|
*
|
||||||
|
* @param caseNodeData The coordination service node data for the case.
|
||||||
|
*/
|
||||||
|
@NbBundle.Messages({
|
||||||
|
"OpenCaseAutoIngestLogAction.menuItemText=Open Auto Ingest Log File"
|
||||||
|
})
|
||||||
|
OpenCaseAutoIngestLogAction(CaseNodeData caseNodeData) {
|
||||||
|
super(Bundle.OpenCaseAutoIngestLogAction_menuItemText());
|
||||||
|
this.caseAutoIngestLogFilePath = Paths.get(caseNodeData.getDirectory().toString(), CASE_AUTO_INGEST_LOG_FILE_NAME);
|
||||||
|
this.setEnabled(caseAutoIngestLogFilePath.toFile().exists());
|
||||||
|
}
|
||||||
|
|
||||||
|
@NbBundle.Messages({
|
||||||
|
"OpenCaseAutoIngestLogAction.deletedLogErrorMsg=The case auto ingest log has been deleted.",
|
||||||
|
"OpenCaseAutoIngestLogAction.logOpenFailedErrorMsg=Failed to open case auto ingest log. See application log for details."
|
||||||
|
})
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent event) {
|
||||||
|
try {
|
||||||
|
if (caseAutoIngestLogFilePath.toFile().exists()) {
|
||||||
|
Desktop.getDesktop().edit(caseAutoIngestLogFilePath.toFile());
|
||||||
|
} else {
|
||||||
|
MessageNotifyUtil.Message.error(Bundle.OpenCaseAutoIngestLogAction_deletedLogErrorMsg());
|
||||||
|
}
|
||||||
|
} catch (IOException ex) {
|
||||||
|
logger.log(Level.SEVERE, String.format("Error opening case auto ingest log file at %s", caseAutoIngestLogFilePath), ex); //NON-NLS
|
||||||
|
MessageNotifyUtil.Message.error(Bundle.OpenCaseAutoIngestLogAction_logOpenFailedErrorMsg());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OpenCaseAutoIngestLogAction clone() throws CloneNotSupportedException {
|
||||||
|
super.clone();
|
||||||
|
throw new CloneNotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
99
Core/src/org/sleuthkit/autopsy/casemodule/OpenMultiUserCaseAction.java
Executable file
99
Core/src/org/sleuthkit/autopsy/casemodule/OpenMultiUserCaseAction.java
Executable file
@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2017-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.awt.event.ActionEvent;
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import javax.swing.AbstractAction;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
|
import org.openide.util.NbBundle;
|
||||||
|
import org.sleuthkit.autopsy.coordinationservice.CaseNodeData;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An action that opens a multi-user case and hides the open multi-user case
|
||||||
|
* dialog given the coordination service node data for the case.
|
||||||
|
*/
|
||||||
|
final class OpenMultiUserCaseAction extends AbstractAction {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
private static final Logger logger = Logger.getLogger(OpenMultiUserCaseAction.class.getName());
|
||||||
|
private final CaseNodeData caseNodeData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an action that opens a multi-user case and hides the open
|
||||||
|
* multi-user case dialog given the coordination service node data for the
|
||||||
|
* case.
|
||||||
|
*/
|
||||||
|
@NbBundle.Messages({
|
||||||
|
"OpenMultiUserCaseAction.menuItemText=Open Case"
|
||||||
|
})
|
||||||
|
OpenMultiUserCaseAction(CaseNodeData caseNodeData) {
|
||||||
|
super(Bundle.OpenMultiUserCaseAction_menuItemText());
|
||||||
|
this.caseNodeData = caseNodeData;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NbBundle.Messages({
|
||||||
|
"# {0} - caseErrorMessage", "OpenMultiUserCaseAction.caseOpeningErrorErrorMsg=Failed to open case: {0}"
|
||||||
|
})
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent event) {
|
||||||
|
StartupWindowProvider.getInstance().close();
|
||||||
|
OpenMultiUserCaseDialog.getInstance().setVisible(false);
|
||||||
|
new Thread(() -> {
|
||||||
|
String caseMetadataFilePath = null;
|
||||||
|
File caseDirectory = caseNodeData.getDirectory().toFile();
|
||||||
|
File[] filesInDirectory = caseDirectory.listFiles();
|
||||||
|
if (filesInDirectory != null) {
|
||||||
|
for (File file : filesInDirectory) {
|
||||||
|
if (file.getName().toLowerCase().endsWith(CaseMetadata.getFileExtension()) && file.isFile()) {
|
||||||
|
caseMetadataFilePath = file.getPath();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (caseMetadataFilePath != null) {
|
||||||
|
try {
|
||||||
|
Case.openAsCurrentCase(caseMetadataFilePath);
|
||||||
|
} catch (CaseActionException ex) {
|
||||||
|
if (null != ex.getCause() && !(ex.getCause() instanceof CaseActionCancelledException)) {
|
||||||
|
logger.log(Level.SEVERE, String.format("Error opening case with metadata file path %s", caseMetadataFilePath), ex); //NON-NLS
|
||||||
|
}
|
||||||
|
SwingUtilities.invokeLater(() -> {
|
||||||
|
MessageNotifyUtil.Message.error(Bundle.OpenMultiUserCaseAction_caseOpeningErrorErrorMsg(ex.getLocalizedMessage()));
|
||||||
|
StartupWindowProvider.getInstance().open();
|
||||||
|
OpenMultiUserCaseDialog.getInstance().setVisible(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
SwingUtilities.invokeLater(() -> {
|
||||||
|
MessageNotifyUtil.Message.error(Bundle.OpenMultiUserCaseAction_caseOpeningErrorErrorMsg("Could not locate case metadata file."));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OpenMultiUserCaseAction clone() throws CloneNotSupportedException {
|
||||||
|
super.clone();
|
||||||
|
throw new CloneNotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2017-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.awt.Dialog;
|
||||||
|
import javax.swing.JDialog;
|
||||||
|
import org.openide.util.NbBundle;
|
||||||
|
import org.openide.windows.WindowManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A singleton JDialog that allows a user to open a multi-user case.
|
||||||
|
*/
|
||||||
|
final class OpenMultiUserCaseDialog extends JDialog {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
private static OpenMultiUserCaseDialog instance;
|
||||||
|
private static OpenMultiUserCasePanel multiUserCasesPanel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the singleton JDialog that allows a user to open a multi-user case.
|
||||||
|
*
|
||||||
|
* @return The singleton JDialog instance.
|
||||||
|
*/
|
||||||
|
public synchronized static OpenMultiUserCaseDialog getInstance() {
|
||||||
|
if (instance == null) {
|
||||||
|
instance = new OpenMultiUserCaseDialog();
|
||||||
|
instance.init();
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a singleton JDialog that allows a user to open a multi-user
|
||||||
|
* case.
|
||||||
|
*/
|
||||||
|
@NbBundle.Messages({
|
||||||
|
"OpenMultiUserCaseDialog.title=Open Multi-User Case"
|
||||||
|
})
|
||||||
|
private OpenMultiUserCaseDialog() {
|
||||||
|
super(WindowManager.getDefault().getMainWindow(), Bundle.OpenMultiUserCaseDialog_title(), Dialog.ModalityType.APPLICATION_MODAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a keyboard action to hide the dialog when the escape key is
|
||||||
|
* pressed and adds a OpenMultiUserCasePanel child component.
|
||||||
|
*/
|
||||||
|
private void init() {
|
||||||
|
multiUserCasesPanel = new OpenMultiUserCasePanel(this);
|
||||||
|
add(multiUserCasesPanel);
|
||||||
|
pack();
|
||||||
|
setResizable(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the dialog visibility. When made visible, the dialog refreshes the
|
||||||
|
* display of its OpenMultiUserCasePanel child component.
|
||||||
|
*
|
||||||
|
* @param makeVisible True or false.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setVisible(boolean makeVisible) {
|
||||||
|
if (makeVisible) {
|
||||||
|
multiUserCasesPanel.refreshDisplay();
|
||||||
|
}
|
||||||
|
super.setVisible(makeVisible);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -28,12 +28,12 @@
|
|||||||
<Component id="caseExplorerScrollPane" max="32767" attributes="0"/>
|
<Component id="caseExplorerScrollPane" max="32767" attributes="0"/>
|
||||||
<Group type="102" attributes="0">
|
<Group type="102" attributes="0">
|
||||||
<Component id="searchLabel" min="-2" max="-2" attributes="0"/>
|
<Component id="searchLabel" min="-2" max="-2" attributes="0"/>
|
||||||
<EmptySpace max="32767" attributes="0"/>
|
<EmptySpace min="-2" pref="32" max="-2" attributes="0"/>
|
||||||
<Component id="bnOpenSingleUserCase" min="-2" pref="192" max="-2" attributes="0"/>
|
<Component id="openSingleUserCaseButton" linkSize="10" min="-2" pref="172" max="-2" attributes="0"/>
|
||||||
<EmptySpace min="-2" pref="190" max="-2" attributes="0"/>
|
<EmptySpace pref="96" max="32767" attributes="0"/>
|
||||||
<Component id="bnOpen" linkSize="1" min="-2" max="-2" attributes="0"/>
|
<Component id="openSelectedCaseButton" linkSize="10" min="-2" pref="160" max="-2" attributes="0"/>
|
||||||
<EmptySpace max="-2" attributes="0"/>
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
<Component id="cancelButton" linkSize="1" min="-2" max="-2" attributes="0"/>
|
<Component id="cancelButton" linkSize="10" min="-2" max="-2" attributes="0"/>
|
||||||
</Group>
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
<EmptySpace max="-2" attributes="0"/>
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
@ -47,10 +47,10 @@
|
|||||||
<Component id="caseExplorerScrollPane" min="-2" pref="450" max="-2" attributes="0"/>
|
<Component id="caseExplorerScrollPane" min="-2" pref="450" max="-2" attributes="0"/>
|
||||||
<EmptySpace max="-2" attributes="0"/>
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
<Group type="103" groupAlignment="3" attributes="0">
|
<Group type="103" groupAlignment="3" attributes="0">
|
||||||
<Component id="cancelButton" alignment="3" min="-2" max="-2" attributes="0"/>
|
<Component id="cancelButton" linkSize="7" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||||
<Component id="bnOpen" alignment="3" min="-2" max="-2" attributes="0"/>
|
<Component id="openSingleUserCaseButton" linkSize="7" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||||
<Component id="bnOpenSingleUserCase" alignment="3" min="-2" max="-2" attributes="0"/>
|
|
||||||
<Component id="searchLabel" alignment="3" max="32767" attributes="0"/>
|
<Component id="searchLabel" alignment="3" max="32767" attributes="0"/>
|
||||||
|
<Component id="openSelectedCaseButton" linkSize="7" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||||
</Group>
|
</Group>
|
||||||
<EmptySpace max="-2" attributes="0"/>
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
</Group>
|
</Group>
|
||||||
@ -58,30 +58,10 @@
|
|||||||
</DimensionLayout>
|
</DimensionLayout>
|
||||||
</Layout>
|
</Layout>
|
||||||
<SubComponents>
|
<SubComponents>
|
||||||
<Component class="javax.swing.JButton" name="bnOpen">
|
<Component class="javax.swing.JButton" name="openSingleUserCaseButton">
|
||||||
<Properties>
|
<Properties>
|
||||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="MultiUserCasesPanel.bnOpen.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="OpenMultiUserCasePanel.openSingleUserCaseButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
</Property>
|
|
||||||
<Property name="enabled" type="boolean" value="false"/>
|
|
||||||
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
|
||||||
<Dimension value="[80, 23]"/>
|
|
||||||
</Property>
|
|
||||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
|
||||||
<Dimension value="[80, 23]"/>
|
|
||||||
</Property>
|
|
||||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
|
||||||
<Dimension value="[80, 23]"/>
|
|
||||||
</Property>
|
|
||||||
</Properties>
|
|
||||||
<Events>
|
|
||||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnOpenActionPerformed"/>
|
|
||||||
</Events>
|
|
||||||
</Component>
|
|
||||||
<Component class="javax.swing.JButton" name="bnOpenSingleUserCase">
|
|
||||||
<Properties>
|
|
||||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
|
||||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="MultiUserCasesPanel.bnOpenSingleUserCase.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
|
||||||
</Property>
|
</Property>
|
||||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
<Dimension value="[156, 23]"/>
|
<Dimension value="[156, 23]"/>
|
||||||
@ -91,13 +71,13 @@
|
|||||||
</Property>
|
</Property>
|
||||||
</Properties>
|
</Properties>
|
||||||
<Events>
|
<Events>
|
||||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnOpenSingleUserCaseActionPerformed"/>
|
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="openSingleUserCaseButtonActionPerformed"/>
|
||||||
</Events>
|
</Events>
|
||||||
</Component>
|
</Component>
|
||||||
<Component class="javax.swing.JButton" name="cancelButton">
|
<Component class="javax.swing.JButton" name="cancelButton">
|
||||||
<Properties>
|
<Properties>
|
||||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="MultiUserCasesPanel.cancelButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="OpenMultiUserCasePanel.cancelButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
</Property>
|
</Property>
|
||||||
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
<Dimension value="[80, 23]"/>
|
<Dimension value="[80, 23]"/>
|
||||||
@ -116,7 +96,7 @@
|
|||||||
<Component class="javax.swing.JLabel" name="searchLabel">
|
<Component class="javax.swing.JLabel" name="searchLabel">
|
||||||
<Properties>
|
<Properties>
|
||||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="MultiUserCasesPanel.searchLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="OpenMultiUserCasePanel.searchLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
</Property>
|
</Property>
|
||||||
</Properties>
|
</Properties>
|
||||||
</Component>
|
</Component>
|
||||||
@ -124,5 +104,15 @@
|
|||||||
|
|
||||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||||
</Container>
|
</Container>
|
||||||
|
<Component class="javax.swing.JButton" name="openSelectedCaseButton">
|
||||||
|
<Properties>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="OpenMultiUserCasePanel.openSelectedCaseButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
<Events>
|
||||||
|
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="openSelectedCaseButtonActionPerformed"/>
|
||||||
|
</Events>
|
||||||
|
</Component>
|
||||||
</SubComponents>
|
</SubComponents>
|
||||||
</Form>
|
</Form>
|
@ -0,0 +1,194 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2017-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 javax.swing.JDialog;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.event.ListSelectionEvent;
|
||||||
|
import org.openide.explorer.ExplorerManager;
|
||||||
|
import org.openide.nodes.Node;
|
||||||
|
import org.openide.util.Lookup;
|
||||||
|
import org.sleuthkit.autopsy.coordinationservice.CaseNodeData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A JPanel that allows a user to open a multi-user case.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("PMD.SingularField") // Matisse-generated UI widgets cause lots of false positives
|
||||||
|
final class OpenMultiUserCasePanel extends JPanel {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
private final JDialog parentDialog;
|
||||||
|
private final MultiUserCasesBrowserPanel caseBrowserPanel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a JPanel that allows a user to open a multi-user case.
|
||||||
|
*
|
||||||
|
* @param parentDialog The parent dialog of the panel, may be null. If
|
||||||
|
* provided, the dialog is hidden when this poanel's
|
||||||
|
* cancel button is pressed.
|
||||||
|
*/
|
||||||
|
OpenMultiUserCasePanel(JDialog parentDialog) {
|
||||||
|
this.parentDialog = parentDialog;
|
||||||
|
initComponents(); // Machine generated code
|
||||||
|
caseBrowserPanel = new MultiUserCasesBrowserPanel();
|
||||||
|
caseExplorerScrollPane.add(caseBrowserPanel);
|
||||||
|
caseExplorerScrollPane.setViewportView(caseBrowserPanel);
|
||||||
|
openSelectedCaseButton.setEnabled(false);
|
||||||
|
caseBrowserPanel.addListSelectionListener((ListSelectionEvent event) -> {
|
||||||
|
openSelectedCaseButton.setEnabled(caseBrowserPanel.getExplorerManager().getSelectedNodes().length > 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refreshes the child component that displays the multi-user cases known to
|
||||||
|
* the coordination service..
|
||||||
|
*/
|
||||||
|
void refreshDisplay() {
|
||||||
|
caseBrowserPanel.refreshCases();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called from within the constructor to initialize the form.
|
||||||
|
* WARNING: Do NOT modify this code. The content of this method is always
|
||||||
|
* regenerated by the Form Editor.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||||
|
private void initComponents() {
|
||||||
|
|
||||||
|
openSingleUserCaseButton = new javax.swing.JButton();
|
||||||
|
cancelButton = new javax.swing.JButton();
|
||||||
|
searchLabel = new javax.swing.JLabel();
|
||||||
|
caseExplorerScrollPane = new javax.swing.JScrollPane();
|
||||||
|
openSelectedCaseButton = new javax.swing.JButton();
|
||||||
|
|
||||||
|
setName("Completed Cases"); // NOI18N
|
||||||
|
setPreferredSize(new java.awt.Dimension(960, 485));
|
||||||
|
|
||||||
|
org.openide.awt.Mnemonics.setLocalizedText(openSingleUserCaseButton, org.openide.util.NbBundle.getMessage(OpenMultiUserCasePanel.class, "OpenMultiUserCasePanel.openSingleUserCaseButton.text")); // NOI18N
|
||||||
|
openSingleUserCaseButton.setMinimumSize(new java.awt.Dimension(156, 23));
|
||||||
|
openSingleUserCaseButton.setPreferredSize(new java.awt.Dimension(156, 23));
|
||||||
|
openSingleUserCaseButton.addActionListener(new java.awt.event.ActionListener() {
|
||||||
|
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||||
|
openSingleUserCaseButtonActionPerformed(evt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
org.openide.awt.Mnemonics.setLocalizedText(cancelButton, org.openide.util.NbBundle.getMessage(OpenMultiUserCasePanel.class, "OpenMultiUserCasePanel.cancelButton.text")); // NOI18N
|
||||||
|
cancelButton.setMaximumSize(new java.awt.Dimension(80, 23));
|
||||||
|
cancelButton.setMinimumSize(new java.awt.Dimension(80, 23));
|
||||||
|
cancelButton.setPreferredSize(new java.awt.Dimension(80, 23));
|
||||||
|
cancelButton.addActionListener(new java.awt.event.ActionListener() {
|
||||||
|
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||||
|
cancelButtonActionPerformed(evt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
org.openide.awt.Mnemonics.setLocalizedText(searchLabel, org.openide.util.NbBundle.getMessage(OpenMultiUserCasePanel.class, "OpenMultiUserCasePanel.searchLabel.text")); // NOI18N
|
||||||
|
|
||||||
|
org.openide.awt.Mnemonics.setLocalizedText(openSelectedCaseButton, org.openide.util.NbBundle.getMessage(OpenMultiUserCasePanel.class, "OpenMultiUserCasePanel.openSelectedCaseButton.text")); // NOI18N
|
||||||
|
openSelectedCaseButton.addActionListener(new java.awt.event.ActionListener() {
|
||||||
|
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||||
|
openSelectedCaseButtonActionPerformed(evt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||||
|
this.setLayout(layout);
|
||||||
|
layout.setHorizontalGroup(
|
||||||
|
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
|
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
|
||||||
|
.addContainerGap()
|
||||||
|
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
|
||||||
|
.addComponent(caseExplorerScrollPane)
|
||||||
|
.addGroup(layout.createSequentialGroup()
|
||||||
|
.addComponent(searchLabel)
|
||||||
|
.addGap(32, 32, 32)
|
||||||
|
.addComponent(openSingleUserCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 172, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 96, Short.MAX_VALUE)
|
||||||
|
.addComponent(openSelectedCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 160, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
|
.addComponent(cancelButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
|
||||||
|
.addContainerGap())
|
||||||
|
);
|
||||||
|
|
||||||
|
layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {cancelButton, openSelectedCaseButton, openSingleUserCaseButton});
|
||||||
|
|
||||||
|
layout.setVerticalGroup(
|
||||||
|
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
|
.addGroup(layout.createSequentialGroup()
|
||||||
|
.addGap(6, 6, 6)
|
||||||
|
.addComponent(caseExplorerScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 450, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
|
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||||
|
.addComponent(cancelButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
|
.addComponent(openSingleUserCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
|
.addComponent(searchLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||||
|
.addComponent(openSelectedCaseButton))
|
||||||
|
.addContainerGap())
|
||||||
|
);
|
||||||
|
|
||||||
|
layout.linkSize(javax.swing.SwingConstants.VERTICAL, new java.awt.Component[] {cancelButton, openSelectedCaseButton, openSingleUserCaseButton});
|
||||||
|
|
||||||
|
}// </editor-fold>//GEN-END:initComponents
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the standard open single user case window.
|
||||||
|
*
|
||||||
|
* @param evt An ActionEvent, unused.
|
||||||
|
*/
|
||||||
|
private void openSingleUserCaseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_openSingleUserCaseButtonActionPerformed
|
||||||
|
Lookup.getDefault().lookup(CaseOpenAction.class).openCaseSelectionWindow();
|
||||||
|
}//GEN-LAST:event_openSingleUserCaseButtonActionPerformed
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the parent open multi-user case dialog.
|
||||||
|
*
|
||||||
|
* @param evt An ActionEvent, unused.
|
||||||
|
*/
|
||||||
|
private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed
|
||||||
|
if (parentDialog != null) {
|
||||||
|
parentDialog.setVisible(false);
|
||||||
|
}
|
||||||
|
}//GEN-LAST:event_cancelButtonActionPerformed
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the multi-user case selected in the child multi-user case browser
|
||||||
|
* panel.
|
||||||
|
*
|
||||||
|
* @param evt An ActionEvent, unused.
|
||||||
|
*/
|
||||||
|
private void openSelectedCaseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_openSelectedCaseButtonActionPerformed
|
||||||
|
ExplorerManager explorerManager = caseBrowserPanel.getExplorerManager();
|
||||||
|
Node[] selectedNodes = explorerManager.getSelectedNodes();
|
||||||
|
if (selectedNodes.length > 0 && selectedNodes[0] instanceof MultiUserCaseNode) {
|
||||||
|
MultiUserCaseNode caseNode = (MultiUserCaseNode) selectedNodes[0];
|
||||||
|
CaseNodeData nodeData = caseNode.getCaseNodeData();
|
||||||
|
new OpenMultiUserCaseAction(nodeData).actionPerformed(evt);
|
||||||
|
}
|
||||||
|
}//GEN-LAST:event_openSelectedCaseButtonActionPerformed
|
||||||
|
|
||||||
|
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||||
|
private javax.swing.JButton cancelButton;
|
||||||
|
private javax.swing.JScrollPane caseExplorerScrollPane;
|
||||||
|
private javax.swing.JButton openSelectedCaseButton;
|
||||||
|
private javax.swing.JButton openSingleUserCaseButton;
|
||||||
|
private javax.swing.JLabel searchLabel;
|
||||||
|
// End of variables declaration//GEN-END:variables
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2011-2017 Basis Technology Corp.
|
* Copyright 2017-2019 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -18,69 +18,115 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.coordinationservice;
|
package org.sleuthkit.autopsy.coordinationservice;
|
||||||
|
|
||||||
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.Paths;
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.util.Date;
|
||||||
|
import org.sleuthkit.autopsy.casemodule.CaseMetadata;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An object that converts case data for a case directory coordination service
|
* An object that converts data for a case directory lock coordination service
|
||||||
* node to and from byte arrays.
|
* node to and from byte arrays.
|
||||||
*/
|
*/
|
||||||
public final class CaseNodeData {
|
public final class CaseNodeData {
|
||||||
|
|
||||||
private static final int CURRENT_VERSION = 0;
|
private static final int CURRENT_VERSION = 1;
|
||||||
|
|
||||||
private int version;
|
/*
|
||||||
|
* Version 0 fields.
|
||||||
|
*/
|
||||||
|
private final int version;
|
||||||
private boolean errorsOccurred;
|
private boolean errorsOccurred;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Version 1 fields.
|
||||||
|
*/
|
||||||
|
private Path directory;
|
||||||
|
private Date createDate;
|
||||||
|
private Date lastAccessDate;
|
||||||
|
private String name;
|
||||||
|
private String displayName;
|
||||||
|
private short deletedItemFlags;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the current version of the case directory coordination service node
|
* Gets the current version of the case directory lock coordination service
|
||||||
* data.
|
* node data.
|
||||||
*
|
*
|
||||||
* @return The version number.
|
* @return The version number.
|
||||||
*/
|
*/
|
||||||
public static int getCurrentVersion() {
|
public static int getCurrentVersion() {
|
||||||
return CaseNodeData.CURRENT_VERSION;
|
return CaseNodeData.CURRENT_VERSION;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses a CaseMetadata object to construct an object that converts data for
|
||||||
|
* a case directory lock coordination service node to and from byte arrays.
|
||||||
|
*
|
||||||
|
* @param metadata The case meta data.
|
||||||
|
*
|
||||||
|
* @throws java.text.ParseException If there is an error parsing dates from
|
||||||
|
* string representations of dates in the
|
||||||
|
* meta data.
|
||||||
|
*/
|
||||||
|
public CaseNodeData(CaseMetadata metadata) throws ParseException {
|
||||||
|
this.version = CURRENT_VERSION;
|
||||||
|
this.errorsOccurred = false;
|
||||||
|
this.directory = Paths.get(metadata.getCaseDirectory());
|
||||||
|
this.createDate = CaseMetadata.getDateFormat().parse(metadata.getCreatedDate());
|
||||||
|
this.lastAccessDate = new Date();
|
||||||
|
this.name = metadata.getCaseName();
|
||||||
|
this.displayName = metadata.getCaseDisplayName();
|
||||||
|
this.deletedItemFlags = 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Uses coordination service node data to construct an object that converts
|
* Uses coordination service node data to construct an object that converts
|
||||||
* case data for a case directory coordination service node to and from byte
|
* data for a case directory lock coordination service node to and from byte
|
||||||
* arrays.
|
* arrays.
|
||||||
*
|
*
|
||||||
* @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) {
|
||||||
this.version = CURRENT_VERSION;
|
throw new IOException(null == nodeData ? "Null node data byte array" : "Zero-length node data byte array");
|
||||||
this.errorsOccurred = false;
|
}
|
||||||
|
DataInputStream inputStream = new DataInputStream(new ByteArrayInputStream(nodeData));
|
||||||
|
this.version = inputStream.readInt();
|
||||||
|
if (this.version > 0) {
|
||||||
|
this.errorsOccurred = inputStream.readBoolean();
|
||||||
} else {
|
} else {
|
||||||
/*
|
short legacyErrorsOccurred = inputStream.readByte();
|
||||||
* Get fields from node data.
|
this.errorsOccurred = (legacyErrorsOccurred < 0);
|
||||||
*/
|
}
|
||||||
ByteBuffer buffer = ByteBuffer.wrap(nodeData);
|
if (this.version > 0) {
|
||||||
try {
|
this.directory = Paths.get(inputStream.readUTF());
|
||||||
if (buffer.hasRemaining()) {
|
this.createDate = new Date(inputStream.readLong());
|
||||||
this.version = buffer.getInt();
|
this.lastAccessDate = new Date(inputStream.readLong());
|
||||||
|
this.name = inputStream.readUTF();
|
||||||
/*
|
this.displayName = inputStream.readUTF();
|
||||||
* Flags bit format: 76543210
|
this.deletedItemFlags = inputStream.readShort();
|
||||||
* 0-6 --> reserved for future use
|
|
||||||
* 7 --> errorsOccurred
|
|
||||||
*/
|
|
||||||
byte flags = buffer.get();
|
|
||||||
this.errorsOccurred = (flags < 0);
|
|
||||||
}
|
|
||||||
} catch (BufferUnderflowException ex) {
|
|
||||||
throw new InvalidDataException("Node data is incomplete", ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets whether or not any errors occurred during the processing of the job.
|
* Gets the node data version number of this node.
|
||||||
|
*
|
||||||
|
* @return The version number.
|
||||||
|
*/
|
||||||
|
public int getVersion() {
|
||||||
|
return this.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets whether or not any errors occurred during the processing of any auto
|
||||||
|
* ingest job for the case represented by this node data.
|
||||||
*
|
*
|
||||||
* @return True or false.
|
* @return True or false.
|
||||||
*/
|
*/
|
||||||
@ -89,7 +135,8 @@ public final class CaseNodeData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets whether or not any errors occurred during the processing of job.
|
* Sets whether or not any errors occurred during the processing of any auto
|
||||||
|
* ingest job for the case represented by this node data.
|
||||||
*
|
*
|
||||||
* @param errorsOccurred True or false.
|
* @param errorsOccurred True or false.
|
||||||
*/
|
*/
|
||||||
@ -98,32 +145,121 @@ public final class CaseNodeData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the node data version number.
|
* Gets the path of the case directory of the case represented by this node
|
||||||
|
* data.
|
||||||
*
|
*
|
||||||
* @return The version number.
|
* @return The case directory path.
|
||||||
*/
|
*/
|
||||||
public int getVersion() {
|
public Path getDirectory() {
|
||||||
return this.version;
|
return this.directory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the path of the case directory of the case represented by this node
|
||||||
|
* data.
|
||||||
|
*
|
||||||
|
* @param caseDirectory The case directory path.
|
||||||
|
*/
|
||||||
|
public void setDirectory(Path caseDirectory) {
|
||||||
|
this.directory = caseDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the date the case represented by this node data was created.
|
||||||
|
*
|
||||||
|
* @return The create date.
|
||||||
|
*/
|
||||||
|
public Date getCreateDate() {
|
||||||
|
return new Date(this.createDate.getTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the date the case represented by this node data was created.
|
||||||
|
*
|
||||||
|
* @param createDate The create date.
|
||||||
|
*/
|
||||||
|
public void setCreateDate(Date createDate) {
|
||||||
|
this.createDate = new Date(createDate.getTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the date the case represented by this node data last accessed.
|
||||||
|
*
|
||||||
|
* @return The last access date.
|
||||||
|
*/
|
||||||
|
public Date getLastAccessDate() {
|
||||||
|
return new Date(this.lastAccessDate.getTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the date the case represented by this node data was last accessed.
|
||||||
|
*
|
||||||
|
* @param lastAccessDate The last access date.
|
||||||
|
*/
|
||||||
|
public void setLastAccessDate(Date lastAccessDate) {
|
||||||
|
this.lastAccessDate = new Date(lastAccessDate.getTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the unique and immutable (user cannot change it) name of the case
|
||||||
|
* represented by this node data.
|
||||||
|
*
|
||||||
|
* @return The case name.
|
||||||
|
*/
|
||||||
|
public String getName() {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the unique and immutable (user cannot change it) name of the case
|
||||||
|
* represented by this node data.
|
||||||
|
*
|
||||||
|
* @param name The case name.
|
||||||
|
*/
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the display name of the case represented by this node data.
|
||||||
|
*
|
||||||
|
* @return The case display name.
|
||||||
|
*/
|
||||||
|
public String getDisplayName() {
|
||||||
|
return this.displayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the display name of the case represented by this node data.
|
||||||
|
*
|
||||||
|
* @param displayName The case display name.
|
||||||
|
*/
|
||||||
|
public void setDisplayName(String displayName) {
|
||||||
|
this.displayName = displayName;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the node data as a byte array that can be sent to the coordination
|
* Gets the node data as a byte array that can be sent to the coordination
|
||||||
* 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 {
|
||||||
ByteBuffer buffer = ByteBuffer.allocate(5);
|
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
|
||||||
|
DataOutputStream outputStream = new DataOutputStream(byteStream);
|
||||||
buffer.putInt(this.version);
|
outputStream.writeInt(this.version);
|
||||||
buffer.put((byte)(this.errorsOccurred ? 0x80 : 0));
|
outputStream.writeBoolean(this.errorsOccurred);
|
||||||
|
outputStream.writeUTF(this.directory.toString());
|
||||||
// Prepare the array
|
outputStream.writeLong(this.createDate.getTime());
|
||||||
byte[] array = new byte[buffer.position()];
|
outputStream.writeLong(this.lastAccessDate.getTime());
|
||||||
buffer.rewind();
|
outputStream.writeUTF(this.name);
|
||||||
buffer.get(array, 0, array.length);
|
outputStream.writeUTF(this.displayName);
|
||||||
|
outputStream.writeShort(this.deletedItemFlags);
|
||||||
return array;
|
outputStream.flush();
|
||||||
|
byteStream.flush();
|
||||||
|
return byteStream.toByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public final static class InvalidDataException extends Exception {
|
public final static class InvalidDataException extends Exception {
|
||||||
|
@ -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();
|
||||||
|
@ -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,8 +1517,8 @@ 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 set error flag in case node data for %s", caseDirectoryPath), ex);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
job.setErrorsOccurred(false);
|
job.setErrorsOccurred(false);
|
||||||
@ -2012,7 +2012,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) {
|
||||||
@ -2213,7 +2213,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);
|
||||||
@ -2301,7 +2301,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;
|
||||||
@ -2481,7 +2481,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;
|
||||||
@ -2520,7 +2520,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;
|
||||||
}
|
}
|
||||||
@ -2577,7 +2577,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);
|
||||||
@ -2611,7 +2611,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);
|
||||||
@ -2693,7 +2693,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();
|
||||||
@ -2755,7 +2755,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);
|
||||||
@ -2893,7 +2893,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);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user