Autopsy  4.6.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
Case.java
Go to the documentation of this file.
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;
68 import static org.sleuthkit.autopsy.casemodule.Bundle.*;
109 import org.sleuthkit.datamodel.BlackboardArtifactTag;
110 import org.sleuthkit.datamodel.CaseDbConnectionInfo;
111 import org.sleuthkit.datamodel.Content;
112 import org.sleuthkit.datamodel.ContentTag;
113 import org.sleuthkit.datamodel.Image;
114 import org.sleuthkit.datamodel.Report;
115 import org.sleuthkit.datamodel.SleuthkitCase;
116 import org.sleuthkit.datamodel.TskCoreException;
117 import org.sleuthkit.datamodel.TskUnsupportedSchemaVersionException;
118 
122 public class Case {
123 
124  private static final int DIR_LOCK_TIMOUT_HOURS = 12;
125  private static final int RESOURCES_LOCK_TIMOUT_HOURS = 12;
126  private static final String SINGLE_USER_CASE_DB_NAME = "autopsy.db";
127  private static final String EVENT_CHANNEL_NAME = "%s-Case-Events"; //NON-NLS
128  private static final String CACHE_FOLDER = "Cache"; //NON-NLS
129  private static final String EXPORT_FOLDER = "Export"; //NON-NLS
130  private static final String LOG_FOLDER = "Log"; //NON-NLS
131  private static final String REPORTS_FOLDER = "Reports"; //NON-NLS
132  private static final String TEMP_FOLDER = "Temp"; //NON-NLS
133  private static final String MODULE_FOLDER = "ModuleOutput"; //NON-NLS
134  private static final String CASE_ACTION_THREAD_NAME = "%s-case-action";
135  private static final String CASE_RESOURCES_THREAD_NAME = "%s-manage-case-resources";
136  private static final Logger logger = Logger.getLogger(Case.class.getName());
138  private static final Object caseActionSerializationLock = new Object();
139  private static volatile Frame mainFrame;
140  private static volatile Case currentCase;
141  private final CaseMetadata metadata;
142  private volatile ExecutorService caseLockingExecutor;
144  private SleuthkitCase caseDb;
145  private CollaborationMonitor collaborationMonitor;
147  private boolean hasDataSources;
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 
166  public enum CaseType {
167 
168  SINGLE_USER_CASE("Single-user case"), //NON-NLS
169  MULTI_USER_CASE("Multi-user case"); //NON-NLS
170 
171  private final String typeName;
172 
180  public static CaseType fromString(String typeName) {
181  if (typeName != null) {
182  for (CaseType c : CaseType.values()) {
183  if (typeName.equalsIgnoreCase(c.toString())) {
184  return c;
185  }
186  }
187  }
188  return null;
189  }
190 
196  @Override
197  public String toString() {
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 
223  private CaseType(String typeName) {
224  this.typeName = typeName;
225  }
226 
237  @Deprecated
238  public boolean equalsName(String otherTypeName) {
239  return (otherTypeName == null) ? false : typeName.equals(otherTypeName);
240  }
241 
242  };
243 
248  public enum Events {
249 
257  @Deprecated
266  @Deprecated
275  @Deprecated
374 
375  };
376 
383  public static void addPropertyChangeListener(PropertyChangeListener listener) {
384  addEventSubscriber(Stream.of(Events.values())
385  .map(Events::toString)
386  .collect(Collectors.toSet()), listener);
387  }
388 
395  public static void removePropertyChangeListener(PropertyChangeListener listener) {
396  removeEventSubscriber(Stream.of(Events.values())
397  .map(Events::toString)
398  .collect(Collectors.toSet()), listener);
399  }
400 
409  @Deprecated
410  public static void addEventSubscriber(Set<String> eventNames, PropertyChangeListener subscriber) {
411  eventPublisher.addSubscriber(eventNames, subscriber);
412  }
413 
420  public static void addEventTypeSubscriber(Set<Events> eventTypes, PropertyChangeListener subscriber) {
421  eventTypes.forEach((Events event) -> {
422  eventPublisher.addSubscriber(event.toString(), subscriber);
423  });
424  }
425 
434  @Deprecated
435  public static void addEventSubscriber(String eventName, PropertyChangeListener subscriber) {
436  eventPublisher.addSubscriber(eventName, subscriber);
437  }
438 
445  public static void removeEventSubscriber(String eventName, PropertyChangeListener subscriber) {
446  eventPublisher.removeSubscriber(eventName, subscriber);
447  }
448 
455  public static void removeEventSubscriber(Set<String> eventNames, PropertyChangeListener subscriber) {
456  eventPublisher.removeSubscriber(eventNames, subscriber);
457  }
458 
465  public static void removeEventTypeSubscriber(Set<Events> eventTypes, PropertyChangeListener subscriber) {
466  eventTypes.forEach((Events event) -> {
467  eventPublisher.removeSubscriber(event.toString(), subscriber);
468  });
469  }
470 
479  public static boolean isValidName(String caseName) {
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
510  public static void createAsCurrentCase(String caseDir, String caseDisplayName, String caseNumber, String examiner, CaseType caseType) throws CaseActionException, CaseActionCancelledException {
511  createAsCurrentCase(caseType, caseDir, new CaseDetails(caseDisplayName, caseNumber, examiner, "", "", ""));
512  }
513 
533  @Messages({
534  "Case.exceptionMessage.emptyCaseName=Must specify a case name.",
535  "Case.exceptionMessage.emptyCaseDir=Must specify a case directory path."
536  })
537  public static void createAsCurrentCase(CaseType caseType, String caseDir, CaseDetails caseDetails) throws CaseActionException, CaseActionCancelledException {
538  if (caseDetails.getCaseDisplayName().isEmpty()) {
539  throw new CaseActionException(Bundle.Case_exceptionMessage_emptyCaseName());
540  }
541  if (caseDir.isEmpty()) {
542  throw new CaseActionException(Bundle.Case_exceptionMessage_emptyCaseDir());
543  }
544  openAsCurrentCase(new Case(caseType, caseDir, caseDetails), true);
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  })
564  public static void openAsCurrentCase(String caseMetadataFilePath) throws CaseActionException {
566  try {
567  metadata = new CaseMetadata(Paths.get(caseMetadataFilePath));
568  } catch (CaseMetadataException ex) {
569  throw new CaseActionException(Bundle.Case_exceptionMessage_failedToReadMetadata(), ex);
570  }
572  throw new CaseActionException(Bundle.Case_exceptionMessage_cannotOpenMultiUserCaseNoSettings());
573  }
574  openAsCurrentCase(new Case(metadata), false);
575  }
576 
582  public static boolean isCaseOpen() {
583  return currentCase != null;
584  }
585 
597  @Deprecated
598  public static Case getCurrentCase() {
599  /*
600  * Throwing an unchecked exception is a bad idea here.
601  *
602  */
603  try {
604  return getOpenCase();
605  } catch (NoCurrentCaseException ex) {
606  throw new IllegalStateException(NbBundle.getMessage(Case.class, "Case.getCurCase.exception.noneOpen"), ex);
607  }
608  }
609 
617  public static Case getOpenCase() throws NoCurrentCaseException {
618  Case openCase = currentCase;
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  })
638  public static void closeCurrentCase() throws CaseActionException {
639  synchronized (caseActionSerializationLock) {
640  if (null == currentCase) {
641  return;
642  }
643  Case closedCase = currentCase;
644  try {
645  eventPublisher.publishLocally(new AutopsyEvent(Events.CURRENT_CASE.toString(), closedCase, null));
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;
648  closedCase.close();
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) {
651  logger.log(Level.SEVERE, String.format("Error closing current case %s (%s) in %s", closedCase.getDisplayName(), closedCase.getName(), closedCase.getCaseDirectory()), ex); //NON-NLS
652  throw ex;
653  } finally {
656  }
657  }
658  }
659  }
660 
669  public static void deleteCurrentCase() throws CaseActionException {
670  synchronized (caseActionSerializationLock) {
671  if (null == currentCase) {
672  return;
673  }
674  CaseMetadata metadata = currentCase.getMetadata();
676  deleteCase(metadata);
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  })
697  public static void deleteCase(CaseMetadata metadata) throws CaseActionException {
698  synchronized (caseActionSerializationLock) {
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  */
708  ProgressIndicator progressIndicator;
710  progressIndicator = new ModalDialogProgressIndicator(mainFrame, Bundle.Case_progressIndicatorTitle_deletingCase());
711  } else {
712  progressIndicator = new LoggingProgressIndicator();
713  }
714  progressIndicator.start(Bundle.Case_progressMessage_preparing());
715  try {
716  if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) {
717  deleteCase(metadata, progressIndicator);
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);
726  deleteCase(metadata, progressIndicator);
727  } catch (CoordinationServiceException ex) {
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  })
749  private static void openAsCurrentCase(Case newCurrentCase, boolean isNewCase) throws CaseActionException, CaseActionCancelledException {
750  synchronized (caseActionSerializationLock) {
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  */
759  MessageNotifyUtil.Message.error(ex.getLocalizedMessage());
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
768  updateGUIForCaseOpened(newCurrentCase);
769  }
770  eventPublisher.publishLocally(new AutopsyEvent(Events.CURRENT_CASE.toString(), null, currentCase));
771  } catch (CaseActionCancelledException ex) {
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 
789  private static String displayNameToUniqueName(String caseDisplayName) {
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 
828  public static void createCaseDirectory(String caseDir, CaseType caseType) throws CaseActionException {
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 
854  if (caseType == CaseType.MULTI_USER_CASE) {
855  hostClause = File.separator + NetworkUtils.getLocalHostName();
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  })
936  private static void deleteCase(CaseMetadata metadata, ProgressIndicator progressIndicator) throws CaseActionException {
937  boolean errorsOccurred = false;
938  if (CaseType.MULTI_USER_CASE == metadata.getCaseType()) {
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  }
952  } catch (UserPreferencesException | ClassNotFoundException | SQLException ex) {
953  logger.log(Level.SEVERE, String.format("Failed to delete case database %s for %s (%s) in %s", metadata.getCaseDatabaseName(), metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex);
954  errorsOccurred = true;
955  }
956  }
957 
958  /*
959  * Delete the text index.
960  */
961  progressIndicator.progress(Bundle.Case_progressMessage_deletingTextIndex());
962  for (KeywordSearchService searchService : Lookup.getDefault().lookupAll(KeywordSearchService.class)) {
963  try {
964  searchService.deleteTextIndex(metadata);
965  } catch (KeywordSearchServiceException ex) {
966  logger.log(Level.SEVERE, String.format("Failed to delete text index for %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex);
967  errorsOccurred = true;
968  }
969  }
970 
971  /*
972  * Delete the case directory.
973  */
974  progressIndicator.progress(Bundle.Case_progressMessage_deletingCaseDirectory());
975  if (!FileUtil.deleteDir(new File(metadata.getCaseDirectory()))) {
976  logger.log(Level.SEVERE, String.format("Failed to delete case directory for %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()));
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"})
1005  private static CoordinationService.Lock acquireExclusiveCaseResourcesLock(String caseDir) throws CaseActionException {
1006  try {
1007  String resourcesNodeName = caseDir + "_resources";
1008  Lock lock = CoordinationService.getInstance().tryGetExclusiveLock(CategoryNode.CASES, resourcesNodeName, RESOURCES_LOCK_TIMOUT_HOURS, TimeUnit.HOURS);
1009  if (null == lock) {
1010  throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireResourcesLock());
1011  }
1012  return lock;
1013  } catch (InterruptedException ex) {
1014  throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser());
1015  } catch (CoordinationServiceException ex) {
1016  throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireResourcesLock(), ex);
1017  }
1018  }
1019 
1020  private static String getNameForTitle() {
1021  //Method should become unnecessary once technical debt story 3334 is done.
1022  if (UserPreferences.getAppName().equals(Version.getName())) {
1023  //Available version number is version number for this application
1024  return String.format("%s %s", UserPreferences.getAppName(), Version.getVersion());
1025  } else {
1026  return UserPreferences.getAppName();
1027  }
1028  }
1029 
1033  private static void updateGUIForCaseOpened(Case newCurrentCase) {
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  */
1040  SleuthkitCase caseDb = newCurrentCase.getSleuthkitCase();
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();
1059  boolean fileExists = (new File(path).isFile() || DriveUtils.driveExists(path));
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  */
1078  CallableSystemAction.get(AddImageAction.class).setEnabled(true);
1079  CallableSystemAction.get(CaseCloseAction.class).setEnabled(true);
1080  CallableSystemAction.get(CasePropertiesAction.class).setEnabled(true);
1081  CallableSystemAction.get(CaseDeleteAction.class).setEnabled(true);
1082  CallableSystemAction.get(OpenTimelineAction.class).setEnabled(true);
1083  CallableSystemAction.get(OpenCommVisualizationToolAction.class).setEnabled(true);
1084  CallableSystemAction.get(CommonFilesSearchAction.class).setEnabled(true);
1085  CallableSystemAction.get(OpenOutputFolderAction.class).setEnabled(false);
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  */
1107  mainFrame.setTitle(newCurrentCase.getDisplayName() + " - " + getNameForTitle());
1108  });
1109  }
1110  }
1111 
1112  /*
1113  * Update the GUI to to reflect the lack of a current case.
1114  */
1115  private static void updateGUIForCaseClosed() {
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);
1128  CallableSystemAction.get(CaseCloseAction.class).setEnabled(false);
1129  CallableSystemAction.get(CasePropertiesAction.class).setEnabled(false);
1130  CallableSystemAction.get(CaseDeleteAction.class).setEnabled(false);
1131  CallableSystemAction.get(OpenTimelineAction.class).setEnabled(false);
1132  CallableSystemAction.get(OpenCommVisualizationToolAction.class).setEnabled(false);
1133  CallableSystemAction.get(OpenOutputFolderAction.class).setEnabled(false);
1134  CallableSystemAction.get(CommonFilesSearchAction.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  */
1146  mainFrame.setTitle(getNameForTitle());
1147  });
1148  }
1149  }
1150 
1154  private static void clearTempSubDir(String tempSubDirPath) {
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()) {
1161  FileUtil.deleteDir(file);
1162  } else {
1163  file.delete();
1164  }
1165  }
1166  }
1167  }
1168  }
1169 
1175  public SleuthkitCase getSleuthkitCase() {
1176  return this.caseDb;
1177  }
1178 
1185  return caseServices;
1186  }
1187 
1194  return metadata.getCaseType();
1195  }
1196 
1202  public String getCreatedDate() {
1203  return metadata.getCreatedDate();
1204  }
1205 
1211  public String getName() {
1212  return metadata.getCaseName();
1213  }
1214 
1220  public String getDisplayName() {
1221  return metadata.getCaseDisplayName();
1222  }
1223 
1229  public String getNumber() {
1230  return metadata.getCaseNumber();
1231  }
1232 
1238  public String getExaminer() {
1239  return metadata.getExaminer();
1240  }
1241 
1247  public String getExaminerPhone() {
1248  return metadata.getExaminerPhone();
1249  }
1250 
1256  public String getExaminerEmail() {
1257  return metadata.getExaminerEmail();
1258  }
1259 
1265  public String getCaseNotes() {
1266  return metadata.getCaseNotes();
1267  }
1268 
1274  public String getCaseDirectory() {
1275  return metadata.getCaseDirectory();
1276  }
1277 
1286  public String getOutputDirectory() {
1287  String caseDirectory = getCaseDirectory();
1288  Path hostPath;
1289  if (CaseType.MULTI_USER_CASE == metadata.getCaseType()) {
1290  hostPath = Paths.get(caseDirectory, NetworkUtils.getLocalHostName());
1291  } else {
1292  hostPath = Paths.get(caseDirectory);
1293  }
1294  if (!hostPath.toFile().exists()) {
1295  hostPath.toFile().mkdirs();
1296  }
1297  return hostPath.toString();
1298  }
1299 
1306  public String getTempDirectory() {
1307  return getOrCreateSubdirectory(TEMP_FOLDER);
1308  }
1309 
1316  public String getCacheDirectory() {
1317  return getOrCreateSubdirectory(CACHE_FOLDER);
1318  }
1319 
1326  public String getExportDirectory() {
1327  return getOrCreateSubdirectory(EXPORT_FOLDER);
1328  }
1329 
1336  public String getLogDirectoryPath() {
1337  return getOrCreateSubdirectory(LOG_FOLDER);
1338  }
1339 
1346  public String getReportDirectory() {
1347  return getOrCreateSubdirectory(REPORTS_FOLDER);
1348  }
1349 
1356  public String getModuleDirectory() {
1357  return getOrCreateSubdirectory(MODULE_FOLDER);
1358  }
1359 
1368  Path path = Paths.get(getModuleDirectory());
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 
1385  public List<Content> getDataSources() throws TskCoreException {
1386  List<Content> list = caseDb.getRootObjects();
1387  hasDataSources = (list.size() > 0);
1388  return list;
1389  }
1390 
1396  public Set<TimeZone> getTimeZones() {
1397  Set<TimeZone> timezones = new HashSet<>();
1398  try {
1399  for (Content c : getDataSources()) {
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 
1418  public String getTextIndexName() {
1419  return getMetadata().getTextIndexName();
1420  }
1421 
1428  public boolean hasData() {
1429  if (!hasDataSources) {
1430  try {
1431  hasDataSources = (getDataSources().size() > 0);
1432  } catch (TskCoreException ex) {
1433  logger.log(Level.SEVERE, "Error accessing case database", ex); //NON-NLS
1434  }
1435  }
1436  return hasDataSources;
1437  }
1438 
1449  public void notifyAddingDataSource(UUID eventId) {
1450  eventPublisher.publish(new AddingDataSourceEvent(eventId));
1451  }
1452 
1463  public void notifyFailedAddingDataSource(UUID addingDataSourceEventId) {
1464  eventPublisher.publish(new AddingDataSourceFailedEvent(addingDataSourceEventId));
1465  }
1466 
1478  public void notifyDataSourceAdded(Content dataSource, UUID addingDataSourceEventId) {
1479  eventPublisher.publish(new DataSourceAddedEvent(dataSource, addingDataSourceEventId));
1480  }
1481 
1489  public void notifyContentTagAdded(ContentTag newTag) {
1490  eventPublisher.publish(new ContentTagAddedEvent(newTag));
1491  }
1492 
1500  public void notifyContentTagDeleted(ContentTag deletedTag) {
1501  eventPublisher.publish(new ContentTagDeletedEvent(deletedTag));
1502  }
1503 
1511  public void notifyTagDefinitionChanged(String changedTagName) {
1512  //leaving new value of changedTagName as null, because we do not currently support changing the display name of a tag.
1513  eventPublisher.publish(new AutopsyEvent(Events.TAG_DEFINITION_CHANGED.toString(), changedTagName, null));
1514  }
1515 
1523  public void notifyBlackBoardArtifactTagAdded(BlackboardArtifactTag newTag) {
1524  eventPublisher.publish(new BlackBoardArtifactTagAddedEvent(newTag));
1525  }
1526 
1534  public void notifyBlackBoardArtifactTagDeleted(BlackboardArtifactTag deletedTag) {
1535  eventPublisher.publish(new BlackBoardArtifactTagDeletedEvent(deletedTag));
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);
1580  eventPublisher.publish(new ReportAddedEvent(report));
1581  return report;
1582  }
1583 
1592  public List<Report> getAllReports() throws TskCoreException {
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);
1607  eventPublisher.publish(new AutopsyEvent(Events.REPORT_DELETED.toString(), report, null));
1608  }
1609  }
1610 
1616  CaseMetadata getMetadata() {
1617  return metadata;
1618  }
1619 
1627  @Messages({
1628  "Case.exceptionMessage.metadataUpdateError=Failed to update case metadata"
1629  })
1630  void updateCaseDetails(CaseDetails caseDetails) throws CaseActionException {
1631  CaseDetails oldCaseDetails = metadata.getCaseDetails();
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  }
1643  if (!oldCaseDetails.getCaseDisplayName().equals(caseDetails.getCaseDisplayName())) {
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(() -> {
1649  mainFrame.setTitle(caseDetails.getCaseDisplayName() + " - " + getNameForTitle());
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 
1671  private Case(CaseType caseType, String caseDir, CaseDetails caseDetails) {
1672  metadata = new CaseMetadata(caseType, caseDir, displayNameToUniqueName(caseDetails.getCaseDisplayName()), caseDetails);
1673  }
1674 
1680  private Case(CaseMetadata caseMetaData) {
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  */
1714  CancelButtonListener cancelButtonListener = null;
1715  ProgressIndicator progressIndicator;
1717  cancelButtonListener = new CancelButtonListener(Bundle.Case_progressMessage_cancelling());
1718  String progressIndicatorTitle = isNewCase ? Bundle.Case_progressIndicatorTitle_creatingCase() : Bundle.Case_progressIndicatorTitle_openingCase();
1719  progressIndicator = new ModalDialogProgressIndicator(
1720  mainFrame,
1721  progressIndicatorTitle,
1722  new String[]{Bundle.Case_progressIndicatorCancelButton_label()},
1723  Bundle.Case_progressIndicatorCancelButton_label(),
1724  cancelButtonListener);
1725  } else {
1726  progressIndicator = new LoggingProgressIndicator();
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  */
1745  TaskThreadFactory threadFactory = new TaskThreadFactory(String.format(CASE_ACTION_THREAD_NAME, metadata.getCaseName()));
1746  caseLockingExecutor = Executors.newSingleThreadExecutor(threadFactory);
1747  Future<Void> future = caseLockingExecutor.submit(() -> {
1748  if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) {
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) {
1764  releaseSharedCaseDirLock(getMetadata().getCaseDirectory());
1765  throw ex;
1766  }
1767  }
1768  return null;
1769  });
1770  if (null != cancelButtonListener) {
1771  cancelButtonListener.setCaseActionFuture(future);
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) {
1789  cancelButtonListener.actionPerformed(null);
1790  } else {
1791  future.cancel(true);
1792  }
1793  ThreadUtils.shutDownTaskExecutor(caseLockingExecutor);
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  */
1802  ThreadUtils.shutDownTaskExecutor(caseLockingExecutor);
1803  throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser());
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  */
1812  ThreadUtils.shutDownTaskExecutor(caseLockingExecutor);
1813  throw new CaseActionException(Bundle.Case_exceptionMessage_execExceptionWrapperMessage(ex.getCause().getLocalizedMessage()), ex);
1814  } finally {
1815  progressIndicator.finish();
1816  }
1817  }
1818 
1830  private void open(boolean isNewCase, ProgressIndicator progressIndicator) throws CaseActionException {
1831  try {
1832  if (Thread.currentThread().isInterrupted()) {
1833  throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser());
1834  }
1835 
1836  if (isNewCase) {
1837  createCaseData(progressIndicator);
1838  } else {
1839  openCaseData(progressIndicator);
1840  }
1841 
1842  if (Thread.currentThread().isInterrupted()) {
1843  throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser());
1844  }
1845 
1846  openServices(progressIndicator);
1847 
1848  if (Thread.currentThread().isInterrupted()) {
1849  throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser());
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  })
1883  private void createCaseData(ProgressIndicator progressIndicator) throws CaseActionException {
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  */
1890  if (new File(metadata.getCaseDirectory()).exists() == false) {
1891  progressIndicator.progress(Bundle.Case_progressMessage_creatingCaseDirectory());
1892  Case.createCaseDirectory(metadata.getCaseDirectory(), metadata.getCaseType());
1893  }
1894 
1895  /*
1896  * Create the case database.
1897  */
1898  progressIndicator.progress(Bundle.Case_progressMessage_creatingCaseDatabase());
1899  try {
1900  if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) {
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  */
1906  caseDb = SleuthkitCase.newCase(Paths.get(metadata.getCaseDirectory(), SINGLE_USER_CASE_DB_NAME).toString());
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  */
1914  caseDb = SleuthkitCase.newCase(metadata.getCaseDisplayName(), UserPreferences.getDatabaseConnectionInfo(), metadata.getCaseDirectory());
1915  metadata.setCaseDatabaseName(caseDb.getDatabaseName());
1916  }
1917  } catch (TskCoreException ex) {
1918  throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotCreateCaseDatabase(ex.getLocalizedMessage()), ex);
1919  } catch (UserPreferencesException ex) {
1920  throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.databaseConnectionInfo.error.msg"), ex);
1921  } catch (CaseMetadataException 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  })
1944  private void openCaseData(ProgressIndicator progressIndicator) throws CaseActionException {
1945  try {
1946  progressIndicator.progress(Bundle.Case_progressMessage_openingCaseDatabase());
1947  String databaseName = metadata.getCaseDatabaseName();
1948  if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) {
1949  caseDb = SleuthkitCase.openCase(Paths.get(metadata.getCaseDirectory(), databaseName).toString());
1951  try {
1952  caseDb = SleuthkitCase.openCase(databaseName, UserPreferences.getDatabaseConnectionInfo(), metadata.getCaseDirectory());
1953  } catch (UserPreferencesException ex) {
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...",})
1980  private void openServices(ProgressIndicator progressIndicator) throws CaseActionException {
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()) {
1988  throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser());
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()) {
1997  throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser());
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()) {
2006  throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser());
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()) {
2016  throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser());
2017  }
2018 
2019  /*
2020  * If this case is a multi-user case, set up for communication with
2021  * other nodes.
2022  */
2023  if (CaseType.MULTI_USER_CASE == metadata.getCaseType()) {
2024  progressIndicator.progress(Bundle.Case_progressMessage_settingUpNetworkCommunications());
2025  try {
2026  eventPublisher.openRemoteEventChannel(String.format(EVENT_CHANNEL_NAME, metadata.getCaseName()));
2027  if (Thread.currentThread().isInterrupted()) {
2028  throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser());
2029  }
2030  collaborationMonitor = new CollaborationMonitor(metadata.getCaseName());
2031  } catch (AutopsyEventException | CollaborationMonitor.CollaborationMonitorException ex) {
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
2039  SwingUtilities.invokeLater(() -> MessageNotifyUtil.Notify.error(
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  })
2056  private void openAppServiceCaseResources() throws CaseActionException {
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  */
2065  for (AutopsyService service : Lookup.getDefault().lookupAll(AutopsyService.class)) {
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  */
2071  CancelButtonListener cancelButtonListener = null;
2072  ProgressIndicator progressIndicator;
2074  cancelButtonListener = new CancelButtonListener(Bundle.Case_serviceOpenCaseResourcesProgressIndicator_cancellingMessage(service.getServiceName()));
2075  progressIndicator = new ModalDialogProgressIndicator(
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 {
2082  progressIndicator = new LoggingProgressIndicator();
2083  }
2084  progressIndicator.start(Bundle.Case_progressMessage_preparing());
2085  AutopsyService.CaseContext context = new AutopsyService.CaseContext(this, progressIndicator);
2086  String threadNameSuffix = service.getServiceName().replaceAll("[ ]", "-"); //NON-NLS
2087  threadNameSuffix = threadNameSuffix.toLowerCase();
2088  TaskThreadFactory threadFactory = new TaskThreadFactory(String.format(CASE_RESOURCES_THREAD_NAME, threadNameSuffix));
2089  ExecutorService executor = Executors.newSingleThreadExecutor(threadFactory);
2090  Future<Void> future = executor.submit(() -> {
2091  service.openCaseResources(context);
2092  return null;
2093  });
2094  if (null != cancelButtonListener) {
2095  cancelButtonListener.setCaseContext(context);
2096  cancelButtonListener.setCaseActionFuture(future);
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  */
2110  Case.logger.log(Level.WARNING, String.format("Opening of %s (%s) in %s cancelled during opening of case resources by %s", getDisplayName(), getName(), getCaseDirectory(), service.getServiceName()));
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  */
2119  Case.logger.log(Level.WARNING, String.format("Opening of case resources by %s for %s (%s) in %s cancelled", service.getServiceName(), getDisplayName(), getName(), getCaseDirectory(), service.getServiceName()));
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(() -> {
2130  MessageNotifyUtil.Notify.error(Bundle.Case_servicesException_notificationTitle(service.getServiceName()), ex.getLocalizedMessage());
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()) {
2145  throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser());
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  */
2158  ProgressIndicator progressIndicator;
2160  progressIndicator = new ModalDialogProgressIndicator(
2161  mainFrame,
2162  Bundle.Case_progressIndicatorTitle_closingCase());
2163  } else {
2164  progressIndicator = new LoggingProgressIndicator();
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(() -> {
2176  if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) {
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 {
2211  ThreadUtils.shutDownTaskExecutor(caseLockingExecutor);
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  })
2227  private void close(ProgressIndicator progressIndicator) {
2229 
2230  /*
2231  * Stop sending/receiving case events to and from other nodes if this is
2232  * a multi-user case.
2233  */
2234  if (CaseType.MULTI_USER_CASE == metadata.getCaseType()) {
2235  progressIndicator.progress(Bundle.Case_progressMessage_shuttingDownNetworkCommunications());
2236  if (null != collaborationMonitor) {
2237  collaborationMonitor.shutdown();
2238  }
2239  eventPublisher.closeRemoteEventChannel();
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  */
2289  for (AutopsyService service : Lookup.getDefault().lookupAll(AutopsyService.class)) {
2290  ProgressIndicator progressIndicator;
2292  progressIndicator = new ModalDialogProgressIndicator(
2293  mainFrame,
2294  Bundle.Case_serviceCloseResourcesProgressIndicator_title(service.getServiceName()));
2295  } else {
2296  progressIndicator = new LoggingProgressIndicator();
2297  }
2298  progressIndicator.start(Bundle.Case_progressMessage_preparing());
2299  AutopsyService.CaseContext context = new AutopsyService.CaseContext(this, progressIndicator);
2300  String threadNameSuffix = service.getServiceName().replaceAll("[ ]", "-"); //NON-NLS
2301  threadNameSuffix = threadNameSuffix.toLowerCase();
2302  TaskThreadFactory threadFactory = new TaskThreadFactory(String.format(CASE_RESOURCES_THREAD_NAME, threadNameSuffix));
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);
2317  SwingUtilities.invokeLater(() -> MessageNotifyUtil.Notify.error(
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."})
2337  private void acquireSharedCaseDirLock(String caseDir) throws CaseActionException {
2338  try {
2339  caseDirLock = CoordinationService.getInstance().tryGetSharedLock(CategoryNode.CASES, caseDir, DIR_LOCK_TIMOUT_HOURS, TimeUnit.HOURS);
2340  if (null == caseDirLock) {
2341  throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireDirLock());
2342  }
2343  } catch (InterruptedException | CoordinationServiceException ex) {
2344  throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireDirLock(), ex);
2345  }
2346  }
2347 
2353  private void releaseSharedCaseDirLock(String caseDir) {
2354  if (caseDirLock != null) {
2355  try {
2356  caseDirLock.release();
2357  caseDirLock = null;
2359  logger.log(Level.SEVERE, String.format("Failed to release shared case directory lock for %s", caseDir), ex);
2360  }
2361  }
2362  }
2363 
2370  private String getOrCreateSubdirectory(String subDirectoryName) {
2371  File subDirectory = Paths.get(getOutputDirectory(), subDirectoryName).toFile();
2372  if (!subDirectory.exists()) {
2373  subDirectory.mkdirs();
2374  }
2375  return subDirectory.toString();
2376 
2377  }
2378 
2383  @ThreadSafe
2384  private final static class CancelButtonListener implements ActionListener {
2385 
2386  private final String cancellationMessage;
2387  @GuardedBy("this")
2388  private boolean cancelRequested;
2389  @GuardedBy("this")
2391  @GuardedBy("this")
2392  private Future<?> caseActionFuture;
2393 
2402  private CancelButtonListener(String cancellationMessage) {
2403  this.cancellationMessage = cancellationMessage;
2404  }
2405 
2411  private synchronized void setCaseContext(CaseContext caseContext) {
2412  this.caseContext = caseContext;
2413  /*
2414  * If the cancel button has already been pressed, pass the
2415  * cancellation on to the case context.
2416  */
2417  if (cancelRequested) {
2418  cancel();
2419  }
2420  }
2421 
2427  private synchronized void setCaseActionFuture(Future<?> caseActionFuture) {
2428  this.caseActionFuture = caseActionFuture;
2429  /*
2430  * If the cancel button has already been pressed, cancel the Future
2431  * of the task.
2432  */
2433  if (cancelRequested) {
2434  cancel();
2435  }
2436  }
2437 
2443  @Override
2444  public synchronized void actionPerformed(ActionEvent event) {
2445  cancel();
2446  }
2447 
2451  private void cancel() {
2452  /*
2453  * At a minimum, set the cancellation requested flag of this
2454  * listener.
2455  */
2456  this.cancelRequested = true;
2457  if (null != this.caseContext) {
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  */
2464  ProgressIndicator progressIndicator = this.caseContext.getProgressIndicator();
2465  if (progressIndicator instanceof ModalDialogProgressIndicator) {
2466  ((ModalDialogProgressIndicator) progressIndicator).setCancelling(cancellationMessage);
2467  }
2468  }
2469  this.caseContext.requestCancel();
2470  }
2471  if (null != this.caseActionFuture) {
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  */
2477  this.caseActionFuture.cancel(true);
2478  }
2479  }
2480  }
2481 
2485  private static class TaskThreadFactory implements ThreadFactory {
2486 
2487  private final String threadName;
2488 
2489  private TaskThreadFactory(String threadName) {
2490  this.threadName = threadName;
2491  }
2492 
2493  @Override
2494  public Thread newThread(Runnable task) {
2495  return new Thread(task, threadName);
2496  }
2497 
2498  }
2499 
2507  @Deprecated
2508  public static String getAppName() {
2509  return UserPreferences.getAppName();
2510  }
2511 
2531  @Deprecated
2532  public static void create(String caseDir, String caseDisplayName, String caseNumber, String examiner) throws CaseActionException {
2533  createAsCurrentCase(caseDir, caseDisplayName, caseNumber, examiner, CaseType.SINGLE_USER_CASE);
2534  }
2535 
2556  @Deprecated
2557  public static void create(String caseDir, String caseDisplayName, String caseNumber, String examiner, CaseType caseType) throws CaseActionException {
2558  createAsCurrentCase(caseDir, caseDisplayName, caseNumber, examiner, caseType);
2559  }
2560 
2572  @Deprecated
2573  public static void open(String caseMetadataFilePath) throws CaseActionException {
2574  openAsCurrentCase(caseMetadataFilePath);
2575  }
2576 
2586  @Deprecated
2587  public void closeCase() throws CaseActionException {
2588  closeCurrentCase();
2589  }
2590 
2596  @Deprecated
2597  public static void invokeStartupDialog() {
2599  }
2600 
2614  @Deprecated
2615  public static String convertTimeZone(String timeZoneId) {
2616  return TimeZoneUtils.convertToAlphaNumericFormat(timeZoneId);
2617  }
2618 
2628  @Deprecated
2629  public static boolean pathExists(String filePath) {
2630  return new File(filePath).isFile();
2631  }
2632 
2641  @Deprecated
2642  public static String getAutopsyVersion() {
2643  return Version.getVersion();
2644  }
2645 
2653  @Deprecated
2654  public static boolean existsCurrentCase() {
2655  return isCaseOpen();
2656  }
2657 
2667  @Deprecated
2668  public static String getModulesOutputDirRelPath() {
2669  return "ModuleOutput"; //NON-NLS
2670  }
2671 
2681  @Deprecated
2682  public static PropertyChangeSupport
2684  return new PropertyChangeSupport(Case.class
2685  );
2686  }
2687 
2696  @Deprecated
2697  public String getModulesOutputDirAbsPath() {
2698  return getModuleDirectory();
2699  }
2700 
2715  @Deprecated
2716  public Image addImage(String imgPath, long imgId, String timeZone) throws CaseActionException {
2717  try {
2718  Image newDataSource = caseDb.getImageById(imgId);
2719  notifyDataSourceAdded(newDataSource, UUID.randomUUID());
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
2734  public Set<TimeZone> getTimeZone() {
2735  return getTimeZones();
2736  }
2737 
2748  @Deprecated
2749  public void deleteReports(Collection<? extends Report> reports, boolean deleteFromDisk) throws TskCoreException {
2750  deleteReports(reports);
2751  }
2752 
2753 }
static final AutopsyEventPublisher eventPublisher
Definition: Case.java:137
List< Content > getDataSources()
Definition: Case.java:1385
void notifyContentTagDeleted(ContentTag deletedTag)
Definition: Case.java:1500
Case(CaseMetadata caseMetaData)
Definition: Case.java:1680
static CaseType fromString(String typeName)
Definition: Case.java:180
static final String CASE_ACTION_THREAD_NAME
Definition: Case.java:134
static void createAsCurrentCase(CaseType caseType, String caseDir, CaseDetails caseDetails)
Definition: Case.java:537
void notifyBlackBoardArtifactTagDeleted(BlackboardArtifactTag deletedTag)
Definition: Case.java:1534
Image addImage(String imgPath, long imgId, String timeZone)
Definition: Case.java:2716
static synchronized IngestManager getInstance()
static CoordinationService.Lock acquireExclusiveCaseResourcesLock(String caseDir)
Definition: Case.java:1005
static boolean existsCurrentCase()
Definition: Case.java:2654
static void removePropertyChangeListener(PropertyChangeListener listener)
Definition: Case.java:395
void start(String message, int totalWorkUnits)
static final Logger logger
Definition: Case.java:136
static final int RESOURCES_LOCK_TIMOUT_HOURS
Definition: Case.java:125
static final String EXPORT_FOLDER
Definition: Case.java:129
void notifyTagDefinitionChanged(String changedTagName)
Definition: Case.java:1511
static volatile Frame mainFrame
Definition: Case.java:139
static String convertTimeZone(String timeZoneId)
Definition: Case.java:2615
static boolean driveExists(String path)
Definition: DriveUtils.java:66
void addSubscriber(Set< String > eventNames, PropertyChangeListener subscriber)
synchronized static void setLogDirectory(String directoryPath)
Definition: Logger.java:89
static final String CACHE_FOLDER
Definition: Case.java:128
Case(CaseType caseType, String caseDir, CaseDetails caseDetails)
Definition: Case.java:1671
void createCaseData(ProgressIndicator progressIndicator)
Definition: Case.java:1883
void addReport(String localPath, String srcModuleName, String reportName)
Definition: Case.java:1549
static void updateGUIForCaseOpened(Case newCurrentCase)
Definition: Case.java:1033
static CaseDbConnectionInfo getDatabaseConnectionInfo()
static void create(String caseDir, String caseDisplayName, String caseNumber, String examiner, CaseType caseType)
Definition: Case.java:2557
static final String SINGLE_USER_CASE_DB_NAME
Definition: Case.java:126
static void deleteCase(CaseMetadata metadata)
Definition: Case.java:697
void deleteReports(Collection<?extends Report > reports, boolean deleteFromDisk)
Definition: Case.java:2749
volatile ExecutorService caseLockingExecutor
Definition: Case.java:142
synchronized void setCaseContext(CaseContext caseContext)
Definition: Case.java:2411
static void clearTempSubDir(String tempSubDirPath)
Definition: Case.java:1154
static boolean isValidName(String caseName)
Definition: Case.java:479
void acquireSharedCaseDirLock(String caseDir)
Definition: Case.java:2337
CollaborationMonitor collaborationMonitor
Definition: Case.java:145
void releaseSharedCaseDirLock(String caseDir)
Definition: Case.java:2353
static void shutDownTaskExecutor(ExecutorService executor)
static String getModulesOutputDirRelPath()
Definition: Case.java:2668
void openCaseData(ProgressIndicator progressIndicator)
Definition: Case.java:1944
synchronized void actionPerformed(ActionEvent event)
Definition: Case.java:2444
static final String MODULE_FOLDER
Definition: Case.java:133
static void create(String caseDir, String caseDisplayName, String caseNumber, String examiner)
Definition: Case.java:2532
synchronized void openRemoteEventChannel(String channelName)
void openServices(ProgressIndicator progressIndicator)
Definition: Case.java:1980
static String displayNameToUniqueName(String caseDisplayName)
Definition: Case.java:789
void open(boolean isNewCase)
Definition: Case.java:1709
static void openAsCurrentCase(String caseMetadataFilePath)
Definition: Case.java:564
static void removeEventSubscriber(Set< String > eventNames, PropertyChangeListener subscriber)
Definition: Case.java:455
Lock tryGetExclusiveLock(CategoryNode category, String nodePath, int timeOut, TimeUnit timeUnit)
static final int DIR_LOCK_TIMOUT_HOURS
Definition: Case.java:124
void close(ProgressIndicator progressIndicator)
Definition: Case.java:2227
void notifyBlackBoardArtifactTagAdded(BlackboardArtifactTag newTag)
Definition: Case.java:1523
static PropertyChangeSupport getPropertyChangeSupport()
Definition: Case.java:2683
static void addPropertyChangeListener(PropertyChangeListener listener)
Definition: Case.java:383
synchronized void setCaseActionFuture(Future<?> caseActionFuture)
Definition: Case.java:2427
void removeSubscriber(Set< String > eventNames, PropertyChangeListener subscriber)
static void removeEventSubscriber(String eventName, PropertyChangeListener subscriber)
Definition: Case.java:445
void deleteReports(Collection<?extends Report > reports)
Definition: Case.java:1604
void notifyDataSourceAdded(Content dataSource, UUID addingDataSourceEventId)
Definition: Case.java:1478
Report addReport(String localPath, String srcModuleName, String reportName, Content parent)
Definition: Case.java:1567
static boolean pathExists(String filePath)
Definition: Case.java:2629
static void open(String caseMetadataFilePath)
Definition: Case.java:2573
static void openAsCurrentCase(Case newCurrentCase, boolean isNewCase)
Definition: Case.java:749
static void error(String title, String message)
String getOrCreateSubdirectory(String subDirectoryName)
Definition: Case.java:2370
boolean equalsName(String otherTypeName)
Definition: Case.java:238
static final String EVENT_CHANNEL_NAME
Definition: Case.java:127
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
static void addEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:420
static String convertToAlphaNumericFormat(String timeZoneId)
static final String LOG_FOLDER
Definition: Case.java:130
Lock tryGetSharedLock(CategoryNode category, String nodePath, int timeOut, TimeUnit timeUnit)
static volatile Case currentCase
Definition: Case.java:140
void notifyAddingDataSource(UUID eventId)
Definition: Case.java:1449
CoordinationService.Lock caseDirLock
Definition: Case.java:143
static void addEventSubscriber(String eventName, PropertyChangeListener subscriber)
Definition: Case.java:435
void notifyContentTagAdded(ContentTag newTag)
Definition: Case.java:1489
void cancelAllIngestJobs(IngestJob.CancellationReason reason)
static final String CASE_RESOURCES_THREAD_NAME
Definition: Case.java:135
static boolean deleteDir(File dirPath)
Definition: FileUtil.java:47
static void createAsCurrentCase(String caseDir, String caseDisplayName, String caseNumber, String examiner, CaseType caseType)
Definition: Case.java:510
void notifyFailedAddingDataSource(UUID addingDataSourceEventId)
Definition: Case.java:1463
static void removeEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:465
static final Object caseActionSerializationLock
Definition: Case.java:138
void open(boolean isNewCase, ProgressIndicator progressIndicator)
Definition: Case.java:1830
static final String REPORTS_FOLDER
Definition: Case.java:131
static void createCaseDirectory(String caseDir, CaseType caseType)
Definition: Case.java:828
static final String TEMP_FOLDER
Definition: Case.java:132
static void addEventSubscriber(Set< String > eventNames, PropertyChangeListener subscriber)
Definition: Case.java:410
static void deleteCase(CaseMetadata metadata, ProgressIndicator progressIndicator)
Definition: Case.java:936

Copyright © 2012-2016 Basis Technology. Generated on: Mon May 7 2018
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.