1 /*
2 * Autopsy Forensic Browser
3 *
4 * Copyright 2011-2018 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
21 import java.awt.Frame;
22 import java.awt.event.ActionEvent;
23 import java.awt.event.ActionListener;
24 import java.beans.PropertyChangeListener;
25 import java.beans.PropertyChangeSupport;
26 import java.io.File;
27 import java.io.IOException;
28 import java.nio.file.InvalidPathException;
29 import java.nio.file.Path;
30 import java.nio.file.Paths;
31 import java.sql.Connection;
32 import java.sql.DriverManager;
33 import java.sql.SQLException;
34 import java.sql.Statement;
35 import java.text.SimpleDateFormat;
36 import java.util.Collection;
37 import java.util.Date;
38 import java.util.HashMap;
39 import java.util.HashSet;
40 import java.util.List;
41 import java.util.Map;
42 import java.util.MissingResourceException;
43 import java.util.Set;
44 import java.util.TimeZone;
45 import java.util.UUID;
46 import java.util.concurrent.CancellationException;
47 import java.util.concurrent.ExecutionException;
48 import java.util.concurrent.ExecutorService;
49 import java.util.concurrent.Executors;
50 import java.util.concurrent.Future;
51 import java.util.concurrent.ThreadFactory;
52 import java.util.concurrent.TimeUnit;
53 import java.util.logging.Level;
54 import java.util.stream.Collectors;
55 import java.util.stream.Stream;
56 import javax.annotation.concurrent.GuardedBy;
57 import javax.annotation.concurrent.ThreadSafe;
58 import javax.swing.JOptionPane;
59 import javax.swing.SwingUtilities;
60 import org.openide.util.Lookup;
61 import org.openide.util.NbBundle;
62 import org.openide.util.NbBundle.Messages;
63 import org.openide.util.actions.CallableSystemAction;
64 import org.openide.windows.WindowManager;
117 import org.
sleuthkit.datamodel.TskUnsupportedSchemaVersionException;
118
123
148
149 /*
150 * Get a reference to the main window of the desktop application to use to
151 * parent pop up dialogs and initialize the application name for use in
152 * changing the main window title.
153 */
154 static {
155 WindowManager.getDefault().invokeWhenUIReady(new Runnable() {
156 @Override
157 public void run() {
158 mainFrame = WindowManager.getDefault().getMainWindow();
159 }
160 });
161 }
162
167
170
172
181 if (typeName != null) {
183 if (typeName.equalsIgnoreCase(c.toString())) {
184 return c;
185 }
186 }
187 }
188 return null;
189 }
190
196 @Override
198 return typeName;
199 }
200
206 @Messages({
207 "Case_caseType_singleUser=Single-user case",
208 "Case_caseType_multiUser=Multi-user case"
209 })
211 if (fromString(typeName) == SINGLE_USER_CASE) {
212 return Bundle.Case_caseType_singleUser();
213 } else {
214 return Bundle.Case_caseType_multiUser();
215 }
216 }
217
224 this.typeName = typeName;
225 }
226
237 @Deprecated
239 return (otherTypeName == null) ? false : typeName.equals(otherTypeName);
240 }
241
242 };
243
249
257 @Deprecated
266 @Deprecated
275 @Deprecated
374
375 };
376
385 .map(Events::toString)
386 .collect(Collectors.toSet()), listener);
387 }
388
397 .map(Events::toString)
398 .collect(Collectors.toSet()), listener);
399 }
400
409 @Deprecated
412 }
413
421 eventTypes.forEach((
Events event) -> {
423 });
424 }
425
434 @Deprecated
437 }
438
447 }
448
457 }
458
466 eventTypes.forEach((
Events event) -> {
468 });
469 }
470
480 return !(caseName.contains("\\") || caseName.contains("/") || caseName.contains(":")
481 || caseName.contains("*") || caseName.contains("?") || caseName.contains("\"")
482 || caseName.contains("<") || caseName.contains(">") || caseName.contains("|"));
483 }
484
509 @Deprecated
512 }
513
533 @Messages({
534 "Case.exceptionMessage.emptyCaseName=Must specify a case name.",
535 "Case.exceptionMessage.emptyCaseDir=Must specify a case directory path."
536 })
539 throw new CaseActionException(Bundle.Case_exceptionMessage_emptyCaseName());
540 }
541 if (caseDir.isEmpty()) {
542 throw new CaseActionException(Bundle.Case_exceptionMessage_emptyCaseDir());
543 }
545 }
546
560 @Messages({
561 "Case.exceptionMessage.failedToReadMetadata=Failed to read case metadata.",
562 "Case.exceptionMessage.cannotOpenMultiUserCaseNoSettings=Multi-user settings are missing (see Tools, Options, Multi-user tab), cannot open a multi-user case."
563 })
566 try {
567 metadata =
new CaseMetadata(Paths.get(caseMetadataFilePath));
569 throw new CaseActionException(Bundle.Case_exceptionMessage_failedToReadMetadata(), ex);
570 }
572 throw new CaseActionException(Bundle.Case_exceptionMessage_cannotOpenMultiUserCaseNoSettings());
573 }
575 }
576
583 return currentCase != null;
584 }
585
597 @Deprecated
599 /*
600 * Throwing an unchecked exception is a bad idea here.
601 *
602 */
603 try {
606 throw new IllegalStateException(NbBundle.getMessage(
Case.class,
"Case.getCurCase.exception.noneOpen"), ex);
607 }
608 }
609
619 if (openCase == null) {
620 throw new NoCurrentCaseException(NbBundle.getMessage(
Case.class,
"Case.getCurCase.exception.noneOpen"));
621 } else {
622 return openCase;
623 }
624 }
625
634 @Messages({
635 "# {0} - exception message", "Case.closeException.couldNotCloseCase=Error closing case: {0}",
636 "Case.progressIndicatorTitle.closingCase=Closing Case"
637 })
640 if (null == currentCase) {
641 return;
642 }
644 try {
646 logger.log(Level.INFO, "Closing current case {0} ({1}) in {2}", new Object[]{closedCase.getDisplayName(), closedCase.getName(), closedCase.getCaseDirectory()}); //NON-NLS
647 currentCase = null;
649 logger.log(Level.INFO, "Closed current case {0} ({1}) in {2}", new Object[]{closedCase.getDisplayName(), closedCase.getName(), closedCase.getCaseDirectory()}); //NON-NLS
650 } catch (CaseActionException ex) {
652 throw ex;
653 } finally {
656 }
657 }
658 }
659 }
660
671 if (null == currentCase) {
672 return;
673 }
677 }
678 }
679
691 @Messages({
692 "Case.progressIndicatorTitle.deletingCase=Deleting Case",
693 "Case.exceptionMessage.cannotDeleteCurrentCase=Cannot delete current case, it must be closed first.",
694 "Case.progressMessage.checkingForOtherUser=Checking to see if another user has the case open...",
695 "Case.exceptionMessage.cannotGetLockToDeleteCase=Cannot delete case because it is open for another user or there is a problem with the coordination service."
696 })
699 if (null != currentCase) {
700 throw new CaseActionException(Bundle.Case_exceptionMessage_cannotDeleteCurrentCase());
701 }
702 }
703
704 /*
705 * Set up either a GUI progress indicator without a cancel button (can't
706 * cancel deleting a case) or a logging progress indicator.
707 */
711 } else {
713 }
714 progressIndicator.
start(Bundle.Case_progressMessage_preparing());
715 try {
718 } else {
719 /*
720 * First, acquire an exclusive case directory lock. The case
721 * cannot be deleted if another node has it open.
722 */
723 progressIndicator.
progress(Bundle.Case_progressMessage_checkingForOtherUser());
725 assert (null != dirLock);
728 throw new CaseActionException(Bundle.Case_exceptionMessage_cannotGetLockToDeleteCase(), ex);
729 }
730 }
731 } finally {
732 progressIndicator.
finish();
733 }
734 }
735
746 @Messages({
747 "Case.exceptionMessage.cannotLocateMainWindow=Cannot locate main application window"
748 })
751 if (null != currentCase) {
752 try {
754 } catch (CaseActionException ex) {
755 /*
756 * Notify the user and continue (the error has already been
757 * logged in closeCurrentCase.
758 */
760 }
761 }
762 try {
763 logger.log(Level.INFO, "Opening {0} ({1}) in {2} as the current case", new Object[]{newCurrentCase.getDisplayName(), newCurrentCase.getName(), newCurrentCase.getCaseDirectory()}); //NON-NLS
764 newCurrentCase.
open(isNewCase);
765 currentCase = newCurrentCase;
766 logger.log(Level.INFO, "Opened {0} ({1}) in {2} as the current case", new Object[]{newCurrentCase.getDisplayName(), newCurrentCase.getName(), newCurrentCase.getCaseDirectory()}); //NON-NLS
769 }
772 logger.log(Level.INFO, String.format(
"Cancelled opening %s (%s) in %s as the current case", newCurrentCase.
getDisplayName(), newCurrentCase.
getName(), newCurrentCase.
getCaseDirectory()));
//NON-NLS
773 throw ex;
774 } catch (CaseActionException ex) {
775 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
776 throw ex;
777 }
778 }
779 }
780
790 /*
791 * Replace all non-ASCII characters.
792 */
793 String uniqueCaseName = caseDisplayName.replaceAll("[^\\p{ASCII}]", "_"); //NON-NLS
794
795 /*
796 * Replace all control characters.
797 */
798 uniqueCaseName = uniqueCaseName.replaceAll("[\\p{Cntrl}]", "_"); //NON-NLS
799
800 /*
801 * Replace /, ,円 :, ?, space, ' ".
802 */
803 uniqueCaseName = uniqueCaseName.replaceAll("[ /?:'\"\\\\]", "_"); //NON-NLS
804
805 /*
806 * Make it all lowercase.
807 */
808 uniqueCaseName = uniqueCaseName.toLowerCase();
809
810 /*
811 * Add a time stamp for uniqueness.
812 */
813 SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
814 Date date = new Date();
815 uniqueCaseName = uniqueCaseName + "_" + dateFormat.format(date);
816
817 return uniqueCaseName;
818 }
819
829
830 File caseDirF = new File(caseDir);
831
832 if (caseDirF.exists()) {
833 if (caseDirF.isFile()) {
834 throw new CaseActionException(
835 NbBundle.getMessage(
Case.class,
"Case.createCaseDir.exception.existNotDir", caseDir));
836
837 } else if (!caseDirF.canRead() || !caseDirF.canWrite()) {
838 throw new CaseActionException(
839 NbBundle.getMessage(
Case.class,
"Case.createCaseDir.exception.existCantRW", caseDir));
840 }
841 }
842
843 try {
844 boolean result = (caseDirF).mkdirs(); // create root case Directory
845
846 if (result == false) {
847 throw new CaseActionException(
848 NbBundle.getMessage(
Case.class,
"Case.createCaseDir.exception.cantCreate", caseDir));
849 }
850
851 // create the folders inside the case directory
852 String hostClause = "";
853
856 }
857 result = result && (new File(caseDir + hostClause + File.separator + EXPORT_FOLDER)).mkdirs()
858 && (new File(caseDir + hostClause + File.separator + LOG_FOLDER)).mkdirs()
859 && (new File(caseDir + hostClause + File.separator + TEMP_FOLDER)).mkdirs()
860 && (new File(caseDir + hostClause + File.separator + CACHE_FOLDER)).mkdirs();
861
862 if (result == false) {
863 throw new CaseActionException(
864 NbBundle.getMessage(
Case.class,
"Case.createCaseDir.exception.cantCreateCaseDir", caseDir));
865 }
866
867 final String modulesOutDir = caseDir + hostClause + File.separator +
MODULE_FOLDER;
868 result = new File(modulesOutDir).mkdir();
869
870 if (result == false) {
871 throw new CaseActionException(
872 NbBundle.getMessage(
Case.class,
"Case.createCaseDir.exception.cantCreateModDir",
873 modulesOutDir));
874 }
875
876 final String reportsOutDir = caseDir + hostClause + File.separator +
REPORTS_FOLDER;
877 result = new File(reportsOutDir).mkdir();
878
879 if (result == false) {
880 throw new CaseActionException(
881 NbBundle.getMessage(
Case.class,
"Case.createCaseDir.exception.cantCreateReportsDir",
882 modulesOutDir));
883
884 }
885
886 } catch (MissingResourceException | CaseActionException e) {
887 throw new CaseActionException(
888 NbBundle.getMessage(
Case.class,
"Case.createCaseDir.exception.gen", caseDir), e);
889 }
890 }
891
899 static Map<Long, String> getImagePaths(SleuthkitCase db) {
900 Map<Long, String> imgPaths = new HashMap<>();
901 try {
902 Map<Long, List<String>> imgPathsList = db.getImagePaths();
903 for (Map.Entry<Long, List<String>> entry : imgPathsList.entrySet()) {
904 if (entry.getValue().size() > 0) {
905 imgPaths.put(entry.getKey(), entry.getValue().get(0));
906 }
907 }
908 } catch (TskCoreException ex) {
909 logger.log(Level.SEVERE, "Error getting image paths", ex); //NON-NLS
910 }
911 return imgPaths;
912 }
913
930 @Messages({
931 "Case.progressMessage.deletingTextIndex=Deleting text index...",
932 "Case.progressMessage.deletingCaseDatabase=Deleting case database...",
933 "Case.progressMessage.deletingCaseDirectory=Deleting case directory...",
934 "Case.exceptionMessage.errorsDeletingCase=Errors occured while deleting the case. See the application log for details"
935 })
937 boolean errorsOccurred = false;
939 /*
940 * Delete the case database from the database server.
941 */
942 try {
943 progressIndicator.
progress(Bundle.Case_progressMessage_deletingCaseDatabase());
944 CaseDbConnectionInfo db;
946 Class.forName("org.postgresql.Driver"); //NON-NLS
947 try (Connection connection = DriverManager.getConnection("jdbc:postgresql://" + db.getHost() + ":" + db.getPort() + "/postgres", db.getUserName(), db.getPassword()); //NON-NLS
948 Statement statement = connection.createStatement();) {
949 String deleteCommand =
"DROP DATABASE \"" + metadata.
getCaseDatabaseName() +
"\"";
//NON-NLS
950 statement.execute(deleteCommand);
951 }
954 errorsOccurred = true;
955 }
956 }
957
958 /*
959 * Delete the text index.
960 */
961 progressIndicator.
progress(Bundle.Case_progressMessage_deletingTextIndex());
963 try {
964 searchService.deleteTextIndex(metadata);
967 errorsOccurred = true;
968 }
969 }
970
971 /*
972 * Delete the case directory.
973 */
974 progressIndicator.
progress(Bundle.Case_progressMessage_deletingCaseDirectory());
977 errorsOccurred = true;
978 }
979
980 /*
981 * If running in a GUI, remove the case from the Recent Cases menu
982 */
984 SwingUtilities.invokeLater(() -> {
985 RecentCases.getInstance().removeRecentCase(metadata.
getCaseDisplayName(), metadata.getFilePath().toString());
986 });
987 }
988
989 if (errorsOccurred) {
990 throw new CaseActionException(Bundle.Case_exceptionMessage_errorsDeletingCase());
991 }
992 }
993
1004 @Messages({"Case.creationException.couldNotAcquireResourcesLock=Failed to get lock on case resources"})
1006 try {
1007 String resourcesNodeName = caseDir + "_resources";
1009 if (null == lock) {
1010 throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireResourcesLock());
1011 }
1012 return lock;
1013 } catch (InterruptedException ex) {
1016 throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireResourcesLock(), ex);
1017 }
1018 }
1019
1021 //Method should become unnecessary once technical debt story 3334 is done.
1023 //Available version number is version number for this application
1025 } else {
1027 }
1028 }
1029
1035 SwingUtilities.invokeLater(() -> {
1036 /*
1037 * If the case database was upgraded for a new schema and a
1038 * backup database was created, notify the user.
1039 */
1041 String backupDbPath = caseDb.getBackupDatabasePath();
1042 if (null != backupDbPath) {
1043 JOptionPane.showMessageDialog(
1044 mainFrame,
1045 NbBundle.getMessage(
Case.class,
"Case.open.msgDlg.updated.msg", backupDbPath),
1046 NbBundle.getMessage(
Case.class,
"Case.open.msgDlg.updated.title"),
1047 JOptionPane.INFORMATION_MESSAGE);
1048 }
1049
1050 /*
1051 * Look for the files for the data sources listed in the case
1052 * database and give the user the opportunity to locate any that
1053 * are missing.
1054 */
1055 Map<Long, String> imgPaths = getImagePaths(caseDb);
1056 for (Map.Entry<Long, String> entry : imgPaths.entrySet()) {
1057 long obj_id = entry.getKey();
1058 String path = entry.getValue();
1060 if (!fileExists) {
1061 int response = JOptionPane.showConfirmDialog(
1062 mainFrame,
1063 NbBundle.getMessage(
Case.class,
"Case.checkImgExist.confDlg.doesntExist.msg", path),
1064 NbBundle.getMessage(
Case.class,
"Case.checkImgExist.confDlg.doesntExist.title"),
1065 JOptionPane.YES_NO_OPTION);
1066 if (response == JOptionPane.YES_OPTION) {
1067 MissingImageDialog.makeDialog(obj_id, caseDb);
1068 } else {
1069 logger.log(Level.SEVERE, "User proceeding with missing image files"); //NON-NLS
1070
1071 }
1072 }
1073 }
1074
1075 /*
1076 * Enable the case-specific actions.
1077 */
1080 CallableSystemAction.get(CasePropertiesAction.class).setEnabled(true);
1081 CallableSystemAction.get(CaseDeleteAction.class).setEnabled(true);
1086
1087 /*
1088 * Add the case to the recent cases tracker that supplies a list
1089 * of recent cases to the recent cases menu item and the
1090 * open/create case dialog.
1091 */
1092 RecentCases.getInstance().addRecentCase(newCurrentCase.
getDisplayName(), newCurrentCase.getMetadata().getFilePath().toString());
1093
1094 /*
1095 * Open the top components (windows within the main application
1096 * window).
1097 */
1098 if (newCurrentCase.
hasData()) {
1100 }
1101
1102 /*
1103 * Reset the main window title to:
1104 *
1105 * [curent case display name] - [application name].
1106 */
1108 });
1109 }
1110 }
1111
1112 /*
1113 * Update the GUI to to reflect the lack of a current case.
1114 */
1117 SwingUtilities.invokeLater(() -> {
1118 /*
1119 * Close the top components (windows within the main application
1120 * window).
1121 */
1123
1124 /*
1125 * Disable the case-specific menu items.
1126 */
1127 CallableSystemAction.get(
AddImageAction.class).setEnabled(
false);
1129 CallableSystemAction.get(CasePropertiesAction.class).setEnabled(false);
1130 CallableSystemAction.get(CaseDeleteAction.class).setEnabled(false);
1135
1136 /*
1137 * Clear the notifications in the notfier component in the lower
1138 * right hand corner of the main application window.
1139 */
1141
1142 /*
1143 * Reset the main window title to be just the application name,
1144 * instead of [curent case display name] - [application name].
1145 */
1147 });
1148 }
1149 }
1150
1155 File tempFolder = new File(tempSubDirPath);
1156 if (tempFolder.isDirectory()) {
1157 File[] files = tempFolder.listFiles();
1158 if (files.length > 0) {
1159 for (File file : files) {
1160 if (file.isDirectory()) {
1162 } else {
1163 file.delete();
1164 }
1165 }
1166 }
1167 }
1168 }
1169
1177 }
1178
1186 }
1187
1195 }
1196
1203 return metadata.getCreatedDate();
1204 }
1205
1213 }
1214
1222 }
1223
1231 }
1232
1240 }
1241
1249 }
1250
1258 }
1259
1267 }
1268
1276 }
1277
1288 Path hostPath;
1291 } else {
1292 hostPath = Paths.get(caseDirectory);
1293 }
1294 if (!hostPath.toFile().exists()) {
1295 hostPath.toFile().mkdirs();
1296 }
1297 return hostPath.toString();
1298 }
1299
1308 }
1309
1318 }
1319
1328 }
1329
1338 }
1339
1348 }
1349
1358 }
1359
1370 return path.subpath(path.getNameCount() - 2, path.getNameCount()).toString();
1371 } else {
1372 return path.subpath(path.getNameCount() - 1, path.getNameCount()).toString();
1373 }
1374 }
1375
1386 List<Content> list = caseDb.getRootObjects();
1387 hasDataSources = (list.size() > 0);
1388 return list;
1389 }
1390
1397 Set<TimeZone> timezones = new HashSet<>();
1398 try {
1400 final Content dataSource = c.getDataSource();
1401 if ((dataSource != null) && (dataSource instanceof Image)) {
1402 Image image = (Image) dataSource;
1403 timezones.add(TimeZone.getTimeZone(image.getTimeZone()));
1404 }
1405 }
1406 } catch (TskCoreException ex) {
1407 logger.log(Level.SEVERE, "Error getting data source time zones", ex); //NON-NLS
1408 }
1409 return timezones;
1410 }
1411
1420 }
1421
1429 if (!hasDataSources) {
1430 try {
1432 } catch (TskCoreException ex) {
1433 logger.log(Level.SEVERE, "Error accessing case database", ex); //NON-NLS
1434 }
1435 }
1437 }
1438
1451 }
1452
1465 }
1466
1480 }
1481
1491 }
1492
1502 }
1503
1512 //leaving new value of changedTagName as null, because we do not currently support changing the display name of a tag.
1514 }
1515
1525 }
1526
1536 }
1537
1549 public void addReport(String localPath, String srcModuleName, String reportName)
throws TskCoreException {
1550 addReport(localPath, srcModuleName, reportName, null);
1551 }
1552
1567 public Report
addReport(String localPath, String srcModuleName, String reportName, Content parent)
throws TskCoreException {
1568 String normalizedLocalPath;
1569 try {
1570 if (localPath.toLowerCase().contains("http:")) {
1571 normalizedLocalPath = localPath;
1572 } else {
1573 normalizedLocalPath = Paths.get(localPath).normalize().toString();
1574 }
1575 } catch (InvalidPathException ex) {
1576 String errorMsg = "Invalid local path provided: " + localPath; // NON-NLS
1577 throw new TskCoreException(errorMsg, ex);
1578 }
1579 Report report = this.caseDb.addReport(normalizedLocalPath, srcModuleName, reportName, parent);
1581 return report;
1582 }
1583
1593 return this.caseDb.getAllReports();
1594 }
1595
1604 public void deleteReports(Collection<? extends Report> reports)
throws TskCoreException {
1605 for (Report report : reports) {
1606 this.caseDb.deleteReport(report);
1608 }
1609 }
1610
1618 }
1619
1627 @Messages({
1628 "Case.exceptionMessage.metadataUpdateError=Failed to update case metadata"
1629 })
1630 void updateCaseDetails(CaseDetails caseDetails) throws CaseActionException {
1632 try {
1633 metadata.setCaseDetails(caseDetails);
1634 } catch (CaseMetadataException ex) {
1635 throw new CaseActionException(Bundle.Case_exceptionMessage_metadataUpdateError(), ex);
1636 }
1637 if (!oldCaseDetails.getCaseNumber().equals(caseDetails.
getCaseNumber())) {
1638 eventPublisher.
publish(
new AutopsyEvent(Events.NUMBER.toString(), oldCaseDetails.getCaseNumber(), caseDetails.
getCaseNumber()));
1639 }
1640 if (!oldCaseDetails.getExaminerName().equals(caseDetails.
getExaminerName())) {
1641 eventPublisher.
publish(
new AutopsyEvent(Events.NUMBER.toString(), oldCaseDetails.getExaminerName(), caseDetails.
getExaminerName()));
1642 }
1644 eventPublisher.
publish(
new AutopsyEvent(Events.NAME.toString(), oldCaseDetails.getCaseDisplayName(), caseDetails.
getCaseDisplayName()));
1645 }
1646 eventPublisher.
publish(
new AutopsyEvent(Events.CASE_DETAILS.toString(), oldCaseDetails, caseDetails));
1647 if (RuntimeProperties.runningWithGUI()) {
1648 SwingUtilities.invokeLater(() -> {
1650 try {
1651 RecentCases.getInstance().updateRecentCase(oldCaseDetails.getCaseDisplayName(), metadata.getFilePath().toString(), caseDetails.
getCaseDisplayName(), metadata.getFilePath().toString());
1652 } catch (Exception ex) {
1653 logger.log(Level.SEVERE, "Error updating case name in UI", ex); //NON-NLS
1654 }
1655 });
1656 }
1657 }
1658
1673 }
1674
1681 metadata = caseMetaData;
1682 }
1683
1699 @Messages({
1700 "Case.progressIndicatorTitle.creatingCase=Creating Case",
1701 "Case.progressIndicatorTitle.openingCase=Opening Case",
1702 "Case.progressIndicatorCancelButton.label=Cancel",
1703 "Case.progressMessage.preparing=Preparing...",
1704 "Case.progressMessage.preparingToOpenCaseResources=<html>Preparing to open case resources.<br>This may take time if another user is upgrading the case.</html>",
1705 "Case.progressMessage.cancelling=Cancelling...",
1706 "Case.exceptionMessage.cancelledByUser=Cancelled by user.",
1707 "# {0} - exception message", "Case.exceptionMessage.execExceptionWrapperMessage={0}"
1708 })
1709 private void open(
boolean isNewCase)
throws CaseActionException {
1710 /*
1711 * Create and start either a GUI progress indicator with a Cancel button
1712 * or a logging progress indicator.
1713 */
1718 String progressIndicatorTitle = isNewCase ? Bundle.Case_progressIndicatorTitle_creatingCase() : Bundle.Case_progressIndicatorTitle_openingCase();
1720 mainFrame,
1721 progressIndicatorTitle,
1722 new String[]{Bundle.Case_progressIndicatorCancelButton_label()},
1723 Bundle.Case_progressIndicatorCancelButton_label(),
1724 cancelButtonListener);
1725 } else {
1727 }
1728 progressIndicator.
start(Bundle.Case_progressMessage_preparing());
1729
1730 /*
1731 * Creating/opening a case is always done by creating a task running in
1732 * the same non-UI thread that will be used to close the case, so a
1733 * single-threaded executor service is created here and saved as case
1734 * state (must be volatile for cancellation to work).
1735 *
1736 * --- If the case is a single-user case, this supports cancelling
1737 * opening of the case by cancelling the task.
1738 *
1739 * --- If the case is a multi-user case, this still supports
1740 * cancellation, but it also makes it possible for the shared case
1741 * directory lock held as long as the case is open to be released in the
1742 * same thread in which it was acquired, as is required by the
1743 * coordination service.
1744 */
1746 caseLockingExecutor = Executors.newSingleThreadExecutor(threadFactory);
1747 Future<Void> future = caseLockingExecutor.submit(() -> {
1749 open(isNewCase, progressIndicator);
1750 } else {
1751 /*
1752 * First, acquire a shared case directory lock that will be held
1753 * as long as this node has this case open. This will prevent
1754 * deletion of the case by another node. Next, acquire an
1755 * exclusive case resources lock to ensure only one node at a
1756 * time can create/open/upgrade/close the case resources.
1757 */
1758 progressIndicator.
progress(Bundle.Case_progressMessage_preparingToOpenCaseResources());
1761 assert (null != resourcesLock);
1762 open(isNewCase, progressIndicator);
1763 } catch (CaseActionException ex) {
1765 throw ex;
1766 }
1767 }
1768 return null;
1769 });
1770 if (null != cancelButtonListener) {
1772 }
1773
1774 /*
1775 * Wait for the case creation/opening task to finish.
1776 */
1777 try {
1778 future.get();
1779 } catch (InterruptedException discarded) {
1780 /*
1781 * The thread this method is running in has been interrupted. Cancel
1782 * the create/open task, wait for it to finish, and shut down the
1783 * executor. This can be done safely because if the task is
1784 * completed with a cancellation condition, the case will have been
1785 * closed and the case directory lock released will have been
1786 * released.
1787 */
1788 if (null != cancelButtonListener) {
1790 } else {
1791 future.cancel(true);
1792 }
1794 } catch (CancellationException discarded) {
1795 /*
1796 * The create/open task has been cancelled. Wait for it to finish,
1797 * and shut down the executor. This can be done safely because if
1798 * the task is completed with a cancellation condition, the case
1799 * will have been closed and the case directory lock released will
1800 * have been released.
1801 */
1804 } catch (ExecutionException ex) {
1805 /*
1806 * The create/open task has thrown an exception. Wait for it to
1807 * finish, and shut down the executor. This can be done safely
1808 * because if the task is completed with an execution condition, the
1809 * case will have been closed and the case directory lock released
1810 * will have been released.
1811 */
1813 throw new CaseActionException(Bundle.Case_exceptionMessage_execExceptionWrapperMessage(ex.getCause().getLocalizedMessage()), ex);
1814 } finally {
1815 progressIndicator.
finish();
1816 }
1817 }
1818
1831 try {
1832 if (Thread.currentThread().isInterrupted()) {
1834 }
1835
1836 if (isNewCase) {
1838 } else {
1840 }
1841
1842 if (Thread.currentThread().isInterrupted()) {
1844 }
1845
1847
1848 if (Thread.currentThread().isInterrupted()) {
1850 }
1851 } catch (CaseActionException ex) {
1852 /*
1853 * Cancellation or failure. Clean up. The sleep is a little hack to
1854 * clear the interrupted flag for this thread if this is a
1855 * cancellation scenario, so that the clean up can run to completion
1856 * in this thread.
1857 */
1858 try {
1859 Thread.sleep(1);
1860 } catch (InterruptedException discarded) {
1861 }
1862 close(progressIndicator);
1863 throw ex;
1864 }
1865 }
1866
1877 @Messages({
1878 "Case.progressMessage.creatingCaseDirectory=Creating case directory...",
1879 "Case.progressMessage.creatingCaseDatabase=Creating case database...",
1880 "# {0} - exception message", "Case.exceptionMessage.couldNotCreateCaseDatabase=Failed to create case database:\n{0}",
1881 "Case.exceptionMessage.couldNotCreateMetadataFile=Failed to create case metadata file."
1882 })
1884 /*
1885 * Create the case directory, if it does not already exist.
1886 *
1887 * TODO (JIRA-2180): Always create the case directory as part of the
1888 * case creation process.
1889 */
1891 progressIndicator.
progress(Bundle.Case_progressMessage_creatingCaseDirectory());
1893 }
1894
1895 /*
1896 * Create the case database.
1897 */
1898 progressIndicator.
progress(Bundle.Case_progressMessage_creatingCaseDatabase());
1899 try {
1901 /*
1902 * For single-user cases, the case database is a SQLite database
1903 * with a standard name, physically located in the root of the
1904 * case directory.
1905 */
1907 metadata.setCaseDatabaseName(SINGLE_USER_CASE_DB_NAME);
1908 } else {
1909 /*
1910 * For multi-user cases, the case database is a PostgreSQL
1911 * database with a name derived from the case display name,
1912 * physically located on a database server.
1913 */
1915 metadata.setCaseDatabaseName(caseDb.getDatabaseName());
1916 }
1917 } catch (TskCoreException ex) {
1918 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotCreateCaseDatabase(ex.getLocalizedMessage()), ex);
1920 throw new CaseActionException(NbBundle.getMessage(
Case.class,
"Case.databaseConnectionInfo.error.msg"), ex);
1922 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotCreateMetadataFile(), ex);
1923 }
1924 }
1925
1936 @Messages({
1937 "Case.progressMessage.openingCaseDatabase=Opening case database...",
1938 "Case.exceptionMessage.couldNotOpenCaseDatabase=Failed to open case database.",
1939 "Case.unsupportedSchemaVersionMessage=Unsupported DB schema version - see log for details",
1940 "Case.databaseConnectionInfo.error.msg=Error accessing database server connection info. See Tools, Options, Multi-User.",
1941 "Case.open.exception.multiUserCaseNotEnabled=Cannot open a multi-user case if multi-user cases are not enabled. "
1942 + "See Tools, Options, Multi-user."
1943 })
1945 try {
1946 progressIndicator.
progress(Bundle.Case_progressMessage_openingCaseDatabase());
1949 caseDb = SleuthkitCase.openCase(Paths.get(metadata.
getCaseDirectory(), databaseName).toString());
1951 try {
1954 throw new CaseActionException(Case_databaseConnectionInfo_error_msg(), ex);
1955 }
1956 } else {
1957 throw new CaseActionException(Case_open_exception_multiUserCaseNotEnabled());
1958 }
1959 } catch (TskUnsupportedSchemaVersionException ex) {
1960 throw new CaseActionException(Bundle.Case_unsupportedSchemaVersionMessage(), ex);
1961 } catch (TskCoreException ex) {
1962 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotOpenCaseDatabase(), ex);
1963 }
1964 }
1965
1974 @Messages({
1975 "Case.progressMessage.switchingLogDirectory=Switching log directory...",
1976 "Case.progressMessage.clearingTempDirectory=Clearing case temp directory...",
1977 "Case.progressMessage.openingCaseLevelServices=Opening case-level services...",
1978 "Case.progressMessage.openingApplicationServiceResources=Opening application service case resources...",
1979 "Case.progressMessage.settingUpNetworkCommunications=Setting up network communications...",})
1981 /*
1982 * Switch to writing to the application logs in the logs subdirectory of
1983 * the case directory.
1984 */
1985 progressIndicator.
progress(Bundle.Case_progressMessage_switchingLogDirectory());
1987 if (Thread.currentThread().isInterrupted()) {
1989 }
1990
1991 /*
1992 * Clear the temp subdirectory of the case directory.
1993 */
1994 progressIndicator.
progress(Bundle.Case_progressMessage_clearingTempDirectory());
1996 if (Thread.currentThread().isInterrupted()) {
1998 }
1999
2000 /*
2001 * Open the case-level services.
2002 */
2003 progressIndicator.
progress(Bundle.Case_progressMessage_openingCaseLevelServices());
2004 this.caseServices =
new Services(caseDb);
2005 if (Thread.currentThread().isInterrupted()) {
2007 }
2008
2009 /*
2010 * Allow any registered application services to open any resources
2011 * specific to this case.
2012 */
2013 progressIndicator.
progress(Bundle.Case_progressMessage_openingApplicationServiceResources());
2015 if (Thread.currentThread().isInterrupted()) {
2017 }
2018
2019 /*
2020 * If this case is a multi-user case, set up for communication with
2021 * other nodes.
2022 */
2024 progressIndicator.
progress(Bundle.Case_progressMessage_settingUpNetworkCommunications());
2025 try {
2027 if (Thread.currentThread().isInterrupted()) {
2029 }
2030 collaborationMonitor =
new CollaborationMonitor(metadata.
getCaseName());
2032 /*
2033 * The collaboration monitor and event channel are not
2034 * essential. Log an error and notify the user, but do not
2035 * throw.
2036 */
2037 logger.log(Level.SEVERE, "Failed to setup network communications", ex); //NON-NLS
2040 NbBundle.getMessage(
Case.class,
"Case.CollaborationSetup.FailNotify.Title"),
2041 NbBundle.getMessage(
Case.class,
"Case.CollaborationSetup.FailNotify.ErrMsg")));
2042 }
2043 }
2044 }
2045 }
2046
2051 @NbBundle.Messages({
2052 "# {0} - service name", "Case.serviceOpenCaseResourcesProgressIndicator.title={0} Opening Case Resources",
2053 "# {0} - service name", "Case.serviceOpenCaseResourcesProgressIndicator.cancellingMessage=Cancelling opening case resources by {0}...",
2054 "# {0} - service name", "Case.servicesException.notificationTitle={0} Error"
2055 })
2057 /*
2058 * Each service gets its own independently cancellable/interruptible
2059 * task, running in a named thread managed by an executor service, with
2060 * its own progress indicator. This allows for cancellation of the
2061 * opening of case resources for individual services. It also makes it
2062 * possible to ensure that each service task completes before the next
2063 * one starts by awaiting termination of the executor service.
2064 */
2066 /*
2067 * Create a progress indicator for the task and start the task. If
2068 * running with a GUI, the progress indicator will be a dialog box
2069 * with a Cancel button.
2070 */
2074 cancelButtonListener =
new CancelButtonListener(Bundle.Case_serviceOpenCaseResourcesProgressIndicator_cancellingMessage(service.getServiceName()));
2076 mainFrame,
2077 Bundle.Case_serviceOpenCaseResourcesProgressIndicator_title(service.getServiceName()),
2078 new String[]{Bundle.Case_progressIndicatorCancelButton_label()},
2079 Bundle.Case_progressIndicatorCancelButton_label(),
2080 cancelButtonListener);
2081 } else {
2083 }
2084 progressIndicator.
start(Bundle.Case_progressMessage_preparing());
2086 String threadNameSuffix = service.getServiceName().replaceAll("[ ]", "-"); //NON-NLS
2087 threadNameSuffix = threadNameSuffix.toLowerCase();
2089 ExecutorService executor = Executors.newSingleThreadExecutor(threadFactory);
2090 Future<Void> future = executor.submit(() -> {
2091 service.openCaseResources(context);
2092 return null;
2093 });
2094 if (null != cancelButtonListener) {
2097 }
2098
2099 /*
2100 * Wait for the task to either be completed or
2101 * cancelled/interrupted, or for the opening of the case to be
2102 * cancelled.
2103 */
2104 try {
2105 future.get();
2106 } catch (InterruptedException discarded) {
2107 /*
2108 * The parent create/open case task has been cancelled.
2109 */
2111 future.cancel(true);
2112 } catch (CancellationException discarded) {
2113 /*
2114 * The opening of case resources by the application service has
2115 * been cancelled, so the executor service has thrown. Note that
2116 * there is no guarantee the task itself has responded to the
2117 * cancellation request yet.
2118 */
2120 } catch (ExecutionException ex) {
2121 /*
2122 * An exception was thrown while executing the task. The
2123 * case-specific application service resources are not
2124 * essential. Log an error and notify the user if running the
2125 * desktop GUI, but do not throw.
2126 */
2127 Case.
logger.log(Level.SEVERE, String.format(
"%s failed to open case resources for %s", service.getServiceName(), this.
getDisplayName()), ex);
2129 SwingUtilities.invokeLater(() -> {
2131 });
2132 }
2133 } finally {
2134 /*
2135 * Shut down the executor service and wait for it to finish.
2136 * This ensures that the task has finished. Without this, it
2137 * would be possible to start the next task before the current
2138 * task responded to a cancellation request.
2139 */
2141 progressIndicator.
finish();
2142 }
2143
2144 if (Thread.currentThread().isInterrupted()) {
2146 }
2147 }
2148 }
2149
2153 private void close() throws CaseActionException {
2154 /*
2155 * Set up either a GUI progress indicator without a Cancel button or a
2156 * logging progress indicator.
2157 */
2161 mainFrame,
2162 Bundle.Case_progressIndicatorTitle_closingCase());
2163 } else {
2165 }
2166 progressIndicator.
start(Bundle.Case_progressMessage_preparing());
2167
2168 /*
2169 * Closing a case is always done in the same non-UI thread that
2170 * opened/created the case. If the case is a multi-user case, this
2171 * ensures that case directory lock that is held as long as the case is
2172 * open is released in the same thread in which it was acquired, as is
2173 * required by the coordination service.
2174 */
2175 Future<Void> future = caseLockingExecutor.submit(() -> {
2177 close(progressIndicator);
2178 } else {
2179 /*
2180 * Acquire an exclusive case resources lock to ensure only one
2181 * node at a time can create/open/upgrade/close the case
2182 * resources.
2183 */
2184 progressIndicator.
progress(Bundle.Case_progressMessage_preparing());
2186 assert (null != resourcesLock);
2187 close(progressIndicator);
2188 } finally {
2189 /*
2190 * Always release the case directory lock that was acquired
2191 * when the case was opened.
2192 */
2194 }
2195 }
2196 return null;
2197 });
2198
2199 try {
2200 future.get();
2201 } catch (InterruptedException | CancellationException unused) {
2202 /*
2203 * The wait has been interrupted by interrupting the thread running
2204 * this method. Not allowing cancellation of case closing, so ignore
2205 * the interrupt. Likewsie, cancellation of the case closing task is
2206 * not supported.
2207 */
2208 } catch (ExecutionException ex) {
2209 throw new CaseActionException(Bundle.Case_exceptionMessage_execExceptionWrapperMessage(ex.getCause().getMessage()), ex);
2210 } finally {
2212 progressIndicator.
finish();
2213 }
2214 }
2215
2221 @Messages({
2222 "Case.progressMessage.shuttingDownNetworkCommunications=Shutting down network communications...",
2223 "Case.progressMessage.closingApplicationServiceResources=Closing case-specific application service resources...",
2224 "Case.progressMessage.closingCaseLevelServices=Closing case-level services...",
2225 "Case.progressMessage.closingCaseDatabase=Closing case database..."
2226 })
2229
2230 /*
2231 * Stop sending/receiving case events to and from other nodes if this is
2232 * a multi-user case.
2233 */
2235 progressIndicator.
progress(Bundle.Case_progressMessage_shuttingDownNetworkCommunications());
2236 if (null != collaborationMonitor) {
2237 collaborationMonitor.shutdown();
2238 }
2240 }
2241
2242 /*
2243 * Allow all registered application services providers to close
2244 * resources related to the case.
2245 */
2246 progressIndicator.
progress(Bundle.Case_progressMessage_closingApplicationServiceResources());
2248
2249 /*
2250 * Close the case-level services.
2251 */
2252 if (null != caseServices) {
2253 progressIndicator.
progress(Bundle.Case_progressMessage_closingCaseLevelServices());
2254 try {
2255 this.caseServices.
close();
2256 } catch (IOException ex) {
2257 logger.log(Level.SEVERE, String.format(
"Error closing internal case services for %s at %s",
this.getName(), this.
getCaseDirectory()), ex);
2258 }
2259 }
2260
2261 /*
2262 * Close the case database
2263 */
2264 if (null != caseDb) {
2265 progressIndicator.
progress(Bundle.Case_progressMessage_closingCaseDatabase());
2266 caseDb.close();
2267 }
2268
2269 /*
2270 * Switch the log directory.
2271 */
2272 progressIndicator.
progress(Bundle.Case_progressMessage_switchingLogDirectory());
2274 }
2275
2280 @Messages({
2281 "# {0} - serviceName", "Case.serviceCloseResourcesProgressIndicator.title={0} Closing Case Resources",
2282 "# {0} - service name", "# {1} - exception message", "Case.servicesException.serviceResourcesCloseError=Could not close case resources for {0} service: {1}"
2283 })
2285 /*
2286 * Each service gets its own independently cancellable task, and thus
2287 * its own task progress indicator.
2288 */
2293 mainFrame,
2294 Bundle.Case_serviceCloseResourcesProgressIndicator_title(service.getServiceName()));
2295 } else {
2297 }
2298 progressIndicator.
start(Bundle.Case_progressMessage_preparing());
2300 String threadNameSuffix = service.getServiceName().replaceAll("[ ]", "-"); //NON-NLS
2301 threadNameSuffix = threadNameSuffix.toLowerCase();
2303 ExecutorService executor = Executors.newSingleThreadExecutor(threadFactory);
2304 Future<Void> future = executor.submit(() -> {
2305 service.closeCaseResources(context);
2306 return null;
2307 });
2308 try {
2309 future.get();
2310 } catch (InterruptedException ex) {
2311 Case.
logger.log(Level.SEVERE, String.format(
"Unexpected interrupt while waiting on %s service to close case resources", service.getServiceName()), ex);
2312 } catch (CancellationException ex) {
2313 Case.
logger.log(Level.SEVERE, String.format(
"Unexpected cancellation while waiting on %s service to close case resources", service.getServiceName()), ex);
2314 } catch (ExecutionException ex) {
2315 Case.
logger.log(Level.SEVERE, String.format(
"%s service failed to open case resources", service.getServiceName()), ex);
2318 Bundle.Case_servicesException_notificationTitle(service.getServiceName()),
2319 Bundle.Case_servicesException_serviceResourcesCloseError(service.getServiceName(), ex.getLocalizedMessage())));
2320 }
2321 } finally {
2323 progressIndicator.
finish();
2324 }
2325 }
2326 }
2327
2336 @Messages({"Case.creationException.couldNotAcquireDirLock=Failed to get lock on case directory."})
2338 try {
2341 throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireDirLock());
2342 }
2344 throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireDirLock(), ex);
2345 }
2346 }
2347
2355 try {
2359 logger.log(Level.SEVERE, String.format("Failed to release shared case directory lock for %s", caseDir), ex);
2360 }
2361 }
2362 }
2363
2372 if (!subDirectory.exists()) {
2373 subDirectory.mkdirs();
2374 }
2375 return subDirectory.toString();
2376
2377 }
2378
2383 @ThreadSafe
2385
2387 @GuardedBy("this")
2389 @GuardedBy("this")
2391 @GuardedBy("this")
2393
2404 }
2405
2413 /*
2414 * If the cancel button has already been pressed, pass the
2415 * cancellation on to the case context.
2416 */
2419 }
2420 }
2421
2429 /*
2430 * If the cancel button has already been pressed, cancel the Future
2431 * of the task.
2432 */
2435 }
2436 }
2437
2443 @Override
2446 }
2447
2452 /*
2453 * At a minimum, set the cancellation requested flag of this
2454 * listener.
2455 */
2458 /*
2459 * Set the cancellation request flag and display the
2460 * cancellation message in the progress indicator for the case
2461 * context associated with this listener.
2462 */
2466 ((ModalDialogProgressIndicator) progressIndicator).setCancelling(cancellationMessage);
2467 }
2468 }
2470 }
2472 /*
2473 * Cancel the Future of the task associated with this listener.
2474 * Note that the task thread will be interrupted if the task is
2475 * blocked.
2476 */
2478 }
2479 }
2480 }
2481
2486
2488
2491 }
2492
2493 @Override
2495 return new Thread(task, threadName);
2496 }
2497
2498 }
2499
2507 @Deprecated
2510 }
2511
2531 @Deprecated
2532 public static void create(String caseDir, String caseDisplayName, String caseNumber, String examiner)
throws CaseActionException {
2534 }
2535
2556 @Deprecated
2557 public static void create(String caseDir, String caseDisplayName, String caseNumber, String examiner,
CaseType caseType)
throws CaseActionException {
2559 }
2560
2572 @Deprecated
2573 public static void open(String caseMetadataFilePath)
throws CaseActionException {
2575 }
2576
2586 @Deprecated
2589 }
2590
2596 @Deprecated
2599 }
2600
2614 @Deprecated
2617 }
2618
2628 @Deprecated
2630 return new File(filePath).isFile();
2631 }
2632
2641 @Deprecated
2644 }
2645
2653 @Deprecated
2656 }
2657
2667 @Deprecated
2669 return "ModuleOutput"; //NON-NLS
2670 }
2671
2681 @Deprecated
2682 public static PropertyChangeSupport
2684 return new PropertyChangeSupport(
Case.class
2685 );
2686 }
2687
2696 @Deprecated
2699 }
2700
2715 @Deprecated
2716 public Image
addImage(String imgPath,
long imgId, String timeZone)
throws CaseActionException {
2717 try {
2718 Image newDataSource = caseDb.getImageById(imgId);
2720 return newDataSource;
2721 } catch (TskCoreException ex) {
2722 throw new CaseActionException(NbBundle.getMessage(this.getClass(), "Case.addImg.exception.msg"), ex);
2723 }
2724 }
2725
2733 @Deprecated
2736 }
2737
2748 @Deprecated
2749 public void deleteReports(Collection<? extends Report> reports,
boolean deleteFromDisk)
throws TskCoreException {
2751 }
2752
2753 }
String getLogDirectoryPath()
static final AutopsyEventPublisher eventPublisher
List< Content > getDataSources()
String getModuleOutputDirectoryRelativePath()
void notifyContentTagDeleted(ContentTag deletedTag)
Case(CaseMetadata caseMetaData)
static CaseType fromString(String typeName)
static final String CASE_ACTION_THREAD_NAME
void publishLocally(AutopsyEvent event)
static void createAsCurrentCase(CaseType caseType, String caseDir, CaseDetails caseDetails)
void notifyBlackBoardArtifactTagDeleted(BlackboardArtifactTag deletedTag)
String getExaminerPhone()
Set< TimeZone > getTimeZones()
static String getNameForTitle()
Image addImage(String imgPath, long imgId, String timeZone)
static synchronized IngestManager getInstance()
static CoordinationService.Lock acquireExclusiveCaseResourcesLock(String caseDir)
static boolean runningWithGUI
static void closeCurrentCase()
String getTempDirectory()
static boolean existsCurrentCase()
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
String getCaseDirectory()
static String getAppName()
void notifyTagDefinitionChanged(String changedTagName)
static Case getOpenCase()
static volatile Frame mainFrame
static String convertTimeZone(String timeZoneId)
static boolean driveExists(String path)
static void openCoreWindows()
void addSubscriber(Set< String > eventNames, PropertyChangeListener subscriber)
synchronized static void setLogDirectory(String directoryPath)
TaskThreadFactory(String threadName)
static final String CACHE_FOLDER
static String getAutopsyVersion()
CaseType(String typeName)
Case(CaseType caseType, String caseDir, CaseDetails caseDetails)
String getReportDirectory()
void createCaseData(ProgressIndicator progressIndicator)
static boolean getIsMultiUserModeEnabled()
void addReport(String localPath, String srcModuleName, String reportName)
static void updateGUIForCaseOpened(Case newCurrentCase)
static CaseDbConnectionInfo getDatabaseConnectionInfo()
String getModulesOutputDirAbsPath()
static void create(String caseDir, String caseDisplayName, String caseNumber, String examiner, CaseType caseType)
void closeAppServiceCaseResources()
static final String SINGLE_USER_CASE_DB_NAME
static void deleteCase(CaseMetadata metadata)
void deleteReports(Collection<?extends Report > reports, boolean deleteFromDisk)
synchronized void closeRemoteEventChannel()
volatile ExecutorService caseLockingExecutor
List< Report > getAllReports()
static void clearTempSubDir(String tempSubDirPath)
static boolean isValidName(String caseName)
void acquireSharedCaseDirLock(String caseDir)
CollaborationMonitor collaborationMonitor
void releaseSharedCaseDirLock(String caseDir)
static void shutDownTaskExecutor(ExecutorService executor)
static void closeCoreWindows()
ProgressIndicator getProgressIndicator()
static String getModulesOutputDirRelPath()
Set< TimeZone > getTimeZone()
void openCaseData(ProgressIndicator progressIndicator)
static final String MODULE_FOLDER
static void create(String caseDir, String caseDisplayName, String caseNumber, String examiner)
synchronized void openRemoteEventChannel(String channelName)
String getCaseDisplayName()
void openAppServiceCaseResources()
void openServices(ProgressIndicator progressIndicator)
static void invokeStartupDialog()
static String displayNameToUniqueName(String caseDisplayName)
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 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()
Thread newThread(Runnable task)
static void removeEventSubscriber(String eventName, PropertyChangeListener subscriber)
final CaseMetadata metadata
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)
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 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)
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)
void notifyFailedAddingDataSource(UUID addingDataSourceEventId)
static void removeEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
static final Object caseActionSerializationLock
void open(boolean isNewCase, ProgressIndicator progressIndicator)
static boolean isCaseOpen()
String getTextIndexName()
static final String REPORTS_FOLDER
void progress(String message)
static void createCaseDirectory(String caseDir, CaseType caseType)
static final String TEMP_FOLDER
BLACKBOARD_ARTIFACT_TAG_DELETED
static void addEventSubscriber(Set< String > eventNames, PropertyChangeListener subscriber)
static void deleteCase(CaseMetadata metadata, ProgressIndicator progressIndicator)
static void error(String message)
String getExaminerEmail()