Autopsy  4.11.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-2019 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 com.google.common.annotations.Beta;
23 import java.awt.Frame;
24 import java.awt.event.ActionEvent;
25 import java.awt.event.ActionListener;
26 import java.beans.PropertyChangeListener;
27 import java.beans.PropertyChangeSupport;
28 import java.io.File;
29 import java.io.IOException;
30 import java.nio.file.InvalidPathException;
31 import java.nio.file.Path;
32 import java.nio.file.Paths;
33 import java.sql.Connection;
34 import java.sql.DriverManager;
35 import java.sql.ResultSet;
36 import java.sql.SQLException;
37 import java.sql.Statement;
38 import java.text.ParseException;
39 import java.text.SimpleDateFormat;
40 import java.util.Collection;
41 import java.util.Date;
42 import java.util.HashMap;
43 import java.util.HashSet;
44 import java.util.List;
45 import java.util.Map;
46 import java.util.Set;
47 import java.util.TimeZone;
48 import java.util.UUID;
49 import java.util.concurrent.CancellationException;
50 import java.util.concurrent.ExecutionException;
51 import java.util.concurrent.ExecutorService;
52 import java.util.concurrent.Executors;
53 import java.util.concurrent.Future;
54 import java.util.concurrent.ThreadFactory;
55 import java.util.concurrent.TimeUnit;
56 import java.util.logging.Level;
57 import java.util.stream.Collectors;
58 import java.util.stream.Stream;
59 import javax.annotation.concurrent.GuardedBy;
60 import javax.annotation.concurrent.ThreadSafe;
61 import javax.swing.JOptionPane;
62 import javax.swing.SwingUtilities;
63 import org.openide.util.Lookup;
64 import org.openide.util.NbBundle;
65 import org.openide.util.NbBundle.Messages;
66 import org.openide.util.actions.CallableSystemAction;
67 import org.openide.windows.WindowManager;
71 import static org.sleuthkit.autopsy.casemodule.Bundle.*;
117 import org.sleuthkit.datamodel.BlackboardArtifactTag;
118 import org.sleuthkit.datamodel.CaseDbConnectionInfo;
119 import org.sleuthkit.datamodel.Content;
120 import org.sleuthkit.datamodel.ContentTag;
121 import org.sleuthkit.datamodel.Image;
122 import org.sleuthkit.datamodel.Report;
123 import org.sleuthkit.datamodel.SleuthkitCase;
124 import org.sleuthkit.datamodel.TskCoreException;
125 import org.sleuthkit.datamodel.TskUnsupportedSchemaVersionException;
126 
130 public class Case {
131 
132  private static final int DIR_LOCK_TIMOUT_HOURS = 12;
133  private static final int RESOURCES_LOCK_TIMOUT_HOURS = 12;
134  private static final String SINGLE_USER_CASE_DB_NAME = "autopsy.db";
135  private static final String EVENT_CHANNEL_NAME = "%s-Case-Events"; //NON-NLS
136  private static final String CACHE_FOLDER = "Cache"; //NON-NLS
137  private static final String EXPORT_FOLDER = "Export"; //NON-NLS
138  private static final String LOG_FOLDER = "Log"; //NON-NLS
139  private static final String REPORTS_FOLDER = "Reports"; //NON-NLS
140  private static final String CONFIG_FOLDER = "Config"; // NON-NLS
141  private static final String TEMP_FOLDER = "Temp"; //NON-NLS
142  private static final String MODULE_FOLDER = "ModuleOutput"; //NON-NLS
143  private static final String CASE_ACTION_THREAD_NAME = "%s-case-action";
144  private static final String CASE_RESOURCES_THREAD_NAME = "%s-manage-case-resources";
145  private static final String NO_NODE_ERROR_MSG_FRAGMENT = "KeeperErrorCode = NoNode";
146  private static final Logger logger = Logger.getLogger(Case.class.getName());
148  private static final Object caseActionSerializationLock = new Object();
149  private static volatile Frame mainFrame;
150  private static volatile Case currentCase;
151  private final CaseMetadata metadata;
152  private volatile ExecutorService caseLockingExecutor;
154  private SleuthkitCase caseDb;
155  private CollaborationMonitor collaborationMonitor;
157  private boolean hasDataSources;
158 
159  /*
160  * Get a reference to the main window of the desktop application to use to
161  * parent pop up dialogs and initialize the application name for use in
162  * changing the main window title.
163  */
164  static {
165  WindowManager.getDefault().invokeWhenUIReady(new Runnable() {
166  @Override
167  public void run() {
168  mainFrame = WindowManager.getDefault().getMainWindow();
169  }
170  });
171  }
172 
176  public enum CaseType {
177 
178  SINGLE_USER_CASE("Single-user case"), //NON-NLS
179  MULTI_USER_CASE("Multi-user case"); //NON-NLS
180 
181  private final String typeName;
182 
190  public static CaseType fromString(String typeName) {
191  if (typeName != null) {
192  for (CaseType c : CaseType.values()) {
193  if (typeName.equalsIgnoreCase(c.toString())) {
194  return c;
195  }
196  }
197  }
198  return null;
199  }
200 
206  @Override
207  public String toString() {
208  return typeName;
209  }
210 
216  @Messages({
217  "Case_caseType_singleUser=Single-user case",
218  "Case_caseType_multiUser=Multi-user case"
219  })
221  if (fromString(typeName) == SINGLE_USER_CASE) {
222  return Bundle.Case_caseType_singleUser();
223  } else {
224  return Bundle.Case_caseType_multiUser();
225  }
226  }
227 
233  private CaseType(String typeName) {
234  this.typeName = typeName;
235  }
236 
247  @Deprecated
248  public boolean equalsName(String otherTypeName) {
249  return (otherTypeName == null) ? false : typeName.equals(otherTypeName);
250  }
251 
252  };
253 
258  public enum Events {
259 
267  @Deprecated
276  @Deprecated
285  @Deprecated
395 
396  };
397 
404  public static void addPropertyChangeListener(PropertyChangeListener listener) {
405  addEventSubscriber(Stream.of(Events.values())
406  .map(Events::toString)
407  .collect(Collectors.toSet()), listener);
408  }
409 
416  public static void removePropertyChangeListener(PropertyChangeListener listener) {
417  removeEventSubscriber(Stream.of(Events.values())
418  .map(Events::toString)
419  .collect(Collectors.toSet()), listener);
420  }
421 
430  @Deprecated
431  public static void addEventSubscriber(Set<String> eventNames, PropertyChangeListener subscriber) {
432  eventPublisher.addSubscriber(eventNames, subscriber);
433  }
434 
441  public static void addEventTypeSubscriber(Set<Events> eventTypes, PropertyChangeListener subscriber) {
442  eventTypes.forEach((Events event) -> {
443  eventPublisher.addSubscriber(event.toString(), subscriber);
444  });
445  }
446 
455  @Deprecated
456  public static void addEventSubscriber(String eventName, PropertyChangeListener subscriber) {
457  eventPublisher.addSubscriber(eventName, subscriber);
458  }
459 
466  public static void removeEventSubscriber(String eventName, PropertyChangeListener subscriber) {
467  eventPublisher.removeSubscriber(eventName, subscriber);
468  }
469 
476  public static void removeEventSubscriber(Set<String> eventNames, PropertyChangeListener subscriber) {
477  eventPublisher.removeSubscriber(eventNames, subscriber);
478  }
479 
486  public static void removeEventTypeSubscriber(Set<Events> eventTypes, PropertyChangeListener subscriber) {
487  eventTypes.forEach((Events event) -> {
488  eventPublisher.removeSubscriber(event.toString(), subscriber);
489  });
490  }
491 
500  public static boolean isValidName(String caseName) {
501  return !(caseName.contains("\\") || caseName.contains("/") || caseName.contains(":")
502  || caseName.contains("*") || caseName.contains("?") || caseName.contains("\"")
503  || caseName.contains("<") || caseName.contains(">") || caseName.contains("|"));
504  }
505 
530  @Deprecated
531  public static void createAsCurrentCase(String caseDir, String caseDisplayName, String caseNumber, String examiner, CaseType caseType) throws CaseActionException, CaseActionCancelledException {
532  createAsCurrentCase(caseType, caseDir, new CaseDetails(caseDisplayName, caseNumber, examiner, "", "", ""));
533  }
534 
554  @Messages({
555  "Case.exceptionMessage.emptyCaseName=Must specify a case name.",
556  "Case.exceptionMessage.emptyCaseDir=Must specify a case directory path."
557  })
558  public static void createAsCurrentCase(CaseType caseType, String caseDir, CaseDetails caseDetails) throws CaseActionException, CaseActionCancelledException {
559  if (caseDetails.getCaseDisplayName().isEmpty()) {
560  throw new CaseActionException(Bundle.Case_exceptionMessage_emptyCaseName());
561  }
562  if (caseDir.isEmpty()) {
563  throw new CaseActionException(Bundle.Case_exceptionMessage_emptyCaseDir());
564  }
565  openAsCurrentCase(new Case(caseType, caseDir, caseDetails), true);
566  }
567 
581  @Messages({
582  "# {0} - exception message", "Case.exceptionMessage.failedToReadMetadata=Failed to read case metadata:\n{0}.",
583  "Case.exceptionMessage.cannotOpenMultiUserCaseNoSettings=Multi-user settings are missing (see Tools, Options, Multi-user tab), cannot open a multi-user case."
584  })
585  public static void openAsCurrentCase(String caseMetadataFilePath) throws CaseActionException {
587  try {
588  metadata = new CaseMetadata(Paths.get(caseMetadataFilePath));
589  } catch (CaseMetadataException ex) {
590  throw new CaseActionException(Bundle.Case_exceptionMessage_failedToReadMetadata(ex.getLocalizedMessage()), ex);
591  }
593  throw new CaseActionException(Bundle.Case_exceptionMessage_cannotOpenMultiUserCaseNoSettings());
594  }
595  openAsCurrentCase(new Case(metadata), false);
596  }
597 
603  public static boolean isCaseOpen() {
604  return currentCase != null;
605  }
606 
614  public static Case getCurrentCase() {
615  try {
616  return getCurrentCaseThrows();
617  } catch (NoCurrentCaseException ex) {
618  /*
619  * Throw a runtime exception, since this is a programming error.
620  */
621  throw new IllegalStateException(NbBundle.getMessage(Case.class, "Case.getCurCase.exception.noneOpen"), ex);
622  }
623  }
624 
643  Case openCase = currentCase;
644  if (openCase == null) {
645  throw new NoCurrentCaseException(NbBundle.getMessage(Case.class, "Case.getCurCase.exception.noneOpen"));
646  } else {
647  return openCase;
648  }
649  }
650 
659  @Messages({
660  "# {0} - exception message", "Case.closeException.couldNotCloseCase=Error closing case: {0}",
661  "Case.progressIndicatorTitle.closingCase=Closing Case"
662  })
663  public static void closeCurrentCase() throws CaseActionException {
664  synchronized (caseActionSerializationLock) {
665  if (null == currentCase) {
666  return;
667  }
668  Case closedCase = currentCase;
669  try {
670  eventPublisher.publishLocally(new AutopsyEvent(Events.CURRENT_CASE.toString(), closedCase, null));
671  logger.log(Level.INFO, "Closing current case {0} ({1}) in {2}", new Object[]{closedCase.getDisplayName(), closedCase.getName(), closedCase.getCaseDirectory()}); //NON-NLS
672  currentCase = null;
673  closedCase.close();
674  logger.log(Level.INFO, "Closed current case {0} ({1}) in {2}", new Object[]{closedCase.getDisplayName(), closedCase.getName(), closedCase.getCaseDirectory()}); //NON-NLS
675  } catch (CaseActionException ex) {
676  logger.log(Level.SEVERE, String.format("Error closing current case %s (%s) in %s", closedCase.getDisplayName(), closedCase.getName(), closedCase.getCaseDirectory()), ex); //NON-NLS
677  throw ex;
678  } finally {
681  }
682  }
683  }
684  }
685 
694  public static void deleteCurrentCase() throws CaseActionException {
695  synchronized (caseActionSerializationLock) {
696  if (null == currentCase) {
697  return;
698  }
699  CaseMetadata metadata = currentCase.getMetadata();
701  deleteCase(metadata);
702  }
703  }
704 
716  @Messages({
717  "Case.progressIndicatorTitle.deletingCase=Deleting Case",
718  "Case.exceptionMessage.cannotDeleteCurrentCase=Cannot delete current case, it must be closed first.",
719  "# {0} - case display name", "Case.exceptionMessage.deletionInterrupted=Deletion of the case {0} was cancelled."
720  })
721  public static void deleteCase(CaseMetadata metadata) throws CaseActionException {
722  synchronized (caseActionSerializationLock) {
723  if (null != currentCase) {
724  throw new CaseActionException(Bundle.Case_exceptionMessage_cannotDeleteCurrentCase());
725  }
726  }
727 
728  ProgressIndicator progressIndicator;
730  progressIndicator = new ModalDialogProgressIndicator(mainFrame, Bundle.Case_progressIndicatorTitle_deletingCase());
731  } else {
732  progressIndicator = new LoggingProgressIndicator();
733  }
734  progressIndicator.start(Bundle.Case_progressMessage_preparing());
735  try {
736  if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) {
737  deleteSingleUserCase(metadata, progressIndicator);
738  } else {
739  try {
740  deleteMultiUserCase(metadata, progressIndicator);
741  } catch (InterruptedException ex) {
742  /*
743  * Note that task cancellation is not currently supported
744  * for this code path, so this catch block is not expected
745  * to be executed.
746  */
747  throw new CaseActionException(Bundle.Case_exceptionMessage_deletionInterrupted(metadata.getCaseDisplayName()), ex);
748  }
749  }
750  } finally {
751  progressIndicator.finish();
752  }
753  }
754 
765  @Messages({
766  "Case.exceptionMessage.cannotLocateMainWindow=Cannot locate main application window"
767  })
768  private static void openAsCurrentCase(Case newCurrentCase, boolean isNewCase) throws CaseActionException, CaseActionCancelledException {
769  synchronized (caseActionSerializationLock) {
770  if (null != currentCase) {
771  try {
773  } catch (CaseActionException ex) {
774  /*
775  * Notify the user and continue (the error has already been
776  * logged in closeCurrentCase.
777  */
778  MessageNotifyUtil.Message.error(ex.getLocalizedMessage());
779  }
780  }
781  try {
782  logger.log(Level.INFO, "Opening {0} ({1}) in {2} as the current case", new Object[]{newCurrentCase.getDisplayName(), newCurrentCase.getName(), newCurrentCase.getCaseDirectory()}); //NON-NLS
783  newCurrentCase.open(isNewCase);
784  currentCase = newCurrentCase;
785  logger.log(Level.INFO, "Opened {0} ({1}) in {2} as the current case", new Object[]{newCurrentCase.getDisplayName(), newCurrentCase.getName(), newCurrentCase.getCaseDirectory()}); //NON-NLS
787  updateGUIForCaseOpened(newCurrentCase);
788  }
789  eventPublisher.publishLocally(new AutopsyEvent(Events.CURRENT_CASE.toString(), null, currentCase));
790  } catch (CaseActionCancelledException ex) {
791  logger.log(Level.INFO, String.format("Cancelled opening %s (%s) in %s as the current case", newCurrentCase.getDisplayName(), newCurrentCase.getName(), newCurrentCase.getCaseDirectory())); //NON-NLS
792  throw ex;
793  } catch (CaseActionException ex) {
794  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
795  throw ex;
796  }
797  }
798  }
799 
808  private static String displayNameToUniqueName(String caseDisplayName) {
809  /*
810  * Replace all non-ASCII characters.
811  */
812  String uniqueCaseName = caseDisplayName.replaceAll("[^\\p{ASCII}]", "_"); //NON-NLS
813 
814  /*
815  * Replace all control characters.
816  */
817  uniqueCaseName = uniqueCaseName.replaceAll("[\\p{Cntrl}]", "_"); //NON-NLS
818 
819  /*
820  * Replace /, \, :, ?, space, ' ".
821  */
822  uniqueCaseName = uniqueCaseName.replaceAll("[ /?:'\"\\\\]", "_"); //NON-NLS
823 
824  /*
825  * Make it all lowercase.
826  */
827  uniqueCaseName = uniqueCaseName.toLowerCase();
828 
829  /*
830  * Add a time stamp for uniqueness.
831  */
832  SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
833  Date date = new Date();
834  uniqueCaseName = uniqueCaseName + "_" + dateFormat.format(date);
835 
836  return uniqueCaseName;
837  }
838 
848  public static void createCaseDirectory(String caseDirPath, CaseType caseType) throws CaseActionException {
849  /*
850  * Check the case directory path and permissions. The case directory may
851  * already exist.
852  */
853  File caseDir = new File(caseDirPath);
854  if (caseDir.exists()) {
855  if (caseDir.isFile()) {
856  throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.existNotDir", caseDirPath));
857  } else if (!caseDir.canRead() || !caseDir.canWrite()) {
858  throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.existCantRW", caseDirPath));
859  }
860  }
861 
862  /*
863  * Create the case directory, if it does not already exist.
864  */
865  if (!caseDir.mkdirs()) {
866  throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreate", caseDirPath));
867  }
868 
869  /*
870  * Create the subdirectories of the case directory, if they do not
871  * already exist. Note that multi-user cases get an extra layer of
872  * subdirectories, one subdirectory per application host machine.
873  */
874  String hostPathComponent = "";
875  if (caseType == CaseType.MULTI_USER_CASE) {
876  hostPathComponent = File.separator + NetworkUtils.getLocalHostName();
877  }
878 
879  Path exportDir = Paths.get(caseDirPath, hostPathComponent, EXPORT_FOLDER);
880  if (!exportDir.toFile().mkdirs()) {
881  throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreateCaseDir", exportDir));
882  }
883 
884  Path logsDir = Paths.get(caseDirPath, hostPathComponent, LOG_FOLDER);
885  if (!logsDir.toFile().mkdirs()) {
886  throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreateCaseDir", logsDir));
887  }
888 
889  Path tempDir = Paths.get(caseDirPath, hostPathComponent, TEMP_FOLDER);
890  if (!tempDir.toFile().mkdirs()) {
891  throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreateCaseDir", tempDir));
892  }
893 
894  Path cacheDir = Paths.get(caseDirPath, hostPathComponent, CACHE_FOLDER);
895  if (!cacheDir.toFile().mkdirs()) {
896  throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreateCaseDir", cacheDir));
897  }
898 
899  Path moduleOutputDir = Paths.get(caseDirPath, hostPathComponent, MODULE_FOLDER);
900  if (!moduleOutputDir.toFile().mkdirs()) {
901  throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreateModDir", moduleOutputDir));
902  }
903 
904  Path reportsDir = Paths.get(caseDirPath, hostPathComponent, REPORTS_FOLDER);
905  if (!reportsDir.toFile().mkdirs()) {
906  throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreateReportsDir", reportsDir));
907  }
908  }
909 
917  static Map<Long, String> getImagePaths(SleuthkitCase db) {
918  Map<Long, String> imgPaths = new HashMap<>();
919  try {
920  Map<Long, List<String>> imgPathsList = db.getImagePaths();
921  for (Map.Entry<Long, List<String>> entry : imgPathsList.entrySet()) {
922  if (entry.getValue().size() > 0) {
923  imgPaths.put(entry.getKey(), entry.getValue().get(0));
924  }
925  }
926  } catch (TskCoreException ex) {
927  logger.log(Level.SEVERE, "Error getting image paths", ex); //NON-NLS
928  }
929  return imgPaths;
930  }
931 
942  @Messages({
943  "Case.creationException.couldNotAcquireResourcesLock=Failed to get lock on case resources"
944  })
945  private static CoordinationService.Lock acquireExclusiveCaseResourcesLock(String caseDir) throws CaseActionException {
946  try {
947  Path caseDirPath = Paths.get(caseDir);
948  String resourcesNodeName = CoordinationServiceUtils.getCaseResourcesNodePath(caseDirPath);
949  Lock lock = CoordinationService.getInstance().tryGetExclusiveLock(CategoryNode.CASES, resourcesNodeName, RESOURCES_LOCK_TIMOUT_HOURS, TimeUnit.HOURS);
950  return lock;
951  } catch (InterruptedException ex) {
952  throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser());
953  } catch (CoordinationServiceException ex) {
954  throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireResourcesLock(), ex);
955  }
956  }
957 
958  private static String getNameForTitle() {
959  //Method should become unnecessary once technical debt story 3334 is done.
960  if (UserPreferences.getAppName().equals(Version.getName())) {
961  //Available version number is version number for this application
962  return String.format("%s %s", UserPreferences.getAppName(), Version.getVersion());
963  } else {
964  return UserPreferences.getAppName();
965  }
966  }
967 
971  private static void updateGUIForCaseOpened(Case newCurrentCase) {
973  SwingUtilities.invokeLater(() -> {
974  /*
975  * If the case database was upgraded for a new schema and a
976  * backup database was created, notify the user.
977  */
978  SleuthkitCase caseDb = newCurrentCase.getSleuthkitCase();
979  String backupDbPath = caseDb.getBackupDatabasePath();
980  if (null != backupDbPath) {
981  JOptionPane.showMessageDialog(
982  mainFrame,
983  NbBundle.getMessage(Case.class, "Case.open.msgDlg.updated.msg", backupDbPath),
984  NbBundle.getMessage(Case.class, "Case.open.msgDlg.updated.title"),
985  JOptionPane.INFORMATION_MESSAGE);
986  }
987 
988  /*
989  * Look for the files for the data sources listed in the case
990  * database and give the user the opportunity to locate any that
991  * are missing.
992  */
993  Map<Long, String> imgPaths = getImagePaths(caseDb);
994  for (Map.Entry<Long, String> entry : imgPaths.entrySet()) {
995  long obj_id = entry.getKey();
996  String path = entry.getValue();
997  boolean fileExists = (new File(path).isFile() || DriveUtils.driveExists(path));
998  if (!fileExists) {
999  int response = JOptionPane.showConfirmDialog(
1000  mainFrame,
1001  NbBundle.getMessage(Case.class, "Case.checkImgExist.confDlg.doesntExist.msg", path),
1002  NbBundle.getMessage(Case.class, "Case.checkImgExist.confDlg.doesntExist.title"),
1003  JOptionPane.YES_NO_OPTION);
1004  if (response == JOptionPane.YES_OPTION) {
1005  MissingImageDialog.makeDialog(obj_id, caseDb);
1006  } else {
1007  logger.log(Level.SEVERE, "User proceeding with missing image files"); //NON-NLS
1008 
1009  }
1010  }
1011  }
1012 
1013  /*
1014  * Enable the case-specific actions.
1015  */
1016  CallableSystemAction.get(AddImageAction.class).setEnabled(true);
1017  CallableSystemAction.get(CaseCloseAction.class).setEnabled(true);
1018  CallableSystemAction.get(CaseDetailsAction.class).setEnabled(true);
1019  CallableSystemAction.get(DataSourceSummaryAction.class).setEnabled(true);
1020  CallableSystemAction.get(CaseDeleteAction.class).setEnabled(true);
1021  CallableSystemAction.get(OpenTimelineAction.class).setEnabled(true);
1022  CallableSystemAction.get(OpenCommVisualizationToolAction.class).setEnabled(true);
1023  CallableSystemAction.get(CommonAttributeSearchAction.class).setEnabled(true);
1024  CallableSystemAction.get(OpenOutputFolderAction.class).setEnabled(false);
1025 
1026  /*
1027  * Add the case to the recent cases tracker that supplies a list
1028  * of recent cases to the recent cases menu item and the
1029  * open/create case dialog.
1030  */
1031  RecentCases.getInstance().addRecentCase(newCurrentCase.getDisplayName(), newCurrentCase.getMetadata().getFilePath().toString());
1032 
1033  /*
1034  * Open the top components (windows within the main application
1035  * window).
1036  *
1037  * Note: If the core windows are not opened here, they will be
1038  * opened via the DirectoryTreeTopComponent 'propertyChange()'
1039  * method on a DATA_SOURCE_ADDED event.
1040  */
1041  if (newCurrentCase.hasData()) {
1043  }
1044 
1045  /*
1046  * Reset the main window title to:
1047  *
1048  * [curent case display name] - [application name].
1049  */
1050  mainFrame.setTitle(newCurrentCase.getDisplayName() + " - " + getNameForTitle());
1051  });
1052  }
1053  }
1054 
1055  /*
1056  * Update the GUI to to reflect the lack of a current case.
1057  */
1058  private static void updateGUIForCaseClosed() {
1060  SwingUtilities.invokeLater(() -> {
1061  /*
1062  * Close the top components (windows within the main application
1063  * window).
1064  */
1066 
1067  /*
1068  * Disable the case-specific menu items.
1069  */
1070  CallableSystemAction.get(AddImageAction.class).setEnabled(false);
1071  CallableSystemAction.get(CaseCloseAction.class).setEnabled(false);
1072  CallableSystemAction.get(CaseDetailsAction.class).setEnabled(false);
1073  CallableSystemAction.get(DataSourceSummaryAction.class).setEnabled(false);
1074  CallableSystemAction.get(CaseDeleteAction.class).setEnabled(false);
1075  CallableSystemAction.get(OpenTimelineAction.class).setEnabled(false);
1076  CallableSystemAction.get(OpenCommVisualizationToolAction.class).setEnabled(false);
1077  CallableSystemAction.get(OpenOutputFolderAction.class).setEnabled(false);
1078  CallableSystemAction.get(CommonAttributeSearchAction.class).setEnabled(false);
1079 
1080  /*
1081  * Clear the notifications in the notfier component in the lower
1082  * right hand corner of the main application window.
1083  */
1085 
1086  /*
1087  * Reset the main window title to be just the application name,
1088  * instead of [curent case display name] - [application name].
1089  */
1090  mainFrame.setTitle(getNameForTitle());
1091  });
1092  }
1093  }
1094 
1098  private static void clearTempSubDir(String tempSubDirPath) {
1099  File tempFolder = new File(tempSubDirPath);
1100  if (tempFolder.isDirectory()) {
1101  File[] files = tempFolder.listFiles();
1102  if (files.length > 0) {
1103  for (File file : files) {
1104  if (file.isDirectory()) {
1105  FileUtil.deleteDir(file);
1106  } else {
1107  file.delete();
1108  }
1109  }
1110  }
1111  }
1112  }
1113 
1119  public SleuthkitCase getSleuthkitCase() {
1120  return this.caseDb;
1121  }
1122 
1129  return caseServices;
1130  }
1131 
1138  return metadata.getCaseType();
1139  }
1140 
1146  public String getCreatedDate() {
1147  return metadata.getCreatedDate();
1148  }
1149 
1155  public String getName() {
1156  return metadata.getCaseName();
1157  }
1158 
1164  public String getDisplayName() {
1165  return metadata.getCaseDisplayName();
1166  }
1167 
1173  public String getNumber() {
1174  return metadata.getCaseNumber();
1175  }
1176 
1182  public String getExaminer() {
1183  return metadata.getExaminer();
1184  }
1185 
1191  public String getExaminerPhone() {
1192  return metadata.getExaminerPhone();
1193  }
1194 
1200  public String getExaminerEmail() {
1201  return metadata.getExaminerEmail();
1202  }
1203 
1209  public String getCaseNotes() {
1210  return metadata.getCaseNotes();
1211  }
1212 
1218  public String getCaseDirectory() {
1219  return metadata.getCaseDirectory();
1220  }
1221 
1230  public String getOutputDirectory() {
1231  String caseDirectory = getCaseDirectory();
1232  Path hostPath;
1233  if (CaseType.MULTI_USER_CASE == metadata.getCaseType()) {
1234  hostPath = Paths.get(caseDirectory, NetworkUtils.getLocalHostName());
1235  } else {
1236  hostPath = Paths.get(caseDirectory);
1237  }
1238  if (!hostPath.toFile().exists()) {
1239  hostPath.toFile().mkdirs();
1240  }
1241  return hostPath.toString();
1242  }
1243 
1250  public String getTempDirectory() {
1251  return getOrCreateSubdirectory(TEMP_FOLDER);
1252  }
1253 
1260  public String getCacheDirectory() {
1261  return getOrCreateSubdirectory(CACHE_FOLDER);
1262  }
1263 
1270  public String getExportDirectory() {
1271  return getOrCreateSubdirectory(EXPORT_FOLDER);
1272  }
1273 
1280  public String getLogDirectoryPath() {
1281  return getOrCreateSubdirectory(LOG_FOLDER);
1282  }
1283 
1290  public String getReportDirectory() {
1291  return getOrCreateSubdirectory(REPORTS_FOLDER);
1292  }
1293 
1300  public String getConfigDirectory() {
1301  return getOrCreateSubdirectory(CONFIG_FOLDER);
1302  }
1303 
1310  public String getModuleDirectory() {
1311  return getOrCreateSubdirectory(MODULE_FOLDER);
1312  }
1313 
1322  Path path = Paths.get(getModuleDirectory());
1324  return path.subpath(path.getNameCount() - 2, path.getNameCount()).toString();
1325  } else {
1326  return path.subpath(path.getNameCount() - 1, path.getNameCount()).toString();
1327  }
1328  }
1329 
1339  public List<Content> getDataSources() throws TskCoreException {
1340  List<Content> list = caseDb.getRootObjects();
1341  hasDataSources = (list.size() > 0);
1342  return list;
1343  }
1344 
1350  public Set<TimeZone> getTimeZones() {
1351  Set<TimeZone> timezones = new HashSet<>();
1352  try {
1353  for (Content c : getDataSources()) {
1354  final Content dataSource = c.getDataSource();
1355  if ((dataSource != null) && (dataSource instanceof Image)) {
1356  Image image = (Image) dataSource;
1357  timezones.add(TimeZone.getTimeZone(image.getTimeZone()));
1358  }
1359  }
1360  } catch (TskCoreException ex) {
1361  logger.log(Level.SEVERE, "Error getting data source time zones", ex); //NON-NLS
1362  }
1363  return timezones;
1364  }
1365 
1372  public String getTextIndexName() {
1373  return getMetadata().getTextIndexName();
1374  }
1375 
1382  public boolean hasData() {
1383  if (!hasDataSources) {
1384  try {
1385  hasDataSources = (getDataSources().size() > 0);
1386  } catch (TskCoreException ex) {
1387  logger.log(Level.SEVERE, "Error accessing case database", ex); //NON-NLS
1388  }
1389  }
1390  return hasDataSources;
1391  }
1392 
1403  public void notifyAddingDataSource(UUID eventId) {
1404  eventPublisher.publish(new AddingDataSourceEvent(eventId));
1405  }
1406 
1417  public void notifyFailedAddingDataSource(UUID addingDataSourceEventId) {
1418  eventPublisher.publish(new AddingDataSourceFailedEvent(addingDataSourceEventId));
1419  }
1420 
1432  public void notifyDataSourceAdded(Content dataSource, UUID addingDataSourceEventId) {
1433  eventPublisher.publish(new DataSourceAddedEvent(dataSource, addingDataSourceEventId));
1434  }
1435 
1445  public void notifyDataSourceNameChanged(Content dataSource, String newName) {
1446  eventPublisher.publish(new DataSourceNameChangedEvent(dataSource, newName));
1447  }
1448 
1456  public void notifyContentTagAdded(ContentTag newTag) {
1457  eventPublisher.publish(new ContentTagAddedEvent(newTag));
1458  }
1459 
1467  public void notifyContentTagDeleted(ContentTag deletedTag) {
1468  eventPublisher.publish(new ContentTagDeletedEvent(deletedTag));
1469  }
1470 
1478  public void notifyTagDefinitionChanged(String changedTagName) {
1479  //leaving new value of changedTagName as null, because we do not currently support changing the display name of a tag.
1480  eventPublisher.publish(new AutopsyEvent(Events.TAG_DEFINITION_CHANGED.toString(), changedTagName, null));
1481  }
1482 
1493  public void notifyCentralRepoCommentChanged(long contentId, String newComment) {
1494  try {
1495  eventPublisher.publish(new CommentChangedEvent(contentId, newComment));
1496  } catch (NoCurrentCaseException ex) {
1497  logger.log(Level.WARNING, "Unable to send notifcation regarding comment change due to no current case being open", ex);
1498  }
1499  }
1500 
1508  public void notifyBlackBoardArtifactTagAdded(BlackboardArtifactTag newTag) {
1509  eventPublisher.publish(new BlackBoardArtifactTagAddedEvent(newTag));
1510  }
1511 
1519  public void notifyBlackBoardArtifactTagDeleted(BlackboardArtifactTag deletedTag) {
1520  eventPublisher.publish(new BlackBoardArtifactTagDeletedEvent(deletedTag));
1521  }
1522 
1534  public void addReport(String localPath, String srcModuleName, String reportName) throws TskCoreException {
1535  addReport(localPath, srcModuleName, reportName, null);
1536  }
1537 
1552  public Report addReport(String localPath, String srcModuleName, String reportName, Content parent) throws TskCoreException {
1553  String normalizedLocalPath;
1554  try {
1555  if (localPath.toLowerCase().contains("http:")) {
1556  normalizedLocalPath = localPath;
1557  } else {
1558  normalizedLocalPath = Paths.get(localPath).normalize().toString();
1559  }
1560  } catch (InvalidPathException ex) {
1561  String errorMsg = "Invalid local path provided: " + localPath; // NON-NLS
1562  throw new TskCoreException(errorMsg, ex);
1563  }
1564  Report report = this.caseDb.addReport(normalizedLocalPath, srcModuleName, reportName, parent);
1565  eventPublisher.publish(new ReportAddedEvent(report));
1566  return report;
1567  }
1568 
1577  public List<Report> getAllReports() throws TskCoreException {
1578  return this.caseDb.getAllReports();
1579  }
1580 
1589  public void deleteReports(Collection<? extends Report> reports) throws TskCoreException {
1590  for (Report report : reports) {
1591  this.caseDb.deleteReport(report);
1592  eventPublisher.publish(new AutopsyEvent(Events.REPORT_DELETED.toString(), report, null));
1593  }
1594  }
1595 
1601  CaseMetadata getMetadata() {
1602  return metadata;
1603  }
1604 
1612  @Messages({
1613  "Case.exceptionMessage.metadataUpdateError=Failed to update case metadata"
1614  })
1615  void updateCaseDetails(CaseDetails caseDetails) throws CaseActionException {
1616  CaseDetails oldCaseDetails = metadata.getCaseDetails();
1617  try {
1618  metadata.setCaseDetails(caseDetails);
1619  } catch (CaseMetadataException ex) {
1620  throw new CaseActionException(Bundle.Case_exceptionMessage_metadataUpdateError(), ex);
1621  }
1622  if (getCaseType() == CaseType.MULTI_USER_CASE && !oldCaseDetails.getCaseDisplayName().equals(caseDetails.getCaseDisplayName())) {
1623  try {
1624  CaseNodeData nodeData = CaseNodeData.readCaseNodeData(metadata.getCaseDirectory());
1625  nodeData.setDisplayName(caseDetails.getCaseDisplayName());
1626  CaseNodeData.writeCaseNodeData(nodeData);
1627  } catch (CaseNodeDataException | InterruptedException ex) {
1628  throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotUpdateCaseNodeData(ex.getLocalizedMessage()), ex);
1629  }
1630  }
1631  if (!oldCaseDetails.getCaseNumber().equals(caseDetails.getCaseNumber())) {
1632  eventPublisher.publish(new AutopsyEvent(Events.NUMBER.toString(), oldCaseDetails.getCaseNumber(), caseDetails.getCaseNumber()));
1633  }
1634  if (!oldCaseDetails.getExaminerName().equals(caseDetails.getExaminerName())) {
1635  eventPublisher.publish(new AutopsyEvent(Events.NUMBER.toString(), oldCaseDetails.getExaminerName(), caseDetails.getExaminerName()));
1636  }
1637  if (!oldCaseDetails.getCaseDisplayName().equals(caseDetails.getCaseDisplayName())) {
1638  eventPublisher.publish(new AutopsyEvent(Events.NAME.toString(), oldCaseDetails.getCaseDisplayName(), caseDetails.getCaseDisplayName()));
1639  }
1640  eventPublisher.publish(new AutopsyEvent(Events.CASE_DETAILS.toString(), oldCaseDetails, caseDetails));
1641  if (RuntimeProperties.runningWithGUI()) {
1642  SwingUtilities.invokeLater(() -> {
1643  mainFrame.setTitle(caseDetails.getCaseDisplayName() + " - " + getNameForTitle());
1644  try {
1645  RecentCases.getInstance().updateRecentCase(oldCaseDetails.getCaseDisplayName(), metadata.getFilePath().toString(), caseDetails.getCaseDisplayName(), metadata.getFilePath().toString());
1646  } catch (Exception ex) {
1647  logger.log(Level.SEVERE, "Error updating case name in UI", ex); //NON-NLS
1648  }
1649  });
1650  }
1651  }
1652 
1665  private Case(CaseType caseType, String caseDir, CaseDetails caseDetails) {
1666  metadata = new CaseMetadata(caseType, caseDir, displayNameToUniqueName(caseDetails.getCaseDisplayName()), caseDetails);
1667  }
1668 
1674  private Case(CaseMetadata caseMetaData) {
1675  metadata = caseMetaData;
1676  }
1677 
1693  @Messages({
1694  "Case.progressIndicatorTitle.creatingCase=Creating Case",
1695  "Case.progressIndicatorTitle.openingCase=Opening Case",
1696  "Case.progressIndicatorCancelButton.label=Cancel",
1697  "Case.progressMessage.preparing=Preparing...",
1698  "Case.progressMessage.preparingToOpenCaseResources=<html>Preparing to open case resources.<br>This may take time if another user is upgrading the case.</html>",
1699  "Case.progressMessage.cancelling=Cancelling...",
1700  "Case.exceptionMessage.cancelledByUser=Cancelled by user.",
1701  "# {0} - exception message", "Case.exceptionMessage.execExceptionWrapperMessage={0}"
1702  })
1703  private void open(boolean isNewCase) throws CaseActionException {
1704  /*
1705  * Create and start either a GUI progress indicator with a Cancel button
1706  * or a logging progress indicator.
1707  */
1708  CancelButtonListener cancelButtonListener = null;
1709  ProgressIndicator progressIndicator;
1711  cancelButtonListener = new CancelButtonListener(Bundle.Case_progressMessage_cancelling());
1712  String progressIndicatorTitle = isNewCase ? Bundle.Case_progressIndicatorTitle_creatingCase() : Bundle.Case_progressIndicatorTitle_openingCase();
1713  progressIndicator = new ModalDialogProgressIndicator(
1714  mainFrame,
1715  progressIndicatorTitle,
1716  new String[]{Bundle.Case_progressIndicatorCancelButton_label()},
1717  Bundle.Case_progressIndicatorCancelButton_label(),
1718  cancelButtonListener);
1719  } else {
1720  progressIndicator = new LoggingProgressIndicator();
1721  }
1722  progressIndicator.start(Bundle.Case_progressMessage_preparing());
1723 
1724  /*
1725  * Creating/opening a case is always done by creating a task running in
1726  * the same non-UI thread that will be used to close the case, so a
1727  * single-threaded executor service is created here and saved as case
1728  * state (must be volatile for cancellation to work).
1729  *
1730  * --- If the case is a single-user case, this supports cancelling
1731  * opening of the case by cancelling the task.
1732  *
1733  * --- If the case is a multi-user case, this still supports
1734  * cancellation, but it also makes it possible for the shared case
1735  * directory lock held as long as the case is open to be released in the
1736  * same thread in which it was acquired, as is required by the
1737  * coordination service.
1738  */
1739  TaskThreadFactory threadFactory = new TaskThreadFactory(String.format(CASE_ACTION_THREAD_NAME, metadata.getCaseName()));
1740  caseLockingExecutor = Executors.newSingleThreadExecutor(threadFactory);
1741  Future<Void> future = caseLockingExecutor.submit(() -> {
1742  if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) {
1743  open(isNewCase, progressIndicator);
1744  } else {
1745  /*
1746  * First, acquire a shared case directory lock that will be held
1747  * as long as this node has this case open. This will prevent
1748  * deletion of the case by another node. Next, acquire an
1749  * exclusive case resources lock to ensure only one node at a
1750  * time can create/open/upgrade/close the case resources.
1751  */
1752  progressIndicator.progress(Bundle.Case_progressMessage_preparingToOpenCaseResources());
1755  if (null == resourcesLock) {
1756  throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireResourcesLock());
1757  }
1758  open(isNewCase, progressIndicator);
1759  } catch (CaseActionException ex) {
1760  releaseSharedCaseDirLock(getMetadata().getCaseDirectory());
1761  throw ex;
1762  }
1763  }
1764  return null;
1765  });
1766  if (null != cancelButtonListener) {
1767  cancelButtonListener.setCaseActionFuture(future);
1768  }
1769 
1770  /*
1771  * Wait for the case creation/opening task to finish.
1772  */
1773  try {
1774  future.get();
1775  } catch (InterruptedException discarded) {
1776  /*
1777  * The thread this method is running in has been interrupted. Cancel
1778  * the create/open task, wait for it to finish, and shut down the
1779  * executor. This can be done safely because if the task is
1780  * completed with a cancellation condition, the case will have been
1781  * closed and the case directory lock released will have been
1782  * released.
1783  */
1784  if (null != cancelButtonListener) {
1785  cancelButtonListener.actionPerformed(null);
1786  } else {
1787  future.cancel(true);
1788  }
1789  ThreadUtils.shutDownTaskExecutor(caseLockingExecutor);
1790  } catch (CancellationException discarded) {
1791  /*
1792  * The create/open task has been cancelled. Wait for it to finish,
1793  * and shut down the executor. This can be done safely because if
1794  * the task is completed with a cancellation condition, the case
1795  * will have been closed and the case directory lock released will
1796  * have been released.
1797  */
1798  ThreadUtils.shutDownTaskExecutor(caseLockingExecutor);
1799  throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser());
1800  } catch (ExecutionException ex) {
1801  /*
1802  * The create/open task has thrown an exception. Wait for it to
1803  * finish, and shut down the executor. This can be done safely
1804  * because if the task is completed with an execution condition, the
1805  * case will have been closed and the case directory lock released
1806  * will have been released.
1807  */
1808  ThreadUtils.shutDownTaskExecutor(caseLockingExecutor);
1809  throw new CaseActionException(Bundle.Case_exceptionMessage_execExceptionWrapperMessage(ex.getCause().getLocalizedMessage()), ex);
1810  } finally {
1811  progressIndicator.finish();
1812  }
1813  }
1814 
1826  private void open(boolean isNewCase, ProgressIndicator progressIndicator) throws CaseActionException {
1827  try {
1829  createCaseDirectoryIfDoesNotExist(progressIndicator);
1831  switchLoggingToCaseLogsDirectory(progressIndicator);
1833  if (isNewCase) {
1834  saveCaseMetadataToFile(progressIndicator);
1835  }
1837  if (isNewCase) {
1838  createCaseNodeData(progressIndicator);
1839  } else {
1840  updateCaseNodeData(progressIndicator);
1841  }
1843  if (!isNewCase) {
1844  deleteTempfilesFromCaseDirectory(progressIndicator);
1845  }
1847  if (isNewCase) {
1848  createCaseDatabase(progressIndicator);
1849  } else {
1850  openCaseDataBase(progressIndicator);
1851  }
1853  openCaseLevelServices(progressIndicator);
1855  openAppServiceCaseResources(progressIndicator);
1857  openCommunicationChannels(progressIndicator);
1858 
1859  } catch (CaseActionException ex) {
1860  /*
1861  * Cancellation or failure. Clean up by calling the close method.
1862  * The sleep is a little hack to clear the interrupted flag for this
1863  * thread if this is a cancellation scenario, so that the clean up
1864  * can run to completion in the current thread.
1865  */
1866  try {
1867  Thread.sleep(1);
1868  } catch (InterruptedException discarded) {
1869  }
1870  close(progressIndicator);
1871  throw ex;
1872  }
1873  }
1874 
1885  public SleuthkitCase createPortableCase(String caseName, File portableCaseFolder) throws TskCoreException {
1886 
1887  if (portableCaseFolder.exists()) {
1888  throw new TskCoreException("Portable case folder " + portableCaseFolder.toString() + " already exists");
1889  }
1890  if (!portableCaseFolder.mkdirs()) {
1891  throw new TskCoreException("Error creating portable case folder " + portableCaseFolder.toString());
1892  }
1893 
1894  CaseDetails details = new CaseDetails(caseName, getNumber(), getExaminer(),
1896  try {
1897  CaseMetadata portableCaseMetadata = new CaseMetadata(Case.CaseType.SINGLE_USER_CASE, portableCaseFolder.toString(),
1898  caseName, details, metadata);
1899  portableCaseMetadata.setCaseDatabaseName(SINGLE_USER_CASE_DB_NAME);
1900  } catch (CaseMetadataException ex) {
1901  throw new TskCoreException("Error creating case metadata", ex);
1902  }
1903 
1904  // Create the Sleuthkit case
1905  SleuthkitCase portableSleuthkitCase;
1906  String dbFilePath = Paths.get(portableCaseFolder.toString(), SINGLE_USER_CASE_DB_NAME).toString();
1907  portableSleuthkitCase = SleuthkitCase.newCase(dbFilePath);
1908 
1909  return portableSleuthkitCase;
1910  }
1911 
1922  if (Thread.currentThread().isInterrupted()) {
1923  throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser());
1924  }
1925  }
1926 
1940  @Messages({
1941  "Case.progressMessage.creatingCaseDirectory=Creating case directory..."
1942  })
1943  private void createCaseDirectoryIfDoesNotExist(ProgressIndicator progressIndicator) throws CaseActionException {
1944  progressIndicator.progress(Bundle.Case_progressMessage_creatingCaseDirectory());
1945  if (new File(metadata.getCaseDirectory()).exists() == false) {
1946  progressIndicator.progress(Bundle.Case_progressMessage_creatingCaseDirectory());
1947  Case.createCaseDirectory(metadata.getCaseDirectory(), metadata.getCaseType());
1948  }
1949  }
1950 
1957  @Messages({
1958  "Case.progressMessage.switchingLogDirectory=Switching log directory..."
1959  })
1960  private void switchLoggingToCaseLogsDirectory(ProgressIndicator progressIndicator) {
1961  progressIndicator.progress(Bundle.Case_progressMessage_switchingLogDirectory());
1963  }
1964 
1976  @Messages({
1977  "Case.progressMessage.savingCaseMetadata=Saving case metadata to file...",
1978  "# {0} - exception message", "Case.exceptionMessage.couldNotSaveCaseMetadata=Failed to save case metadata:\n{0}."
1979  })
1980  private void saveCaseMetadataToFile(ProgressIndicator progressIndicator) throws CaseActionException {
1981  progressIndicator.progress(Bundle.Case_progressMessage_savingCaseMetadata());
1982  try {
1983  this.metadata.writeToFile();
1984  } catch (CaseMetadataException ex) {
1985  throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotSaveCaseMetadata(ex.getLocalizedMessage()), ex);
1986  }
1987  }
1988 
2000  @Messages({
2001  "Case.progressMessage.creatingCaseNodeData=Creating coordination service node data...",
2002  "# {0} - exception message", "Case.exceptionMessage.couldNotCreateCaseNodeData=Failed to create coordination service node data:\n{0}."
2003  })
2004  private void createCaseNodeData(ProgressIndicator progressIndicator) throws CaseActionException {
2006  progressIndicator.progress(Bundle.Case_progressMessage_creatingCaseNodeData());
2007  try {
2008  CaseNodeData.createCaseNodeData(metadata);
2009  } catch (CaseNodeDataException | InterruptedException ex) {
2010  throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotCreateCaseNodeData(ex.getLocalizedMessage()), ex);
2011  }
2012  }
2013  }
2014 
2026  @Messages({
2027  "Case.progressMessage.updatingCaseNodeData=Updating coordination service node data...",
2028  "# {0} - exception message", "Case.exceptionMessage.couldNotUpdateCaseNodeData=Failed to update coordination service node data:\n{0}."
2029  })
2030  private void updateCaseNodeData(ProgressIndicator progressIndicator) throws CaseActionException {
2032  progressIndicator.progress(Bundle.Case_progressMessage_updatingCaseNodeData());
2033  try {
2035  nodeData.setLastAccessDate(new Date());
2036  CaseNodeData.writeCaseNodeData(nodeData);
2037  } catch (CaseNodeDataException | InterruptedException ex) {
2038  throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotUpdateCaseNodeData(ex.getLocalizedMessage()), ex);
2039  }
2040  }
2041  }
2042 
2048  @Messages({
2049  "Case.progressMessage.clearingTempDirectory=Clearing case temp directory..."
2050  })
2051  private void deleteTempfilesFromCaseDirectory(ProgressIndicator progressIndicator) {
2052  /*
2053  * Clear the temp subdirectory of the case directory.
2054  */
2055  progressIndicator.progress(Bundle.Case_progressMessage_clearingTempDirectory());
2057  }
2058 
2070  @Messages({
2071  "Case.progressMessage.creatingCaseDatabase=Creating case database...",
2072  "# {0} - exception message", "Case.exceptionMessage.couldNotGetDbServerConnectionInfo=Failed to get case database server conneciton info:\n{0}.",
2073  "# {0} - exception message", "Case.exceptionMessage.couldNotCreateCaseDatabase=Failed to create case database:\n{0}.",
2074  "# {0} - exception message", "Case.exceptionMessage.couldNotSaveDbNameToMetadataFile=Failed to save case database name to case metadata file:\n{0}."
2075  })
2076  private void createCaseDatabase(ProgressIndicator progressIndicator) throws CaseActionException {
2077  progressIndicator.progress(Bundle.Case_progressMessage_creatingCaseDatabase());
2078  try {
2079  if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) {
2080  /*
2081  * For single-user cases, the case database is a SQLite database
2082  * with a standard name, physically located in the case
2083  * directory.
2084  */
2085  caseDb = SleuthkitCase.newCase(Paths.get(metadata.getCaseDirectory(), SINGLE_USER_CASE_DB_NAME).toString());
2086  metadata.setCaseDatabaseName(SINGLE_USER_CASE_DB_NAME);
2087  } else {
2088  /*
2089  * For multi-user cases, the case database is a PostgreSQL
2090  * database with a name derived from the case display name,
2091  * physically located on the PostgreSQL database server.
2092  */
2093  caseDb = SleuthkitCase.newCase(metadata.getCaseDisplayName(), UserPreferences.getDatabaseConnectionInfo(), metadata.getCaseDirectory());
2094  metadata.setCaseDatabaseName(caseDb.getDatabaseName());
2095  }
2096  } catch (TskCoreException ex) {
2097  throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotCreateCaseDatabase(ex.getLocalizedMessage()), ex);
2098  } catch (UserPreferencesException ex) {
2099  throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotGetDbServerConnectionInfo(ex.getLocalizedMessage()), ex);
2100  } catch (CaseMetadataException ex) {
2101  throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotSaveDbNameToMetadataFile(ex.getLocalizedMessage()), ex);
2102  }
2103  }
2104 
2116  @Messages({
2117  "Case.progressMessage.openingCaseDatabase=Opening case database...",
2118  "# {0} - exception message", "Case.exceptionMessage.couldNotOpenCaseDatabase=Failed to open case database:\n{0}.",
2119  "# {0} - exception message", "Case.exceptionMessage.unsupportedSchemaVersionMessage=Unsupported case database schema version:\n{0}.",
2120  "Case.open.exception.multiUserCaseNotEnabled=Cannot open a multi-user case if multi-user cases are not enabled. See Tools, Options, Multi-User."
2121  })
2122  private void openCaseDataBase(ProgressIndicator progressIndicator) throws CaseActionException {
2123  progressIndicator.progress(Bundle.Case_progressMessage_openingCaseDatabase());
2124  try {
2125  String databaseName = metadata.getCaseDatabaseName();
2126  if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) {
2127  caseDb = SleuthkitCase.openCase(Paths.get(metadata.getCaseDirectory(), databaseName).toString());
2129  caseDb = SleuthkitCase.openCase(databaseName, UserPreferences.getDatabaseConnectionInfo(), metadata.getCaseDirectory());
2130  } else {
2131  throw new CaseActionException(Case_open_exception_multiUserCaseNotEnabled());
2132  }
2133  } catch (TskUnsupportedSchemaVersionException ex) {
2134  throw new CaseActionException(Bundle.Case_exceptionMessage_unsupportedSchemaVersionMessage(ex.getLocalizedMessage()), ex);
2135  } catch (UserPreferencesException ex) {
2136  throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotGetDbServerConnectionInfo(ex.getLocalizedMessage()), ex);
2137  } catch (TskCoreException ex) {
2138  throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotOpenCaseDatabase(ex.getLocalizedMessage()), ex);
2139  }
2140  }
2141 
2148  @Messages({
2149  "Case.progressMessage.openingCaseLevelServices=Opening case-level services...",})
2150  private void openCaseLevelServices(ProgressIndicator progressIndicator) {
2151  progressIndicator.progress(Bundle.Case_progressMessage_openingCaseLevelServices());
2152  this.caseServices = new Services(caseDb);
2153  }
2154 
2166  @NbBundle.Messages({
2167  "Case.progressMessage.openingApplicationServiceResources=Opening application service case resources...",
2168  "# {0} - service name", "Case.serviceOpenCaseResourcesProgressIndicator.title={0} Opening Case Resources",
2169  "# {0} - service name", "Case.serviceOpenCaseResourcesProgressIndicator.cancellingMessage=Cancelling opening case resources by {0}...",
2170  "# {0} - service name", "Case.servicesException.notificationTitle={0} Error"
2171  })
2172  private void openAppServiceCaseResources(ProgressIndicator progressIndicator) throws CaseActionException {
2173  /*
2174  * Each service gets its own independently cancellable/interruptible
2175  * task, running in a named thread managed by an executor service, with
2176  * its own progress indicator. This allows for cancellation of the
2177  * opening of case resources for individual services. It also makes it
2178  * possible to ensure that each service task completes before the next
2179  * one starts by awaiting termination of the executor service.
2180  */
2181  progressIndicator.progress(Bundle.Case_progressMessage_openingApplicationServiceResources());
2182  for (AutopsyService service : Lookup.getDefault().lookupAll(AutopsyService.class)) {
2183  /*
2184  * Create a progress indicator for the task and start the task. If
2185  * running with a GUI, the progress indicator will be a dialog box
2186  * with a Cancel button.
2187  */
2188  CancelButtonListener cancelButtonListener = null;
2189  ProgressIndicator appServiceProgressIndicator;
2191  cancelButtonListener = new CancelButtonListener(Bundle.Case_serviceOpenCaseResourcesProgressIndicator_cancellingMessage(service.getServiceName()));
2192  appServiceProgressIndicator = new ModalDialogProgressIndicator(
2193  mainFrame,
2194  Bundle.Case_serviceOpenCaseResourcesProgressIndicator_title(service.getServiceName()),
2195  new String[]{Bundle.Case_progressIndicatorCancelButton_label()},
2196  Bundle.Case_progressIndicatorCancelButton_label(),
2197  cancelButtonListener);
2198  } else {
2199  appServiceProgressIndicator = new LoggingProgressIndicator();
2200  }
2201  appServiceProgressIndicator.start(Bundle.Case_progressMessage_preparing());
2202  AutopsyService.CaseContext context = new AutopsyService.CaseContext(this, appServiceProgressIndicator);
2203  String threadNameSuffix = service.getServiceName().replaceAll("[ ]", "-"); //NON-NLS
2204  threadNameSuffix = threadNameSuffix.toLowerCase();
2205  TaskThreadFactory threadFactory = new TaskThreadFactory(String.format(CASE_RESOURCES_THREAD_NAME, threadNameSuffix));
2206  ExecutorService executor = Executors.newSingleThreadExecutor(threadFactory);
2207  Future<Void> future = executor.submit(() -> {
2208  service.openCaseResources(context);
2209  return null;
2210  });
2211  if (null != cancelButtonListener) {
2212  cancelButtonListener.setCaseContext(context);
2213  cancelButtonListener.setCaseActionFuture(future);
2214  }
2215 
2216  /*
2217  * Wait for the task to either be completed or
2218  * cancelled/interrupted, or for the opening of the case to be
2219  * cancelled.
2220  */
2221  try {
2222  future.get();
2223  } catch (InterruptedException discarded) {
2224  /*
2225  * The parent create/open case task has been cancelled.
2226  */
2227  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()));
2228  future.cancel(true);
2229  } catch (CancellationException discarded) {
2230  /*
2231  * The opening of case resources by the application service has
2232  * been cancelled, so the executor service has thrown. Note that
2233  * there is no guarantee the task itself has responded to the
2234  * cancellation request yet.
2235  */
2236  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()));
2237  } catch (ExecutionException ex) {
2238  /*
2239  * An exception was thrown while executing the task. The
2240  * case-specific application service resources are not
2241  * essential. Log an error and notify the user if running the
2242  * desktop GUI, but do not throw.
2243  */
2244  Case.logger.log(Level.SEVERE, String.format("%s failed to open case resources for %s", service.getServiceName(), this.getDisplayName()), ex);
2246  SwingUtilities.invokeLater(() -> {
2247  MessageNotifyUtil.Notify.error(Bundle.Case_servicesException_notificationTitle(service.getServiceName()), ex.getLocalizedMessage());
2248  });
2249  }
2250  } finally {
2251  /*
2252  * Shut down the executor service and wait for it to finish.
2253  * This ensures that the task has finished. Without this, it
2254  * would be possible to start the next task before the current
2255  * task responded to a cancellation request.
2256  */
2258  appServiceProgressIndicator.finish();
2259  }
2261  }
2262  }
2263 
2275  @Messages({
2276  "Case.progressMessage.settingUpNetworkCommunications=Setting up network communications...",
2277  "# {0} - exception message", "Case.exceptionMessage.couldNotOpenRemoteEventChannel=Failed to open remote events channel:\n{0}.",
2278  "# {0} - exception message", "Case.exceptionMessage.couldNotCreatCollaborationMonitor=Failed to create collaboration monitor:\n{0}."
2279  })
2280  private void openCommunicationChannels(ProgressIndicator progressIndicator) throws CaseActionException {
2281  if (CaseType.MULTI_USER_CASE == metadata.getCaseType()) {
2282  progressIndicator.progress(Bundle.Case_progressMessage_settingUpNetworkCommunications());
2283  try {
2284  eventPublisher.openRemoteEventChannel(String.format(EVENT_CHANNEL_NAME, metadata.getCaseName()));
2286  collaborationMonitor = new CollaborationMonitor(metadata.getCaseName());
2287  } catch (AutopsyEventException ex) {
2288  throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotOpenRemoteEventChannel(ex.getLocalizedMessage()), ex);
2289  } catch (CollaborationMonitor.CollaborationMonitorException ex) {
2290  throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotCreatCollaborationMonitor(ex.getLocalizedMessage()), ex);
2291  }
2292  }
2293  }
2294 
2303  private void close() throws CaseActionException {
2304  /*
2305  * Set up either a GUI progress indicator without a Cancel button or a
2306  * logging progress indicator.
2307  */
2308  ProgressIndicator progressIndicator;
2310  progressIndicator = new ModalDialogProgressIndicator(
2311  mainFrame,
2312  Bundle.Case_progressIndicatorTitle_closingCase());
2313  } else {
2314  progressIndicator = new LoggingProgressIndicator();
2315  }
2316  progressIndicator.start(Bundle.Case_progressMessage_preparing());
2317 
2318  /*
2319  * Closing a case is always done in the same non-UI thread that
2320  * opened/created the case. If the case is a multi-user case, this
2321  * ensures that case directory lock that is held as long as the case is
2322  * open is released in the same thread in which it was acquired, as is
2323  * required by the coordination service.
2324  */
2325  Future<Void> future = caseLockingExecutor.submit(() -> {
2326  if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) {
2327  close(progressIndicator);
2328  } else {
2329  /*
2330  * Acquire an exclusive case resources lock to ensure only one
2331  * node at a time can create/open/upgrade/close the case
2332  * resources.
2333  */
2334  progressIndicator.progress(Bundle.Case_progressMessage_preparing());
2336  if (null == resourcesLock) {
2337  throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireResourcesLock());
2338  }
2339  close(progressIndicator);
2340  } finally {
2341  /*
2342  * Always release the case directory lock that was acquired
2343  * when the case was opened.
2344  */
2346  }
2347  }
2348  return null;
2349  });
2350 
2351  try {
2352  future.get();
2353  } catch (InterruptedException | CancellationException unused) {
2354  /*
2355  * The wait has been interrupted by interrupting the thread running
2356  * this method. Not allowing cancellation of case closing, so ignore
2357  * the interrupt. Likewsie, cancellation of the case closing task is
2358  * not supported.
2359  */
2360  } catch (ExecutionException ex) {
2361  throw new CaseActionException(Bundle.Case_exceptionMessage_execExceptionWrapperMessage(ex.getCause().getMessage()), ex);
2362  } finally {
2363  ThreadUtils.shutDownTaskExecutor(caseLockingExecutor);
2364  progressIndicator.finish();
2365  }
2366  }
2367 
2373  @Messages({
2374  "Case.progressMessage.shuttingDownNetworkCommunications=Shutting down network communications...",
2375  "Case.progressMessage.closingApplicationServiceResources=Closing case-specific application service resources...",
2376  "Case.progressMessage.closingCaseLevelServices=Closing case-level services...",
2377  "Case.progressMessage.closingCaseDatabase=Closing case database..."
2378  })
2379  private void close(ProgressIndicator progressIndicator) {
2381 
2382  /*
2383  * Stop sending/receiving case events to and from other nodes if this is
2384  * a multi-user case.
2385  */
2386  if (CaseType.MULTI_USER_CASE == metadata.getCaseType()) {
2387  progressIndicator.progress(Bundle.Case_progressMessage_shuttingDownNetworkCommunications());
2388  if (null != collaborationMonitor) {
2389  collaborationMonitor.shutdown();
2390  }
2391  eventPublisher.closeRemoteEventChannel();
2392  }
2393 
2394  /*
2395  * Allow all registered application services providers to close
2396  * resources related to the case.
2397  */
2398  progressIndicator.progress(Bundle.Case_progressMessage_closingApplicationServiceResources());
2400 
2401  /*
2402  * Close the case-level services.
2403  */
2404  if (null != caseServices) {
2405  progressIndicator.progress(Bundle.Case_progressMessage_closingCaseLevelServices());
2406  try {
2407  this.caseServices.close();
2408  } catch (IOException ex) {
2409  logger.log(Level.SEVERE, String.format("Error closing internal case services for %s at %s", this.getName(), this.getCaseDirectory()), ex);
2410  }
2411  }
2412 
2413  /*
2414  * Close the case database
2415  */
2416  if (null != caseDb) {
2417  progressIndicator.progress(Bundle.Case_progressMessage_closingCaseDatabase());
2418  caseDb.close();
2419  }
2420 
2421  /*
2422  * Switch the log directory.
2423  */
2424  progressIndicator.progress(Bundle.Case_progressMessage_switchingLogDirectory());
2426  }
2427 
2432  @Messages({
2433  "# {0} - serviceName", "Case.serviceCloseResourcesProgressIndicator.title={0} Closing Case Resources",
2434  "# {0} - service name", "# {1} - exception message", "Case.servicesException.serviceResourcesCloseError=Could not close case resources for {0} service: {1}"
2435  })
2437  /*
2438  * Each service gets its own independently cancellable task, and thus
2439  * its own task progress indicator.
2440  */
2441  for (AutopsyService service : Lookup.getDefault().lookupAll(AutopsyService.class)) {
2442  ProgressIndicator progressIndicator;
2444  progressIndicator = new ModalDialogProgressIndicator(
2445  mainFrame,
2446  Bundle.Case_serviceCloseResourcesProgressIndicator_title(service.getServiceName()));
2447  } else {
2448  progressIndicator = new LoggingProgressIndicator();
2449  }
2450  progressIndicator.start(Bundle.Case_progressMessage_preparing());
2451  AutopsyService.CaseContext context = new AutopsyService.CaseContext(this, progressIndicator);
2452  String threadNameSuffix = service.getServiceName().replaceAll("[ ]", "-"); //NON-NLS
2453  threadNameSuffix = threadNameSuffix.toLowerCase();
2454  TaskThreadFactory threadFactory = new TaskThreadFactory(String.format(CASE_RESOURCES_THREAD_NAME, threadNameSuffix));
2455  ExecutorService executor = Executors.newSingleThreadExecutor(threadFactory);
2456  Future<Void> future = executor.submit(() -> {
2457  service.closeCaseResources(context);
2458  return null;
2459  });
2460  try {
2461  future.get();
2462  } catch (InterruptedException ex) {
2463  Case.logger.log(Level.SEVERE, String.format("Unexpected interrupt while waiting on %s service to close case resources", service.getServiceName()), ex);
2464  } catch (CancellationException ex) {
2465  Case.logger.log(Level.SEVERE, String.format("Unexpected cancellation while waiting on %s service to close case resources", service.getServiceName()), ex);
2466  } catch (ExecutionException ex) {
2467  Case.logger.log(Level.SEVERE, String.format("%s service failed to open case resources", service.getServiceName()), ex);
2469  SwingUtilities.invokeLater(() -> MessageNotifyUtil.Notify.error(
2470  Bundle.Case_servicesException_notificationTitle(service.getServiceName()),
2471  Bundle.Case_servicesException_serviceResourcesCloseError(service.getServiceName(), ex.getLocalizedMessage())));
2472  }
2473  } finally {
2475  progressIndicator.finish();
2476  }
2477  }
2478  }
2479 
2488  @Messages({"Case.creationException.couldNotAcquireDirLock=Failed to get lock on case directory"})
2489  private void acquireSharedCaseDirLock(String caseDir) throws CaseActionException {
2490  try {
2491  caseDirLock = CoordinationService.getInstance().tryGetSharedLock(CategoryNode.CASES, caseDir, DIR_LOCK_TIMOUT_HOURS, TimeUnit.HOURS);
2492  if (null == caseDirLock) {
2493  throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireDirLock());
2494  }
2495  } catch (InterruptedException | CoordinationServiceException ex) {
2496  throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireDirLock(), ex);
2497  }
2498  }
2499 
2505  private void releaseSharedCaseDirLock(String caseDir) {
2506  if (caseDirLock != null) {
2507  try {
2508  caseDirLock.release();
2509  caseDirLock = null;
2511  logger.log(Level.SEVERE, String.format("Failed to release shared case directory lock for %s", caseDir), ex);
2512  }
2513  }
2514  }
2515 
2522  private String getOrCreateSubdirectory(String subDirectoryName) {
2523  File subDirectory = Paths.get(getOutputDirectory(), subDirectoryName).toFile();
2524  if (!subDirectory.exists()) {
2525  subDirectory.mkdirs();
2526  }
2527  return subDirectory.toString();
2528 
2529  }
2530 
2542  @Messages({
2543  "Case.exceptionMessage.errorsDeletingCase=Errors occured while deleting the case. See the application log for details."
2544  })
2545  private static void deleteSingleUserCase(CaseMetadata metadata, ProgressIndicator progressIndicator) throws CaseActionException {
2546  boolean errorsOccurred = false;
2547  try {
2548  deleteTextIndex(metadata, progressIndicator);
2549  } catch (KeywordSearchServiceException ex) {
2550  errorsOccurred = true;
2551  logger.log(Level.WARNING, String.format("Failed to delete text index for %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex); //NON-NLS
2552  }
2553 
2554  try {
2555  deleteCaseDirectory(metadata, progressIndicator);
2556  } catch (CaseActionException ex) {
2557  errorsOccurred = true;
2558  logger.log(Level.WARNING, String.format("Failed to delete case directory for %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex); //NON-NLS
2559  }
2560 
2561  deleteFromRecentCases(metadata, progressIndicator);
2562 
2563  if (errorsOccurred) {
2564  throw new CaseActionException(Bundle.Case_exceptionMessage_errorsDeletingCase());
2565  }
2566  }
2567 
2587  @Messages({
2588  "Case.progressMessage.connectingToCoordSvc=Connecting to coordination service...",
2589  "# {0} - exception message", "Case.exceptionMessage.failedToConnectToCoordSvc=Failed to connect to coordination service:\n{0}.",
2590  "Case.exceptionMessage.cannotGetLockToDeleteCase=Cannot delete case because it is open for another user or host.",
2591  "# {0} - exception message", "Case.exceptionMessage.failedToLockCaseForDeletion=Failed to exclusively lock case for deletion:\n{0}.",
2592  "Case.progressMessage.fetchingCoordSvcNodeData=Fetching coordination service node data for the case...",
2593  "# {0} - exception message", "Case.exceptionMessage.failedToFetchCoordSvcNodeData=Failed to fetch coordination service node data:\n{0}.",
2594  "Case.progressMessage.deletingResourcesCoordSvcNode=Deleting case resources coordination service node...",
2595  "Case.progressMessage.deletingCaseDirCoordSvcNode=Deleting case directory coordination service node..."
2596  })
2597  private static void deleteMultiUserCase(CaseMetadata metadata, ProgressIndicator progressIndicator) throws CaseActionException, InterruptedException {
2598  progressIndicator.progress(Bundle.Case_progressMessage_connectingToCoordSvc());
2599  CoordinationService coordinationService;
2600  try {
2601  coordinationService = CoordinationService.getInstance();
2602  } catch (CoordinationServiceException ex) {
2603  logger.log(Level.SEVERE, String.format("Failed to connect to coordination service when attempting to delete %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex); //NON-NLS
2604  throw new CaseActionException(Bundle.Case_exceptionMessage_failedToConnectToCoordSvc(ex.getLocalizedMessage()));
2605  }
2606 
2607  CaseNodeData caseNodeData;
2608  boolean errorsOccurred = false;
2609  try (CoordinationService.Lock dirLock = coordinationService.tryGetExclusiveLock(CategoryNode.CASES, metadata.getCaseDirectory())) {
2610  if (dirLock == null) {
2611  logger.log(Level.INFO, String.format("Could not delete %s (%s) in %s because a case directory lock was held by another host", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory())); //NON-NLS
2612  throw new CaseActionException(Bundle.Case_exceptionMessage_cannotGetLockToDeleteCase());
2613  }
2614 
2615  progressIndicator.progress(Bundle.Case_progressMessage_fetchingCoordSvcNodeData());
2616  try {
2617  caseNodeData = CaseNodeData.readCaseNodeData(metadata.getCaseDirectory());
2618  } catch (CaseNodeDataException | InterruptedException ex) {
2619  logger.log(Level.SEVERE, String.format("Failed to get coordination service node data %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex); //NON-NLS
2620  throw new CaseActionException(Bundle.Case_exceptionMessage_failedToFetchCoordSvcNodeData(ex.getLocalizedMessage()));
2621  }
2622 
2623  errorsOccurred = deleteMultiUserCase(caseNodeData, metadata, progressIndicator, logger);
2624 
2625  progressIndicator.progress(Bundle.Case_progressMessage_deletingResourcesCoordSvcNode());
2626  try {
2627  String resourcesLockNodePath = CoordinationServiceUtils.getCaseResourcesNodePath(caseNodeData.getDirectory());
2628  coordinationService.deleteNode(CategoryNode.CASES, resourcesLockNodePath);
2629  } catch (CoordinationServiceException ex) {
2630  if (!isNoNodeException(ex)) {
2631  errorsOccurred = true;
2632  logger.log(Level.WARNING, String.format("Error deleting the case resources coordination service node for the case at %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex); //NON-NLS
2633  }
2634  } catch (InterruptedException ex) {
2635  logger.log(Level.WARNING, String.format("Error deleting the case resources coordination service node for the case at %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex); //NON-NLS
2636  }
2637 
2638  } catch (CoordinationServiceException ex) {
2639  logger.log(Level.SEVERE, String.format("Error exclusively locking the case directory for %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex); //NON-NLS
2640  throw new CaseActionException(Bundle.Case_exceptionMessage_failedToLockCaseForDeletion(ex.getLocalizedMessage()));
2641  }
2642 
2643  if (!errorsOccurred) {
2644  progressIndicator.progress(Bundle.Case_progressMessage_deletingCaseDirCoordSvcNode());
2645  try {
2646  String casDirNodePath = CoordinationServiceUtils.getCaseDirectoryNodePath(caseNodeData.getDirectory());
2647  coordinationService.deleteNode(CategoryNode.CASES, casDirNodePath);
2648  } catch (CoordinationServiceException | InterruptedException ex) {
2649  logger.log(Level.SEVERE, String.format("Error deleting the case directory lock node for %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex); //NON-NLS
2650  errorsOccurred = true;
2651  }
2652  }
2653 
2654  if (errorsOccurred) {
2655  throw new CaseActionException(Bundle.Case_exceptionMessage_errorsDeletingCase());
2656  }
2657  }
2658 
2683  @Beta
2684  public static boolean deleteMultiUserCase(CaseNodeData caseNodeData, CaseMetadata metadata, ProgressIndicator progressIndicator, Logger logger) throws InterruptedException {
2685  boolean errorsOccurred = false;
2686  try {
2687  deleteMultiUserCaseDatabase(caseNodeData, metadata, progressIndicator, logger);
2688  deleteMultiUserCaseTextIndex(caseNodeData, metadata, progressIndicator, logger);
2689  deleteMultiUserCaseDirectory(caseNodeData, metadata, progressIndicator, logger);
2690  deleteFromRecentCases(metadata, progressIndicator);
2691  } catch (UserPreferencesException | ClassNotFoundException | SQLException ex) {
2692  errorsOccurred = true;
2693  logger.log(Level.WARNING, String.format("Failed to delete the case database for %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex); //NON-NLS
2694  } catch (KeywordSearchServiceException ex) {
2695  errorsOccurred = true;
2696  logger.log(Level.WARNING, String.format("Failed to delete the text index for %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex); //NON-NLS
2697  } catch (CaseActionException ex) {
2698  errorsOccurred = true;
2699  logger.log(Level.WARNING, String.format("Failed to delete the case directory for %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex); //NON-NLS
2700  }
2701  return errorsOccurred;
2702  }
2703 
2724  @Messages({
2725  "Case.progressMessage.deletingCaseDatabase=Deleting case database..."
2726  })
2727  private static void deleteMultiUserCaseDatabase(CaseNodeData caseNodeData, CaseMetadata metadata, ProgressIndicator progressIndicator, Logger logger) throws UserPreferencesException, ClassNotFoundException, SQLException, InterruptedException {
2728  if (!caseNodeData.isDeletedFlagSet(CaseNodeData.DeletedFlags.CASE_DB)) {
2729  progressIndicator.progress(Bundle.Case_progressMessage_deletingCaseDatabase());
2730  logger.log(Level.INFO, String.format("Deleting case database for %s (%s) in %s", caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory())); //NON-NLS
2731  CaseDbConnectionInfo info = UserPreferences.getDatabaseConnectionInfo();
2732  String url = "jdbc:postgresql://" + info.getHost() + ":" + info.getPort() + "/postgres"; //NON-NLS
2733  Class.forName("org.postgresql.Driver"); //NON-NLS
2734  try (Connection connection = DriverManager.getConnection(url, info.getUserName(), info.getPassword()); Statement statement = connection.createStatement()) {
2735  String dbExistsQuery = "SELECT 1 from pg_database WHERE datname = '" + metadata.getCaseDatabaseName() + "'"; //NON-NLS
2736  try (ResultSet queryResult = statement.executeQuery(dbExistsQuery)) {
2737  if (queryResult.next()) {
2738  String deleteCommand = "DROP DATABASE \"" + metadata.getCaseDatabaseName() + "\""; //NON-NLS
2739  statement.execute(deleteCommand);
2740  }
2741  }
2742  }
2744  }
2745  }
2746 
2762  private static void deleteMultiUserCaseTextIndex(CaseNodeData caseNodeData, CaseMetadata metadata, ProgressIndicator progressIndicator, Logger logger) throws KeywordSearchServiceException, InterruptedException {
2764  logger.log(Level.INFO, String.format("Deleting text index for %s", caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory())); //NON-NLS
2765  deleteTextIndex(metadata, progressIndicator);
2767  }
2768  }
2769 
2779  @Messages({
2780  "Case.progressMessage.deletingTextIndex=Deleting text index..."
2781  })
2782  private static void deleteTextIndex(CaseMetadata metadata, ProgressIndicator progressIndicator) throws KeywordSearchServiceException {
2783  progressIndicator.progress(Bundle.Case_progressMessage_deletingTextIndex());
2784  for (KeywordSearchService searchService : Lookup.getDefault().lookupAll(KeywordSearchService.class)) {
2785  searchService.deleteTextIndex(metadata);
2786  }
2787  }
2788 
2803  private static void deleteMultiUserCaseDirectory(CaseNodeData caseNodeData, CaseMetadata metadata, ProgressIndicator progressIndicator, Logger logger) throws CaseActionException, InterruptedException {
2804  if (!caseNodeData.isDeletedFlagSet(CaseNodeData.DeletedFlags.CASE_DIR)) {
2805  logger.log(Level.INFO, String.format("Deleting case directory for %s", caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory())); //NON-NLS
2806  deleteCaseDirectory(metadata, progressIndicator);
2808  }
2809  }
2810 
2820  @Messages({
2821  "Case.progressMessage.deletingCaseDirectory=Deleting case directory..."
2822  })
2823  private static void deleteCaseDirectory(CaseMetadata metadata, ProgressIndicator progressIndicator) throws CaseActionException {
2824  progressIndicator.progress(Bundle.Case_progressMessage_deletingCaseDirectory());
2825  if (!FileUtil.deleteDir(new File(metadata.getCaseDirectory()))) {
2826  throw new CaseActionException(String.format("Failed to delete %s", metadata.getCaseDirectory())); //NON-NLS
2827  }
2828  }
2829 
2837  @Messages({
2838  "Case.progressMessage.removingCaseFromRecentCases=Removing case from Recent Cases menu..."
2839  })
2840  private static void deleteFromRecentCases(CaseMetadata metadata, ProgressIndicator progressIndicator) {
2842  progressIndicator.progress(Bundle.Case_progressMessage_removingCaseFromRecentCases());
2843  SwingUtilities.invokeLater(() -> {
2844  RecentCases.getInstance().removeRecentCase(metadata.getCaseDisplayName(), metadata.getFilePath().toString());
2845  });
2846  }
2847  }
2848 
2859  boolean isNodeNodeEx = false;
2860  Throwable cause = ex.getCause();
2861  if (cause != null) {
2862  String causeMessage = cause.getMessage();
2863  isNodeNodeEx = causeMessage.contains(NO_NODE_ERROR_MSG_FRAGMENT);
2864  }
2865  return isNodeNodeEx;
2866  }
2867 
2879  private static void setDeletedItemFlag(CaseNodeData caseNodeData, CaseNodeData.DeletedFlags flag) throws InterruptedException {
2880  try {
2881  caseNodeData.setDeletedFlag(flag);
2882  CaseNodeData.writeCaseNodeData(caseNodeData);
2883  } catch (CaseNodeDataException ex) {
2884  logger.log(Level.SEVERE, String.format("Error updating deleted item flag %s for %s (%s) in %s", flag.name(), caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory()), ex);
2885  }
2886  }
2887 
2892  @ThreadSafe
2893  private final static class CancelButtonListener implements ActionListener {
2894 
2895  private final String cancellationMessage;
2896  @GuardedBy("this")
2897  private boolean cancelRequested;
2898  @GuardedBy("this")
2900  @GuardedBy("this")
2901  private Future<?> caseActionFuture;
2902 
2911  private CancelButtonListener(String cancellationMessage) {
2912  this.cancellationMessage = cancellationMessage;
2913  }
2914 
2920  private synchronized void setCaseContext(CaseContext caseContext) {
2921  this.caseContext = caseContext;
2922  /*
2923  * If the cancel button has already been pressed, pass the
2924  * cancellation on to the case context.
2925  */
2926  if (cancelRequested) {
2927  cancel();
2928  }
2929  }
2930 
2936  private synchronized void setCaseActionFuture(Future<?> caseActionFuture) {
2937  this.caseActionFuture = caseActionFuture;
2938  /*
2939  * If the cancel button has already been pressed, cancel the Future
2940  * of the task.
2941  */
2942  if (cancelRequested) {
2943  cancel();
2944  }
2945  }
2946 
2952  @Override
2953  public synchronized void actionPerformed(ActionEvent event) {
2954  cancel();
2955  }
2956 
2960  private void cancel() {
2961  /*
2962  * At a minimum, set the cancellation requested flag of this
2963  * listener.
2964  */
2965  this.cancelRequested = true;
2966  if (null != this.caseContext) {
2967  /*
2968  * Set the cancellation request flag and display the
2969  * cancellation message in the progress indicator for the case
2970  * context associated with this listener.
2971  */
2973  ProgressIndicator progressIndicator = this.caseContext.getProgressIndicator();
2974  if (progressIndicator instanceof ModalDialogProgressIndicator) {
2975  ((ModalDialogProgressIndicator) progressIndicator).setCancelling(cancellationMessage);
2976  }
2977  }
2978  this.caseContext.requestCancel();
2979  }
2980  if (null != this.caseActionFuture) {
2981  /*
2982  * Cancel the Future of the task associated with this listener.
2983  * Note that the task thread will be interrupted if the task is
2984  * blocked.
2985  */
2986  this.caseActionFuture.cancel(true);
2987  }
2988  }
2989  }
2990 
2994  private static class TaskThreadFactory implements ThreadFactory {
2995 
2996  private final String threadName;
2997 
2998  private TaskThreadFactory(String threadName) {
2999  this.threadName = threadName;
3000  }
3001 
3002  @Override
3003  public Thread newThread(Runnable task) {
3004  return new Thread(task, threadName);
3005  }
3006 
3007  }
3008 
3016  @Deprecated
3017  public static String getAppName() {
3018  return UserPreferences.getAppName();
3019  }
3020 
3040  @Deprecated
3041  public static void create(String caseDir, String caseDisplayName, String caseNumber, String examiner) throws CaseActionException {
3042  createAsCurrentCase(caseDir, caseDisplayName, caseNumber, examiner, CaseType.SINGLE_USER_CASE);
3043  }
3044 
3065  @Deprecated
3066  public static void create(String caseDir, String caseDisplayName, String caseNumber, String examiner, CaseType caseType) throws CaseActionException {
3067  createAsCurrentCase(caseDir, caseDisplayName, caseNumber, examiner, caseType);
3068  }
3069 
3081  @Deprecated
3082  public static void open(String caseMetadataFilePath) throws CaseActionException {
3083  openAsCurrentCase(caseMetadataFilePath);
3084  }
3085 
3095  @Deprecated
3096  public void closeCase() throws CaseActionException {
3097  closeCurrentCase();
3098  }
3099 
3105  @Deprecated
3106  public static void invokeStartupDialog() {
3108  }
3109 
3123  @Deprecated
3124  public static String convertTimeZone(String timeZoneId) {
3125  return TimeZoneUtils.convertToAlphaNumericFormat(timeZoneId);
3126  }
3127 
3137  @Deprecated
3138  public static boolean pathExists(String filePath) {
3139  return new File(filePath).isFile();
3140  }
3141 
3150  @Deprecated
3151  public static String getAutopsyVersion() {
3152  return Version.getVersion();
3153  }
3154 
3162  @Deprecated
3163  public static boolean existsCurrentCase() {
3164  return isCaseOpen();
3165  }
3166 
3176  @Deprecated
3177  public static String getModulesOutputDirRelPath() {
3178  return "ModuleOutput"; //NON-NLS
3179  }
3180 
3190  @Deprecated
3191  public static PropertyChangeSupport
3193  return new PropertyChangeSupport(Case.class
3194  );
3195  }
3196 
3205  @Deprecated
3206  public String getModulesOutputDirAbsPath() {
3207  return getModuleDirectory();
3208  }
3209 
3224  @Deprecated
3225  public Image addImage(String imgPath, long imgId, String timeZone) throws CaseActionException {
3226  try {
3227  Image newDataSource = caseDb.getImageById(imgId);
3228  notifyDataSourceAdded(newDataSource, UUID.randomUUID());
3229  return newDataSource;
3230  } catch (TskCoreException ex) {
3231  throw new CaseActionException(NbBundle.getMessage(this.getClass(), "Case.addImg.exception.msg"), ex);
3232  }
3233  }
3234 
3242  @Deprecated
3243  public Set<TimeZone> getTimeZone() {
3244  return getTimeZones();
3245  }
3246 
3257  @Deprecated
3258  public void deleteReports(Collection<? extends Report> reports, boolean deleteFromDisk) throws TskCoreException {
3259  deleteReports(reports);
3260  }
3261 
3262 }
static final AutopsyEventPublisher eventPublisher
Definition: Case.java:147
List< Content > getDataSources()
Definition: Case.java:1339
static CaseNodeData createCaseNodeData(final CaseMetadata metadata)
void notifyContentTagDeleted(ContentTag deletedTag)
Definition: Case.java:1467
Case(CaseMetadata caseMetaData)
Definition: Case.java:1674
static CaseType fromString(String typeName)
Definition: Case.java:190
static final String CASE_ACTION_THREAD_NAME
Definition: Case.java:143
static void setDeletedItemFlag(CaseNodeData caseNodeData, CaseNodeData.DeletedFlags flag)
Definition: Case.java:2879
static void createAsCurrentCase(CaseType caseType, String caseDir, CaseDetails caseDetails)
Definition: Case.java:558
void notifyBlackBoardArtifactTagDeleted(BlackboardArtifactTag deletedTag)
Definition: Case.java:1519
static boolean deleteMultiUserCase(CaseNodeData caseNodeData, CaseMetadata metadata, ProgressIndicator progressIndicator, Logger logger)
Definition: Case.java:2684
Image addImage(String imgPath, long imgId, String timeZone)
Definition: Case.java:3225
static synchronized IngestManager getInstance()
void deleteNode(CategoryNode category, String nodePath)
static final String NO_NODE_ERROR_MSG_FRAGMENT
Definition: Case.java:145
static CoordinationService.Lock acquireExclusiveCaseResourcesLock(String caseDir)
Definition: Case.java:945
static boolean existsCurrentCase()
Definition: Case.java:3163
static void removePropertyChangeListener(PropertyChangeListener listener)
Definition: Case.java:416
void start(String message, int totalWorkUnits)
static final Logger logger
Definition: Case.java:146
static final int RESOURCES_LOCK_TIMOUT_HOURS
Definition: Case.java:133
static final String EXPORT_FOLDER
Definition: Case.java:137
static void createCaseDirectory(String caseDirPath, CaseType caseType)
Definition: Case.java:848
void notifyTagDefinitionChanged(String changedTagName)
Definition: Case.java:1478
static volatile Frame mainFrame
Definition: Case.java:149
static String convertTimeZone(String timeZoneId)
Definition: Case.java:3124
static boolean driveExists(String path)
Definition: DriveUtils.java:66
static void deleteSingleUserCase(CaseMetadata metadata, ProgressIndicator progressIndicator)
Definition: Case.java:2545
void addSubscriber(Set< String > eventNames, PropertyChangeListener subscriber)
synchronized static void setLogDirectory(String directoryPath)
Definition: Logger.java:89
void notifyCentralRepoCommentChanged(long contentId, String newComment)
Definition: Case.java:1493
static final String CACHE_FOLDER
Definition: Case.java:136
static void deleteMultiUserCaseDirectory(CaseNodeData caseNodeData, CaseMetadata metadata, ProgressIndicator progressIndicator, Logger logger)
Definition: Case.java:2803
Case(CaseType caseType, String caseDir, CaseDetails caseDetails)
Definition: Case.java:1665
void addReport(String localPath, String srcModuleName, String reportName)
Definition: Case.java:1534
static void updateGUIForCaseOpened(Case newCurrentCase)
Definition: Case.java:971
void notifyDataSourceNameChanged(Content dataSource, String newName)
Definition: Case.java:1445
static CaseDbConnectionInfo getDatabaseConnectionInfo()
static void create(String caseDir, String caseDisplayName, String caseNumber, String examiner, CaseType caseType)
Definition: Case.java:3066
static final String SINGLE_USER_CASE_DB_NAME
Definition: Case.java:134
static void deleteCase(CaseMetadata metadata)
Definition: Case.java:721
void deleteReports(Collection<?extends Report > reports, boolean deleteFromDisk)
Definition: Case.java:3258
volatile ExecutorService caseLockingExecutor
Definition: Case.java:152
synchronized void setCaseContext(CaseContext caseContext)
Definition: Case.java:2920
static void clearTempSubDir(String tempSubDirPath)
Definition: Case.java:1098
static boolean isValidName(String caseName)
Definition: Case.java:500
void acquireSharedCaseDirLock(String caseDir)
Definition: Case.java:2489
void openCaseDataBase(ProgressIndicator progressIndicator)
Definition: Case.java:2122
void createCaseDirectoryIfDoesNotExist(ProgressIndicator progressIndicator)
Definition: Case.java:1943
CollaborationMonitor collaborationMonitor
Definition: Case.java:155
void openCommunicationChannels(ProgressIndicator progressIndicator)
Definition: Case.java:2280
void releaseSharedCaseDirLock(String caseDir)
Definition: Case.java:2505
static void shutDownTaskExecutor(ExecutorService executor)
static String getModulesOutputDirRelPath()
Definition: Case.java:3177
void saveCaseMetadataToFile(ProgressIndicator progressIndicator)
Definition: Case.java:1980
static void deleteMultiUserCaseTextIndex(CaseNodeData caseNodeData, CaseMetadata metadata, ProgressIndicator progressIndicator, Logger logger)
Definition: Case.java:2762
synchronized void actionPerformed(ActionEvent event)
Definition: Case.java:2953
static final String MODULE_FOLDER
Definition: Case.java:142
void openCaseLevelServices(ProgressIndicator progressIndicator)
Definition: Case.java:2150
static void create(String caseDir, String caseDisplayName, String caseNumber, String examiner)
Definition: Case.java:3041
synchronized void openRemoteEventChannel(String channelName)
void createCaseNodeData(ProgressIndicator progressIndicator)
Definition: Case.java:2004
static String displayNameToUniqueName(String caseDisplayName)
Definition: Case.java:808
static void deleteFromRecentCases(CaseMetadata metadata, ProgressIndicator progressIndicator)
Definition: Case.java:2840
void open(boolean isNewCase)
Definition: Case.java:1703
static void openAsCurrentCase(String caseMetadataFilePath)
Definition: Case.java:585
static void removeEventSubscriber(Set< String > eventNames, PropertyChangeListener subscriber)
Definition: Case.java:476
Lock tryGetExclusiveLock(CategoryNode category, String nodePath, int timeOut, TimeUnit timeUnit)
static boolean isNoNodeException(CoordinationServiceException ex)
Definition: Case.java:2858
default void setCancelling(String cancellingMessage)
static final int DIR_LOCK_TIMOUT_HOURS
Definition: Case.java:132
void close(ProgressIndicator progressIndicator)
Definition: Case.java:2379
void notifyBlackBoardArtifactTagAdded(BlackboardArtifactTag newTag)
Definition: Case.java:1508
static PropertyChangeSupport getPropertyChangeSupport()
Definition: Case.java:3192
static void addPropertyChangeListener(PropertyChangeListener listener)
Definition: Case.java:404
synchronized void setCaseActionFuture(Future<?> caseActionFuture)
Definition: Case.java:2936
void removeSubscriber(Set< String > eventNames, PropertyChangeListener subscriber)
void createCaseDatabase(ProgressIndicator progressIndicator)
Definition: Case.java:2076
void openAppServiceCaseResources(ProgressIndicator progressIndicator)
Definition: Case.java:2172
static void removeEventSubscriber(String eventName, PropertyChangeListener subscriber)
Definition: Case.java:466
void switchLoggingToCaseLogsDirectory(ProgressIndicator progressIndicator)
Definition: Case.java:1960
static void deleteTextIndex(CaseMetadata metadata, ProgressIndicator progressIndicator)
Definition: Case.java:2782
void deleteTempfilesFromCaseDirectory(ProgressIndicator progressIndicator)
Definition: Case.java:2051
void deleteReports(Collection<?extends Report > reports)
Definition: Case.java:1589
void notifyDataSourceAdded(Content dataSource, UUID addingDataSourceEventId)
Definition: Case.java:1432
Report addReport(String localPath, String srcModuleName, String reportName, Content parent)
Definition: Case.java:1552
static boolean pathExists(String filePath)
Definition: Case.java:3138
SleuthkitCase createPortableCase(String caseName, File portableCaseFolder)
Definition: Case.java:1885
static void open(String caseMetadataFilePath)
Definition: Case.java:3082
static void openAsCurrentCase(Case newCurrentCase, boolean isNewCase)
Definition: Case.java:768
static void error(String title, String message)
String getOrCreateSubdirectory(String subDirectoryName)
Definition: Case.java:2522
boolean equalsName(String otherTypeName)
Definition: Case.java:248
static final String EVENT_CHANNEL_NAME
Definition: Case.java:135
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
static void addEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:441
static String convertToAlphaNumericFormat(String timeZoneId)
static final String LOG_FOLDER
Definition: Case.java:138
Lock tryGetSharedLock(CategoryNode category, String nodePath, int timeOut, TimeUnit timeUnit)
static volatile Case currentCase
Definition: Case.java:150
void notifyAddingDataSource(UUID eventId)
Definition: Case.java:1403
CoordinationService.Lock caseDirLock
Definition: Case.java:153
static void addEventSubscriber(String eventName, PropertyChangeListener subscriber)
Definition: Case.java:456
void notifyContentTagAdded(ContentTag newTag)
Definition: Case.java:1456
void cancelAllIngestJobs(IngestJob.CancellationReason reason)
static final String CASE_RESOURCES_THREAD_NAME
Definition: Case.java:144
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:531
static void deleteCaseDirectory(CaseMetadata metadata, ProgressIndicator progressIndicator)
Definition: Case.java:2823
void notifyFailedAddingDataSource(UUID addingDataSourceEventId)
Definition: Case.java:1417
static void deleteMultiUserCase(CaseMetadata metadata, ProgressIndicator progressIndicator)
Definition: Case.java:2597
static final String CONFIG_FOLDER
Definition: Case.java:140
static void removeEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:486
static final Object caseActionSerializationLock
Definition: Case.java:148
void open(boolean isNewCase, ProgressIndicator progressIndicator)
Definition: Case.java:1826
static void deleteMultiUserCaseDatabase(CaseNodeData caseNodeData, CaseMetadata metadata, ProgressIndicator progressIndicator, Logger logger)
Definition: Case.java:2727
static final String REPORTS_FOLDER
Definition: Case.java:139
static final String TEMP_FOLDER
Definition: Case.java:141
static void addEventSubscriber(Set< String > eventNames, PropertyChangeListener subscriber)
Definition: Case.java:431
void updateCaseNodeData(ProgressIndicator progressIndicator)
Definition: Case.java:2030

Copyright © 2012-2018 Basis Technology. Generated on: Fri Jun 21 2019
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.