1 /*
2 * Autopsy Forensic Browser
3 *
4 * Copyright 2011-2017 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;
116 import org.
sleuthkit.datamodel.TskUnsupportedSchemaVersionException;
117
122
147
148 /*
149 * Get a reference to the main window of the desktop application to use to
150 * parent pop up dialogs and initialize the application name for use in
151 * changing the main window title.
152 */
153 static {
154 WindowManager.getDefault().invokeWhenUIReady(new Runnable() {
155 @Override
156 public void run() {
157 mainFrame = WindowManager.getDefault().getMainWindow();
158 }
159 });
160 }
161
166
169
171
180 if (typeName != null) {
182 if (typeName.equalsIgnoreCase(c.toString())) {
183 return c;
184 }
185 }
186 }
187 return null;
188 }
189
195 @Override
197 return typeName;
198 }
199
205 @Messages({
206 "Case_caseType_singleUser=Single-user case",
207 "Case_caseType_multiUser=Multi-user case"
208 })
210 if (fromString(typeName) == SINGLE_USER_CASE) {
211 return Bundle.Case_caseType_singleUser();
212 } else {
213 return Bundle.Case_caseType_multiUser();
214 }
215 }
216
223 this.typeName = typeName;
224 }
225
236 @Deprecated
238 return (otherTypeName == null) ? false : typeName.equals(otherTypeName);
239 }
240
241 };
242
248
256 @Deprecated
265 @Deprecated
274 @Deprecated
373
374 };
375
384 .map(Events::toString)
385 .collect(Collectors.toSet()), listener);
386 }
387
396 .map(Events::toString)
397 .collect(Collectors.toSet()), listener);
398 }
399
408 @Deprecated
411 }
412
420 eventTypes.forEach((
Events event) -> {
422 });
423 }
424
433 @Deprecated
436 }
437
446 }
447
456 }
457
465 eventTypes.forEach((
Events event) -> {
467 });
468 }
469
479 return !(caseName.contains("\\") || caseName.contains("/") || caseName.contains(":")
480 || caseName.contains("*") || caseName.contains("?") || caseName.contains("\"")
481 || caseName.contains("<") || caseName.contains(">") || caseName.contains("|"));
482 }
483
508 @Deprecated
511 }
512
532 @Messages({
533 "Case.exceptionMessage.emptyCaseName=Must specify a case name.",
534 "Case.exceptionMessage.emptyCaseDir=Must specify a case directory path."
535 })
538 throw new CaseActionException(Bundle.Case_exceptionMessage_emptyCaseName());
539 }
540 if (caseDir.isEmpty()) {
541 throw new CaseActionException(Bundle.Case_exceptionMessage_emptyCaseDir());
542 }
544 }
545
559 @Messages({
560 "Case.exceptionMessage.failedToReadMetadata=Failed to read case metadata.",
561 "Case.exceptionMessage.cannotOpenMultiUserCaseNoSettings=Multi-user settings are missing (see Tools, Options, Multi-user tab), cannot open a multi-user case."
562 })
565 try {
566 metadata =
new CaseMetadata(Paths.get(caseMetadataFilePath));
568 throw new CaseActionException(Bundle.Case_exceptionMessage_failedToReadMetadata(), ex);
569 }
571 throw new CaseActionException(Bundle.Case_exceptionMessage_cannotOpenMultiUserCaseNoSettings());
572 }
574 }
575
582 return currentCase != null;
583 }
584
593 /*
594 * Throwing an unchecked exception is a bad idea here.
595 *
596 * TODO (JIRA-2229): Case.getCurrentCase() method throws unchecked
597 * IllegalStateException; change to throw checked exception or return
598 * null
599 */
600 if (null != currentCase) {
602 } else {
603 throw new IllegalStateException(NbBundle.getMessage(
Case.class,
"Case.getCurCase.exception.noneOpen"));
604 }
605 }
606
615 @Messages({
616 "# {0} - exception message", "Case.closeException.couldNotCloseCase=Error closing case: {0}",
617 "Case.progressIndicatorTitle.closingCase=Closing Case"
618 })
621 if (null == currentCase) {
622 return;
623 }
625 try {
627 logger.log(Level.INFO, "Closing current case {0} ({1}) in {2}", new Object[]{closedCase.getDisplayName(), closedCase.getName(), closedCase.getCaseDirectory()}); //NON-NLS
628 currentCase = null;
630 logger.log(Level.INFO, "Closed current case {0} ({1}) in {2}", new Object[]{closedCase.getDisplayName(), closedCase.getName(), closedCase.getCaseDirectory()}); //NON-NLS
631 } catch (CaseActionException ex) {
633 throw ex;
634 } finally {
637 }
638 }
639 }
640 }
641
652 if (null == currentCase) {
653 return;
654 }
658 }
659 }
660
672 @Messages({
673 "Case.progressIndicatorTitle.deletingCase=Deleting Case",
674 "Case.exceptionMessage.cannotDeleteCurrentCase=Cannot delete current case, it must be closed first.",
675 "Case.progressMessage.checkingForOtherUser=Checking to see if another user has the case open...",
676 "Case.exceptionMessage.cannotGetLockToDeleteCase=Cannot delete case because it is open for another user or there is a problem with the coordination service."
677 })
680 if (null != currentCase) {
681 throw new CaseActionException(Bundle.Case_exceptionMessage_cannotDeleteCurrentCase());
682 }
683 }
684
685 /*
686 * Set up either a GUI progress indicator without a cancel button (can't
687 * cancel deleting a case) or a logging progress indicator.
688 */
692 } else {
694 }
695 progressIndicator.
start(Bundle.Case_progressMessage_preparing());
696 try {
699 } else {
700 /*
701 * First, acquire an exclusive case directory lock. The case
702 * cannot be deleted if another node has it open.
703 */
704 progressIndicator.
progress(Bundle.Case_progressMessage_checkingForOtherUser());
706 assert (null != dirLock);
709 throw new CaseActionException(Bundle.Case_exceptionMessage_cannotGetLockToDeleteCase(), ex);
710 }
711 }
712 } finally {
713 progressIndicator.
finish();
714 }
715 }
716
727 @Messages({
728 "Case.exceptionMessage.cannotLocateMainWindow=Cannot locate main application window"
729 })
732 if (null != currentCase) {
733 try {
735 } catch (CaseActionException ex) {
736 /*
737 * Notify the user and continue (the error has already been
738 * logged in closeCurrentCase.
739 */
741 }
742 }
743 try {
744 logger.log(Level.INFO, "Opening {0} ({1}) in {2} as the current case", new Object[]{newCurrentCase.getDisplayName(), newCurrentCase.getName(), newCurrentCase.getCaseDirectory()}); //NON-NLS
745 newCurrentCase.
open(isNewCase);
746 currentCase = newCurrentCase;
747 logger.log(Level.INFO, "Opened {0} ({1}) in {2} as the current case", new Object[]{newCurrentCase.getDisplayName(), newCurrentCase.getName(), newCurrentCase.getCaseDirectory()}); //NON-NLS
750 }
753 logger.log(Level.INFO, String.format(
"Cancelled opening %s (%s) in %s as the current case", newCurrentCase.
getDisplayName(), newCurrentCase.
getName(), newCurrentCase.
getCaseDirectory()));
//NON-NLS
754 throw ex;
755 } catch (CaseActionException ex) {
756 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
757 throw ex;
758 }
759 }
760 }
761
771 /*
772 * Replace all non-ASCII characters.
773 */
774 String uniqueCaseName = caseDisplayName.replaceAll("[^\\p{ASCII}]", "_"); //NON-NLS
775
776 /*
777 * Replace all control characters.
778 */
779 uniqueCaseName = uniqueCaseName.replaceAll("[\\p{Cntrl}]", "_"); //NON-NLS
780
781 /*
782 * Replace /, ,円 :, ?, space, ' ".
783 */
784 uniqueCaseName = uniqueCaseName.replaceAll("[ /?:'\"\\\\]", "_"); //NON-NLS
785
786 /*
787 * Make it all lowercase.
788 */
789 uniqueCaseName = uniqueCaseName.toLowerCase();
790
791 /*
792 * Add a time stamp for uniqueness.
793 */
794 SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
795 Date date = new Date();
796 uniqueCaseName = uniqueCaseName + "_" + dateFormat.format(date);
797
798 return uniqueCaseName;
799 }
800
809 static void createCaseDirectory(String caseDir,
CaseType caseType)
throws CaseActionException {
810
811 File caseDirF = new File(caseDir);
812
813 if (caseDirF.exists()) {
814 if (caseDirF.isFile()) {
815 throw new CaseActionException(
816 NbBundle.getMessage(
Case.class,
"Case.createCaseDir.exception.existNotDir", caseDir));
817
818 } else if (!caseDirF.canRead() || !caseDirF.canWrite()) {
819 throw new CaseActionException(
820 NbBundle.getMessage(
Case.class,
"Case.createCaseDir.exception.existCantRW", caseDir));
821 }
822 }
823
824 try {
825 boolean result = (caseDirF).mkdirs(); // create root case Directory
826
827 if (result == false) {
828 throw new CaseActionException(
829 NbBundle.getMessage(
Case.class,
"Case.createCaseDir.exception.cantCreate", caseDir));
830 }
831
832 // create the folders inside the case directory
833 String hostClause = "";
834
836 hostClause = File.separator + NetworkUtils.getLocalHostName();
837 }
838 result = result && (new File(caseDir + hostClause + File.separator + EXPORT_FOLDER)).mkdirs()
839 && (new File(caseDir + hostClause + File.separator + LOG_FOLDER)).mkdirs()
840 && (new File(caseDir + hostClause + File.separator + TEMP_FOLDER)).mkdirs()
841 && (new File(caseDir + hostClause + File.separator + CACHE_FOLDER)).mkdirs();
842
843 if (result == false) {
844 throw new CaseActionException(
845 NbBundle.getMessage(
Case.class,
"Case.createCaseDir.exception.cantCreateCaseDir", caseDir));
846 }
847
848 final String modulesOutDir = caseDir + hostClause + File.separator +
MODULE_FOLDER;
849 result = new File(modulesOutDir).mkdir();
850
851 if (result == false) {
852 throw new CaseActionException(
853 NbBundle.getMessage(
Case.class,
"Case.createCaseDir.exception.cantCreateModDir",
854 modulesOutDir));
855 }
856
857 final String reportsOutDir = caseDir + hostClause + File.separator +
REPORTS_FOLDER;
858 result = new File(reportsOutDir).mkdir();
859
860 if (result == false) {
861 throw new CaseActionException(
862 NbBundle.getMessage(
Case.class,
"Case.createCaseDir.exception.cantCreateReportsDir",
863 modulesOutDir));
864
865 }
866
867 } catch (MissingResourceException | CaseActionException e) {
868 throw new CaseActionException(
869 NbBundle.getMessage(
Case.class,
"Case.createCaseDir.exception.gen", caseDir), e);
870 }
871 }
872
880 static Map<Long, String> getImagePaths(SleuthkitCase db) {
881 Map<Long, String> imgPaths = new HashMap<>();
882 try {
883 Map<Long, List<String>> imgPathsList = db.getImagePaths();
884 for (Map.Entry<Long, List<String>> entry : imgPathsList.entrySet()) {
885 if (entry.getValue().size() > 0) {
886 imgPaths.put(entry.getKey(), entry.getValue().get(0));
887 }
888 }
889 } catch (TskCoreException ex) {
890 logger.log(Level.SEVERE, "Error getting image paths", ex); //NON-NLS
891 }
892 return imgPaths;
893 }
894
911 @Messages({
912 "Case.progressMessage.deletingTextIndex=Deleting text index...",
913 "Case.progressMessage.deletingCaseDatabase=Deleting case database...",
914 "Case.progressMessage.deletingCaseDirectory=Deleting case directory...",
915 "Case.exceptionMessage.errorsDeletingCase=Errors occured while deleting the case. See the application log for details"
916 })
918 boolean errorsOccurred = false;
920 /*
921 * Delete the case database from the database server.
922 */
923 try {
924 progressIndicator.
progress(Bundle.Case_progressMessage_deletingCaseDatabase());
925 CaseDbConnectionInfo db;
927 Class.forName("org.postgresql.Driver"); //NON-NLS
928 try (Connection connection = DriverManager.getConnection("jdbc:postgresql://" + db.getHost() + ":" + db.getPort() + "/postgres", db.getUserName(), db.getPassword()); //NON-NLS
929 Statement statement = connection.createStatement();) {
930 String deleteCommand =
"DROP DATABASE \"" + metadata.
getCaseDatabaseName() +
"\"";
//NON-NLS
931 statement.execute(deleteCommand);
932 }
935 errorsOccurred = true;
936 }
937 }
938
939 /*
940 * Delete the text index.
941 */
942 progressIndicator.
progress(Bundle.Case_progressMessage_deletingTextIndex());
944 try {
945 searchService.deleteTextIndex(metadata);
948 errorsOccurred = true;
949 }
950 }
951
952 /*
953 * Delete the case directory.
954 */
955 progressIndicator.
progress(Bundle.Case_progressMessage_deletingCaseDirectory());
958 errorsOccurred = true;
959 }
960
961 /*
962 * If running in a GUI, remove the case from the Recent Cases menu
963 */
965 SwingUtilities.invokeLater(() -> {
966 RecentCases.getInstance().removeRecentCase(metadata.
getCaseDisplayName(), metadata.getFilePath().toString());
967 });
968 }
969
970 if (errorsOccurred) {
971 throw new CaseActionException(Bundle.Case_exceptionMessage_errorsDeletingCase());
972 }
973 }
974
985 @Messages({"Case.creationException.couldNotAcquireResourcesLock=Failed to get lock on case resources"})
987 try {
988 String resourcesNodeName = caseDir + "_resources";
990 if (null == lock) {
991 throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireResourcesLock());
992 }
993 return lock;
994 } catch (InterruptedException ex) {
997 throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireResourcesLock(), ex);
998 }
999 }
1000
1002 //Method should become unnecessary once technical debt story 3334 is done.
1004 //Available version number is version number for this application
1006 }
1007 else {
1009 }
1010 }
1011
1017 SwingUtilities.invokeLater(() -> {
1018 /*
1019 * If the case database was upgraded for a new schema and a
1020 * backup database was created, notify the user.
1021 */
1023 String backupDbPath = caseDb.getBackupDatabasePath();
1024 if (null != backupDbPath) {
1025 JOptionPane.showMessageDialog(
1026 mainFrame,
1027 NbBundle.getMessage(
Case.class,
"Case.open.msgDlg.updated.msg", backupDbPath),
1028 NbBundle.getMessage(
Case.class,
"Case.open.msgDlg.updated.title"),
1029 JOptionPane.INFORMATION_MESSAGE);
1030 }
1031
1032 /*
1033 * Look for the files for the data sources listed in the case
1034 * database and give the user the opportunity to locate any that
1035 * are missing.
1036 */
1037 Map<Long, String> imgPaths = getImagePaths(caseDb);
1038 for (Map.Entry<Long, String> entry : imgPaths.entrySet()) {
1039 long obj_id = entry.getKey();
1040 String path = entry.getValue();
1042 if (!fileExists) {
1043 int response = JOptionPane.showConfirmDialog(
1044 mainFrame,
1045 NbBundle.getMessage(
Case.class,
"Case.checkImgExist.confDlg.doesntExist.msg", path),
1046 NbBundle.getMessage(
Case.class,
"Case.checkImgExist.confDlg.doesntExist.title"),
1047 JOptionPane.YES_NO_OPTION);
1048 if (response == JOptionPane.YES_OPTION) {
1049 MissingImageDialog.makeDialog(obj_id, caseDb);
1050 } else {
1051 logger.log(Level.SEVERE, "User proceeding with missing image files"); //NON-NLS
1052
1053 }
1054 }
1055 }
1056
1057 /*
1058 * Enable the case-specific actions.
1059 */
1062 CallableSystemAction.get(CasePropertiesAction.class).setEnabled(true);
1063 CallableSystemAction.get(CaseDeleteAction.class).setEnabled(true);
1067
1068 /*
1069 * Add the case to the recent cases tracker that supplies a list
1070 * of recent cases to the recent cases menu item and the
1071 * open/create case dialog.
1072 */
1073 RecentCases.getInstance().addRecentCase(newCurrentCase.
getDisplayName(), newCurrentCase.getMetadata().getFilePath().toString());
1074
1075 /*
1076 * Open the top components (windows within the main application
1077 * window).
1078 */
1079 if (newCurrentCase.
hasData()) {
1081 }
1082
1083 /*
1084 * Reset the main window title to:
1085 *
1086 * [curent case display name] - [application name].
1087 */
1089 });
1090 }
1091 }
1092
1093 /*
1094 * Update the GUI to to reflect the lack of a current case.
1095 */
1098 SwingUtilities.invokeLater(() -> {
1099 /*
1100 * Close the top components (windows within the main application
1101 * window).
1102 */
1104
1105 /*
1106 * Disable the case-specific menu items.
1107 */
1108 CallableSystemAction.get(
AddImageAction.class).setEnabled(
false);
1110 CallableSystemAction.get(CasePropertiesAction.class).setEnabled(false);
1111 CallableSystemAction.get(CaseDeleteAction.class).setEnabled(false);
1115
1116 /*
1117 * Clear the notifications in the notfier component in the lower
1118 * right hand corner of the main application window.
1119 */
1121
1122 /*
1123 * Reset the main window title to be just the application name,
1124 * instead of [curent case display name] - [application name].
1125 */
1127 });
1128 }
1129 }
1130
1135 File tempFolder = new File(tempSubDirPath);
1136 if (tempFolder.isDirectory()) {
1137 File[] files = tempFolder.listFiles();
1138 if (files.length > 0) {
1139 for (File file : files) {
1140 if (file.isDirectory()) {
1142 } else {
1143 file.delete();
1144 }
1145 }
1146 }
1147 }
1148 }
1149
1157 }
1158
1166 }
1167
1175 }
1176
1183 return metadata.getCreatedDate();
1184 }
1185
1193 }
1194
1202 }
1203
1211 }
1212
1220 }
1221
1229 }
1230
1238 }
1239
1247 }
1248
1256 }
1257
1268 Path hostPath;
1271 } else {
1272 hostPath = Paths.get(caseDirectory);
1273 }
1274 if (!hostPath.toFile().exists()) {
1275 hostPath.toFile().mkdirs();
1276 }
1277 return hostPath.toString();
1278 }
1279
1288 }
1289
1298 }
1299
1308 }
1309
1318 }
1319
1328 }
1329
1338 }
1339
1350 return path.subpath(path.getNameCount() - 2, path.getNameCount()).toString();
1351 } else {
1352 return path.subpath(path.getNameCount() - 1, path.getNameCount()).toString();
1353 }
1354 }
1355
1366 List<Content> list = caseDb.getRootObjects();
1367 hasDataSources = (list.size() > 0);
1368 return list;
1369 }
1370
1377 Set<TimeZone> timezones = new HashSet<>();
1378 try {
1380 final Content dataSource = c.getDataSource();
1381 if ((dataSource != null) && (dataSource instanceof Image)) {
1382 Image image = (Image) dataSource;
1383 timezones.add(TimeZone.getTimeZone(image.getTimeZone()));
1384 }
1385 }
1386 } catch (TskCoreException ex) {
1387 logger.log(Level.SEVERE, "Error getting data source time zones", ex); //NON-NLS
1388 }
1389 return timezones;
1390 }
1391
1400 }
1401
1409 if (!hasDataSources) {
1410 try {
1412 } catch (TskCoreException ex) {
1413 logger.log(Level.SEVERE, "Error accessing case database", ex); //NON-NLS
1414 }
1415 }
1417 }
1418
1431 }
1432
1445 }
1446
1460 }
1461
1471 }
1472
1482 }
1483
1492 //leaving new value of changedTagName as null, because we do not currently support changing the display name of a tag.
1494 }
1495
1505 }
1506
1516 }
1517
1529 public void addReport(String localPath, String srcModuleName, String reportName)
throws TskCoreException {
1530 String normalizedLocalPath;
1531 try {
1532 normalizedLocalPath = Paths.get(localPath).normalize().toString();
1533 } catch (InvalidPathException ex) {
1534 String errorMsg = "Invalid local path provided: " + localPath; // NON-NLS
1535 throw new TskCoreException(errorMsg, ex);
1536 }
1537 Report report = this.caseDb.addReport(normalizedLocalPath, srcModuleName, reportName);
1539 }
1540
1550 return this.caseDb.getAllReports();
1551 }
1552
1561 public void deleteReports(Collection<? extends Report> reports)
throws TskCoreException {
1562 for (Report report : reports) {
1563 this.caseDb.deleteReport(report);
1565 }
1566 }
1567
1575 }
1576
1584 @Messages({
1585 "Case.exceptionMessage.metadataUpdateError=Failed to update case metadata"
1586 })
1587 void updateCaseDetails(CaseDetails caseDetails) throws CaseActionException {
1589 try {
1590 metadata.setCaseDetails(caseDetails);
1591 } catch (CaseMetadataException ex) {
1592 throw new CaseActionException(Bundle.Case_exceptionMessage_metadataUpdateError(), ex);
1593 }
1594 if (!oldCaseDetails.getCaseNumber().equals(caseDetails.
getCaseNumber())) {
1595 eventPublisher.
publish(
new AutopsyEvent(Events.NUMBER.toString(), oldCaseDetails.getCaseNumber(), caseDetails.
getCaseNumber()));
1596 }
1597 if (!oldCaseDetails.getExaminerName().equals(caseDetails.
getExaminerName())) {
1598 eventPublisher.
publish(
new AutopsyEvent(Events.NUMBER.toString(), oldCaseDetails.getExaminerName(), caseDetails.
getExaminerName()));
1599 }
1601 eventPublisher.
publish(
new AutopsyEvent(Events.NAME.toString(), oldCaseDetails.getCaseDisplayName(), caseDetails.
getCaseDisplayName()));
1602 }
1603 eventPublisher.
publish(
new AutopsyEvent(Events.CASE_DETAILS.toString(), oldCaseDetails, caseDetails));
1604 if (RuntimeProperties.runningWithGUI()) {
1605 SwingUtilities.invokeLater(() -> {
1607 try {
1608 RecentCases.getInstance().updateRecentCase(oldCaseDetails.getCaseDisplayName(), metadata.getFilePath().toString(), caseDetails.
getCaseDisplayName(), metadata.getFilePath().toString());
1609 } catch (Exception ex) {
1610 logger.log(Level.SEVERE, "Error updating case name in UI", ex); //NON-NLS
1611 }
1612 });
1613 }
1614 }
1615
1629 }
1630
1637 metadata = caseMetaData;
1638 }
1639
1655 @Messages({
1656 "Case.progressIndicatorTitle.creatingCase=Creating Case",
1657 "Case.progressIndicatorTitle.openingCase=Opening Case",
1658 "Case.progressIndicatorCancelButton.label=Cancel",
1659 "Case.progressMessage.preparing=Preparing...",
1660 "Case.progressMessage.preparingToOpenCaseResources=<html>Preparing to open case resources.<br>This may take time if another user is upgrading the case.</html>",
1661 "Case.progressMessage.cancelling=Cancelling...",
1662 "Case.exceptionMessage.cancelledByUser=Cancelled by user.",
1663 "# {0} - exception message", "Case.exceptionMessage.execExceptionWrapperMessage={0}"
1664 })
1665 private void open(
boolean isNewCase)
throws CaseActionException {
1666 /*
1667 * Create and start either a GUI progress indicator with a Cancel button
1668 * or a logging progress indicator.
1669 */
1674 String progressIndicatorTitle = isNewCase ? Bundle.Case_progressIndicatorTitle_creatingCase() : Bundle.Case_progressIndicatorTitle_openingCase();
1676 mainFrame,
1677 progressIndicatorTitle,
1678 new String[]{Bundle.Case_progressIndicatorCancelButton_label()},
1679 Bundle.Case_progressIndicatorCancelButton_label(),
1680 cancelButtonListener);
1681 } else {
1683 }
1684 progressIndicator.
start(Bundle.Case_progressMessage_preparing());
1685
1686 /*
1687 * Creating/opening a case is always done by creating a task running in
1688 * the same non-UI thread that will be used to close the case, so a
1689 * single-threaded executor service is created here and saved as case
1690 * state (must be volatile for cancellation to work).
1691 *
1692 * --- If the case is a single-user case, this supports cancelling
1693 * opening of the case by cancelling the task.
1694 *
1695 * --- If the case is a multi-user case, this still supports
1696 * cancellation, but it also makes it possible for the shared case
1697 * directory lock held as long as the case is open to be released in the
1698 * same thread in which it was acquired, as is required by the
1699 * coordination service.
1700 */
1702 caseLockingExecutor = Executors.newSingleThreadExecutor(threadFactory);
1703 Future<Void> future = caseLockingExecutor.submit(() -> {
1705 open(isNewCase, progressIndicator);
1706 } else {
1707 /*
1708 * First, acquire a shared case directory lock that will be held
1709 * as long as this node has this case open. This will prevent
1710 * deletion of the case by another node. Next, acquire an
1711 * exclusive case resources lock to ensure only one node at a
1712 * time can create/open/upgrade/close the case resources.
1713 */
1714 progressIndicator.
progress(Bundle.Case_progressMessage_preparingToOpenCaseResources());
1717 assert (null != resourcesLock);
1718 open(isNewCase, progressIndicator);
1719 } catch (CaseActionException ex) {
1721 throw ex;
1722 }
1723 }
1724 return null;
1725 });
1726 if (null != cancelButtonListener) {
1728 }
1729
1730 /*
1731 * Wait for the case creation/opening task to finish.
1732 */
1733 try {
1734 future.get();
1735 } catch (InterruptedException discarded) {
1736 /*
1737 * The thread this method is running in has been interrupted. Cancel
1738 * the create/open task, wait for it to finish, and shut down the
1739 * executor. This can be done safely because if the task is
1740 * completed with a cancellation condition, the case will have been
1741 * closed and the case directory lock released will have been
1742 * released.
1743 */
1744 if (null != cancelButtonListener) {
1746 } else {
1747 future.cancel(true);
1748 }
1750 } catch (CancellationException discarded) {
1751 /*
1752 * The create/open task has been cancelled. Wait for it to finish,
1753 * and shut down the executor. This can be done safely because if
1754 * the task is completed with a cancellation condition, the case
1755 * will have been closed and the case directory lock released will
1756 * have been released.
1757 */
1760 } catch (ExecutionException ex) {
1761 /*
1762 * The create/open task has thrown an exception. Wait for it to
1763 * finish, and shut down the executor. This can be done safely
1764 * because if the task is completed with an execution condition, the
1765 * case will have been closed and the case directory lock released
1766 * will have been released.
1767 */
1769 throw new CaseActionException(Bundle.Case_exceptionMessage_execExceptionWrapperMessage(ex.getCause().getLocalizedMessage()), ex);
1770 } finally {
1771 progressIndicator.
finish();
1772 }
1773 }
1774
1787 try {
1788 if (Thread.currentThread().isInterrupted()) {
1790 }
1791
1792 if (isNewCase) {
1794 } else {
1796 }
1797
1798 if (Thread.currentThread().isInterrupted()) {
1800 }
1801
1803
1804 if (Thread.currentThread().isInterrupted()) {
1806 }
1807 } catch (CaseActionException ex) {
1808 /*
1809 * Cancellation or failure. Clean up. The sleep is a little hack to
1810 * clear the interrupted flag for this thread if this is a
1811 * cancellation scenario, so that the clean up can run to completion
1812 * in this thread.
1813 */
1814 try {
1815 Thread.sleep(1);
1816 } catch (InterruptedException discarded) {
1817 }
1818 close(progressIndicator);
1819 throw ex;
1820 }
1821 }
1822
1833 @Messages({
1834 "Case.progressMessage.creatingCaseDirectory=Creating case directory...",
1835 "Case.progressMessage.creatingCaseDatabase=Creating case database...",
1836 "# {0} - exception message", "Case.exceptionMessage.couldNotCreateCaseDatabase=Failed to create case database:\n{0}",
1837 "Case.exceptionMessage.couldNotCreateMetadataFile=Failed to create case metadata file."
1838 })
1840 /*
1841 * Create the case directory, if it does not already exist.
1842 *
1843 * TODO (JIRA-2180): Always create the case directory as part of the
1844 * case creation process.
1845 */
1847 progressIndicator.
progress(Bundle.Case_progressMessage_creatingCaseDirectory());
1849 }
1850
1851 /*
1852 * Create the case database.
1853 */
1854 progressIndicator.
progress(Bundle.Case_progressMessage_creatingCaseDatabase());
1855 try {
1857 /*
1858 * For single-user cases, the case database is a SQLite database
1859 * with a standard name, physically located in the root of the
1860 * case directory.
1861 */
1863 metadata.setCaseDatabaseName(SINGLE_USER_CASE_DB_NAME);
1864 } else {
1865 /*
1866 * For multi-user cases, the case database is a PostgreSQL
1867 * database with a name derived from the case display name,
1868 * physically located on a database server.
1869 */
1871 metadata.setCaseDatabaseName(caseDb.getDatabaseName());
1872 }
1873 } catch (TskCoreException ex) {
1874 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotCreateCaseDatabase(ex.getLocalizedMessage()), ex);
1876 throw new CaseActionException(NbBundle.getMessage(
Case.class,
"Case.databaseConnectionInfo.error.msg"), ex);
1878 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotCreateMetadataFile(), ex);
1879 }
1880 }
1881
1892 @Messages({
1893 "Case.progressMessage.openingCaseDatabase=Opening case database...",
1894 "Case.exceptionMessage.couldNotOpenCaseDatabase=Failed to open case database.",
1895 "Case.unsupportedSchemaVersionMessage=Unsupported DB schema version - see log for details",
1896 "Case.databaseConnectionInfo.error.msg=Error accessing database server connection info. See Tools, Options, Multi-User.",
1897 "Case.open.exception.multiUserCaseNotEnabled=Cannot open a multi-user case if multi-user cases are not enabled. "
1898 + "See Tools, Options, Multi-user."
1899 })
1901 try {
1902 progressIndicator.
progress(Bundle.Case_progressMessage_openingCaseDatabase());
1905 caseDb = SleuthkitCase.openCase(Paths.get(metadata.
getCaseDirectory(), databaseName).toString());
1907 try {
1910 throw new CaseActionException(Case_databaseConnectionInfo_error_msg(), ex);
1911 }
1912 } else {
1913 throw new CaseActionException(Case_open_exception_multiUserCaseNotEnabled());
1914 }
1915 } catch (TskUnsupportedSchemaVersionException ex) {
1916 throw new CaseActionException(Bundle.Case_unsupportedSchemaVersionMessage(), ex);
1917 } catch (TskCoreException ex) {
1918 throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotOpenCaseDatabase(), ex);
1919 }
1920 }
1921
1930 @Messages({
1931 "Case.progressMessage.switchingLogDirectory=Switching log directory...",
1932 "Case.progressMessage.clearingTempDirectory=Clearing case temp directory...",
1933 "Case.progressMessage.openingCaseLevelServices=Opening case-level services...",
1934 "Case.progressMessage.openingApplicationServiceResources=Opening application service case resources...",
1935 "Case.progressMessage.settingUpNetworkCommunications=Setting up network communications...",})
1937 /*
1938 * Switch to writing to the application logs in the logs subdirectory of
1939 * the case directory.
1940 */
1941 progressIndicator.
progress(Bundle.Case_progressMessage_switchingLogDirectory());
1943 if (Thread.currentThread().isInterrupted()) {
1945 }
1946
1947 /*
1948 * Clear the temp subdirectory of the case directory.
1949 */
1950 progressIndicator.
progress(Bundle.Case_progressMessage_clearingTempDirectory());
1952 if (Thread.currentThread().isInterrupted()) {
1954 }
1955
1956 /*
1957 * Open the case-level services.
1958 */
1959 progressIndicator.
progress(Bundle.Case_progressMessage_openingCaseLevelServices());
1960 this.caseServices =
new Services(caseDb);
1961 if (Thread.currentThread().isInterrupted()) {
1963 }
1964
1965 /*
1966 * Allow any registered application services to open any resources
1967 * specific to this case.
1968 */
1969 progressIndicator.
progress(Bundle.Case_progressMessage_openingApplicationServiceResources());
1971 if (Thread.currentThread().isInterrupted()) {
1973 }
1974
1975 /*
1976 * If this case is a multi-user case, set up for communication with
1977 * other nodes.
1978 */
1980 progressIndicator.
progress(Bundle.Case_progressMessage_settingUpNetworkCommunications());
1981 try {
1983 if (Thread.currentThread().isInterrupted()) {
1985 }
1986 collaborationMonitor =
new CollaborationMonitor(metadata.
getCaseName());
1988 /*
1989 * The collaboration monitor and event channel are not
1990 * essential. Log an error and notify the user, but do not
1991 * throw.
1992 */
1993 logger.log(Level.SEVERE, "Failed to setup network communications", ex); //NON-NLS
1996 NbBundle.getMessage(
Case.class,
"Case.CollaborationSetup.FailNotify.Title"),
1997 NbBundle.getMessage(
Case.class,
"Case.CollaborationSetup.FailNotify.ErrMsg")));
1998 }
1999 }
2000 }
2001 }
2002
2007 @NbBundle.Messages({
2008 "# {0} - service name", "Case.serviceOpenCaseResourcesProgressIndicator.title={0} Opening Case Resources",
2009 "# {0} - service name", "Case.serviceOpenCaseResourcesProgressIndicator.cancellingMessage=Cancelling opening case resources by {0}...",
2010 "# {0} - service name", "Case.servicesException.notificationTitle={0} Error"
2011 })
2013 /*
2014 * Each service gets its own independently cancellable/interruptible
2015 * task, running in a named thread managed by an executor service, with
2016 * its own progress indicator. This allows for cancellation of the
2017 * opening of case resources for individual services. It also makes it
2018 * possible to ensure that each service task completes before the next
2019 * one starts by awaiting termination of the executor service.
2020 */
2022 /*
2023 * Create a progress indicator for the task and start the task. If
2024 * running with a GUI, the progress indicator will be a dialog box
2025 * with a Cancel button.
2026 */
2030 cancelButtonListener =
new CancelButtonListener(Bundle.Case_serviceOpenCaseResourcesProgressIndicator_cancellingMessage(service.getServiceName()));
2032 mainFrame,
2033 Bundle.Case_serviceOpenCaseResourcesProgressIndicator_title(service.getServiceName()),
2034 new String[]{Bundle.Case_progressIndicatorCancelButton_label()},
2035 Bundle.Case_progressIndicatorCancelButton_label(),
2036 cancelButtonListener);
2037 } else {
2039 }
2040 progressIndicator.
start(Bundle.Case_progressMessage_preparing());
2042 String threadNameSuffix = service.getServiceName().replaceAll("[ ]", "-"); //NON-NLS
2043 threadNameSuffix = threadNameSuffix.toLowerCase();
2045 ExecutorService executor = Executors.newSingleThreadExecutor(threadFactory);
2046 Future<Void> future = executor.submit(() -> {
2047 service.openCaseResources(context);
2048 return null;
2049 });
2050 if (null != cancelButtonListener) {
2053 }
2054
2055 /*
2056 * Wait for the task to either be completed or
2057 * cancelled/interrupted, or for the opening of the case to be
2058 * cancelled.
2059 */
2060 try {
2061 future.get();
2062 } catch (InterruptedException discarded) {
2063 /*
2064 * The parent create/open case task has been cancelled.
2065 */
2067 future.cancel(true);
2068 } catch (CancellationException discarded) {
2069 /*
2070 * The opening of case resources by the application service has
2071 * been cancelled, so the executor service has thrown. Note that
2072 * there is no guarantee the task itself has responded to the
2073 * cancellation request yet.
2074 */
2076 } catch (ExecutionException ex) {
2077 /*
2078 * An exception was thrown while executing the task. The
2079 * case-specific application service resources are not
2080 * essential. Log an error and notify the user if running the
2081 * desktop GUI, but do not throw.
2082 */
2083 Case.
logger.log(Level.SEVERE, String.format(
"%s failed to open case resources for %s", service.getServiceName(), this.
getDisplayName()), ex);
2085 SwingUtilities.invokeLater(() -> {
2087 });
2088 }
2089 } finally {
2090 /*
2091 * Shut down the executor service and wait for it to finish.
2092 * This ensures that the task has finished. Without this, it
2093 * would be possible to start the next task before the current
2094 * task responded to a cancellation request.
2095 */
2097 progressIndicator.
finish();
2098 }
2099
2100 if (Thread.currentThread().isInterrupted()) {
2102 }
2103 }
2104 }
2105
2109 private void close() throws CaseActionException {
2110 /*
2111 * Set up either a GUI progress indicator without a Cancel button or a
2112 * logging progress indicator.
2113 */
2117 mainFrame,
2118 Bundle.Case_progressIndicatorTitle_closingCase());
2119 } else {
2121 }
2122 progressIndicator.
start(Bundle.Case_progressMessage_preparing());
2123
2124 /*
2125 * Closing a case is always done in the same non-UI thread that
2126 * opened/created the case. If the case is a multi-user case, this
2127 * ensures that case directory lock that is held as long as the case is
2128 * open is released in the same thread in which it was acquired, as is
2129 * required by the coordination service.
2130 */
2131 Future<Void> future = caseLockingExecutor.submit(() -> {
2133 close(progressIndicator);
2134 } else {
2135 /*
2136 * Acquire an exclusive case resources lock to ensure only one
2137 * node at a time can create/open/upgrade/close the case
2138 * resources.
2139 */
2140 progressIndicator.
progress(Bundle.Case_progressMessage_preparing());
2142 assert (null != resourcesLock);
2143 close(progressIndicator);
2144 } finally {
2145 /*
2146 * Always release the case directory lock that was acquired
2147 * when the case was opened.
2148 */
2150 }
2151 }
2152 return null;
2153 });
2154
2155 try {
2156 future.get();
2157 } catch (InterruptedException | CancellationException unused) {
2158 /*
2159 * The wait has been interrupted by interrupting the thread running
2160 * this method. Not allowing cancellation of case closing, so ignore
2161 * the interrupt. Likewsie, cancellation of the case closing task is
2162 * not supported.
2163 */
2164 } catch (ExecutionException ex) {
2165 throw new CaseActionException(Bundle.Case_exceptionMessage_execExceptionWrapperMessage(ex.getCause().getMessage()), ex);
2166 } finally {
2168 progressIndicator.
finish();
2169 }
2170 }
2171
2177 @Messages({
2178 "Case.progressMessage.shuttingDownNetworkCommunications=Shutting down network communications...",
2179 "Case.progressMessage.closingApplicationServiceResources=Closing case-specific application service resources...",
2180 "Case.progressMessage.closingCaseLevelServices=Closing case-level services...",
2181 "Case.progressMessage.closingCaseDatabase=Closing case database..."
2182 })
2185
2186 /*
2187 * Stop sending/receiving case events to and from other nodes if this is
2188 * a multi-user case.
2189 */
2191 progressIndicator.
progress(Bundle.Case_progressMessage_shuttingDownNetworkCommunications());
2192 if (null != collaborationMonitor) {
2193 collaborationMonitor.shutdown();
2194 }
2196 }
2197
2198 /*
2199 * Allow all registered application services providers to close
2200 * resources related to the case.
2201 */
2202 progressIndicator.
progress(Bundle.Case_progressMessage_closingApplicationServiceResources());
2204
2205 /*
2206 * Close the case-level services.
2207 */
2208 if (null != caseServices) {
2209 progressIndicator.
progress(Bundle.Case_progressMessage_closingCaseLevelServices());
2210 try {
2211 this.caseServices.
close();
2212 } catch (IOException ex) {
2213 logger.log(Level.SEVERE, String.format(
"Error closing internal case services for %s at %s",
this.getName(), this.
getCaseDirectory()), ex);
2214 }
2215 }
2216
2217 /*
2218 * Close the case database
2219 */
2220 if (null != caseDb) {
2221 progressIndicator.
progress(Bundle.Case_progressMessage_closingCaseDatabase());
2222 caseDb.close();
2223 }
2224
2225 /*
2226 * Switch the log directory.
2227 */
2228 progressIndicator.
progress(Bundle.Case_progressMessage_switchingLogDirectory());
2230 }
2231
2236 @Messages({
2237 "# {0} - serviceName", "Case.serviceCloseResourcesProgressIndicator.title={0} Closing Case Resources",
2238 "# {0} - service name", "# {1} - exception message", "Case.servicesException.serviceResourcesCloseError=Could not close case resources for {0} service: {1}"
2239 })
2241 /*
2242 * Each service gets its own independently cancellable task, and thus
2243 * its own task progress indicator.
2244 */
2249 mainFrame,
2250 Bundle.Case_serviceCloseResourcesProgressIndicator_title(service.getServiceName()));
2251 } else {
2253 }
2254 progressIndicator.
start(Bundle.Case_progressMessage_preparing());
2256 String threadNameSuffix = service.getServiceName().replaceAll("[ ]", "-"); //NON-NLS
2257 threadNameSuffix = threadNameSuffix.toLowerCase();
2259 ExecutorService executor = Executors.newSingleThreadExecutor(threadFactory);
2260 Future<Void> future = executor.submit(() -> {
2261 service.closeCaseResources(context);
2262 return null;
2263 });
2264 try {
2265 future.get();
2266 } catch (InterruptedException ex) {
2267 Case.
logger.log(Level.SEVERE, String.format(
"Unexpected interrupt while waiting on %s service to close case resources", service.getServiceName()), ex);
2268 } catch (CancellationException ex) {
2269 Case.
logger.log(Level.SEVERE, String.format(
"Unexpected cancellation while waiting on %s service to close case resources", service.getServiceName()), ex);
2270 } catch (ExecutionException ex) {
2271 Case.
logger.log(Level.SEVERE, String.format(
"%s service failed to open case resources", service.getServiceName()), ex);
2274 Bundle.Case_servicesException_notificationTitle(service.getServiceName()),
2275 Bundle.Case_servicesException_serviceResourcesCloseError(service.getServiceName(), ex.getLocalizedMessage())));
2276 }
2277 } finally {
2279 progressIndicator.
finish();
2280 }
2281 }
2282 }
2283
2292 @Messages({"Case.creationException.couldNotAcquireDirLock=Failed to get lock on case directory."})
2294 try {
2297 throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireDirLock());
2298 }
2300 throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireDirLock(), ex);
2301 }
2302 }
2303
2311 try {
2315 logger.log(Level.SEVERE, String.format("Failed to release shared case directory lock for %s", caseDir), ex);
2316 }
2317 }
2318 }
2319
2328 if (!subDirectory.exists()) {
2329 subDirectory.mkdirs();
2330 }
2331 return subDirectory.toString();
2332
2333 }
2334
2339 @ThreadSafe
2341
2343 @GuardedBy("this")
2345 @GuardedBy("this")
2347 @GuardedBy("this")
2349
2360 }
2361
2369 /*
2370 * If the cancel button has already been pressed, pass the
2371 * cancellation on to the case context.
2372 */
2375 }
2376 }
2377
2385 /*
2386 * If the cancel button has already been pressed, cancel the Future
2387 * of the task.
2388 */
2391 }
2392 }
2393
2399 @Override
2402 }
2403
2408 /*
2409 * At a minimum, set the cancellation requested flag of this
2410 * listener.
2411 */
2414 /*
2415 * Set the cancellation request flag and display the
2416 * cancellation message in the progress indicator for the case
2417 * context associated with this listener.
2418 */
2422 ((ModalDialogProgressIndicator) progressIndicator).setCancelling(cancellationMessage);
2423 }
2424 }
2426 }
2428 /*
2429 * Cancel the Future of the task associated with this listener.
2430 * Note that the task thread will be interrupted if the task is
2431 * blocked.
2432 */
2434 }
2435 }
2436 }
2437
2442
2444
2447 }
2448
2449 @Override
2451 return new Thread(task, threadName);
2452 }
2453
2454 }
2455
2463 @Deprecated
2466 }
2467
2487 @Deprecated
2488 public static void create(String caseDir, String caseDisplayName, String caseNumber, String examiner)
throws CaseActionException {
2490 }
2491
2512 @Deprecated
2513 public static void create(String caseDir, String caseDisplayName, String caseNumber, String examiner,
CaseType caseType)
throws CaseActionException {
2515 }
2516
2528 @Deprecated
2529 public static void open(String caseMetadataFilePath)
throws CaseActionException {
2531 }
2532
2542 @Deprecated
2545 }
2546
2552 @Deprecated
2555 }
2556
2570 @Deprecated
2573 }
2574
2584 @Deprecated
2586 return new File(filePath).isFile();
2587 }
2588
2597 @Deprecated
2600 }
2601
2609 @Deprecated
2612 }
2613
2623 @Deprecated
2625 return "ModuleOutput"; //NON-NLS
2626 }
2627
2637 @Deprecated
2638 public static PropertyChangeSupport
2640 return new PropertyChangeSupport(
Case.class
2641 );
2642 }
2643
2652 @Deprecated
2655 }
2656
2671 @Deprecated
2672 public Image
addImage(String imgPath,
long imgId, String timeZone)
throws CaseActionException {
2673 try {
2674 Image newDataSource = caseDb.getImageById(imgId);
2676 return newDataSource;
2677 } catch (TskCoreException ex) {
2678 throw new CaseActionException(NbBundle.getMessage(this.getClass(), "Case.addImg.exception.msg"), ex);
2679 }
2680 }
2681
2689 @Deprecated
2692 }
2693
2704 @Deprecated
2705 public void deleteReports(Collection<? extends Report> reports,
boolean deleteFromDisk)
throws TskCoreException {
2707 }
2708
2709 }
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 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)
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 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()