19 package org.sleuthkit.autopsy.casemodule;
21 import com.google.common.annotations.Beta;
23 import java.awt.Frame;
24 import java.awt.event.ActionEvent;
25 import java.awt.event.ActionListener;
26 import java.beans.PropertyChangeListener;
27 import java.beans.PropertyChangeSupport;
29 import java.io.IOException;
30 import java.nio.file.InvalidPathException;
31 import java.nio.file.Path;
32 import java.nio.file.Paths;
33 import java.sql.Connection;
34 import java.sql.DriverManager;
35 import java.sql.ResultSet;
36 import java.sql.SQLException;
37 import java.sql.Statement;
38 import java.text.ParseException;
39 import java.text.SimpleDateFormat;
40 import java.util.Collection;
41 import java.util.Date;
42 import java.util.HashMap;
43 import java.util.HashSet;
44 import java.util.List;
47 import java.util.TimeZone;
48 import java.util.UUID;
49 import java.util.concurrent.CancellationException;
50 import java.util.concurrent.ExecutionException;
51 import java.util.concurrent.ExecutorService;
52 import java.util.concurrent.Executors;
53 import java.util.concurrent.Future;
54 import java.util.concurrent.ThreadFactory;
55 import java.util.concurrent.TimeUnit;
56 import java.util.logging.Level;
57 import java.util.stream.Collectors;
58 import java.util.stream.Stream;
59 import javax.annotation.concurrent.GuardedBy;
60 import javax.annotation.concurrent.ThreadSafe;
61 import javax.swing.JOptionPane;
62 import javax.swing.SwingUtilities;
63 import org.openide.util.Lookup;
64 import org.openide.util.NbBundle;
65 import org.openide.util.NbBundle.Messages;
66 import org.openide.util.actions.CallableSystemAction;
67 import org.openide.windows.WindowManager;
125 import org.
sleuthkit.datamodel.TskUnsupportedSchemaVersionException;
165 WindowManager.getDefault().invokeWhenUIReady(
new Runnable() {
168 mainFrame = WindowManager.getDefault().getMainWindow();
191 if (typeName != null) {
193 if (typeName.equalsIgnoreCase(c.toString())) {
217 "Case_caseType_singleUser=Single-user case",
218 "Case_caseType_multiUser=Multi-user case"
221 if (fromString(typeName) == SINGLE_USER_CASE) {
222 return Bundle.Case_caseType_singleUser();
224 return Bundle.Case_caseType_multiUser();
234 this.typeName = typeName;
249 return (otherTypeName == null) ?
false : typeName.equals(otherTypeName);
406 .map(Events::toString)
407 .collect(Collectors.toSet()), listener);
418 .map(Events::toString)
419 .collect(Collectors.toSet()), listener);
442 eventTypes.forEach((
Events event) -> {
487 eventTypes.forEach((
Events event) -> {
501 return !(caseName.contains(
"\\") || caseName.contains(
"/") || caseName.contains(
":")
502 || caseName.contains(
"*") || caseName.contains(
"?") || caseName.contains(
"\"")
503 || caseName.contains(
"<") || caseName.contains(
">") || caseName.contains(
"|"));
555 "Case.exceptionMessage.emptyCaseName=Must specify a case name.",
556 "Case.exceptionMessage.emptyCaseDir=Must specify a case directory path."
560 throw new CaseActionException(Bundle.Case_exceptionMessage_emptyCaseName());
562 if (caseDir.isEmpty()) {
563 throw new CaseActionException(Bundle.Case_exceptionMessage_emptyCaseDir());
582 "# {0} - exception message",
"Case.exceptionMessage.failedToReadMetadata=Failed to read case metadata:\n{0}.",
583 "Case.exceptionMessage.cannotOpenMultiUserCaseNoSettings=Multi-user settings are missing (see Tools, Options, Multi-user tab), cannot open a multi-user case."
588 metadata =
new CaseMetadata(Paths.get(caseMetadataFilePath));
590 throw new CaseActionException(Bundle.Case_exceptionMessage_failedToReadMetadata(ex.getLocalizedMessage()), ex);
593 throw new CaseActionException(Bundle.Case_exceptionMessage_cannotOpenMultiUserCaseNoSettings());
604 return currentCase != null;
621 throw new IllegalStateException(NbBundle.getMessage(
Case.class,
"Case.getCurCase.exception.noneOpen"), ex);
644 if (openCase == null) {
645 throw new NoCurrentCaseException(NbBundle.getMessage(
Case.class,
"Case.getCurCase.exception.noneOpen"));
660 "# {0} - exception message",
"Case.closeException.couldNotCloseCase=Error closing case: {0}",
661 "Case.progressIndicatorTitle.closingCase=Closing Case"
665 if (null == currentCase) {
671 logger.log(Level.INFO,
"Closing current case {0} ({1}) in {2}",
new Object[]{closedCase.getDisplayName(), closedCase.getName(), closedCase.getCaseDirectory()});
674 logger.log(Level.INFO,
"Closed current case {0} ({1}) in {2}",
new Object[]{closedCase.getDisplayName(), closedCase.getName(), closedCase.getCaseDirectory()});
675 }
catch (CaseActionException ex) {
696 if (null == currentCase) {
717 "Case.progressIndicatorTitle.deletingCase=Deleting Case",
718 "Case.exceptionMessage.cannotDeleteCurrentCase=Cannot delete current case, it must be closed first.",
719 "# {0} - case display name",
"Case.exceptionMessage.deletionInterrupted=Deletion of the case {0} was cancelled."
723 if (null != currentCase) {
724 throw new CaseActionException(Bundle.Case_exceptionMessage_cannotDeleteCurrentCase());
734 progressIndicator.
start(Bundle.Case_progressMessage_preparing());
741 }
catch (InterruptedException ex) {
747 throw new CaseActionException(Bundle.Case_exceptionMessage_deletionInterrupted(metadata.
getCaseDisplayName()), ex);
751 progressIndicator.
finish();
766 "Case.exceptionMessage.cannotLocateMainWindow=Cannot locate main application window"
770 if (null != currentCase) {
773 }
catch (CaseActionException ex) {
782 logger.log(Level.INFO,
"Opening {0} ({1}) in {2} as the current case",
new Object[]{newCurrentCase.getDisplayName(), newCurrentCase.getName(), newCurrentCase.getCaseDirectory()});
783 newCurrentCase.
open(isNewCase);
784 currentCase = newCurrentCase;
785 logger.log(Level.INFO,
"Opened {0} ({1}) in {2} as the current case",
new Object[]{newCurrentCase.getDisplayName(), newCurrentCase.getName(), newCurrentCase.getCaseDirectory()});
791 logger.log(Level.INFO, String.format(
"Cancelled opening %s (%s) in %s as the current case", newCurrentCase.
getDisplayName(), newCurrentCase.
getName(), newCurrentCase.
getCaseDirectory()));
793 }
catch (CaseActionException ex) {
794 logger.log(Level.SEVERE, String.format(
"Error opening %s (%s) in %s as the current case", newCurrentCase.
getDisplayName(), newCurrentCase.
getName(), newCurrentCase.
getCaseDirectory()), ex);
812 String uniqueCaseName = caseDisplayName.replaceAll(
"[^\\p{ASCII}]",
"_");
817 uniqueCaseName = uniqueCaseName.replaceAll(
"[\\p{Cntrl}]",
"_");
822 uniqueCaseName = uniqueCaseName.replaceAll(
"[ /?:'\"\\\\]",
"_");
827 uniqueCaseName = uniqueCaseName.toLowerCase();
832 SimpleDateFormat dateFormat =
new SimpleDateFormat(
"yyyyMMdd_HHmmss");
833 Date date =
new Date();
834 uniqueCaseName = uniqueCaseName +
"_" + dateFormat.format(date);
836 return uniqueCaseName;
853 File caseDir =
new File(caseDirPath);
854 if (caseDir.exists()) {
855 if (caseDir.isFile()) {
856 throw new CaseActionException(NbBundle.getMessage(
Case.class,
"Case.createCaseDir.exception.existNotDir", caseDirPath));
857 }
else if (!caseDir.canRead() || !caseDir.canWrite()) {
858 throw new CaseActionException(NbBundle.getMessage(
Case.class,
"Case.createCaseDir.exception.existCantRW", caseDirPath));
865 if (!caseDir.mkdirs()) {
866 throw new CaseActionException(NbBundle.getMessage(
Case.class,
"Case.createCaseDir.exception.cantCreate", caseDirPath));
874 String hostPathComponent =
"";
879 Path exportDir = Paths.get(caseDirPath, hostPathComponent, EXPORT_FOLDER);
880 if (!exportDir.toFile().mkdirs()) {
881 throw new CaseActionException(NbBundle.getMessage(
Case.class,
"Case.createCaseDir.exception.cantCreateCaseDir", exportDir));
884 Path logsDir = Paths.get(caseDirPath, hostPathComponent, LOG_FOLDER);
885 if (!logsDir.toFile().mkdirs()) {
886 throw new CaseActionException(NbBundle.getMessage(
Case.class,
"Case.createCaseDir.exception.cantCreateCaseDir", logsDir));
889 Path tempDir = Paths.get(caseDirPath, hostPathComponent, TEMP_FOLDER);
890 if (!tempDir.toFile().mkdirs()) {
891 throw new CaseActionException(NbBundle.getMessage(
Case.class,
"Case.createCaseDir.exception.cantCreateCaseDir", tempDir));
894 Path cacheDir = Paths.get(caseDirPath, hostPathComponent, CACHE_FOLDER);
895 if (!cacheDir.toFile().mkdirs()) {
896 throw new CaseActionException(NbBundle.getMessage(
Case.class,
"Case.createCaseDir.exception.cantCreateCaseDir", cacheDir));
899 Path moduleOutputDir = Paths.get(caseDirPath, hostPathComponent, MODULE_FOLDER);
900 if (!moduleOutputDir.toFile().mkdirs()) {
901 throw new CaseActionException(NbBundle.getMessage(
Case.class,
"Case.createCaseDir.exception.cantCreateModDir", moduleOutputDir));
904 Path reportsDir = Paths.get(caseDirPath, hostPathComponent, REPORTS_FOLDER);
905 if (!reportsDir.toFile().mkdirs()) {
906 throw new CaseActionException(NbBundle.getMessage(
Case.class,
"Case.createCaseDir.exception.cantCreateReportsDir", reportsDir));
917 static Map<Long, String> getImagePaths(SleuthkitCase db) {
918 Map<Long, String> imgPaths =
new HashMap<>();
920 Map<Long, List<String>> imgPathsList = db.getImagePaths();
921 for (Map.Entry<Long, List<String>> entry : imgPathsList.entrySet()) {
922 if (entry.getValue().size() > 0) {
923 imgPaths.put(entry.getKey(), entry.getValue().get(0));
926 }
catch (TskCoreException ex) {
927 logger.log(Level.SEVERE,
"Error getting image paths", ex);
943 "Case.creationException.couldNotAcquireResourcesLock=Failed to get lock on case resources"
947 Path caseDirPath = Paths.get(caseDir);
951 }
catch (InterruptedException ex) {
954 throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireResourcesLock(), ex);
973 SwingUtilities.invokeLater(() -> {
979 String backupDbPath = caseDb.getBackupDatabasePath();
980 if (null != backupDbPath) {
981 JOptionPane.showMessageDialog(
983 NbBundle.getMessage(
Case.class,
"Case.open.msgDlg.updated.msg", backupDbPath),
984 NbBundle.getMessage(
Case.class,
"Case.open.msgDlg.updated.title"),
985 JOptionPane.INFORMATION_MESSAGE);
993 Map<Long, String> imgPaths = getImagePaths(caseDb);
994 for (Map.Entry<Long, String> entry : imgPaths.entrySet()) {
995 long obj_id = entry.getKey();
996 String path = entry.getValue();
999 int response = JOptionPane.showConfirmDialog(
1001 NbBundle.getMessage(
Case.class,
"Case.checkImgExist.confDlg.doesntExist.msg", path),
1002 NbBundle.getMessage(
Case.class,
"Case.checkImgExist.confDlg.doesntExist.title"),
1003 JOptionPane.YES_NO_OPTION);
1004 if (response == JOptionPane.YES_OPTION) {
1005 MissingImageDialog.makeDialog(obj_id, caseDb);
1007 logger.log(Level.SEVERE,
"User proceeding with missing image files");
1018 CallableSystemAction.get(CaseDetailsAction.class).setEnabled(
true);
1020 CallableSystemAction.get(CaseDeleteAction.class).setEnabled(
true);
1031 RecentCases.getInstance().addRecentCase(newCurrentCase.
getDisplayName(), newCurrentCase.getMetadata().getFilePath().toString());
1041 if (newCurrentCase.
hasData()) {
1060 SwingUtilities.invokeLater(() -> {
1070 CallableSystemAction.get(
AddImageAction.class).setEnabled(
false);
1072 CallableSystemAction.get(CaseDetailsAction.class).setEnabled(
false);
1074 CallableSystemAction.get(CaseDeleteAction.class).setEnabled(
false);
1099 File tempFolder =
new File(tempSubDirPath);
1100 if (tempFolder.isDirectory()) {
1101 File[] files = tempFolder.listFiles();
1102 if (files.length > 0) {
1103 for (File file : files) {
1104 if (file.isDirectory()) {
1236 hostPath = Paths.get(caseDirectory);
1238 if (!hostPath.toFile().exists()) {
1239 hostPath.toFile().mkdirs();
1241 return hostPath.toString();
1324 return path.subpath(path.getNameCount() - 2, path.getNameCount()).toString();
1326 return path.subpath(path.getNameCount() - 1, path.getNameCount()).toString();
1340 List<Content> list = caseDb.getRootObjects();
1341 hasDataSources = (list.size() > 0);
1351 Set<TimeZone> timezones =
new HashSet<>();
1354 final Content dataSource = c.getDataSource();
1355 if ((dataSource != null) && (dataSource instanceof Image)) {
1356 Image image = (Image) dataSource;
1357 timezones.add(TimeZone.getTimeZone(image.getTimeZone()));
1360 }
catch (TskCoreException ex) {
1361 logger.log(Level.SEVERE,
"Error getting data source time zones", ex);
1383 if (!hasDataSources) {
1386 }
catch (TskCoreException ex) {
1387 logger.log(Level.SEVERE,
"Error accessing case database", ex);
1497 logger.log(Level.WARNING,
"Unable to send notifcation regarding comment change due to no current case being open", ex);
1534 public void addReport(String localPath, String srcModuleName, String reportName)
throws TskCoreException {
1535 addReport(localPath, srcModuleName, reportName, null);
1552 public Report
addReport(String localPath, String srcModuleName, String reportName, Content parent)
throws TskCoreException {
1553 String normalizedLocalPath;
1555 if (localPath.toLowerCase().contains(
"http:")) {
1556 normalizedLocalPath = localPath;
1558 normalizedLocalPath = Paths.get(localPath).normalize().toString();
1560 }
catch (InvalidPathException ex) {
1561 String errorMsg =
"Invalid local path provided: " + localPath;
1562 throw new TskCoreException(errorMsg, ex);
1564 Report report = this.caseDb.addReport(normalizedLocalPath, srcModuleName, reportName, parent);
1578 return this.caseDb.getAllReports();
1589 public void deleteReports(Collection<? extends Report> reports)
throws TskCoreException {
1590 for (Report report : reports) {
1591 this.caseDb.deleteReport(report);
1613 "Case.exceptionMessage.metadataUpdateError=Failed to update case metadata"
1615 void updateCaseDetails(CaseDetails caseDetails)
throws CaseActionException {
1618 metadata.setCaseDetails(caseDetails);
1619 }
catch (CaseMetadataException ex) {
1620 throw new CaseActionException(Bundle.Case_exceptionMessage_metadataUpdateError(), ex);
1624 CaseNodeData nodeData = CaseNodeData.readCaseNodeData(metadata.
getCaseDirectory());
1626 CaseNodeData.writeCaseNodeData(nodeData);
1627 }
catch (CaseNodeDataException | InterruptedException ex) {
1628 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotUpdateCaseNodeData(ex.getLocalizedMessage()), ex);
1631 if (!oldCaseDetails.getCaseNumber().equals(caseDetails.
getCaseNumber())) {
1632 eventPublisher.
publish(
new AutopsyEvent(Events.NUMBER.toString(), oldCaseDetails.getCaseNumber(), caseDetails.
getCaseNumber()));
1634 if (!oldCaseDetails.getExaminerName().equals(caseDetails.
getExaminerName())) {
1635 eventPublisher.
publish(
new AutopsyEvent(Events.NUMBER.toString(), oldCaseDetails.getExaminerName(), caseDetails.
getExaminerName()));
1638 eventPublisher.
publish(
new AutopsyEvent(Events.NAME.toString(), oldCaseDetails.getCaseDisplayName(), caseDetails.
getCaseDisplayName()));
1640 eventPublisher.
publish(
new AutopsyEvent(Events.CASE_DETAILS.toString(), oldCaseDetails, caseDetails));
1641 if (RuntimeProperties.runningWithGUI()) {
1642 SwingUtilities.invokeLater(() -> {
1645 RecentCases.getInstance().updateRecentCase(oldCaseDetails.getCaseDisplayName(), metadata.getFilePath().toString(), caseDetails.
getCaseDisplayName(), metadata.getFilePath().toString());
1646 }
catch (Exception ex) {
1647 logger.log(Level.SEVERE,
"Error updating case name in UI", ex);
1675 metadata = caseMetaData;
1694 "Case.progressIndicatorTitle.creatingCase=Creating Case",
1695 "Case.progressIndicatorTitle.openingCase=Opening Case",
1696 "Case.progressIndicatorCancelButton.label=Cancel",
1697 "Case.progressMessage.preparing=Preparing...",
1698 "Case.progressMessage.preparingToOpenCaseResources=<html>Preparing to open case resources.<br>This may take time if another user is upgrading the case.</html>",
1699 "Case.progressMessage.cancelling=Cancelling...",
1700 "Case.exceptionMessage.cancelledByUser=Cancelled by user.",
1701 "# {0} - exception message",
"Case.exceptionMessage.execExceptionWrapperMessage={0}"
1703 private void open(
boolean isNewCase)
throws CaseActionException {
1712 String progressIndicatorTitle = isNewCase ? Bundle.Case_progressIndicatorTitle_creatingCase() : Bundle.Case_progressIndicatorTitle_openingCase();
1715 progressIndicatorTitle,
1716 new String[]{Bundle.Case_progressIndicatorCancelButton_label()},
1717 Bundle.Case_progressIndicatorCancelButton_label(),
1718 cancelButtonListener);
1722 progressIndicator.
start(Bundle.Case_progressMessage_preparing());
1740 caseLockingExecutor = Executors.newSingleThreadExecutor(threadFactory);
1741 Future<Void> future = caseLockingExecutor.submit(() -> {
1743 open(isNewCase, progressIndicator);
1752 progressIndicator.
progress(Bundle.Case_progressMessage_preparingToOpenCaseResources());
1755 if (null == resourcesLock) {
1756 throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireResourcesLock());
1758 open(isNewCase, progressIndicator);
1759 }
catch (CaseActionException ex) {
1766 if (null != cancelButtonListener) {
1775 }
catch (InterruptedException discarded) {
1784 if (null != cancelButtonListener) {
1787 future.cancel(
true);
1790 }
catch (CancellationException discarded) {
1800 }
catch (ExecutionException ex) {
1809 throw new CaseActionException(Bundle.Case_exceptionMessage_execExceptionWrapperMessage(ex.getCause().getLocalizedMessage()), ex);
1811 progressIndicator.
finish();
1859 }
catch (CaseActionException ex) {
1868 }
catch (InterruptedException discarded) {
1870 close(progressIndicator);
1885 public SleuthkitCase
createPortableCase(String caseName, File portableCaseFolder)
throws TskCoreException {
1887 if (portableCaseFolder.exists()) {
1888 throw new TskCoreException(
"Portable case folder " + portableCaseFolder.toString() +
" already exists");
1890 if (!portableCaseFolder.mkdirs()) {
1891 throw new TskCoreException(
"Error creating portable case folder " + portableCaseFolder.toString());
1899 portableCaseMetadata.setCaseDatabaseName(SINGLE_USER_CASE_DB_NAME);
1901 throw new TskCoreException(
"Error creating case metadata", ex);
1905 SleuthkitCase portableSleuthkitCase;
1907 portableSleuthkitCase = SleuthkitCase.newCase(dbFilePath);
1909 return portableSleuthkitCase;
1922 if (Thread.currentThread().isInterrupted()) {
1923 throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser());
1941 "Case.progressMessage.creatingCaseDirectory=Creating case directory..."
1944 progressIndicator.
progress(Bundle.Case_progressMessage_creatingCaseDirectory());
1946 progressIndicator.
progress(Bundle.Case_progressMessage_creatingCaseDirectory());
1958 "Case.progressMessage.switchingLogDirectory=Switching log directory..."
1961 progressIndicator.
progress(Bundle.Case_progressMessage_switchingLogDirectory());
1977 "Case.progressMessage.savingCaseMetadata=Saving case metadata to file...",
1978 "# {0} - exception message",
"Case.exceptionMessage.couldNotSaveCaseMetadata=Failed to save case metadata:\n{0}."
1981 progressIndicator.
progress(Bundle.Case_progressMessage_savingCaseMetadata());
1983 this.metadata.writeToFile();
1985 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotSaveCaseMetadata(ex.getLocalizedMessage()), ex);
2001 "Case.progressMessage.creatingCaseNodeData=Creating coordination service node data...",
2002 "# {0} - exception message",
"Case.exceptionMessage.couldNotCreateCaseNodeData=Failed to create coordination service node data:\n{0}."
2006 progressIndicator.
progress(Bundle.Case_progressMessage_creatingCaseNodeData());
2010 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotCreateCaseNodeData(ex.getLocalizedMessage()), ex);
2027 "Case.progressMessage.updatingCaseNodeData=Updating coordination service node data...",
2028 "# {0} - exception message",
"Case.exceptionMessage.couldNotUpdateCaseNodeData=Failed to update coordination service node data:\n{0}."
2032 progressIndicator.
progress(Bundle.Case_progressMessage_updatingCaseNodeData());
2038 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotUpdateCaseNodeData(ex.getLocalizedMessage()), ex);
2049 "Case.progressMessage.clearingTempDirectory=Clearing case temp directory..."
2055 progressIndicator.
progress(Bundle.Case_progressMessage_clearingTempDirectory());
2071 "Case.progressMessage.creatingCaseDatabase=Creating case database...",
2072 "# {0} - exception message",
"Case.exceptionMessage.couldNotGetDbServerConnectionInfo=Failed to get case database server conneciton info:\n{0}.",
2073 "# {0} - exception message",
"Case.exceptionMessage.couldNotCreateCaseDatabase=Failed to create case database:\n{0}.",
2074 "# {0} - exception message",
"Case.exceptionMessage.couldNotSaveDbNameToMetadataFile=Failed to save case database name to case metadata file:\n{0}."
2077 progressIndicator.
progress(Bundle.Case_progressMessage_creatingCaseDatabase());
2086 metadata.setCaseDatabaseName(SINGLE_USER_CASE_DB_NAME);
2094 metadata.setCaseDatabaseName(caseDb.getDatabaseName());
2096 }
catch (TskCoreException ex) {
2097 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotCreateCaseDatabase(ex.getLocalizedMessage()), ex);
2099 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotGetDbServerConnectionInfo(ex.getLocalizedMessage()), ex);
2101 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotSaveDbNameToMetadataFile(ex.getLocalizedMessage()), ex);
2117 "Case.progressMessage.openingCaseDatabase=Opening case database...",
2118 "# {0} - exception message",
"Case.exceptionMessage.couldNotOpenCaseDatabase=Failed to open case database:\n{0}.",
2119 "# {0} - exception message",
"Case.exceptionMessage.unsupportedSchemaVersionMessage=Unsupported case database schema version:\n{0}.",
2120 "Case.open.exception.multiUserCaseNotEnabled=Cannot open a multi-user case if multi-user cases are not enabled. See Tools, Options, Multi-User."
2123 progressIndicator.
progress(Bundle.Case_progressMessage_openingCaseDatabase());
2127 caseDb = SleuthkitCase.openCase(Paths.get(metadata.
getCaseDirectory(), databaseName).toString());
2131 throw new CaseActionException(Case_open_exception_multiUserCaseNotEnabled());
2133 }
catch (TskUnsupportedSchemaVersionException ex) {
2134 throw new CaseActionException(Bundle.Case_exceptionMessage_unsupportedSchemaVersionMessage(ex.getLocalizedMessage()), ex);
2136 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotGetDbServerConnectionInfo(ex.getLocalizedMessage()), ex);
2137 }
catch (TskCoreException ex) {
2138 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotOpenCaseDatabase(ex.getLocalizedMessage()), ex);
2149 "Case.progressMessage.openingCaseLevelServices=Opening case-level services...",})
2151 progressIndicator.
progress(Bundle.Case_progressMessage_openingCaseLevelServices());
2152 this.caseServices =
new Services(caseDb);
2166 @NbBundle.Messages({
2167 "Case.progressMessage.openingApplicationServiceResources=Opening application service case resources...",
2168 "# {0} - service name",
"Case.serviceOpenCaseResourcesProgressIndicator.title={0} Opening Case Resources",
2169 "# {0} - service name",
"Case.serviceOpenCaseResourcesProgressIndicator.cancellingMessage=Cancelling opening case resources by {0}...",
2170 "# {0} - service name",
"Case.servicesException.notificationTitle={0} Error"
2181 progressIndicator.
progress(Bundle.Case_progressMessage_openingApplicationServiceResources());
2191 cancelButtonListener =
new CancelButtonListener(Bundle.Case_serviceOpenCaseResourcesProgressIndicator_cancellingMessage(service.getServiceName()));
2194 Bundle.Case_serviceOpenCaseResourcesProgressIndicator_title(service.getServiceName()),
2195 new String[]{Bundle.Case_progressIndicatorCancelButton_label()},
2196 Bundle.Case_progressIndicatorCancelButton_label(),
2197 cancelButtonListener);
2201 appServiceProgressIndicator.
start(Bundle.Case_progressMessage_preparing());
2203 String threadNameSuffix = service.getServiceName().replaceAll(
"[ ]",
"-");
2204 threadNameSuffix = threadNameSuffix.toLowerCase();
2206 ExecutorService executor = Executors.newSingleThreadExecutor(threadFactory);
2207 Future<Void> future = executor.submit(() -> {
2208 service.openCaseResources(context);
2211 if (null != cancelButtonListener) {
2223 }
catch (InterruptedException discarded) {
2228 future.cancel(
true);
2229 }
catch (CancellationException discarded) {
2237 }
catch (ExecutionException ex) {
2244 Case.
logger.log(Level.SEVERE, String.format(
"%s failed to open case resources for %s", service.getServiceName(), this.
getDisplayName()), ex);
2246 SwingUtilities.invokeLater(() -> {
2258 appServiceProgressIndicator.
finish();
2276 "Case.progressMessage.settingUpNetworkCommunications=Setting up network communications...",
2277 "# {0} - exception message",
"Case.exceptionMessage.couldNotOpenRemoteEventChannel=Failed to open remote events channel:\n{0}.",
2278 "# {0} - exception message",
"Case.exceptionMessage.couldNotCreatCollaborationMonitor=Failed to create collaboration monitor:\n{0}."
2282 progressIndicator.
progress(Bundle.Case_progressMessage_settingUpNetworkCommunications());
2286 collaborationMonitor =
new CollaborationMonitor(metadata.
getCaseName());
2288 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotOpenRemoteEventChannel(ex.getLocalizedMessage()), ex);
2289 }
catch (CollaborationMonitor.CollaborationMonitorException ex) {
2290 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotCreatCollaborationMonitor(ex.getLocalizedMessage()), ex);
2303 private void close() throws CaseActionException {
2312 Bundle.Case_progressIndicatorTitle_closingCase());
2316 progressIndicator.
start(Bundle.Case_progressMessage_preparing());
2325 Future<Void> future = caseLockingExecutor.submit(() -> {
2327 close(progressIndicator);
2334 progressIndicator.
progress(Bundle.Case_progressMessage_preparing());
2336 if (null == resourcesLock) {
2337 throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireResourcesLock());
2339 close(progressIndicator);
2353 }
catch (InterruptedException | CancellationException unused) {
2360 }
catch (ExecutionException ex) {
2361 throw new CaseActionException(Bundle.Case_exceptionMessage_execExceptionWrapperMessage(ex.getCause().getMessage()), ex);
2364 progressIndicator.
finish();
2374 "Case.progressMessage.shuttingDownNetworkCommunications=Shutting down network communications...",
2375 "Case.progressMessage.closingApplicationServiceResources=Closing case-specific application service resources...",
2376 "Case.progressMessage.closingCaseLevelServices=Closing case-level services...",
2377 "Case.progressMessage.closingCaseDatabase=Closing case database..."
2387 progressIndicator.
progress(Bundle.Case_progressMessage_shuttingDownNetworkCommunications());
2388 if (null != collaborationMonitor) {
2389 collaborationMonitor.shutdown();
2398 progressIndicator.
progress(Bundle.Case_progressMessage_closingApplicationServiceResources());
2404 if (null != caseServices) {
2405 progressIndicator.
progress(Bundle.Case_progressMessage_closingCaseLevelServices());
2407 this.caseServices.
close();
2408 }
catch (IOException ex) {
2409 logger.log(Level.SEVERE, String.format(
"Error closing internal case services for %s at %s",
this.getName(), this.
getCaseDirectory()), ex);
2416 if (null != caseDb) {
2417 progressIndicator.
progress(Bundle.Case_progressMessage_closingCaseDatabase());
2424 progressIndicator.
progress(Bundle.Case_progressMessage_switchingLogDirectory());
2433 "# {0} - serviceName",
"Case.serviceCloseResourcesProgressIndicator.title={0} Closing Case Resources",
2434 "# {0} - service name",
"# {1} - exception message",
"Case.servicesException.serviceResourcesCloseError=Could not close case resources for {0} service: {1}"
2446 Bundle.Case_serviceCloseResourcesProgressIndicator_title(service.getServiceName()));
2450 progressIndicator.
start(Bundle.Case_progressMessage_preparing());
2452 String threadNameSuffix = service.getServiceName().replaceAll(
"[ ]",
"-");
2453 threadNameSuffix = threadNameSuffix.toLowerCase();
2455 ExecutorService executor = Executors.newSingleThreadExecutor(threadFactory);
2456 Future<Void> future = executor.submit(() -> {
2457 service.closeCaseResources(context);
2462 }
catch (InterruptedException ex) {
2463 Case.
logger.log(Level.SEVERE, String.format(
"Unexpected interrupt while waiting on %s service to close case resources", service.getServiceName()), ex);
2464 }
catch (CancellationException ex) {
2465 Case.
logger.log(Level.SEVERE, String.format(
"Unexpected cancellation while waiting on %s service to close case resources", service.getServiceName()), ex);
2466 }
catch (ExecutionException ex) {
2467 Case.
logger.log(Level.SEVERE, String.format(
"%s service failed to open case resources", service.getServiceName()), ex);
2470 Bundle.Case_servicesException_notificationTitle(service.getServiceName()),
2471 Bundle.Case_servicesException_serviceResourcesCloseError(service.getServiceName(), ex.getLocalizedMessage())));
2475 progressIndicator.
finish();
2488 @Messages({
"Case.creationException.couldNotAcquireDirLock=Failed to get lock on case directory"})
2493 throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireDirLock());
2496 throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireDirLock(), ex);
2511 logger.log(Level.SEVERE, String.format(
"Failed to release shared case directory lock for %s", caseDir), ex);
2524 if (!subDirectory.exists()) {
2525 subDirectory.mkdirs();
2527 return subDirectory.toString();
2543 "Case.exceptionMessage.errorsDeletingCase=Errors occured while deleting the case. See the application log for details."
2546 boolean errorsOccurred =
false;
2550 errorsOccurred =
true;
2556 }
catch (CaseActionException ex) {
2557 errorsOccurred =
true;
2563 if (errorsOccurred) {
2564 throw new CaseActionException(Bundle.Case_exceptionMessage_errorsDeletingCase());
2588 "Case.progressMessage.connectingToCoordSvc=Connecting to coordination service...",
2589 "# {0} - exception message",
"Case.exceptionMessage.failedToConnectToCoordSvc=Failed to connect to coordination service:\n{0}.",
2590 "Case.exceptionMessage.cannotGetLockToDeleteCase=Cannot delete case because it is open for another user or host.",
2591 "# {0} - exception message",
"Case.exceptionMessage.failedToLockCaseForDeletion=Failed to exclusively lock case for deletion:\n{0}.",
2592 "Case.progressMessage.fetchingCoordSvcNodeData=Fetching coordination service node data for the case...",
2593 "# {0} - exception message",
"Case.exceptionMessage.failedToFetchCoordSvcNodeData=Failed to fetch coordination service node data:\n{0}.",
2594 "Case.progressMessage.deletingResourcesCoordSvcNode=Deleting case resources coordination service node...",
2595 "Case.progressMessage.deletingCaseDirCoordSvcNode=Deleting case directory coordination service node..."
2598 progressIndicator.
progress(Bundle.Case_progressMessage_connectingToCoordSvc());
2604 throw new CaseActionException(Bundle.Case_exceptionMessage_failedToConnectToCoordSvc(ex.getLocalizedMessage()));
2608 boolean errorsOccurred =
false;
2610 if (dirLock == null) {
2612 throw new CaseActionException(Bundle.Case_exceptionMessage_cannotGetLockToDeleteCase());
2615 progressIndicator.
progress(Bundle.Case_progressMessage_fetchingCoordSvcNodeData());
2620 throw new CaseActionException(Bundle.Case_exceptionMessage_failedToFetchCoordSvcNodeData(ex.getLocalizedMessage()));
2625 progressIndicator.
progress(Bundle.Case_progressMessage_deletingResourcesCoordSvcNode());
2631 errorsOccurred =
true;
2632 logger.log(Level.WARNING, String.format(
"Error deleting the case resources coordination service node for the case at %s (%s) in %s", metadata.
getCaseDisplayName(), metadata.
getCaseName(), metadata.
getCaseDirectory()), ex);
2634 }
catch (InterruptedException ex) {
2635 logger.log(Level.WARNING, String.format(
"Error deleting the case resources coordination service node for the case at %s (%s) in %s", metadata.
getCaseDisplayName(), metadata.
getCaseName(), metadata.
getCaseDirectory()), ex);
2640 throw new CaseActionException(Bundle.Case_exceptionMessage_failedToLockCaseForDeletion(ex.getLocalizedMessage()));
2643 if (!errorsOccurred) {
2644 progressIndicator.
progress(Bundle.Case_progressMessage_deletingCaseDirCoordSvcNode());
2650 errorsOccurred =
true;
2654 if (errorsOccurred) {
2655 throw new CaseActionException(Bundle.Case_exceptionMessage_errorsDeletingCase());
2685 boolean errorsOccurred =
false;
2692 errorsOccurred =
true;
2695 errorsOccurred =
true;
2697 }
catch (CaseActionException ex) {
2698 errorsOccurred =
true;
2701 return errorsOccurred;
2725 "Case.progressMessage.deletingCaseDatabase=Deleting case database..."
2729 progressIndicator.
progress(Bundle.Case_progressMessage_deletingCaseDatabase());
2730 logger.log(Level.INFO, String.format(
"Deleting case database for %s (%s) in %s", caseNodeData.
getDisplayName(), caseNodeData.
getName(), caseNodeData.
getDirectory()));
2732 String url =
"jdbc:postgresql://" + info.getHost() +
":" + info.getPort() +
"/postgres";
2733 Class.forName(
"org.postgresql.Driver");
2734 try (Connection connection = DriverManager.getConnection(url, info.getUserName(), info.getPassword()); Statement statement = connection.createStatement()) {
2735 String dbExistsQuery =
"SELECT 1 from pg_database WHERE datname = '" + metadata.
getCaseDatabaseName() +
"'";
2736 try (ResultSet queryResult = statement.executeQuery(dbExistsQuery)) {
2737 if (queryResult.next()) {
2739 statement.execute(deleteCommand);
2780 "Case.progressMessage.deletingTextIndex=Deleting text index..."
2783 progressIndicator.
progress(Bundle.Case_progressMessage_deletingTextIndex());
2785 searchService.deleteTextIndex(metadata);
2821 "Case.progressMessage.deletingCaseDirectory=Deleting case directory..."
2824 progressIndicator.
progress(Bundle.Case_progressMessage_deletingCaseDirectory());
2826 throw new CaseActionException(String.format(
"Failed to delete %s", metadata.
getCaseDirectory()));
2838 "Case.progressMessage.removingCaseFromRecentCases=Removing case from Recent Cases menu..."
2842 progressIndicator.
progress(Bundle.Case_progressMessage_removingCaseFromRecentCases());
2843 SwingUtilities.invokeLater(() -> {
2844 RecentCases.getInstance().removeRecentCase(metadata.
getCaseDisplayName(), metadata.getFilePath().toString());
2859 boolean isNodeNodeEx =
false;
2860 Throwable cause = ex.getCause();
2861 if (cause != null) {
2862 String causeMessage = cause.getMessage();
2863 isNodeNodeEx = causeMessage.contains(NO_NODE_ERROR_MSG_FRAGMENT);
2865 return isNodeNodeEx;
2884 logger.log(Level.SEVERE, String.format(
"Error updating deleted item flag %s for %s (%s) in %s", flag.name(), caseNodeData.
getDisplayName(), caseNodeData.
getName(), caseNodeData.
getDirectory()), ex);
2975 ((ModalDialogProgressIndicator) progressIndicator).
setCancelling(cancellationMessage);
3004 return new Thread(task, threadName);
3041 public static void create(String caseDir, String caseDisplayName, String caseNumber, String examiner)
throws CaseActionException {
3066 public static void create(String caseDir, String caseDisplayName, String caseNumber, String examiner,
CaseType caseType)
throws CaseActionException {
3082 public static void open(String caseMetadataFilePath)
throws CaseActionException {
3139 return new File(filePath).isFile();
3178 return "ModuleOutput";
3191 public static PropertyChangeSupport
3193 return new PropertyChangeSupport(
Case.class
3225 public Image
addImage(String imgPath,
long imgId, String timeZone)
throws CaseActionException {
3227 Image newDataSource = caseDb.getImageById(imgId);
3229 return newDataSource;
3230 }
catch (TskCoreException ex) {
3231 throw new CaseActionException(NbBundle.getMessage(
this.getClass(),
"Case.addImg.exception.msg"), ex);
3258 public void deleteReports(Collection<? extends Report> reports,
boolean deleteFromDisk)
throws TskCoreException {
String getLogDirectoryPath()
static final AutopsyEventPublisher eventPublisher
List< Content > getDataSources()
String getModuleOutputDirectoryRelativePath()
static CaseNodeData createCaseNodeData(final CaseMetadata metadata)
void notifyContentTagDeleted(ContentTag deletedTag)
Case(CaseMetadata caseMetaData)
static CaseType fromString(String typeName)
static void checkForUserCancellation()
static final String CASE_ACTION_THREAD_NAME
static void setDeletedItemFlag(CaseNodeData caseNodeData, CaseNodeData.DeletedFlags flag)
void publishLocally(AutopsyEvent event)
static void createAsCurrentCase(CaseType caseType, String caseDir, CaseDetails caseDetails)
static String getCaseDirectoryNodePath(Path caseDirectoryPath)
void notifyBlackBoardArtifactTagDeleted(BlackboardArtifactTag deletedTag)
String getExaminerPhone()
Set< TimeZone > getTimeZones()
static boolean deleteMultiUserCase(CaseNodeData caseNodeData, CaseMetadata metadata, ProgressIndicator progressIndicator, Logger logger)
static String getNameForTitle()
Image addImage(String imgPath, long imgId, String timeZone)
static synchronized IngestManager getInstance()
void deleteNode(CategoryNode category, String nodePath)
static final String NO_NODE_ERROR_MSG_FRAGMENT
static CoordinationService.Lock acquireExclusiveCaseResourcesLock(String caseDir)
static boolean runningWithGUI
static void closeCurrentCase()
void setDeletedFlag(DeletedFlags flag)
String getTempDirectory()
static boolean existsCurrentCase()
boolean isDeletedFlagSet(DeletedFlags flag)
static void removePropertyChangeListener(PropertyChangeListener listener)
void start(String message, int totalWorkUnits)
static final Logger logger
void publish(AutopsyEvent event)
static final int RESOURCES_LOCK_TIMOUT_HOURS
String getLocalizedDisplayName()
ADDING_DATA_SOURCE_FAILED
static final String EXPORT_FOLDER
static void createCaseDirectory(String caseDirPath, CaseType caseType)
String getCaseDirectory()
static String getAppName()
void notifyTagDefinitionChanged(String changedTagName)
static volatile Frame mainFrame
static String convertTimeZone(String timeZoneId)
static boolean driveExists(String path)
static void openCoreWindows()
static void deleteSingleUserCase(CaseMetadata metadata, ProgressIndicator progressIndicator)
void addSubscriber(Set< String > eventNames, PropertyChangeListener subscriber)
synchronized static void setLogDirectory(String directoryPath)
void notifyCentralRepoCommentChanged(long contentId, String newComment)
TaskThreadFactory(String threadName)
static final String CACHE_FOLDER
static String getAutopsyVersion()
CaseType(String typeName)
static void deleteMultiUserCaseDirectory(CaseNodeData caseNodeData, CaseMetadata metadata, ProgressIndicator progressIndicator, Logger logger)
Case(CaseType caseType, String caseDir, CaseDetails caseDetails)
String getReportDirectory()
static String getCaseResourcesNodePath(Path caseDirectoryPath)
static CaseNodeData readCaseNodeData(String nodePath)
static boolean getIsMultiUserModeEnabled()
void addReport(String localPath, String srcModuleName, String reportName)
static void updateGUIForCaseOpened(Case newCurrentCase)
void notifyDataSourceNameChanged(Content dataSource, String newName)
static CaseDbConnectionInfo getDatabaseConnectionInfo()
String getModulesOutputDirAbsPath()
static void create(String caseDir, String caseDisplayName, String caseNumber, String examiner, CaseType caseType)
void closeAppServiceCaseResources()
static final String SINGLE_USER_CASE_DB_NAME
static void deleteCase(CaseMetadata metadata)
void deleteReports(Collection<?extends Report > reports, boolean deleteFromDisk)
synchronized void closeRemoteEventChannel()
volatile ExecutorService caseLockingExecutor
List< Report > getAllReports()
static void clearTempSubDir(String tempSubDirPath)
static boolean isValidName(String caseName)
void acquireSharedCaseDirLock(String caseDir)
void openCaseDataBase(ProgressIndicator progressIndicator)
void createCaseDirectoryIfDoesNotExist(ProgressIndicator progressIndicator)
CollaborationMonitor collaborationMonitor
void openCommunicationChannels(ProgressIndicator progressIndicator)
void releaseSharedCaseDirLock(String caseDir)
static void shutDownTaskExecutor(ExecutorService executor)
static void closeCoreWindows()
ProgressIndicator getProgressIndicator()
static String getModulesOutputDirRelPath()
void saveCaseMetadataToFile(ProgressIndicator progressIndicator)
static void deleteMultiUserCaseTextIndex(CaseNodeData caseNodeData, CaseMetadata metadata, ProgressIndicator progressIndicator, Logger logger)
Set< TimeZone > getTimeZone()
static void writeCaseNodeData(CaseNodeData nodeData)
static final String MODULE_FOLDER
void openCaseLevelServices(ProgressIndicator progressIndicator)
static void create(String caseDir, String caseDisplayName, String caseNumber, String examiner)
synchronized void openRemoteEventChannel(String channelName)
String getCaseDisplayName()
void createCaseNodeData(ProgressIndicator progressIndicator)
static void invokeStartupDialog()
static String displayNameToUniqueName(String caseDisplayName)
static void deleteFromRecentCases(CaseMetadata metadata, ProgressIndicator progressIndicator)
void open(boolean isNewCase)
static void openAsCurrentCase(String caseMetadataFilePath)
static void removeEventSubscriber(Set< String > eventNames, PropertyChangeListener subscriber)
Lock tryGetExclusiveLock(CategoryNode category, String nodePath, int timeOut, TimeUnit timeUnit)
static boolean isNoNodeException(CoordinationServiceException ex)
default void setCancelling(String cancellingMessage)
void setLastAccessDate(Date lastAccessDate)
static final int DIR_LOCK_TIMOUT_HOURS
void close(ProgressIndicator progressIndicator)
SleuthkitCase getSleuthkitCase()
void notifyBlackBoardArtifactTagAdded(BlackboardArtifactTag newTag)
static PropertyChangeSupport getPropertyChangeSupport()
String getCacheDirectory()
static void addPropertyChangeListener(PropertyChangeListener listener)
void removeSubscriber(Set< String > eventNames, PropertyChangeListener subscriber)
String getModuleDirectory()
void createCaseDatabase(ProgressIndicator progressIndicator)
void openAppServiceCaseResources(ProgressIndicator progressIndicator)
Thread newThread(Runnable task)
static void removeEventSubscriber(String eventName, PropertyChangeListener subscriber)
void switchLoggingToCaseLogsDirectory(ProgressIndicator progressIndicator)
final CaseMetadata metadata
static void deleteTextIndex(CaseMetadata metadata, ProgressIndicator progressIndicator)
void deleteTempfilesFromCaseDirectory(ProgressIndicator progressIndicator)
void deleteReports(Collection<?extends Report > reports)
void notifyDataSourceAdded(Content dataSource, UUID addingDataSourceEventId)
Report addReport(String localPath, String srcModuleName, String reportName, Content parent)
static boolean pathExists(String filePath)
SleuthkitCase createPortableCase(String caseName, File portableCaseFolder)
BLACKBOARD_ARTIFACT_TAG_ADDED
static void open(String caseMetadataFilePath)
static void openAsCurrentCase(Case newCurrentCase, boolean isNewCase)
String getOutputDirectory()
static void error(String title, String message)
String getOrCreateSubdirectory(String subDirectoryName)
boolean equalsName(String otherTypeName)
static final String EVENT_CHANNEL_NAME
static Case getCurrentCase()
static String getLocalHostName()
synchronized static Logger getLogger(String name)
static Case getCurrentCaseThrows()
static void addEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
static String convertToAlphaNumericFormat(String timeZoneId)
static final String LOG_FOLDER
Lock tryGetSharedLock(CategoryNode category, String nodePath, int timeOut, TimeUnit timeUnit)
String getConfigDirectory()
static String getAppName()
static synchronized CoordinationService getInstance()
static volatile Case currentCase
static String getVersion()
String getExportDirectory()
static void updateGUIForCaseClosed()
void notifyAddingDataSource(UUID eventId)
CoordinationService.Lock caseDirLock
static void addEventSubscriber(String eventName, PropertyChangeListener subscriber)
void notifyContentTagAdded(ContentTag newTag)
void cancelAllIngestJobs(IngestJob.CancellationReason reason)
static final String CASE_RESOURCES_THREAD_NAME
static StartupWindowProvider getInstance()
static void deleteCurrentCase()
static boolean deleteDir(File dirPath)
static void createAsCurrentCase(String caseDir, String caseDisplayName, String caseNumber, String examiner, CaseType caseType)
static void deleteCaseDirectory(CaseMetadata metadata, ProgressIndicator progressIndicator)
void notifyFailedAddingDataSource(UUID addingDataSourceEventId)
static void deleteMultiUserCase(CaseMetadata metadata, ProgressIndicator progressIndicator)
static final String CONFIG_FOLDER
static void removeEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
static final Object caseActionSerializationLock
void open(boolean isNewCase, ProgressIndicator progressIndicator)
static void deleteMultiUserCaseDatabase(CaseNodeData caseNodeData, CaseMetadata metadata, ProgressIndicator progressIndicator, Logger logger)
static boolean isCaseOpen()
String getTextIndexName()
static final String REPORTS_FOLDER
void progress(String message)
static final String TEMP_FOLDER
BLACKBOARD_ARTIFACT_TAG_DELETED
static void addEventSubscriber(Set< String > eventNames, PropertyChangeListener subscriber)
void updateCaseNodeData(ProgressIndicator progressIndicator)
static void error(String message)
String getExaminerEmail()