1 /*
2 * Autopsy Forensic Browser
3 *
4 * Copyright 2012-2020 Basis Technology Corp.
5 * Contact: carrier <at> sleuthkit <dot> org
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 */
19 package org.sleuthkit.autopsy.casemodule;
20
22 import com.google.common.annotations.Beta;
23 import com.google.common.eventbus.Subscribe;
24 import com.google.common.util.concurrent.ThreadFactoryBuilder;
26 import java.awt.Frame;
27 import java.awt.event.ActionEvent;
28 import java.awt.event.ActionListener;
29 import java.beans.PropertyChangeListener;
30 import java.beans.PropertyChangeSupport;
31 import java.io.File;
32 import java.nio.file.InvalidPathException;
33 import java.nio.file.Path;
34 import java.nio.file.Paths;
35 import java.sql.Connection;
36 import java.sql.DriverManager;
37 import java.sql.ResultSet;
38 import java.sql.SQLException;
39 import java.sql.Statement;
40 import java.text.SimpleDateFormat;
41 import java.util.Collection;
42 import java.util.Date;
43 import java.util.HashMap;
44 import java.util.HashSet;
45 import java.util.List;
46 import java.util.Map;
47 import java.util.Set;
48 import java.util.TimeZone;
49 import java.util.UUID;
50 import java.util.concurrent.CancellationException;
51 import java.util.concurrent.ExecutionException;
52 import java.util.concurrent.ExecutorService;
53 import java.util.concurrent.Executors;
54 import java.util.concurrent.Future;
55 import java.util.concurrent.ThreadFactory;
56 import java.util.concurrent.TimeUnit;
57 import java.util.logging.Level;
58 import java.util.stream.Collectors;
59 import java.util.stream.Stream;
60 import javax.annotation.concurrent.GuardedBy;
61 import javax.annotation.concurrent.ThreadSafe;
62 import javax.swing.JOptionPane;
63 import javax.swing.SwingUtilities;
64 import org.openide.util.Lookup;
65 import org.openide.util.NbBundle;
66 import org.openide.util.NbBundle.Messages;
67 import org.openide.util.actions.CallableSystemAction;
68 import org.openide.windows.WindowManager;
137 import org.
sleuthkit.datamodel.TskUnsupportedSchemaVersionException;
138
143
163 = Executors.newSingleThreadExecutor(
new ThreadFactoryBuilder().setNameFormat(
"case-open-file-systems-%d").build());
173
174 /*
175 * Get a reference to the main window of the desktop application to use to
176 * parent pop up dialogs and initialize the application name for use in
177 * changing the main window title.
178 */
179 static {
180 WindowManager.getDefault().invokeWhenUIReady(() -> {
181 mainFrame = WindowManager.getDefault().getMainWindow();
182 });
183 }
184
189
192
194
203 if (typeName != null) {
205 if (typeName.equalsIgnoreCase(c.toString())) {
206 return c;
207 }
208 }
209 }
210 return null;
211 }
212
218 @Override
220 return typeName;
221 }
222
228 @Messages({
229 "Case_caseType_singleUser=Single-user case",
230 "Case_caseType_multiUser=Multi-user case"
231 })
233 if (fromString(typeName) == SINGLE_USER_CASE) {
234 return Bundle.Case_caseType_singleUser();
235 } else {
236 return Bundle.Case_caseType_multiUser();
237 }
238 }
239
246 this.typeName = typeName;
247 }
248
259 @Deprecated
261 return (otherTypeName == null) ? false : typeName.equals(otherTypeName);
262 }
263
264 };
265
271
279 @Deprecated
288 @Deprecated
297 @Deprecated
408 /*
409 * An item in the central repository has had its comment modified. The
410 * old value is null, the new value is string for current comment.
411 */
413
414 };
415
422
423 @Subscribe
426 }
427
428 @SuppressWarnings("deprecation")
429 @Subscribe
431 for (BlackboardArtifact.Type artifactType : event.getArtifactTypes()) {
432 /*
433 * IngestServices.fireModuleDataEvent is deprecated to
434 * discourage ingest module writers from using it (they should
435 * use org.sleuthkit.datamodel.Blackboard.postArtifact(s)
436 * instead), but a way to publish
437 * Blackboard.ArtifactsPostedEvents from the SleuthKit layer as
438 * Autopsy ModuleDataEvents is still needed.
439 */
441 event.getModuleName(),
442 artifactType,
443 event.getArtifacts(artifactType)));
444 }
445 }
446 }
447
456 .map(Events::toString)
457 .collect(Collectors.toSet()), listener);
458 }
459
468 .map(Events::toString)
469 .collect(Collectors.toSet()), listener);
470 }
471
480 @Deprecated
483 }
484
492 eventTypes.forEach((
Events event) -> {
494 });
495 }
496
505 @Deprecated
508 }
509
518 }
519
528 }
529
537 eventTypes.forEach((
Events event) -> {
539 });
540 }
541
551 return !(caseName.contains("\\") || caseName.contains("/") || caseName.contains(":")
552 || caseName.contains("*") || caseName.contains("?") || caseName.contains("\"")
553 || caseName.contains("<") || caseName.contains(">") || caseName.contains("|"));
554 }
555
580 @Deprecated
583 }
584
604 @Messages({
605 "Case.exceptionMessage.emptyCaseName=Must specify a case name.",
606 "Case.exceptionMessage.emptyCaseDir=Must specify a case directory path."
607 })
610 throw new CaseActionException(Bundle.Case_exceptionMessage_emptyCaseName());
611 }
612 if (caseDir.isEmpty()) {
613 throw new CaseActionException(Bundle.Case_exceptionMessage_emptyCaseDir());
614 }
616 }
617
631 @Messages({
632 "# {0} - exception message", "Case.exceptionMessage.failedToReadMetadata=Failed to read case metadata:\n{0}.",
633 "Case.exceptionMessage.cannotOpenMultiUserCaseNoSettings=Multi-user settings are missing (see Tools, Options, Multi-user tab), cannot open a multi-user case."
634 })
637 try {
638 metadata =
new CaseMetadata(Paths.get(caseMetadataFilePath));
640 throw new CaseActionException(Bundle.Case_exceptionMessage_failedToReadMetadata(ex.getLocalizedMessage()), ex);
641 }
643 throw new CaseActionException(Bundle.Case_exceptionMessage_cannotOpenMultiUserCaseNoSettings());
644 }
646 }
647
654 return currentCase != null;
655 }
656
665 try {
668 /*
669 * Throw a runtime exception, since this is a programming error.
670 */
671 throw new IllegalStateException(NbBundle.getMessage(
Case.class,
"Case.getCurCase.exception.noneOpen"), ex);
672 }
673 }
674
690 /*
691 * TODO (JIRA-3825): Introduce a reference counting scheme for this get
692 * case method.
693 */
695 if (openCase == null) {
696 throw new NoCurrentCaseException(NbBundle.getMessage(
Case.class,
"Case.getCurCase.exception.noneOpen"));
697 } else {
698 return openCase;
699 }
700 }
701
710 @Messages({
711 "# {0} - exception message", "Case.closeException.couldNotCloseCase=Error closing case: {0}",
712 "Case.progressIndicatorTitle.closingCase=Closing Case"
713 })
716 if (null == currentCase) {
717 return;
718 }
720 try {
722 logger.log(Level.INFO, "Closing current case {0} ({1}) in {2}", new Object[]{closedCase.getDisplayName(), closedCase.getName(), closedCase.getCaseDirectory()}); //NON-NLS
723 currentCase = null;
725 logger.log(Level.INFO, "Closed current case {0} ({1}) in {2}", new Object[]{closedCase.getDisplayName(), closedCase.getName(), closedCase.getCaseDirectory()}); //NON-NLS
726 } catch (CaseActionException ex) {
728 throw ex;
729 } finally {
732 }
733 }
734 }
735 }
736
747 if (null == currentCase) {
748 return;
749 }
753 }
754 }
755
766 @Messages({
767 "Case.progressIndicatorTitle.deletingDataSource=Removing Data Source"
768 })
769 static void deleteDataSourceFromCurrentCase(Long dataSourceObjectID) throws CaseActionException {
771 if (null == currentCase) {
772 return;
773 }
774
775 /*
776 * Close the current case to release the shared case lock.
777 */
778 CaseMetadata caseMetadata = currentCase.getMetadata();
780
781 /*
782 * Re-open the case with an exclusive case lock, delete the data
783 * source, and close the case again, releasing the exclusive case
784 * lock.
785 */
786 Case theCase =
new Case(caseMetadata);
787 theCase.doOpenCaseAction(Bundle.Case_progressIndicatorTitle_deletingDataSource(), theCase::deleteDataSource, CaseLockType.EXCLUSIVE, false, dataSourceObjectID);
788
789 /*
790 * Re-open the case with a shared case lock.
791 */
793 }
794 }
795
807 @Messages({
808 "Case.progressIndicatorTitle.deletingCase=Deleting Case",
809 "Case.exceptionMessage.cannotDeleteCurrentCase=Cannot delete current case, it must be closed first.",
810 "# {0} - case display name", "Case.exceptionMessage.deletionInterrupted=Deletion of the case {0} was cancelled."
811 })
814 if (null != currentCase) {
815 throw new CaseActionException(Bundle.Case_exceptionMessage_cannotDeleteCurrentCase());
816 }
817 }
818
822 } else {
824 }
825 progressIndicator.
start(Bundle.Case_progressMessage_preparing());
826 try {
829 } else {
830 try {
832 } catch (InterruptedException ex) {
833 /*
834 * Note that task cancellation is not currently supported
835 * for this code path, so this catch block is not expected
836 * to be executed.
837 */
838 throw new CaseActionException(Bundle.Case_exceptionMessage_deletionInterrupted(metadata.
getCaseDisplayName()), ex);
839 }
840 }
841 } finally {
842 progressIndicator.
finish();
843 }
844 }
845
856 @Messages({
857 "Case.progressIndicatorTitle.creatingCase=Creating Case",
858 "Case.progressIndicatorTitle.openingCase=Opening Case",
859 "Case.exceptionMessage.cannotLocateMainWindow=Cannot locate main application window"
860 })
863 if (null != currentCase) {
864 try {
866 } catch (CaseActionException ex) {
867 /*
868 * Notify the user and continue (the error has already been
869 * logged in closeCurrentCase.
870 */
872 }
873 }
874 try {
875 logger.log(Level.INFO, "Opening {0} ({1}) in {2} as the current case", new Object[]{newCurrentCase.getDisplayName(), newCurrentCase.getName(), newCurrentCase.getCaseDirectory()}); //NON-NLS
876 String progressIndicatorTitle;
878 if (isNewCase) {
879 progressIndicatorTitle = Bundle.Case_progressIndicatorTitle_creatingCase();
880 openCaseAction = newCurrentCase::create;
881 } else {
882 progressIndicatorTitle = Bundle.Case_progressIndicatorTitle_openingCase();
883 openCaseAction = newCurrentCase::open;
884 }
886 currentCase = newCurrentCase;
887 logger.log(Level.INFO, "Opened {0} ({1}) in {2} as the current case", new Object[]{newCurrentCase.getDisplayName(), newCurrentCase.getName(), newCurrentCase.getCaseDirectory()}); //NON-NLS
890 }
893 logger.log(Level.INFO, String.format(
"Cancelled opening %s (%s) in %s as the current case", newCurrentCase.
getDisplayName(), newCurrentCase.
getName(), newCurrentCase.
getCaseDirectory()));
//NON-NLS
894 throw ex;
895 } catch (CaseActionException ex) {
896 logger.log(Level.SEVERE, String.format(
"Error opening %s (%s) in %s as the current case", newCurrentCase.
getDisplayName(), newCurrentCase.
getName(), newCurrentCase.
getCaseDirectory()), ex);
//NON-NLS
897 throw ex;
898 }
899 }
900 }
901
911 /*
912 * Replace all non-ASCII characters.
913 */
914 String uniqueCaseName = caseDisplayName.replaceAll("[^\\p{ASCII}]", "_"); //NON-NLS
915
916 /*
917 * Replace all control characters.
918 */
919 uniqueCaseName = uniqueCaseName.replaceAll("[\\p{Cntrl}]", "_"); //NON-NLS
920
921 /*
922 * Replace /, ,円 :, ?, space, ' ".
923 */
924 uniqueCaseName = uniqueCaseName.replaceAll("[ /?:'\"\\\\]", "_"); //NON-NLS
925
926 /*
927 * Make it all lowercase.
928 */
929 uniqueCaseName = uniqueCaseName.toLowerCase();
930
931 /*
932 * Add a time stamp for uniqueness.
933 */
934 SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
935 Date date = new Date();
936 uniqueCaseName = uniqueCaseName + "_" + dateFormat.format(date);
937
938 return uniqueCaseName;
939 }
940
951 /*
952 * Check the case directory path and permissions. The case directory may
953 * already exist.
954 */
955 File caseDir = new File(caseDirPath);
956 if (caseDir.exists()) {
957 if (caseDir.isFile()) {
958 throw new CaseActionException(NbBundle.getMessage(
Case.class,
"Case.createCaseDir.exception.existNotDir", caseDirPath));
959 } else if (!caseDir.canRead() || !caseDir.canWrite()) {
960 throw new CaseActionException(NbBundle.getMessage(
Case.class,
"Case.createCaseDir.exception.existCantRW", caseDirPath));
961 }
962 }
963
964 /*
965 * Create the case directory, if it does not already exist.
966 */
967 if (!caseDir.mkdirs()) {
968 throw new CaseActionException(NbBundle.getMessage(
Case.class,
"Case.createCaseDir.exception.cantCreate", caseDirPath));
969 }
970
971 /*
972 * Create the subdirectories of the case directory, if they do not
973 * already exist. Note that multi-user cases get an extra layer of
974 * subdirectories, one subdirectory per application host machine.
975 */
976 String hostPathComponent = "";
979 }
980
981 Path exportDir = Paths.get(caseDirPath, hostPathComponent, EXPORT_FOLDER);
982 if (!exportDir.toFile().mkdirs()) {
983 throw new CaseActionException(NbBundle.getMessage(
Case.class,
"Case.createCaseDir.exception.cantCreateCaseDir", exportDir));
984 }
985
986 Path logsDir = Paths.get(caseDirPath, hostPathComponent, LOG_FOLDER);
987 if (!logsDir.toFile().mkdirs()) {
988 throw new CaseActionException(NbBundle.getMessage(
Case.class,
"Case.createCaseDir.exception.cantCreateCaseDir", logsDir));
989 }
990
991 Path tempDir = Paths.get(caseDirPath, hostPathComponent, TEMP_FOLDER);
992 if (!tempDir.toFile().mkdirs()) {
993 throw new CaseActionException(NbBundle.getMessage(
Case.class,
"Case.createCaseDir.exception.cantCreateCaseDir", tempDir));
994 }
995
996 Path cacheDir = Paths.get(caseDirPath, hostPathComponent, CACHE_FOLDER);
997 if (!cacheDir.toFile().mkdirs()) {
998 throw new CaseActionException(NbBundle.getMessage(
Case.class,
"Case.createCaseDir.exception.cantCreateCaseDir", cacheDir));
999 }
1000
1001 Path moduleOutputDir = Paths.get(caseDirPath, hostPathComponent, MODULE_FOLDER);
1002 if (!moduleOutputDir.toFile().mkdirs()) {
1003 throw new CaseActionException(NbBundle.getMessage(
Case.class,
"Case.createCaseDir.exception.cantCreateModDir", moduleOutputDir));
1004 }
1005
1006 Path reportsDir = Paths.get(caseDirPath, hostPathComponent, REPORTS_FOLDER);
1007 if (!reportsDir.toFile().mkdirs()) {
1008 throw new CaseActionException(NbBundle.getMessage(
Case.class,
"Case.createCaseDir.exception.cantCreateReportsDir", reportsDir));
1009 }
1010 }
1011
1019 static Map<Long, String> getImagePaths(SleuthkitCase db) {
1020 Map<Long, String> imgPaths = new HashMap<>();
1021 try {
1022 Map<Long, List<String>> imgPathsList = db.getImagePaths();
1023 for (Map.Entry<Long, List<String>> entry : imgPathsList.entrySet()) {
1024 if (entry.getValue().size() > 0) {
1025 imgPaths.put(entry.getKey(), entry.getValue().get(0));
1026 }
1027 }
1028 } catch (TskCoreException ex) {
1029 logger.log(Level.SEVERE, "Error getting image paths", ex); //NON-NLS
1030 }
1031 return imgPaths;
1032 }
1033
1044 @Messages({
1045 "Case.creationException.couldNotAcquireResourcesLock=Failed to get lock on case resources"
1046 })
1048 try {
1049 Path caseDirPath = Paths.get(caseDir);
1052 return lock;
1053 } catch (InterruptedException ex) {
1056 throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireResourcesLock(), ex);
1057 }
1058 }
1059
1061 //Method should become unnecessary once technical debt story 3334 is done.
1063 //Available version number is version number for this application
1065 } else {
1067 }
1068 }
1069
1075 SwingUtilities.invokeLater(() -> {
1076 /*
1077 * If the case database was upgraded for a new schema and a
1078 * backup database was created, notify the user.
1079 */
1081 String backupDbPath = caseDb.getBackupDatabasePath();
1082 if (null != backupDbPath) {
1083 JOptionPane.showMessageDialog(
1084 mainFrame,
1085 NbBundle.getMessage(
Case.class,
"Case.open.msgDlg.updated.msg", backupDbPath),
1086 NbBundle.getMessage(
Case.class,
"Case.open.msgDlg.updated.title"),
1087 JOptionPane.INFORMATION_MESSAGE);
1088 }
1089
1090 /*
1091 * Look for the files for the data sources listed in the case
1092 * database and give the user the opportunity to locate any that
1093 * are missing.
1094 */
1095 Map<Long, String> imgPaths = getImagePaths(caseDb);
1096 for (Map.Entry<Long, String> entry : imgPaths.entrySet()) {
1097 long obj_id = entry.getKey();
1098 String path = entry.getValue();
1100 if (!fileExists) {
1101 int response = JOptionPane.showConfirmDialog(
1102 mainFrame,
1103 NbBundle.getMessage(
Case.class,
"Case.checkImgExist.confDlg.doesntExist.msg", path),
1104 NbBundle.getMessage(
Case.class,
"Case.checkImgExist.confDlg.doesntExist.title"),
1105 JOptionPane.YES_NO_OPTION);
1106 if (response == JOptionPane.YES_OPTION) {
1107 MissingImageDialog.makeDialog(obj_id, caseDb);
1108 } else {
1109 logger.log(Level.SEVERE, "User proceeding with missing image files"); //NON-NLS
1110
1111 }
1112 }
1113 }
1114
1115 /*
1116 * Enable the case-specific actions.
1117 */
1120 CallableSystemAction.get(CaseDetailsAction.class).setEnabled(true);
1128
1129 /*
1130 * Add the case to the recent cases tracker that supplies a list
1131 * of recent cases to the recent cases menu item and the
1132 * open/create case dialog.
1133 */
1134 RecentCases.getInstance().addRecentCase(newCurrentCase.
getDisplayName(), newCurrentCase.getMetadata().getFilePath().toString());
1135
1136 /*
1137 * Open the top components (windows within the main application
1138 * window).
1139 *
1140 * Note: If the core windows are not opened here, they will be
1141 * opened via the DirectoryTreeTopComponent 'propertyChange()'
1142 * method on a DATA_SOURCE_ADDED event.
1143 */
1144 if (newCurrentCase.
hasData()) {
1146 }
1147
1148 /*
1149 * Reset the main window title to:
1150 *
1151 * [curent case display name] - [application name].
1152 */
1154 });
1155 }
1156 }
1157
1158 /*
1159 * Update the GUI to to reflect the lack of a current case.
1160 */
1163 SwingUtilities.invokeLater(() -> {
1164 /*
1165 * Close the top components (windows within the main application
1166 * window).
1167 */
1169
1170 /*
1171 * Disable the case-specific menu items.
1172 */
1173 CallableSystemAction.get(
AddImageAction.class).setEnabled(
false);
1175 CallableSystemAction.get(CaseDetailsAction.class).setEnabled(false);
1177 CallableSystemAction.get(CaseDeleteAction.class).setEnabled(false);
1183
1184 /*
1185 * Clear the notifications in the notfier component in the lower
1186 * right hand corner of the main application window.
1187 */
1189
1190 /*
1191 * Reset the main window title to be just the application name,
1192 * instead of [curent case display name] - [application name].
1193 */
1195 });
1196 }
1197 }
1198
1203 File tempFolder = new File(tempSubDirPath);
1204 if (tempFolder.isDirectory()) {
1205 File[] files = tempFolder.listFiles();
1206 if (files.length > 0) {
1207 for (File file : files) {
1208 if (file.isDirectory()) {
1210 } else {
1211 file.delete();
1212 }
1213 }
1214 }
1215 }
1216 }
1217
1225 }
1226
1234 }
1235
1243 }
1244
1252 }
1253
1261 }
1262
1270 }
1271
1279 }
1280
1288 }
1289
1297 }
1298
1306 }
1307
1315 }
1316
1324 }
1325
1336 Path hostPath;
1339 } else {
1340 hostPath = Paths.get(caseDirectory);
1341 }
1342 if (!hostPath.toFile().exists()) {
1343 hostPath.toFile().mkdirs();
1344 }
1345 return hostPath.toString();
1346 }
1347
1356 }
1357
1366 }
1367
1376 }
1377
1386 }
1387
1396 }
1397
1406 }
1407
1416 }
1417
1428 return path.subpath(path.getNameCount() - 2, path.getNameCount()).toString();
1429 } else {
1430 return path.subpath(path.getNameCount() - 1, path.getNameCount()).toString();
1431 }
1432 }
1433
1444 return caseDb.getRootObjects();
1445 }
1446
1453 Set<TimeZone> timezones = new HashSet<>();
1454 try {
1456 final Content dataSource = c.getDataSource();
1457 if ((dataSource != null) && (dataSource instanceof Image)) {
1458 Image image = (Image) dataSource;
1459 timezones.add(TimeZone.getTimeZone(image.getTimeZone()));
1460 }
1461 }
1462 } catch (TskCoreException ex) {
1463 logger.log(Level.SEVERE, "Error getting data source time zones", ex); //NON-NLS
1464 }
1465 return timezones;
1466 }
1467
1476 }
1477
1485 boolean hasDataSources = false;
1486 try {
1488 } catch (TskCoreException ex) {
1489 logger.log(Level.SEVERE, "Error accessing case database", ex); //NON-NLS
1490 }
1491 return hasDataSources;
1492 }
1493
1506 }
1507
1520 }
1521
1535 }
1536
1548 }
1549
1559 }
1560
1572 }
1573
1583 }
1584
1593 //leaving new value of changedTagName as null, because we do not currently support changing the display name of a tag.
1595 }
1596
1608 try {
1611 logger.log(Level.WARNING, "Unable to send notifcation regarding comment change due to no current case being open", ex);
1612 }
1613 }
1614
1624 }
1625
1637 }
1638
1648 }
1649
1661 public void addReport(String localPath, String srcModuleName, String reportName)
throws TskCoreException {
1662 addReport(localPath, srcModuleName, reportName, null);
1663 }
1664
1679 public Report
addReport(String localPath, String srcModuleName, String reportName, Content parent)
throws TskCoreException {
1680 String normalizedLocalPath;
1681 try {
1682 if (localPath.toLowerCase().contains("http:")) {
1683 normalizedLocalPath = localPath;
1684 } else {
1685 normalizedLocalPath = Paths.get(localPath).normalize().toString();
1686 }
1687 } catch (InvalidPathException ex) {
1688 String errorMsg = "Invalid local path provided: " + localPath; // NON-NLS
1689 throw new TskCoreException(errorMsg, ex);
1690 }
1691 Report report = this.caseDb.addReport(normalizedLocalPath, srcModuleName, reportName, parent);
1693 return report;
1694 }
1695
1705 return this.caseDb.getAllReports();
1706 }
1707
1716 public void deleteReports(Collection<? extends Report> reports)
throws TskCoreException {
1717 for (Report report : reports) {
1718 this.caseDb.deleteReport(report);
1720 }
1721 }
1722
1730 }
1731
1739 @Messages({
1740 "Case.exceptionMessage.metadataUpdateError=Failed to update case metadata"
1741 })
1742 void updateCaseDetails(CaseDetails caseDetails) throws CaseActionException {
1744 try {
1745 metadata.setCaseDetails(caseDetails);
1746 } catch (CaseMetadataException ex) {
1747 throw new CaseActionException(Bundle.Case_exceptionMessage_metadataUpdateError(), ex);
1748 }
1750 try {
1751 CaseNodeData nodeData = CaseNodeData.readCaseNodeData(metadata.
getCaseDirectory());
1753 CaseNodeData.writeCaseNodeData(nodeData);
1754 } catch (CaseNodeDataException | InterruptedException ex) {
1755 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotUpdateCaseNodeData(ex.getLocalizedMessage()), ex);
1756 }
1757 }
1758 if (!oldCaseDetails.getCaseNumber().equals(caseDetails.
getCaseNumber())) {
1759 eventPublisher.
publish(
new AutopsyEvent(Events.NUMBER.toString(), oldCaseDetails.getCaseNumber(), caseDetails.
getCaseNumber()));
1760 }
1761 if (!oldCaseDetails.getExaminerName().equals(caseDetails.
getExaminerName())) {
1762 eventPublisher.
publish(
new AutopsyEvent(Events.NUMBER.toString(), oldCaseDetails.getExaminerName(), caseDetails.
getExaminerName()));
1763 }
1765 eventPublisher.
publish(
new AutopsyEvent(Events.NAME.toString(), oldCaseDetails.getCaseDisplayName(), caseDetails.
getCaseDisplayName()));
1766 }
1767 eventPublisher.
publish(
new AutopsyEvent(Events.CASE_DETAILS.toString(), oldCaseDetails, caseDetails));
1768 if (RuntimeProperties.runningWithGUI()) {
1769 SwingUtilities.invokeLater(() -> {
1771 try {
1772 RecentCases.getInstance().updateRecentCase(oldCaseDetails.getCaseDisplayName(), metadata.getFilePath().toString(), caseDetails.
getCaseDisplayName(), metadata.getFilePath().toString());
1773 } catch (Exception ex) {
1774 logger.log(Level.SEVERE, "Error updating case name in UI", ex); //NON-NLS
1775 }
1776 });
1777 }
1778 }
1779
1794 }
1795
1802 metadata = caseMetaData;
1804 }
1805
1835 @Messages({
1836 "Case.progressIndicatorCancelButton.label=Cancel",
1837 "Case.progressMessage.preparing=Preparing...",
1838 "Case.progressMessage.cancelling=Cancelling...",
1839 "Case.exceptionMessage.cancelled=Cancelled.",
1840 "# {0} - exception message", "Case.exceptionMessage.execExceptionWrapperMessage={0}"
1841 })
1843 /*
1844 * Create and start either a GUI progress indicator (with or without a
1845 * cancel button) or a logging progress indicator.
1846 */
1850 if (allowCancellation) {
1853 mainFrame,
1854 progressIndicatorTitle,
1855 new String[]{Bundle.Case_progressIndicatorCancelButton_label()},
1856 Bundle.Case_progressIndicatorCancelButton_label(),
1857 cancelButtonListener);
1858 } else {
1860 mainFrame,
1861 progressIndicatorTitle);
1862 }
1863 } else {
1865 }
1866 progressIndicator.
start(Bundle.Case_progressMessage_preparing());
1867
1868 /*
1869 * Do the case action in the single thread in the case action executor.
1870 * If the case is a multi-user case, a case lock is acquired and held
1871 * until explictly released and an exclusive case resources lock is
1872 * aquired and held for the duration of the action.
1873 */
1875 caseActionExecutor = Executors.newSingleThreadExecutor(threadFactory);
1876 Future<Void> future = caseActionExecutor.submit(() -> {
1878 caseAction.
execute(progressIndicator, additionalParams);
1879 } else {
1882 if (null == resourcesLock) {
1883 throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireResourcesLock());
1884 }
1885 caseAction.
execute(progressIndicator, additionalParams);
1886 } catch (CaseActionException ex) {
1888 throw ex;
1889 }
1890 }
1891 return null;
1892 });
1893 if (null != cancelButtonListener) {
1895 }
1896
1897 /*
1898 * Wait for the case action task to finish.
1899 */
1900 try {
1901 future.get();
1902 } catch (InterruptedException discarded) {
1903 /*
1904 * The thread this method is running in has been interrupted.
1905 */
1906 if (null != cancelButtonListener) {
1908 } else {
1909 future.cancel(true);
1910 }
1912 } catch (CancellationException discarded) {
1913 /*
1914 * The case action has been cancelled.
1915 */
1918 } catch (ExecutionException ex) {
1919 /*
1920 * The case action has thrown an exception.
1921 */
1923 throw new CaseActionException(Bundle.Case_exceptionMessage_execExceptionWrapperMessage(ex.getCause().getLocalizedMessage()), ex);
1924 } finally {
1925 progressIndicator.
finish();
1926 }
1927 }
1928
1945 assert (additionalParams == null);
1946 try {
1964 return null;
1965
1966 } catch (CaseActionException ex) {
1967 /*
1968 * Cancellation or failure. The sleep is a little hack to clear the
1969 * interrupted flag for this thread if this is a cancellation
1970 * scenario, so that the clean up can run to completion in the
1971 * current thread.
1972 */
1973 try {
1974 Thread.sleep(1);
1975 } catch (InterruptedException discarded) {
1976 }
1977 close(progressIndicator);
1978 throw ex;
1979 }
1980 }
1981
1997 assert (additionalParams == null);
1998 try {
2015 return null;
2016
2017 } catch (CaseActionException ex) {
2018 /*
2019 * Cancellation or failure. The sleep is a little hack to clear the
2020 * interrupted flag for this thread if this is a cancellation
2021 * scenario, so that the clean up can run to completion in the
2022 * current thread.
2023 */
2024 try {
2025 Thread.sleep(1);
2026 } catch (InterruptedException discarded) {
2027 }
2028 close(progressIndicator);
2029 throw ex;
2030 }
2031 }
2032
2042 @Messages({
2043 "# {0} - case", "Case.openFileSystems.retrievingImages=Retrieving images for case: {0}...",
2044 "# {0} - image", "Case.openFileSystems.openingImage=Opening all filesystems for image: {0}..."
2045 })
2047 if (backgroundOpenFileSystemsFuture != null && !backgroundOpenFileSystemsFuture.isDone()) {
2048 backgroundOpenFileSystemsFuture.cancel(true);
2049 }
2050
2053 }
2054
2060
2065
2077 caseName = (this.tskCase != null) ? this.tskCase.getDatabaseName() : "";
2078 }
2079
2088 if (Thread.interrupted()) {
2089 throw new InterruptedException();
2090 }
2091 }
2092
2099 progressIndicator.
progress(Bundle.Case_openFileSystems_retrievingImages(caseName));
2100 try {
2101 return this.tskCase.getImages();
2102 } catch (TskCoreException ex) {
2103 logger.log(
2104 Level.SEVERE,
2105 String.format("Could not obtain images while opening case: %s.", caseName),
2106 ex);
2107
2108 return null;
2109 }
2110 }
2111
2121 private void openFileSystems(List<Image> images)
throws TskCoreException, InterruptedException {
2122 byte[] tempBuff = new byte[512];
2123
2124 for (Image image : images) {
2125 String imageStr = image.getName();
2126
2127 progressIndicator.
progress(Bundle.Case_openFileSystems_openingImage(imageStr));
2128
2129 Collection<FileSystem> fileSystems = this.tskCase.getImageFileSystems(image);
2131 for (FileSystem fileSystem : fileSystems) {
2132 fileSystem.read(tempBuff, 0, 512);
2134 }
2135
2136 }
2137 }
2138
2139 @Override
2141 try {
2144 if (images == null) {
2145 return;
2146 }
2147
2149 // If we have a large number of images, don't try to preload anything
2150 logger.log(
2151 Level.INFO,
2152 String.format("Skipping background load of file systems due to large number of images in case (%d)", images.size()));
2153 return;
2154 }
2155
2158 } catch (InterruptedException ex) {
2159 logger.log(
2160 Level.INFO,
2161 String.format("Background operation opening all file systems in %s has been cancelled.", caseName));
2162 } catch (Exception ex) {
2163 // Exception firewall
2164 logger.log(Level.WARNING, "Error while opening file systems in background", ex);
2165 }
2166 }
2167
2168 }
2169
2184 @Messages({
2185 "Case.progressMessage.deletingDataSource=Removing the data source from the case...",
2186 "Case.exceptionMessage.dataSourceNotFound=The data source was not found.",
2187 "Case.exceptionMessage.errorDeletingDataSourceFromCaseDb=An error occurred while removing the data source from the case database.",
2188 "Case.exceptionMessage.errorDeletingDataSourceFromTextIndex=An error occurred while removing the data source from the text index.",})
2189 Void deleteDataSource(ProgressIndicator progressIndicator, Object additionalParams) throws CaseActionException {
2190 assert (additionalParams instanceof Long);
2191 open(progressIndicator, null);
2192 try {
2193 progressIndicator.progress(Bundle.Case_progressMessage_deletingDataSource());
2194 Long dataSourceObjectID = (Long) additionalParams;
2195 try {
2196 DataSource dataSource = this.caseDb.getDataSource(dataSourceObjectID);
2197 if (dataSource == null) {
2198 throw new CaseActionException(Bundle.Case_exceptionMessage_dataSourceNotFound());
2199 }
2200 SleuthkitCaseAdminUtil.deleteDataSource(this.caseDb, dataSourceObjectID);
2201 } catch (TskDataException | TskCoreException ex) {
2202 throw new CaseActionException(Bundle.Case_exceptionMessage_errorDeletingDataSourceFromCaseDb(), ex);
2203 }
2204 try {
2206 } catch (KeywordSearchServiceException ex) {
2207 throw new CaseActionException(Bundle.Case_exceptionMessage_errorDeletingDataSourceFromTextIndex(), ex);
2208 }
2209 eventPublisher.
publish(
new DataSourceDeletedEvent(dataSourceObjectID));
2210 return null;
2211 } finally {
2212 close(progressIndicator);
2214 }
2215 }
2216
2227 public SleuthkitCase
createPortableCase(String caseName, File portableCaseFolder)
throws TskCoreException {
2228
2229 if (portableCaseFolder.exists()) {
2230 throw new TskCoreException("Portable case folder " + portableCaseFolder.toString() + " already exists");
2231 }
2232 if (!portableCaseFolder.mkdirs()) {
2233 throw new TskCoreException("Error creating portable case folder " + portableCaseFolder.toString());
2234 }
2235
2238 try {
2241 portableCaseMetadata.setCaseDatabaseName(SINGLE_USER_CASE_DB_NAME);
2243 throw new TskCoreException("Error creating case metadata", ex);
2244 }
2245
2246 // Create the Sleuthkit case
2247 SleuthkitCase portableSleuthkitCase;
2249 portableSleuthkitCase = SleuthkitCase.newCase(dbFilePath);
2250
2251 return portableSleuthkitCase;
2252 }
2253
2264 if (Thread.currentThread().isInterrupted()) {
2265 throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelled());
2266 }
2267 }
2268
2279 @Messages({
2280 "Case.progressMessage.creatingCaseDirectory=Creating case directory..."
2281 })
2283 /*
2284 * TODO (JIRA-2180): Always create the case directory as part of the
2285 * case creation process.
2286 */
2287 progressIndicator.
progress(Bundle.Case_progressMessage_creatingCaseDirectory());
2289 progressIndicator.
progress(Bundle.Case_progressMessage_creatingCaseDirectory());
2291 }
2292 }
2293
2300 @Messages({
2301 "Case.progressMessage.switchingLogDirectory=Switching log directory..."
2302 })
2304 progressIndicator.
progress(Bundle.Case_progressMessage_switchingLogDirectory());
2306 }
2307
2319 @Messages({
2320 "Case.progressMessage.savingCaseMetadata=Saving case metadata to file...",
2321 "# {0} - exception message", "Case.exceptionMessage.couldNotSaveCaseMetadata=Failed to save case metadata:\n{0}."
2322 })
2324 progressIndicator.
progress(Bundle.Case_progressMessage_savingCaseMetadata());
2325 try {
2326 this.metadata.writeToFile();
2328 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotSaveCaseMetadata(ex.getLocalizedMessage()), ex);
2329 }
2330 }
2331
2343 @Messages({
2344 "Case.progressMessage.creatingCaseNodeData=Creating coordination service node data...",
2345 "# {0} - exception message", "Case.exceptionMessage.couldNotCreateCaseNodeData=Failed to create coordination service node data:\n{0}."
2346 })
2349 progressIndicator.
progress(Bundle.Case_progressMessage_creatingCaseNodeData());
2350 try {
2353 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotCreateCaseNodeData(ex.getLocalizedMessage()), ex);
2354 }
2355 }
2356 }
2357
2369 @Messages({
2370 "Case.progressMessage.updatingCaseNodeData=Updating coordination service node data...",
2371 "# {0} - exception message", "Case.exceptionMessage.couldNotUpdateCaseNodeData=Failed to update coordination service node data:\n{0}."
2372 })
2375 progressIndicator.
progress(Bundle.Case_progressMessage_updatingCaseNodeData());
2376 try {
2381 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotUpdateCaseNodeData(ex.getLocalizedMessage()), ex);
2382 }
2383 }
2384 }
2385
2391 @Messages({
2392 "Case.progressMessage.clearingTempDirectory=Clearing case temp directory..."
2393 })
2395 /*
2396 * Clear the temp subdirectory of the case directory.
2397 */
2398 progressIndicator.
progress(Bundle.Case_progressMessage_clearingTempDirectory());
2400 }
2401
2413 @Messages({
2414 "Case.progressMessage.creatingCaseDatabase=Creating case database...",
2415 "# {0} - exception message", "Case.exceptionMessage.couldNotGetDbServerConnectionInfo=Failed to get case database server conneciton info:\n{0}.",
2416 "# {0} - exception message", "Case.exceptionMessage.couldNotCreateCaseDatabase=Failed to create case database:\n{0}.",
2417 "# {0} - exception message", "Case.exceptionMessage.couldNotSaveDbNameToMetadataFile=Failed to save case database name to case metadata file:\n{0}."
2418 })
2420 progressIndicator.
progress(Bundle.Case_progressMessage_creatingCaseDatabase());
2421 try {
2423 /*
2424 * For single-user cases, the case database is a SQLite database
2425 * with a standard name, physically located in the case
2426 * directory.
2427 */
2429 metadata.setCaseDatabaseName(SINGLE_USER_CASE_DB_NAME);
2430 } else {
2431 /*
2432 * For multi-user cases, the case database is a PostgreSQL
2433 * database with a name derived from the case display name,
2434 * physically located on the PostgreSQL database server.
2435 */
2437 metadata.setCaseDatabaseName(caseDb.getDatabaseName());
2438 }
2439 } catch (TskCoreException ex) {
2440 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotCreateCaseDatabase(ex.getLocalizedMessage()), ex);
2442 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotGetDbServerConnectionInfo(ex.getLocalizedMessage()), ex);
2444 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotSaveDbNameToMetadataFile(ex.getLocalizedMessage()), ex);
2445 }
2446 }
2447
2459 @Messages({
2460 "Case.progressMessage.openingCaseDatabase=Opening case database...",
2461 "# {0} - exception message", "Case.exceptionMessage.couldNotOpenCaseDatabase=Failed to open case database:\n{0}.",
2462 "# {0} - exception message", "Case.exceptionMessage.unsupportedSchemaVersionMessage=Unsupported case database schema version:\n{0}.",
2463 "Case.open.exception.multiUserCaseNotEnabled=Cannot open a multi-user case if multi-user cases are not enabled. See Tools, Options, Multi-User."
2464 })
2466 progressIndicator.
progress(Bundle.Case_progressMessage_openingCaseDatabase());
2467 try {
2470 caseDb = SleuthkitCase.openCase(Paths.get(metadata.
getCaseDirectory(), databaseName).toString());
2473 } else {
2474 throw new CaseActionException(Bundle.Case_open_exception_multiUserCaseNotEnabled());
2475 }
2476 } catch (TskUnsupportedSchemaVersionException ex) {
2477 throw new CaseActionException(Bundle.Case_exceptionMessage_unsupportedSchemaVersionMessage(ex.getLocalizedMessage()), ex);
2479 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotGetDbServerConnectionInfo(ex.getLocalizedMessage()), ex);
2480 } catch (TskCoreException ex) {
2481 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotOpenCaseDatabase(ex.getLocalizedMessage()), ex);
2482 }
2483 }
2484
2491 @Messages({
2492 "Case.progressMessage.openingCaseLevelServices=Opening case-level services...",})
2494 progressIndicator.
progress(Bundle.Case_progressMessage_openingCaseLevelServices());
2495 this.caseServices =
new Services(caseDb);
2496 /*
2497 * RC Note: JM put this initialization here. I'm not sure why. However,
2498 * my attempt to put it in the openCaseDatabase method seems to lead to
2499 * intermittent unchecked exceptions concerning a missing subscriber.
2500 */
2501 caseDb.registerForEvents(sleuthkitEventListener);
2502 }
2503
2516 @NbBundle.Messages({
2517 "Case.progressMessage.openingApplicationServiceResources=Opening application service case resources...",
2518 "# {0} - service name", "Case.serviceOpenCaseResourcesProgressIndicator.title={0} Opening Case Resources",
2519 "# {0} - service name", "Case.serviceOpenCaseResourcesProgressIndicator.cancellingMessage=Cancelling opening case resources by {0}...",
2520 "# {0} - service name", "Case.servicesException.notificationTitle={0} Error"
2521 })
2523 /*
2524 * Each service gets its own independently cancellable/interruptible
2525 * task, running in a named thread managed by an executor service, with
2526 * its own progress indicator. This allows for cancellation of the
2527 * opening of case resources for individual services. It also makes it
2528 * possible to ensure that each service task completes before the next
2529 * one starts by awaiting termination of the executor service.
2530 */
2531 progressIndicator.
progress(Bundle.Case_progressMessage_openingApplicationServiceResources());
2532
2534 )) {
2535 /*
2536 * Create a progress indicator for the task and start the task. If
2537 * running with a GUI, the progress indicator will be a dialog box
2538 * with a Cancel button.
2539 */
2543 cancelButtonListener =
new CancelButtonListener(Bundle.Case_serviceOpenCaseResourcesProgressIndicator_cancellingMessage(service.getServiceName()));
2545 mainFrame,
2546 Bundle.Case_serviceOpenCaseResourcesProgressIndicator_title(service.getServiceName()),
2547 new String[]{Bundle.Case_progressIndicatorCancelButton_label()},
2548 Bundle.Case_progressIndicatorCancelButton_label(),
2549 cancelButtonListener);
2550 } else {
2552 }
2553 appServiceProgressIndicator.
start(Bundle.Case_progressMessage_preparing());
2555 String threadNameSuffix = service.getServiceName().replaceAll("[ ]", "-"); //NON-NLS
2556 threadNameSuffix = threadNameSuffix.toLowerCase();
2558 ExecutorService executor = Executors.newSingleThreadExecutor(threadFactory);
2559 Future<Void> future = executor.submit(() -> {
2560 service.openCaseResources(context);
2561 return null;
2562 });
2563 if (null != cancelButtonListener) {
2566 }
2567
2568 /*
2569 * Wait for the task to either be completed or
2570 * cancelled/interrupted, or for the opening of the case to be
2571 * cancelled.
2572 */
2573 try {
2574 future.get();
2575 } catch (InterruptedException discarded) {
2576 /*
2577 * The parent create/open case task has been cancelled.
2578 */
2580 future.cancel(true);
2581 } catch (CancellationException discarded) {
2582 /*
2583 * The opening of case resources by the application service has
2584 * been cancelled, so the executor service has thrown. Note that
2585 * there is no guarantee the task itself has responded to the
2586 * cancellation request yet.
2587 */
2589 } catch (ExecutionException ex) {
2590 /*
2591 * An exception was thrown while executing the task. The
2592 * case-specific application service resources are not
2593 * essential. Log an error and notify the user if running the
2594 * desktop GUI, but do not throw.
2595 */
2596 Case.
logger.log(Level.SEVERE, String.format(
"%s failed to open case resources for %s", service.getServiceName(), this.
getDisplayName()), ex);
2598 SwingUtilities.invokeLater(() -> {
2600 });
2601 }
2602 } finally {
2603 /*
2604 * Shut down the executor service and wait for it to finish.
2605 * This ensures that the task has finished. Without this, it
2606 * would be possible to start the next task before the current
2607 * task responded to a cancellation request.
2608 */
2610 appServiceProgressIndicator.
finish();
2611 }
2613 }
2614 }
2615
2627 @Messages({
2628 "Case.progressMessage.settingUpNetworkCommunications=Setting up network communications...",
2629 "# {0} - exception message", "Case.exceptionMessage.couldNotOpenRemoteEventChannel=Failed to open remote events channel:\n{0}.",
2630 "# {0} - exception message", "Case.exceptionMessage.couldNotCreatCollaborationMonitor=Failed to create collaboration monitor:\n{0}."
2631 })
2634 progressIndicator.
progress(Bundle.Case_progressMessage_settingUpNetworkCommunications());
2635 try {
2638 collaborationMonitor =
new CollaborationMonitor(metadata.
getCaseName());
2640 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotOpenRemoteEventChannel(ex.getLocalizedMessage()), ex);
2641 } catch (CollaborationMonitor.CollaborationMonitorException ex) {
2642 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotCreatCollaborationMonitor(ex.getLocalizedMessage()), ex);
2643 }
2644 }
2645 }
2646
2662 /*
2663 * Set up either a GUI progress indicator without a Cancel button or a
2664 * logging progress indicator.
2665 */
2669 mainFrame,
2670 Bundle.Case_progressIndicatorTitle_closingCase());
2671 } else {
2673 }
2674 progressIndicator.
start(Bundle.Case_progressMessage_preparing());
2675
2676 /*
2677 * Closing a case is always done in the same non-UI thread that
2678 * opened/created the case. If the case is a multi-user case, this
2679 * ensures that case lock that is held as long as the case is open is
2680 * released in the same thread in which it was acquired, as is required
2681 * by the coordination service.
2682 */
2683 Future<Void> future = caseActionExecutor.submit(() -> {
2685 close(progressIndicator);
2686 } else {
2687 /*
2688 * Acquire an exclusive case resources lock to ensure only one
2689 * node at a time can create/open/upgrade/close the case
2690 * resources.
2691 */
2692 progressIndicator.
progress(Bundle.Case_progressMessage_preparing());
2694 if (null == resourcesLock) {
2695 throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireResourcesLock());
2696 }
2697 close(progressIndicator);
2698 } finally {
2699 /*
2700 * Always release the case directory lock that was acquired
2701 * when the case was opened.
2702 */
2704 }
2705 }
2706 return null;
2707 });
2708
2709 try {
2710 future.get();
2711 } catch (InterruptedException | CancellationException unused) {
2712 /*
2713 * The wait has been interrupted by interrupting the thread running
2714 * this method. Not allowing cancellation of case closing, so ignore
2715 * the interrupt. Likewise, cancellation of the case closing task is
2716 * not supported.
2717 */
2718 } catch (ExecutionException ex) {
2719 throw new CaseActionException(Bundle.Case_exceptionMessage_execExceptionWrapperMessage(ex.getCause().getMessage()), ex);
2720 } finally {
2722 progressIndicator.
finish();
2723 }
2724 }
2725
2731 @Messages({
2732 "Case.progressMessage.shuttingDownNetworkCommunications=Shutting down network communications...",
2733 "Case.progressMessage.closingApplicationServiceResources=Closing case-specific application service resources...",
2734 "Case.progressMessage.closingCaseDatabase=Closing case database..."
2735 })
2738
2739 /*
2740 * Stop sending/receiving case events to and from other nodes if this is
2741 * a multi-user case.
2742 */
2744 progressIndicator.
progress(Bundle.Case_progressMessage_shuttingDownNetworkCommunications());
2745 if (null != collaborationMonitor) {
2746 collaborationMonitor.shutdown();
2747 }
2749 }
2750
2751 /*
2752 * Allow all registered application services providers to close
2753 * resources related to the case.
2754 */
2755 progressIndicator.
progress(Bundle.Case_progressMessage_closingApplicationServiceResources());
2757
2758 /*
2759 * Close the case database.
2760 */
2761 if (null != caseDb) {
2762 progressIndicator.
progress(Bundle.Case_progressMessage_closingCaseDatabase());
2763 caseDb.unregisterForEvents(sleuthkitEventListener);
2764 caseDb.close();
2765 }
2766
2767 /*
2768 * Switch the log directory.
2769 */
2770 progressIndicator.
progress(Bundle.Case_progressMessage_switchingLogDirectory());
2772 }
2773
2778 @Messages({
2779 "# {0} - serviceName", "Case.serviceCloseResourcesProgressIndicator.title={0} Closing Case Resources",
2780 "# {0} - service name", "# {1} - exception message", "Case.servicesException.serviceResourcesCloseError=Could not close case resources for {0} service: {1}"
2781 })
2783 /*
2784 * Each service gets its own independently cancellable task, and thus
2785 * its own task progress indicator.
2786 */
2788 )) {
2792 mainFrame,
2793 Bundle.Case_serviceCloseResourcesProgressIndicator_title(service.getServiceName()));
2794 } else {
2796 }
2797 progressIndicator.
start(Bundle.Case_progressMessage_preparing());
2799 String threadNameSuffix = service.getServiceName().replaceAll("[ ]", "-"); //NON-NLS
2800 threadNameSuffix = threadNameSuffix.toLowerCase();
2802 ExecutorService executor = Executors.newSingleThreadExecutor(threadFactory);
2803 Future<Void> future = executor.submit(() -> {
2804 service.closeCaseResources(context);
2805 return null;
2806 });
2807 try {
2808 future.get();
2809 } catch (InterruptedException ex) {
2810 Case.
logger.log(Level.SEVERE, String.format(
"Unexpected interrupt while waiting on %s service to close case resources", service.getServiceName()), ex);
2811 } catch (CancellationException ex) {
2812 Case.
logger.log(Level.SEVERE, String.format(
"Unexpected cancellation while waiting on %s service to close case resources", service.getServiceName()), ex);
2813 } catch (ExecutionException ex) {
2814 Case.
logger.log(Level.SEVERE, String.format(
"%s service failed to open case resources", service.getServiceName()), ex);
2817 Bundle.Case_servicesException_notificationTitle(service.getServiceName()),
2818 Bundle.Case_servicesException_serviceResourcesCloseError(service.getServiceName(), ex.getLocalizedMessage())));
2819 }
2820 } finally {
2822 progressIndicator.
finish();
2823 }
2824 }
2825 }
2826
2832 @Messages({
2833 "Case.lockingException.couldNotAcquireSharedLock=Failed to get an shared lock on the case.",
2834 "Case.lockingException.couldNotAcquireExclusiveLock=Failed to get a exclusive lock on the case."
2835 })
2838 try {
2845 throw new CaseActionException(Bundle.Case_lockingException_couldNotAcquireSharedLock());
2846 } else {
2847 throw new CaseActionException(Bundle.Case_lockingException_couldNotAcquireExclusiveLock());
2848 }
2849 }
2852 throw new CaseActionException(Bundle.Case_lockingException_couldNotAcquireSharedLock(), ex);
2853 } else {
2854 throw new CaseActionException(Bundle.Case_lockingException_couldNotAcquireExclusiveLock(), ex);
2855 }
2856 }
2857 }
2858
2864 try {
2868 logger.log(Level.SEVERE, String.format(
"Failed to release shared case directory lock for %s", getMetadata().
getCaseDirectory()), ex);
2869 }
2870 }
2871 }
2872
2881 if (!subDirectory.exists()) {
2882 subDirectory.mkdirs();
2883 }
2884 return subDirectory.toString();
2885
2886 }
2887
2899 @Messages({
2900 "Case.exceptionMessage.errorsDeletingCase=Errors occured while deleting the case. See the application log for details."
2901 })
2903 boolean errorsOccurred = false;
2904 try {
2907 errorsOccurred = true;
2909 }
2910
2911 try {
2913 } catch (CaseActionException ex) {
2914 errorsOccurred = true;
2916 }
2917
2919
2920 if (errorsOccurred) {
2921 throw new CaseActionException(Bundle.Case_exceptionMessage_errorsDeletingCase());
2922 }
2923 }
2924
2944 @Messages({
2945 "Case.progressMessage.connectingToCoordSvc=Connecting to coordination service...",
2946 "# {0} - exception message", "Case.exceptionMessage.failedToConnectToCoordSvc=Failed to connect to coordination service:\n{0}.",
2947 "Case.exceptionMessage.cannotGetLockToDeleteCase=Cannot delete case because it is open for another user or host.",
2948 "# {0} - exception message", "Case.exceptionMessage.failedToLockCaseForDeletion=Failed to exclusively lock case for deletion:\n{0}.",
2949 "Case.progressMessage.fetchingCoordSvcNodeData=Fetching coordination service node data for the case...",
2950 "# {0} - exception message", "Case.exceptionMessage.failedToFetchCoordSvcNodeData=Failed to fetch coordination service node data:\n{0}.",
2951 "Case.progressMessage.deletingResourcesCoordSvcNode=Deleting case resources coordination service node...",
2952 "Case.progressMessage.deletingCaseDirCoordSvcNode=Deleting case directory coordination service node..."
2953 })
2955 progressIndicator.
progress(Bundle.Case_progressMessage_connectingToCoordSvc());
2957 try {
2960 logger.log(Level.SEVERE, String.format(
"Failed to connect to coordination service when attempting to delete %s (%s) in %s", metadata.
getCaseDisplayName(), metadata.
getCaseName(), metadata.
getCaseDirectory()), ex);
//NON-NLS
2961 throw new CaseActionException(Bundle.Case_exceptionMessage_failedToConnectToCoordSvc(ex.getLocalizedMessage()));
2962 }
2963
2965 boolean errorsOccurred = false;
2967 if (dirLock == null) {
2968 logger.log(Level.INFO, String.format(
"Could not delete %s (%s) in %s because a case directory lock was held by another host", metadata.
getCaseDisplayName(), metadata.
getCaseName(), metadata.
getCaseDirectory()));
//NON-NLS
2969 throw new CaseActionException(Bundle.Case_exceptionMessage_cannotGetLockToDeleteCase());
2970 }
2971
2972 progressIndicator.
progress(Bundle.Case_progressMessage_fetchingCoordSvcNodeData());
2973 try {
2977 throw new CaseActionException(Bundle.Case_exceptionMessage_failedToFetchCoordSvcNodeData(ex.getLocalizedMessage()));
2978 }
2979
2981
2982 progressIndicator.
progress(Bundle.Case_progressMessage_deletingResourcesCoordSvcNode());
2983 try {
2988 errorsOccurred = true;
2989 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);
//NON-NLS
2990 }
2991 } catch (InterruptedException ex) {
2992 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);
//NON-NLS
2993 }
2994
2997 throw new CaseActionException(Bundle.Case_exceptionMessage_failedToLockCaseForDeletion(ex.getLocalizedMessage()));
2998 }
2999
3000 if (!errorsOccurred) {
3001 progressIndicator.
progress(Bundle.Case_progressMessage_deletingCaseDirCoordSvcNode());
3002 try {
3007 errorsOccurred = true;
3008 }
3009 }
3010
3011 if (errorsOccurred) {
3012 throw new CaseActionException(Bundle.Case_exceptionMessage_errorsDeletingCase());
3013 }
3014 }
3015
3040 @Beta
3042 boolean errorsOccurred = false;
3043 try {
3049 errorsOccurred = true;
3052 errorsOccurred = true;
3054 } catch (CaseActionException ex) {
3055 errorsOccurred = true;
3057 }
3058 return errorsOccurred;
3059 }
3060
3081 @Messages({
3082 "Case.progressMessage.deletingCaseDatabase=Deleting case database..."
3083 })
3086 progressIndicator.
progress(Bundle.Case_progressMessage_deletingCaseDatabase());
3087 logger.log(Level.INFO, String.format(
"Deleting case database for %s (%s) in %s", caseNodeData.
getDisplayName(), caseNodeData.
getName(), caseNodeData.
getDirectory()));
//NON-NLS
3089 String url = "jdbc:postgresql://" + info.getHost() + ":" + info.getPort() + "/postgres"; //NON-NLS
3090 Class.forName("org.postgresql.Driver"); //NON-NLS
3091 try (Connection connection = DriverManager.getConnection(url, info.getUserName(), info.getPassword()); Statement statement = connection.createStatement()) {
3092 String dbExistsQuery =
"SELECT 1 from pg_database WHERE datname = '" + metadata.
getCaseDatabaseName() +
"'";
//NON-NLS
3093 try (ResultSet queryResult = statement.executeQuery(dbExistsQuery)) {
3094 if (queryResult.next()) {
3095 String deleteCommand =
"DROP DATABASE \"" + metadata.
getCaseDatabaseName() +
"\"";
//NON-NLS
3096 statement.execute(deleteCommand);
3097 }
3098 }
3099 }
3101 }
3102 }
3103
3121 logger.log(Level.INFO, String.format(
"Deleting text index for %s", caseNodeData.
getDisplayName(), caseNodeData.
getName(), caseNodeData.
getDirectory()));
//NON-NLS
3124 }
3125 }
3126
3136 @Messages({
3137 "Case.progressMessage.deletingTextIndex=Deleting text index..."
3138 })
3140 progressIndicator.
progress(Bundle.Case_progressMessage_deletingTextIndex());
3141
3143 )) {
3144 searchService.deleteTextIndex(metadata);
3145 }
3146 }
3147
3164 logger.log(Level.INFO, String.format(
"Deleting case directory for %s", caseNodeData.
getDisplayName(), caseNodeData.
getName(), caseNodeData.
getDirectory()));
//NON-NLS
3167 }
3168 }
3169
3179 @Messages({
3180 "Case.progressMessage.deletingCaseDirectory=Deleting case directory..."
3181 })
3183 progressIndicator.
progress(Bundle.Case_progressMessage_deletingCaseDirectory());
3185 throw new CaseActionException(String.format(
"Failed to delete %s", metadata.
getCaseDirectory()));
//NON-NLS
3186 }
3187 }
3188
3196 @Messages({
3197 "Case.progressMessage.removingCaseFromRecentCases=Removing case from Recent Cases menu..."
3198 })
3201 progressIndicator.
progress(Bundle.Case_progressMessage_removingCaseFromRecentCases());
3202 SwingUtilities.invokeLater(() -> {
3203 RecentCases.getInstance().removeRecentCase(metadata.
getCaseDisplayName(), metadata.getFilePath().toString());
3204 });
3205 }
3206 }
3207
3218 boolean isNodeNodeEx = false;
3219 Throwable cause = ex.getCause();
3220 if (cause != null) {
3221 String causeMessage = cause.getMessage();
3222 isNodeNodeEx = causeMessage.contains(NO_NODE_ERROR_MSG_FRAGMENT);
3223 }
3224 return isNodeNodeEx;
3225 }
3226
3239 try {
3243 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);
3244
3245 }
3246 }
3247
3257
3268 R
execute(T progressIndicator, V additionalParams)
throws CaseActionException;
3269 }
3270
3277 }
3278
3283 @ThreadSafe
3285
3287 @GuardedBy("this")
3289 @GuardedBy("this")
3291 @GuardedBy("this")
3293
3304 }
3305
3313 /*
3314 * If the cancel button has already been pressed, pass the
3315 * cancellation on to the case context.
3316 */
3319 }
3320 }
3321
3329 /*
3330 * If the cancel button has already been pressed, cancel the Future
3331 * of the task.
3332 */
3335 }
3336 }
3337
3343 @Override
3346 }
3347
3352 /*
3353 * At a minimum, set the cancellation requested flag of this
3354 * listener.
3355 */
3358 /*
3359 * Set the cancellation request flag and display the
3360 * cancellation message in the progress indicator for the case
3361 * context associated with this listener.
3362 */
3366 ((ModalDialogProgressIndicator) progressIndicator).
setCancelling(cancellationMessage);
3367 }
3368 }
3370 }
3372 /*
3373 * Cancel the Future of the task associated with this listener.
3374 * Note that the task thread will be interrupted if the task is
3375 * blocked.
3376 */
3378 }
3379 }
3380 }
3381
3386
3388
3391 }
3392
3393 @Override
3395 return new Thread(task, threadName);
3396 }
3397
3398 }
3399
3407 @Deprecated
3410 }
3411
3431 @Deprecated
3432 public static void create(String caseDir, String caseDisplayName, String caseNumber, String examiner)
throws CaseActionException {
3434 }
3435
3456 @Deprecated
3457 public static void create(String caseDir, String caseDisplayName, String caseNumber, String examiner,
CaseType caseType)
throws CaseActionException {
3459 }
3460
3472 @Deprecated
3473 public static void open(String caseMetadataFilePath)
throws CaseActionException {
3475 }
3476
3486 @Deprecated
3489 }
3490
3496 @Deprecated
3499 }
3500
3514 @Deprecated
3517 }
3518
3528 @Deprecated
3530 return new File(filePath).isFile();
3531 }
3532
3541 @Deprecated
3544 }
3545
3553 @Deprecated
3556 }
3557
3567 @Deprecated
3569 return "ModuleOutput"; //NON-NLS
3570 }
3571
3581 @Deprecated
3582 public static PropertyChangeSupport
3584 return new PropertyChangeSupport(
Case.class
3585 );
3586 }
3587
3596 @Deprecated
3599 }
3600
3615 @Deprecated
3616 public Image
addImage(String imgPath,
long imgId, String timeZone)
throws CaseActionException {
3617 try {
3618 Image newDataSource = caseDb.getImageById(imgId);
3620 return newDataSource;
3621 } catch (TskCoreException ex) {
3622 throw new CaseActionException(NbBundle.getMessage(this.getClass(), "Case.addImg.exception.msg"), ex);
3623 }
3624 }
3625
3633 @Deprecated
3636 }
3637
3648 @Deprecated
3649 public void deleteReports(Collection<? extends Report> reports,
boolean deleteFromDisk)
throws TskCoreException {
3651 }
3652
3653 }
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)
final SleuthkitEventListener sleuthkitEventListener
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()
Void open(ProgressIndicator progressIndicator, Object additionalParams)
Set< TimeZone > getTimeZones()
CoordinationService.Lock caseLock
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 ExecutorService openFileSystemsExecutor
static final String NO_NODE_ERROR_MSG_FRAGMENT
static boolean runningWithGUI
static void closeCurrentCase()
void setDeletedFlag(DeletedFlags flag)
void acquireCaseLock(CaseLockType lockType)
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 notifyBlackBoardArtifactTagAdded(BlackboardArtifactTag newTag, List< BlackboardArtifactTag > removedTagList)
void publish(AutopsyEvent event)
String getLocalizedDisplayName()
static Future<?> backgroundOpenFileSystemsFuture
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)
void openFileSystemsInBackground()
void notifyContentTagAdded(ContentTag newTag, List< ContentTag > deletedTagList)
void deleteDataSource(Long dataSourceId)
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)
void publishTimelineEventAddedEvent(TimelineManager.TimelineEventAddedEvent event)
synchronized static void setLogDirectory(String directoryPath)
volatile ExecutorService caseActionExecutor
void notifyCentralRepoCommentChanged(long contentId, String newComment)
TaskThreadFactory(String threadName)
static final String CACHE_FOLDER
static String getAutopsyVersion()
CaseType(String typeName)
List< Image > getImages()
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 int CASE_LOCK_TIMEOUT_MINS
static final String SINGLE_USER_CASE_DB_NAME
static void deleteCase(CaseMetadata metadata)
void deleteReports(Collection<?extends Report > reports, boolean deleteFromDisk)
synchronized void closeRemoteEventChannel()
KeywordSearchService getKeywordSearchService()
static final int CASE_RESOURCES_LOCK_TIMEOUT_HOURS
final long MAX_IMAGE_THRESHOLD
List< Report > getAllReports()
static void clearTempSubDir(String tempSubDirPath)
static boolean canAddDataSources()
static boolean isValidName(String caseName)
void openCaseDataBase(ProgressIndicator progressIndicator)
void createCaseDirectoryIfDoesNotExist(ProgressIndicator progressIndicator)
CollaborationMonitor collaborationMonitor
void openCommunicationChannels(ProgressIndicator progressIndicator)
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)
void doOpenCaseAction(String progressIndicatorTitle, CaseAction< ProgressIndicator, Object, Void > caseAction, CaseLockType caseLockType, boolean allowCancellation, Object additionalParams)
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)
void openFileSystems(List< Image > images)
synchronized void openRemoteEventChannel(String channelName)
String getCaseDisplayName()
void createCaseNodeData(ProgressIndicator progressIndicator)
static void invokeStartupDialog()
void publishArtifactsPostedEvent(Blackboard.ArtifactsPostedEvent event)
static String displayNameToUniqueName(String caseDisplayName)
static void deleteFromRecentCases(CaseMetadata metadata, ProgressIndicator progressIndicator)
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)
void close(ProgressIndicator progressIndicator)
final 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)
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)
R execute(T progressIndicator, V additionalParams)
BLACKBOARD_ARTIFACT_TAG_ADDED
static void open(String caseMetadataFilePath)
static void openAsCurrentCase(Case newCurrentCase, boolean isNewCase)
static CoordinationService.Lock acquireCaseResourcesLock(String caseDir)
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
Void create(ProgressIndicator progressIndicator, Object additionalParams)
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()
void openAppServiceCaseResources(ProgressIndicator progressIndicator, boolean isNewCase)
String getExportDirectory()
static void updateGUIForCaseClosed()
void notifyAddingDataSource(UUID eventId)
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
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 checkForCancellation()
static boolean canDeleteCurrentCase()
static void addEventSubscriber(Set< String > eventNames, PropertyChangeListener subscriber)
void updateCaseNodeData(ProgressIndicator progressIndicator)
static void error(String message)
final SleuthkitCase tskCase
static synchronized IngestServices getInstance()
String getExaminerEmail()