19 package org.sleuthkit.autopsy.casemodule;
21 import com.google.common.annotations.Beta;
22 import com.google.common.eventbus.Subscribe;
24 import java.awt.Frame;
25 import java.awt.event.ActionEvent;
26 import java.awt.event.ActionListener;
27 import java.beans.PropertyChangeListener;
28 import java.beans.PropertyChangeSupport;
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.SimpleDateFormat;
39 import java.util.Collection;
40 import java.util.Date;
41 import java.util.HashMap;
42 import java.util.HashSet;
43 import java.util.List;
46 import java.util.TimeZone;
47 import java.util.UUID;
48 import java.util.concurrent.CancellationException;
49 import java.util.concurrent.ExecutionException;
50 import java.util.concurrent.ExecutorService;
51 import java.util.concurrent.Executors;
52 import java.util.concurrent.Future;
53 import java.util.concurrent.ThreadFactory;
54 import java.util.concurrent.TimeUnit;
55 import java.util.logging.Level;
56 import java.util.stream.Collectors;
57 import java.util.stream.Stream;
58 import javax.annotation.concurrent.GuardedBy;
59 import javax.annotation.concurrent.ThreadSafe;
60 import javax.swing.JOptionPane;
61 import javax.swing.SwingUtilities;
62 import org.openide.util.Lookup;
63 import org.openide.util.NbBundle;
64 import org.openide.util.NbBundle.Messages;
65 import org.openide.util.actions.CallableSystemAction;
66 import org.openide.windows.WindowManager;
129 import org.
sleuthkit.datamodel.TskUnsupportedSchemaVersionException;
170 WindowManager.getDefault().invokeWhenUIReady(
new Runnable() {
173 mainFrame = WindowManager.getDefault().getMainWindow();
196 if (typeName != null) {
198 if (typeName.equalsIgnoreCase(c.toString())) {
222 "Case_caseType_singleUser=Single-user case",
223 "Case_caseType_multiUser=Multi-user case"
226 if (fromString(typeName) == SINGLE_USER_CASE) {
227 return Bundle.Case_caseType_singleUser();
229 return Bundle.Case_caseType_multiUser();
239 this.typeName = typeName;
254 return (otherTypeName == null) ?
false : typeName.equals(otherTypeName);
416 @SuppressWarnings(
"deprecation")
419 for (BlackboardArtifact.Type artifactType : event.getArtifactTypes()) {
427 event.getModuleName(),
429 event.getArtifacts(artifactType)));
442 .map(Events::toString)
443 .collect(Collectors.toSet()), listener);
454 .map(Events::toString)
455 .collect(Collectors.toSet()), listener);
478 eventTypes.forEach((
Events event) -> {
523 eventTypes.forEach((
Events event) -> {
537 return !(caseName.contains(
"\\") || caseName.contains(
"/") || caseName.contains(
":")
538 || caseName.contains(
"*") || caseName.contains(
"?") || caseName.contains(
"\"")
539 || caseName.contains(
"<") || caseName.contains(
">") || caseName.contains(
"|"));
591 "Case.exceptionMessage.emptyCaseName=Must specify a case name.",
592 "Case.exceptionMessage.emptyCaseDir=Must specify a case directory path."
596 throw new CaseActionException(Bundle.Case_exceptionMessage_emptyCaseName());
598 if (caseDir.isEmpty()) {
599 throw new CaseActionException(Bundle.Case_exceptionMessage_emptyCaseDir());
618 "# {0} - exception message",
"Case.exceptionMessage.failedToReadMetadata=Failed to read case metadata:\n{0}.",
619 "Case.exceptionMessage.cannotOpenMultiUserCaseNoSettings=Multi-user settings are missing (see Tools, Options, Multi-user tab), cannot open a multi-user case."
624 metadata =
new CaseMetadata(Paths.get(caseMetadataFilePath));
626 throw new CaseActionException(Bundle.Case_exceptionMessage_failedToReadMetadata(ex.getLocalizedMessage()), ex);
629 throw new CaseActionException(Bundle.Case_exceptionMessage_cannotOpenMultiUserCaseNoSettings());
640 return currentCase != null;
657 throw new IllegalStateException(NbBundle.getMessage(
Case.class,
"Case.getCurCase.exception.noneOpen"), ex);
680 if (openCase == null) {
681 throw new NoCurrentCaseException(NbBundle.getMessage(
Case.class,
"Case.getCurCase.exception.noneOpen"));
696 "# {0} - exception message",
"Case.closeException.couldNotCloseCase=Error closing case: {0}",
697 "Case.progressIndicatorTitle.closingCase=Closing Case"
701 if (null == currentCase) {
707 logger.log(Level.INFO,
"Closing current case {0} ({1}) in {2}",
new Object[]{closedCase.getDisplayName(), closedCase.getName(), closedCase.getCaseDirectory()});
710 logger.log(Level.INFO,
"Closed current case {0} ({1}) in {2}",
new Object[]{closedCase.getDisplayName(), closedCase.getName(), closedCase.getCaseDirectory()});
711 }
catch (CaseActionException ex) {
732 if (null == currentCase) {
753 "Case.progressIndicatorTitle.deletingCase=Deleting Case",
754 "Case.exceptionMessage.cannotDeleteCurrentCase=Cannot delete current case, it must be closed first.",
755 "# {0} - case display name",
"Case.exceptionMessage.deletionInterrupted=Deletion of the case {0} was cancelled."
759 if (null != currentCase) {
760 throw new CaseActionException(Bundle.Case_exceptionMessage_cannotDeleteCurrentCase());
770 progressIndicator.
start(Bundle.Case_progressMessage_preparing());
777 }
catch (InterruptedException ex) {
783 throw new CaseActionException(Bundle.Case_exceptionMessage_deletionInterrupted(metadata.
getCaseDisplayName()), ex);
787 progressIndicator.
finish();
802 "Case.exceptionMessage.cannotLocateMainWindow=Cannot locate main application window"
806 if (null != currentCase) {
809 }
catch (CaseActionException ex) {
818 logger.log(Level.INFO,
"Opening {0} ({1}) in {2} as the current case",
new Object[]{newCurrentCase.getDisplayName(), newCurrentCase.getName(), newCurrentCase.getCaseDirectory()});
819 newCurrentCase.
open(isNewCase);
820 currentCase = newCurrentCase;
821 logger.log(Level.INFO,
"Opened {0} ({1}) in {2} as the current case",
new Object[]{newCurrentCase.getDisplayName(), newCurrentCase.getName(), newCurrentCase.getCaseDirectory()});
827 logger.log(Level.INFO, String.format(
"Cancelled opening %s (%s) in %s as the current case", newCurrentCase.
getDisplayName(), newCurrentCase.
getName(), newCurrentCase.
getCaseDirectory()));
829 }
catch (CaseActionException ex) {
830 logger.log(Level.SEVERE, String.format(
"Error opening %s (%s) in %s as the current case", newCurrentCase.
getDisplayName(), newCurrentCase.
getName(), newCurrentCase.
getCaseDirectory()), ex);
848 String uniqueCaseName = caseDisplayName.replaceAll(
"[^\\p{ASCII}]",
"_");
853 uniqueCaseName = uniqueCaseName.replaceAll(
"[\\p{Cntrl}]",
"_");
858 uniqueCaseName = uniqueCaseName.replaceAll(
"[ /?:'\"\\\\]",
"_");
863 uniqueCaseName = uniqueCaseName.toLowerCase();
868 SimpleDateFormat dateFormat =
new SimpleDateFormat(
"yyyyMMdd_HHmmss");
869 Date date =
new Date();
870 uniqueCaseName = uniqueCaseName +
"_" + dateFormat.format(date);
872 return uniqueCaseName;
889 File caseDir =
new File(caseDirPath);
890 if (caseDir.exists()) {
891 if (caseDir.isFile()) {
892 throw new CaseActionException(NbBundle.getMessage(
Case.class,
"Case.createCaseDir.exception.existNotDir", caseDirPath));
893 }
else if (!caseDir.canRead() || !caseDir.canWrite()) {
894 throw new CaseActionException(NbBundle.getMessage(
Case.class,
"Case.createCaseDir.exception.existCantRW", caseDirPath));
901 if (!caseDir.mkdirs()) {
902 throw new CaseActionException(NbBundle.getMessage(
Case.class,
"Case.createCaseDir.exception.cantCreate", caseDirPath));
910 String hostPathComponent =
"";
915 Path exportDir = Paths.get(caseDirPath, hostPathComponent, EXPORT_FOLDER);
916 if (!exportDir.toFile().mkdirs()) {
917 throw new CaseActionException(NbBundle.getMessage(
Case.class,
"Case.createCaseDir.exception.cantCreateCaseDir", exportDir));
920 Path logsDir = Paths.get(caseDirPath, hostPathComponent, LOG_FOLDER);
921 if (!logsDir.toFile().mkdirs()) {
922 throw new CaseActionException(NbBundle.getMessage(
Case.class,
"Case.createCaseDir.exception.cantCreateCaseDir", logsDir));
925 Path tempDir = Paths.get(caseDirPath, hostPathComponent, TEMP_FOLDER);
926 if (!tempDir.toFile().mkdirs()) {
927 throw new CaseActionException(NbBundle.getMessage(
Case.class,
"Case.createCaseDir.exception.cantCreateCaseDir", tempDir));
930 Path cacheDir = Paths.get(caseDirPath, hostPathComponent, CACHE_FOLDER);
931 if (!cacheDir.toFile().mkdirs()) {
932 throw new CaseActionException(NbBundle.getMessage(
Case.class,
"Case.createCaseDir.exception.cantCreateCaseDir", cacheDir));
935 Path moduleOutputDir = Paths.get(caseDirPath, hostPathComponent, MODULE_FOLDER);
936 if (!moduleOutputDir.toFile().mkdirs()) {
937 throw new CaseActionException(NbBundle.getMessage(
Case.class,
"Case.createCaseDir.exception.cantCreateModDir", moduleOutputDir));
940 Path reportsDir = Paths.get(caseDirPath, hostPathComponent, REPORTS_FOLDER);
941 if (!reportsDir.toFile().mkdirs()) {
942 throw new CaseActionException(NbBundle.getMessage(
Case.class,
"Case.createCaseDir.exception.cantCreateReportsDir", reportsDir));
953 static Map<Long, String> getImagePaths(SleuthkitCase db) {
954 Map<Long, String> imgPaths =
new HashMap<>();
956 Map<Long, List<String>> imgPathsList = db.getImagePaths();
957 for (Map.Entry<Long, List<String>> entry : imgPathsList.entrySet()) {
958 if (entry.getValue().size() > 0) {
959 imgPaths.put(entry.getKey(), entry.getValue().get(0));
962 }
catch (TskCoreException ex) {
963 logger.log(Level.SEVERE,
"Error getting image paths", ex);
979 "Case.creationException.couldNotAcquireResourcesLock=Failed to get lock on case resources"
983 Path caseDirPath = Paths.get(caseDir);
987 }
catch (InterruptedException ex) {
990 throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireResourcesLock(), ex);
1009 SwingUtilities.invokeLater(() -> {
1015 String backupDbPath = caseDb.getBackupDatabasePath();
1016 if (null != backupDbPath) {
1017 JOptionPane.showMessageDialog(
1019 NbBundle.getMessage(
Case.class,
"Case.open.msgDlg.updated.msg", backupDbPath),
1020 NbBundle.getMessage(
Case.class,
"Case.open.msgDlg.updated.title"),
1021 JOptionPane.INFORMATION_MESSAGE);
1029 Map<Long, String> imgPaths = getImagePaths(caseDb);
1030 for (Map.Entry<Long, String> entry : imgPaths.entrySet()) {
1031 long obj_id = entry.getKey();
1032 String path = entry.getValue();
1035 int response = JOptionPane.showConfirmDialog(
1037 NbBundle.getMessage(
Case.class,
"Case.checkImgExist.confDlg.doesntExist.msg", path),
1038 NbBundle.getMessage(
Case.class,
"Case.checkImgExist.confDlg.doesntExist.title"),
1039 JOptionPane.YES_NO_OPTION);
1040 if (response == JOptionPane.YES_OPTION) {
1041 MissingImageDialog.makeDialog(obj_id, caseDb);
1043 logger.log(Level.SEVERE,
"User proceeding with missing image files");
1054 CallableSystemAction.get(CaseDetailsAction.class).setEnabled(
true);
1056 CallableSystemAction.get(CaseDeleteAction.class).setEnabled(
true);
1067 RecentCases.getInstance().addRecentCase(newCurrentCase.
getDisplayName(), newCurrentCase.getMetadata().getFilePath().toString());
1077 if (newCurrentCase.
hasData()) {
1096 SwingUtilities.invokeLater(() -> {
1106 CallableSystemAction.get(
AddImageAction.class).setEnabled(
false);
1108 CallableSystemAction.get(CaseDetailsAction.class).setEnabled(
false);
1110 CallableSystemAction.get(CaseDeleteAction.class).setEnabled(
false);
1135 File tempFolder =
new File(tempSubDirPath);
1136 if (tempFolder.isDirectory()) {
1137 File[] files = tempFolder.listFiles();
1138 if (files.length > 0) {
1139 for (File file : files) {
1140 if (file.isDirectory()) {
1272 hostPath = Paths.get(caseDirectory);
1274 if (!hostPath.toFile().exists()) {
1275 hostPath.toFile().mkdirs();
1277 return hostPath.toString();
1360 return path.subpath(path.getNameCount() - 2, path.getNameCount()).toString();
1362 return path.subpath(path.getNameCount() - 1, path.getNameCount()).toString();
1376 List<Content> list = caseDb.getRootObjects();
1377 hasDataSources = (list.size() > 0);
1387 Set<TimeZone> timezones =
new HashSet<>();
1390 final Content dataSource = c.getDataSource();
1391 if ((dataSource != null) && (dataSource instanceof Image)) {
1392 Image image = (Image) dataSource;
1393 timezones.add(TimeZone.getTimeZone(image.getTimeZone()));
1396 }
catch (TskCoreException ex) {
1397 logger.log(Level.SEVERE,
"Error getting data source time zones", ex);
1419 if (!hasDataSources) {
1422 }
catch (TskCoreException ex) {
1423 logger.log(Level.SEVERE,
"Error accessing case database", ex);
1533 logger.log(Level.WARNING,
"Unable to send notifcation regarding comment change due to no current case being open", ex);
1570 public void addReport(String localPath, String srcModuleName, String reportName)
throws TskCoreException {
1571 addReport(localPath, srcModuleName, reportName, null);
1588 public Report
addReport(String localPath, String srcModuleName, String reportName, Content parent)
throws TskCoreException {
1589 String normalizedLocalPath;
1591 if (localPath.toLowerCase().contains(
"http:")) {
1592 normalizedLocalPath = localPath;
1594 normalizedLocalPath = Paths.get(localPath).normalize().toString();
1596 }
catch (InvalidPathException ex) {
1597 String errorMsg =
"Invalid local path provided: " + localPath;
1598 throw new TskCoreException(errorMsg, ex);
1600 Report report = this.caseDb.addReport(normalizedLocalPath, srcModuleName, reportName, parent);
1614 return this.caseDb.getAllReports();
1625 public void deleteReports(Collection<? extends Report> reports)
throws TskCoreException {
1626 for (Report report : reports) {
1627 this.caseDb.deleteReport(report);
1649 "Case.exceptionMessage.metadataUpdateError=Failed to update case metadata"
1651 void updateCaseDetails(CaseDetails caseDetails)
throws CaseActionException {
1654 metadata.setCaseDetails(caseDetails);
1655 }
catch (CaseMetadataException ex) {
1656 throw new CaseActionException(Bundle.Case_exceptionMessage_metadataUpdateError(), ex);
1660 CaseNodeData nodeData = CaseNodeData.readCaseNodeData(metadata.
getCaseDirectory());
1662 CaseNodeData.writeCaseNodeData(nodeData);
1663 }
catch (CaseNodeDataException | InterruptedException ex) {
1664 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotUpdateCaseNodeData(ex.getLocalizedMessage()), ex);
1667 if (!oldCaseDetails.getCaseNumber().equals(caseDetails.
getCaseNumber())) {
1668 eventPublisher.
publish(
new AutopsyEvent(Events.NUMBER.toString(), oldCaseDetails.getCaseNumber(), caseDetails.
getCaseNumber()));
1670 if (!oldCaseDetails.getExaminerName().equals(caseDetails.
getExaminerName())) {
1671 eventPublisher.
publish(
new AutopsyEvent(Events.NUMBER.toString(), oldCaseDetails.getExaminerName(), caseDetails.
getExaminerName()));
1674 eventPublisher.
publish(
new AutopsyEvent(Events.NAME.toString(), oldCaseDetails.getCaseDisplayName(), caseDetails.
getCaseDisplayName()));
1676 eventPublisher.
publish(
new AutopsyEvent(Events.CASE_DETAILS.toString(), oldCaseDetails, caseDetails));
1677 if (RuntimeProperties.runningWithGUI()) {
1678 SwingUtilities.invokeLater(() -> {
1681 RecentCases.getInstance().updateRecentCase(oldCaseDetails.getCaseDisplayName(), metadata.getFilePath().toString(), caseDetails.
getCaseDisplayName(), metadata.getFilePath().toString());
1682 }
catch (Exception ex) {
1683 logger.log(Level.SEVERE,
"Error updating case name in UI", ex);
1711 metadata = caseMetaData;
1730 "Case.progressIndicatorTitle.creatingCase=Creating Case",
1731 "Case.progressIndicatorTitle.openingCase=Opening Case",
1732 "Case.progressIndicatorCancelButton.label=Cancel",
1733 "Case.progressMessage.preparing=Preparing...",
1734 "Case.progressMessage.preparingToOpenCaseResources=<html>Preparing to open case resources.<br>This may take time if another user is upgrading the case.</html>",
1735 "Case.progressMessage.cancelling=Cancelling...",
1736 "Case.exceptionMessage.cancelledByUser=Cancelled by user.",
1737 "# {0} - exception message",
"Case.exceptionMessage.execExceptionWrapperMessage={0}"
1739 private void open(
boolean isNewCase)
throws CaseActionException {
1748 String progressIndicatorTitle = isNewCase ? Bundle.Case_progressIndicatorTitle_creatingCase() : Bundle.Case_progressIndicatorTitle_openingCase();
1751 progressIndicatorTitle,
1752 new String[]{Bundle.Case_progressIndicatorCancelButton_label()},
1753 Bundle.Case_progressIndicatorCancelButton_label(),
1754 cancelButtonListener);
1758 progressIndicator.
start(Bundle.Case_progressMessage_preparing());
1776 caseLockingExecutor = Executors.newSingleThreadExecutor(threadFactory);
1777 Future<Void> future = caseLockingExecutor.submit(() -> {
1779 open(isNewCase, progressIndicator);
1788 progressIndicator.
progress(Bundle.Case_progressMessage_preparingToOpenCaseResources());
1791 if (null == resourcesLock) {
1792 throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireResourcesLock());
1794 open(isNewCase, progressIndicator);
1795 }
catch (CaseActionException ex) {
1802 if (null != cancelButtonListener) {
1811 }
catch (InterruptedException discarded) {
1820 if (null != cancelButtonListener) {
1823 future.cancel(
true);
1826 }
catch (CancellationException discarded) {
1836 }
catch (ExecutionException ex) {
1845 throw new CaseActionException(Bundle.Case_exceptionMessage_execExceptionWrapperMessage(ex.getCause().getLocalizedMessage()), ex);
1847 progressIndicator.
finish();
1895 }
catch (CaseActionException ex) {
1904 }
catch (InterruptedException discarded) {
1906 close(progressIndicator);
1921 public SleuthkitCase
createPortableCase(String caseName, File portableCaseFolder)
throws TskCoreException {
1923 if (portableCaseFolder.exists()) {
1924 throw new TskCoreException(
"Portable case folder " + portableCaseFolder.toString() +
" already exists");
1926 if (!portableCaseFolder.mkdirs()) {
1927 throw new TskCoreException(
"Error creating portable case folder " + portableCaseFolder.toString());
1935 portableCaseMetadata.setCaseDatabaseName(SINGLE_USER_CASE_DB_NAME);
1937 throw new TskCoreException(
"Error creating case metadata", ex);
1941 SleuthkitCase portableSleuthkitCase;
1943 portableSleuthkitCase = SleuthkitCase.newCase(dbFilePath);
1945 return portableSleuthkitCase;
1958 if (Thread.currentThread().isInterrupted()) {
1959 throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser());
1977 "Case.progressMessage.creatingCaseDirectory=Creating case directory..."
1980 progressIndicator.
progress(Bundle.Case_progressMessage_creatingCaseDirectory());
1982 progressIndicator.
progress(Bundle.Case_progressMessage_creatingCaseDirectory());
1994 "Case.progressMessage.switchingLogDirectory=Switching log directory..."
1997 progressIndicator.
progress(Bundle.Case_progressMessage_switchingLogDirectory());
2013 "Case.progressMessage.savingCaseMetadata=Saving case metadata to file...",
2014 "# {0} - exception message",
"Case.exceptionMessage.couldNotSaveCaseMetadata=Failed to save case metadata:\n{0}."
2017 progressIndicator.
progress(Bundle.Case_progressMessage_savingCaseMetadata());
2019 this.metadata.writeToFile();
2021 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotSaveCaseMetadata(ex.getLocalizedMessage()), ex);
2037 "Case.progressMessage.creatingCaseNodeData=Creating coordination service node data...",
2038 "# {0} - exception message",
"Case.exceptionMessage.couldNotCreateCaseNodeData=Failed to create coordination service node data:\n{0}."
2042 progressIndicator.
progress(Bundle.Case_progressMessage_creatingCaseNodeData());
2046 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotCreateCaseNodeData(ex.getLocalizedMessage()), ex);
2063 "Case.progressMessage.updatingCaseNodeData=Updating coordination service node data...",
2064 "# {0} - exception message",
"Case.exceptionMessage.couldNotUpdateCaseNodeData=Failed to update coordination service node data:\n{0}."
2068 progressIndicator.
progress(Bundle.Case_progressMessage_updatingCaseNodeData());
2074 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotUpdateCaseNodeData(ex.getLocalizedMessage()), ex);
2085 "Case.progressMessage.clearingTempDirectory=Clearing case temp directory..."
2091 progressIndicator.
progress(Bundle.Case_progressMessage_clearingTempDirectory());
2107 "Case.progressMessage.creatingCaseDatabase=Creating case database...",
2108 "# {0} - exception message",
"Case.exceptionMessage.couldNotGetDbServerConnectionInfo=Failed to get case database server conneciton info:\n{0}.",
2109 "# {0} - exception message",
"Case.exceptionMessage.couldNotCreateCaseDatabase=Failed to create case database:\n{0}.",
2110 "# {0} - exception message",
"Case.exceptionMessage.couldNotSaveDbNameToMetadataFile=Failed to save case database name to case metadata file:\n{0}."
2113 progressIndicator.
progress(Bundle.Case_progressMessage_creatingCaseDatabase());
2122 metadata.setCaseDatabaseName(SINGLE_USER_CASE_DB_NAME);
2130 metadata.setCaseDatabaseName(caseDb.getDatabaseName());
2132 }
catch (TskCoreException ex) {
2133 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotCreateCaseDatabase(ex.getLocalizedMessage()), ex);
2135 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotGetDbServerConnectionInfo(ex.getLocalizedMessage()), ex);
2137 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotSaveDbNameToMetadataFile(ex.getLocalizedMessage()), ex);
2153 "Case.progressMessage.openingCaseDatabase=Opening case database...",
2154 "# {0} - exception message",
"Case.exceptionMessage.couldNotOpenCaseDatabase=Failed to open case database:\n{0}.",
2155 "# {0} - exception message",
"Case.exceptionMessage.unsupportedSchemaVersionMessage=Unsupported case database schema version:\n{0}.",
2156 "Case.open.exception.multiUserCaseNotEnabled=Cannot open a multi-user case if multi-user cases are not enabled. See Tools, Options, Multi-User."
2159 progressIndicator.
progress(Bundle.Case_progressMessage_openingCaseDatabase());
2163 caseDb = SleuthkitCase.openCase(Paths.get(metadata.
getCaseDirectory(), databaseName).toString());
2167 throw new CaseActionException(Bundle.Case_open_exception_multiUserCaseNotEnabled());
2169 }
catch (TskUnsupportedSchemaVersionException ex) {
2170 throw new CaseActionException(Bundle.Case_exceptionMessage_unsupportedSchemaVersionMessage(ex.getLocalizedMessage()), ex);
2172 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotGetDbServerConnectionInfo(ex.getLocalizedMessage()), ex);
2173 }
catch (TskCoreException ex) {
2174 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotOpenCaseDatabase(ex.getLocalizedMessage()), ex);
2185 "Case.progressMessage.openingCaseLevelServices=Opening case-level services...",})
2187 progressIndicator.
progress(Bundle.Case_progressMessage_openingCaseLevelServices());
2188 this.caseServices =
new Services(caseDb);
2190 caseDb.registerForEvents(tskEventForwarder);
2204 @NbBundle.Messages({
2205 "Case.progressMessage.openingApplicationServiceResources=Opening application service case resources...",
2206 "# {0} - service name",
"Case.serviceOpenCaseResourcesProgressIndicator.title={0} Opening Case Resources",
2207 "# {0} - service name",
"Case.serviceOpenCaseResourcesProgressIndicator.cancellingMessage=Cancelling opening case resources by {0}...",
2208 "# {0} - service name",
"Case.servicesException.notificationTitle={0} Error"
2219 progressIndicator.
progress(Bundle.Case_progressMessage_openingApplicationServiceResources());
2229 cancelButtonListener =
new CancelButtonListener(Bundle.Case_serviceOpenCaseResourcesProgressIndicator_cancellingMessage(service.getServiceName()));
2232 Bundle.Case_serviceOpenCaseResourcesProgressIndicator_title(service.getServiceName()),
2233 new String[]{Bundle.Case_progressIndicatorCancelButton_label()},
2234 Bundle.Case_progressIndicatorCancelButton_label(),
2235 cancelButtonListener);
2239 appServiceProgressIndicator.
start(Bundle.Case_progressMessage_preparing());
2241 String threadNameSuffix = service.getServiceName().replaceAll(
"[ ]",
"-");
2242 threadNameSuffix = threadNameSuffix.toLowerCase();
2244 ExecutorService executor = Executors.newSingleThreadExecutor(threadFactory);
2245 Future<Void> future = executor.submit(() -> {
2246 service.openCaseResources(context);
2249 if (null != cancelButtonListener) {
2261 }
catch (InterruptedException discarded) {
2266 future.cancel(
true);
2267 }
catch (CancellationException discarded) {
2275 }
catch (ExecutionException ex) {
2282 Case.
logger.log(Level.SEVERE, String.format(
"%s failed to open case resources for %s", service.getServiceName(), this.
getDisplayName()), ex);
2284 SwingUtilities.invokeLater(() -> {
2296 appServiceProgressIndicator.
finish();
2314 "Case.progressMessage.settingUpNetworkCommunications=Setting up network communications...",
2315 "# {0} - exception message",
"Case.exceptionMessage.couldNotOpenRemoteEventChannel=Failed to open remote events channel:\n{0}.",
2316 "# {0} - exception message",
"Case.exceptionMessage.couldNotCreatCollaborationMonitor=Failed to create collaboration monitor:\n{0}."
2320 progressIndicator.
progress(Bundle.Case_progressMessage_settingUpNetworkCommunications());
2324 collaborationMonitor =
new CollaborationMonitor(metadata.
getCaseName());
2326 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotOpenRemoteEventChannel(ex.getLocalizedMessage()), ex);
2327 }
catch (CollaborationMonitor.CollaborationMonitorException ex) {
2328 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotCreatCollaborationMonitor(ex.getLocalizedMessage()), ex);
2341 private void close() throws CaseActionException {
2350 Bundle.Case_progressIndicatorTitle_closingCase());
2354 progressIndicator.
start(Bundle.Case_progressMessage_preparing());
2363 Future<Void> future = caseLockingExecutor.submit(() -> {
2365 close(progressIndicator);
2372 progressIndicator.
progress(Bundle.Case_progressMessage_preparing());
2374 if (null == resourcesLock) {
2375 throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireResourcesLock());
2377 close(progressIndicator);
2391 }
catch (InterruptedException | CancellationException unused) {
2398 }
catch (ExecutionException ex) {
2399 throw new CaseActionException(Bundle.Case_exceptionMessage_execExceptionWrapperMessage(ex.getCause().getMessage()), ex);
2402 progressIndicator.
finish();
2412 "Case.progressMessage.shuttingDownNetworkCommunications=Shutting down network communications...",
2413 "Case.progressMessage.closingApplicationServiceResources=Closing case-specific application service resources...",
2414 "Case.progressMessage.closingCaseDatabase=Closing case database..."
2424 progressIndicator.
progress(Bundle.Case_progressMessage_shuttingDownNetworkCommunications());
2425 if (null != collaborationMonitor) {
2426 collaborationMonitor.shutdown();
2435 progressIndicator.
progress(Bundle.Case_progressMessage_closingApplicationServiceResources());
2441 if (null != caseDb) {
2442 progressIndicator.
progress(Bundle.Case_progressMessage_closingCaseDatabase());
2443 caseDb.unregisterForEvents(tskEventForwarder);
2450 progressIndicator.
progress(Bundle.Case_progressMessage_switchingLogDirectory());
2459 "# {0} - serviceName",
"Case.serviceCloseResourcesProgressIndicator.title={0} Closing Case Resources",
2460 "# {0} - service name",
"# {1} - exception message",
"Case.servicesException.serviceResourcesCloseError=Could not close case resources for {0} service: {1}"
2472 Bundle.Case_serviceCloseResourcesProgressIndicator_title(service.getServiceName()));
2476 progressIndicator.
start(Bundle.Case_progressMessage_preparing());
2478 String threadNameSuffix = service.getServiceName().replaceAll(
"[ ]",
"-");
2479 threadNameSuffix = threadNameSuffix.toLowerCase();
2481 ExecutorService executor = Executors.newSingleThreadExecutor(threadFactory);
2482 Future<Void> future = executor.submit(() -> {
2483 service.closeCaseResources(context);
2488 }
catch (InterruptedException ex) {
2489 Case.
logger.log(Level.SEVERE, String.format(
"Unexpected interrupt while waiting on %s service to close case resources", service.getServiceName()), ex);
2490 }
catch (CancellationException ex) {
2491 Case.
logger.log(Level.SEVERE, String.format(
"Unexpected cancellation while waiting on %s service to close case resources", service.getServiceName()), ex);
2492 }
catch (ExecutionException ex) {
2493 Case.
logger.log(Level.SEVERE, String.format(
"%s service failed to open case resources", service.getServiceName()), ex);
2496 Bundle.Case_servicesException_notificationTitle(service.getServiceName()),
2497 Bundle.Case_servicesException_serviceResourcesCloseError(service.getServiceName(), ex.getLocalizedMessage())));
2501 progressIndicator.
finish();
2514 @Messages({
"Case.creationException.couldNotAcquireDirLock=Failed to get lock on case directory"})
2519 throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireDirLock());
2522 throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireDirLock(), ex);
2537 logger.log(Level.SEVERE, String.format(
"Failed to release shared case directory lock for %s", caseDir), ex);
2550 if (!subDirectory.exists()) {
2551 subDirectory.mkdirs();
2553 return subDirectory.toString();
2569 "Case.exceptionMessage.errorsDeletingCase=Errors occured while deleting the case. See the application log for details."
2572 boolean errorsOccurred =
false;
2576 errorsOccurred =
true;
2582 }
catch (CaseActionException ex) {
2583 errorsOccurred =
true;
2589 if (errorsOccurred) {
2590 throw new CaseActionException(Bundle.Case_exceptionMessage_errorsDeletingCase());
2614 "Case.progressMessage.connectingToCoordSvc=Connecting to coordination service...",
2615 "# {0} - exception message",
"Case.exceptionMessage.failedToConnectToCoordSvc=Failed to connect to coordination service:\n{0}.",
2616 "Case.exceptionMessage.cannotGetLockToDeleteCase=Cannot delete case because it is open for another user or host.",
2617 "# {0} - exception message",
"Case.exceptionMessage.failedToLockCaseForDeletion=Failed to exclusively lock case for deletion:\n{0}.",
2618 "Case.progressMessage.fetchingCoordSvcNodeData=Fetching coordination service node data for the case...",
2619 "# {0} - exception message",
"Case.exceptionMessage.failedToFetchCoordSvcNodeData=Failed to fetch coordination service node data:\n{0}.",
2620 "Case.progressMessage.deletingResourcesCoordSvcNode=Deleting case resources coordination service node...",
2621 "Case.progressMessage.deletingCaseDirCoordSvcNode=Deleting case directory coordination service node..."
2624 progressIndicator.
progress(Bundle.Case_progressMessage_connectingToCoordSvc());
2630 throw new CaseActionException(Bundle.Case_exceptionMessage_failedToConnectToCoordSvc(ex.getLocalizedMessage()));
2634 boolean errorsOccurred =
false;
2636 if (dirLock == null) {
2638 throw new CaseActionException(Bundle.Case_exceptionMessage_cannotGetLockToDeleteCase());
2641 progressIndicator.
progress(Bundle.Case_progressMessage_fetchingCoordSvcNodeData());
2646 throw new CaseActionException(Bundle.Case_exceptionMessage_failedToFetchCoordSvcNodeData(ex.getLocalizedMessage()));
2651 progressIndicator.
progress(Bundle.Case_progressMessage_deletingResourcesCoordSvcNode());
2657 errorsOccurred =
true;
2658 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);
2660 }
catch (InterruptedException ex) {
2661 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);
2666 throw new CaseActionException(Bundle.Case_exceptionMessage_failedToLockCaseForDeletion(ex.getLocalizedMessage()));
2669 if (!errorsOccurred) {
2670 progressIndicator.
progress(Bundle.Case_progressMessage_deletingCaseDirCoordSvcNode());
2676 errorsOccurred =
true;
2680 if (errorsOccurred) {
2681 throw new CaseActionException(Bundle.Case_exceptionMessage_errorsDeletingCase());
2711 boolean errorsOccurred =
false;
2718 errorsOccurred =
true;
2721 errorsOccurred =
true;
2723 }
catch (CaseActionException ex) {
2724 errorsOccurred =
true;
2727 return errorsOccurred;
2751 "Case.progressMessage.deletingCaseDatabase=Deleting case database..."
2755 progressIndicator.
progress(Bundle.Case_progressMessage_deletingCaseDatabase());
2756 logger.log(Level.INFO, String.format(
"Deleting case database for %s (%s) in %s", caseNodeData.
getDisplayName(), caseNodeData.
getName(), caseNodeData.
getDirectory()));
2758 String url =
"jdbc:postgresql://" + info.getHost() +
":" + info.getPort() +
"/postgres";
2759 Class.forName(
"org.postgresql.Driver");
2760 try (Connection connection = DriverManager.getConnection(url, info.getUserName(), info.getPassword()); Statement statement = connection.createStatement()) {
2761 String dbExistsQuery =
"SELECT 1 from pg_database WHERE datname = '" + metadata.
getCaseDatabaseName() +
"'";
2762 try (ResultSet queryResult = statement.executeQuery(dbExistsQuery)) {
2763 if (queryResult.next()) {
2765 statement.execute(deleteCommand);
2806 "Case.progressMessage.deletingTextIndex=Deleting text index..."
2809 progressIndicator.
progress(Bundle.Case_progressMessage_deletingTextIndex());
2811 searchService.deleteTextIndex(metadata);
2847 "Case.progressMessage.deletingCaseDirectory=Deleting case directory..."
2850 progressIndicator.
progress(Bundle.Case_progressMessage_deletingCaseDirectory());
2852 throw new CaseActionException(String.format(
"Failed to delete %s", metadata.
getCaseDirectory()));
2864 "Case.progressMessage.removingCaseFromRecentCases=Removing case from Recent Cases menu..."
2868 progressIndicator.
progress(Bundle.Case_progressMessage_removingCaseFromRecentCases());
2869 SwingUtilities.invokeLater(() -> {
2870 RecentCases.getInstance().removeRecentCase(metadata.
getCaseDisplayName(), metadata.getFilePath().toString());
2885 boolean isNodeNodeEx =
false;
2886 Throwable cause = ex.getCause();
2887 if (cause != null) {
2888 String causeMessage = cause.getMessage();
2889 isNodeNodeEx = causeMessage.contains(NO_NODE_ERROR_MSG_FRAGMENT);
2891 return isNodeNodeEx;
2910 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);
3001 ((ModalDialogProgressIndicator) progressIndicator).
setCancelling(cancellationMessage);
3030 return new Thread(task, threadName);
3067 public static void create(String caseDir, String caseDisplayName, String caseNumber, String examiner)
throws CaseActionException {
3092 public static void create(String caseDir, String caseDisplayName, String caseNumber, String examiner,
CaseType caseType)
throws CaseActionException {
3108 public static void open(String caseMetadataFilePath)
throws CaseActionException {
3165 return new File(filePath).isFile();
3204 return "ModuleOutput";
3217 public static PropertyChangeSupport
3219 return new PropertyChangeSupport(
Case.class
3251 public Image
addImage(String imgPath,
long imgId, String timeZone)
throws CaseActionException {
3253 Image newDataSource = caseDb.getImageById(imgId);
3255 return newDataSource;
3256 }
catch (TskCoreException ex) {
3257 throw new CaseActionException(NbBundle.getMessage(
this.getClass(),
"Case.addImg.exception.msg"), ex);
3284 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)
void rebroadcastArtifactsPosted(Blackboard.ArtifactsPostedEvent event)
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)
void rebroadcastTimelineEventCreated(TimelineManager.TimelineEventAddedEvent event)
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)
void fireModuleDataEvent(ModuleDataEvent moduleDataEvent)
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)
final TSKCaseRepublisher tskEventForwarder
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)
static synchronized IngestServices getInstance()
String getExaminerEmail()