Autopsy  4.12.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 2012-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;
22 import com.google.common.eventbus.Subscribe;
24 import java.awt.Frame;
25 import java.awt.event.ActionEvent;
26 import java.awt.event.ActionListener;
27 import java.beans.PropertyChangeListener;
28 import java.beans.PropertyChangeSupport;
29 import java.io.File;
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.SimpleDateFormat;
39 import java.util.Collection;
40 import java.util.Date;
41 import java.util.HashMap;
42 import java.util.HashSet;
43 import java.util.List;
44 import java.util.Map;
45 import java.util.Set;
46 import java.util.TimeZone;
47 import java.util.UUID;
48 import java.util.concurrent.CancellationException;
49 import java.util.concurrent.ExecutionException;
50 import java.util.concurrent.ExecutorService;
51 import java.util.concurrent.Executors;
52 import java.util.concurrent.Future;
53 import java.util.concurrent.ThreadFactory;
54 import java.util.concurrent.TimeUnit;
55 import java.util.logging.Level;
56 import java.util.stream.Collectors;
57 import java.util.stream.Stream;
58 import javax.annotation.concurrent.GuardedBy;
59 import javax.annotation.concurrent.ThreadSafe;
60 import javax.swing.JOptionPane;
61 import javax.swing.SwingUtilities;
62 import org.openide.util.Lookup;
63 import org.openide.util.NbBundle;
64 import org.openide.util.NbBundle.Messages;
65 import org.openide.util.actions.CallableSystemAction;
66 import org.openide.windows.WindowManager;
118 import org.sleuthkit.datamodel.Blackboard;
119 import org.sleuthkit.datamodel.BlackboardArtifact;
120 import org.sleuthkit.datamodel.BlackboardArtifactTag;
121 import org.sleuthkit.datamodel.CaseDbConnectionInfo;
122 import org.sleuthkit.datamodel.Content;
123 import org.sleuthkit.datamodel.ContentTag;
124 import org.sleuthkit.datamodel.Image;
125 import org.sleuthkit.datamodel.Report;
126 import org.sleuthkit.datamodel.SleuthkitCase;
127 import org.sleuthkit.datamodel.TimelineManager;
128 import org.sleuthkit.datamodel.TskCoreException;
129 import org.sleuthkit.datamodel.TskUnsupportedSchemaVersionException;
130 
134 public class Case {
135 
136  private static final int DIR_LOCK_TIMOUT_HOURS = 12;
137  private static final int RESOURCES_LOCK_TIMOUT_HOURS = 12;
138  private static final String SINGLE_USER_CASE_DB_NAME = "autopsy.db";
139  private static final String EVENT_CHANNEL_NAME = "%s-Case-Events"; //NON-NLS
140  private static final String CACHE_FOLDER = "Cache"; //NON-NLS
141  private static final String EXPORT_FOLDER = "Export"; //NON-NLS
142  private static final String LOG_FOLDER = "Log"; //NON-NLS
143  private static final String REPORTS_FOLDER = "Reports"; //NON-NLS
144  private static final String CONFIG_FOLDER = "Config"; // NON-NLS
145  private static final String TEMP_FOLDER = "Temp"; //NON-NLS
146  private static final String MODULE_FOLDER = "ModuleOutput"; //NON-NLS
147  private static final String CASE_ACTION_THREAD_NAME = "%s-case-action";
148  private static final String CASE_RESOURCES_THREAD_NAME = "%s-manage-case-resources";
149  private static final String NO_NODE_ERROR_MSG_FRAGMENT = "KeeperErrorCode = NoNode";
150  private static final Logger logger = Logger.getLogger(Case.class.getName());
152  private static final Object caseActionSerializationLock = new Object();
153  private static volatile Frame mainFrame;
154  private static volatile Case currentCase;
155  private final CaseMetadata metadata;
156  private volatile ExecutorService caseLockingExecutor;
158  private SleuthkitCase caseDb;
159  private CollaborationMonitor collaborationMonitor;
161  private boolean hasDataSources;
163 
164  /*
165  * Get a reference to the main window of the desktop application to use to
166  * parent pop up dialogs and initialize the application name for use in
167  * changing the main window title.
168  */
169  static {
170  WindowManager.getDefault().invokeWhenUIReady(new Runnable() {
171  @Override
172  public void run() {
173  mainFrame = WindowManager.getDefault().getMainWindow();
174  }
175  });
176  }
177 
181  public enum CaseType {
182 
183  SINGLE_USER_CASE("Single-user case"), //NON-NLS
184  MULTI_USER_CASE("Multi-user case"); //NON-NLS
185 
186  private final String typeName;
187 
195  public static CaseType fromString(String typeName) {
196  if (typeName != null) {
197  for (CaseType c : CaseType.values()) {
198  if (typeName.equalsIgnoreCase(c.toString())) {
199  return c;
200  }
201  }
202  }
203  return null;
204  }
205 
211  @Override
212  public String toString() {
213  return typeName;
214  }
215 
221  @Messages({
222  "Case_caseType_singleUser=Single-user case",
223  "Case_caseType_multiUser=Multi-user case"
224  })
226  if (fromString(typeName) == SINGLE_USER_CASE) {
227  return Bundle.Case_caseType_singleUser();
228  } else {
229  return Bundle.Case_caseType_multiUser();
230  }
231  }
232 
238  private CaseType(String typeName) {
239  this.typeName = typeName;
240  }
241 
252  @Deprecated
253  public boolean equalsName(String otherTypeName) {
254  return (otherTypeName == null) ? false : typeName.equals(otherTypeName);
255  }
256 
257  };
258 
263  public enum Events {
264 
272  @Deprecated
281  @Deprecated
290  @Deprecated
401  /* An item in the central repository has had its comment
402  * modified. The old value is null, the new value is string for current
403  * comment.
404  */
406 
407  };
408 
409  private final class TSKCaseRepublisher {
410 
411  @Subscribe
412  public void rebroadcastTimelineEventCreated(TimelineManager.TimelineEventAddedEvent event) {
413  eventPublisher.publish(new TimelineEventAddedEvent(event));
414  }
415 
416  @SuppressWarnings("deprecation")
417  @Subscribe
418  public void rebroadcastArtifactsPosted(Blackboard.ArtifactsPostedEvent event) {
419  for (BlackboardArtifact.Type artifactType : event.getArtifactTypes()) {
420  /*
421  * fireModuleDataEvent is deprecated so module writers don't use
422  * it (they should use Blackboard.postArtifact(s) instead), but
423  * we still need a way to rebroadcast the ArtifactsPostedEvent
424  * as a ModuleDataEvent.
425  */
427  event.getModuleName(),
428  artifactType,
429  event.getArtifacts(artifactType)));
430  }
431  }
432  }
433 
440  public static void addPropertyChangeListener(PropertyChangeListener listener) {
441  addEventSubscriber(Stream.of(Events.values())
442  .map(Events::toString)
443  .collect(Collectors.toSet()), listener);
444  }
445 
452  public static void removePropertyChangeListener(PropertyChangeListener listener) {
453  removeEventSubscriber(Stream.of(Events.values())
454  .map(Events::toString)
455  .collect(Collectors.toSet()), listener);
456  }
457 
466  @Deprecated
467  public static void addEventSubscriber(Set<String> eventNames, PropertyChangeListener subscriber) {
468  eventPublisher.addSubscriber(eventNames, subscriber);
469  }
470 
477  public static void addEventTypeSubscriber(Set<Events> eventTypes, PropertyChangeListener subscriber) {
478  eventTypes.forEach((Events event) -> {
479  eventPublisher.addSubscriber(event.toString(), subscriber);
480  });
481  }
482 
491  @Deprecated
492  public static void addEventSubscriber(String eventName, PropertyChangeListener subscriber) {
493  eventPublisher.addSubscriber(eventName, subscriber);
494  }
495 
502  public static void removeEventSubscriber(String eventName, PropertyChangeListener subscriber) {
503  eventPublisher.removeSubscriber(eventName, subscriber);
504  }
505 
512  public static void removeEventSubscriber(Set<String> eventNames, PropertyChangeListener subscriber) {
513  eventPublisher.removeSubscriber(eventNames, subscriber);
514  }
515 
522  public static void removeEventTypeSubscriber(Set<Events> eventTypes, PropertyChangeListener subscriber) {
523  eventTypes.forEach((Events event) -> {
524  eventPublisher.removeSubscriber(event.toString(), subscriber);
525  });
526  }
527 
536  public static boolean isValidName(String caseName) {
537  return !(caseName.contains("\\") || caseName.contains("/") || caseName.contains(":")
538  || caseName.contains("*") || caseName.contains("?") || caseName.contains("\"")
539  || caseName.contains("<") || caseName.contains(">") || caseName.contains("|"));
540  }
541 
566  @Deprecated
567  public static void createAsCurrentCase(String caseDir, String caseDisplayName, String caseNumber, String examiner, CaseType caseType) throws CaseActionException, CaseActionCancelledException {
568  createAsCurrentCase(caseType, caseDir, new CaseDetails(caseDisplayName, caseNumber, examiner, "", "", ""));
569  }
570 
590  @Messages({
591  "Case.exceptionMessage.emptyCaseName=Must specify a case name.",
592  "Case.exceptionMessage.emptyCaseDir=Must specify a case directory path."
593  })
594  public static void createAsCurrentCase(CaseType caseType, String caseDir, CaseDetails caseDetails) throws CaseActionException, CaseActionCancelledException {
595  if (caseDetails.getCaseDisplayName().isEmpty()) {
596  throw new CaseActionException(Bundle.Case_exceptionMessage_emptyCaseName());
597  }
598  if (caseDir.isEmpty()) {
599  throw new CaseActionException(Bundle.Case_exceptionMessage_emptyCaseDir());
600  }
601  openAsCurrentCase(new Case(caseType, caseDir, caseDetails), true);
602  }
603 
617  @Messages({
618  "# {0} - exception message", "Case.exceptionMessage.failedToReadMetadata=Failed to read case metadata:\n{0}.",
619  "Case.exceptionMessage.cannotOpenMultiUserCaseNoSettings=Multi-user settings are missing (see Tools, Options, Multi-user tab), cannot open a multi-user case."
620  })
621  public static void openAsCurrentCase(String caseMetadataFilePath) throws CaseActionException {
623  try {
624  metadata = new CaseMetadata(Paths.get(caseMetadataFilePath));
625  } catch (CaseMetadataException ex) {
626  throw new CaseActionException(Bundle.Case_exceptionMessage_failedToReadMetadata(ex.getLocalizedMessage()), ex);
627  }
629  throw new CaseActionException(Bundle.Case_exceptionMessage_cannotOpenMultiUserCaseNoSettings());
630  }
631  openAsCurrentCase(new Case(metadata), false);
632  }
633 
639  public static boolean isCaseOpen() {
640  return currentCase != null;
641  }
642 
650  public static Case getCurrentCase() {
651  try {
652  return getCurrentCaseThrows();
653  } catch (NoCurrentCaseException ex) {
654  /*
655  * Throw a runtime exception, since this is a programming error.
656  */
657  throw new IllegalStateException(NbBundle.getMessage(Case.class, "Case.getCurCase.exception.noneOpen"), ex);
658  }
659  }
660 
679  Case openCase = currentCase;
680  if (openCase == null) {
681  throw new NoCurrentCaseException(NbBundle.getMessage(Case.class, "Case.getCurCase.exception.noneOpen"));
682  } else {
683  return openCase;
684  }
685  }
686 
695  @Messages({
696  "# {0} - exception message", "Case.closeException.couldNotCloseCase=Error closing case: {0}",
697  "Case.progressIndicatorTitle.closingCase=Closing Case"
698  })
699  public static void closeCurrentCase() throws CaseActionException {
700  synchronized (caseActionSerializationLock) {
701  if (null == currentCase) {
702  return;
703  }
704  Case closedCase = currentCase;
705  try {
706  eventPublisher.publishLocally(new AutopsyEvent(Events.CURRENT_CASE.toString(), closedCase, null));
707  logger.log(Level.INFO, "Closing current case {0} ({1}) in {2}", new Object[]{closedCase.getDisplayName(), closedCase.getName(), closedCase.getCaseDirectory()}); //NON-NLS
708  currentCase = null;
709  closedCase.close();
710  logger.log(Level.INFO, "Closed current case {0} ({1}) in {2}", new Object[]{closedCase.getDisplayName(), closedCase.getName(), closedCase.getCaseDirectory()}); //NON-NLS
711  } catch (CaseActionException ex) {
712  logger.log(Level.SEVERE, String.format("Error closing current case %s (%s) in %s", closedCase.getDisplayName(), closedCase.getName(), closedCase.getCaseDirectory()), ex); //NON-NLS
713  throw ex;
714  } finally {
717  }
718  }
719  }
720  }
721 
730  public static void deleteCurrentCase() throws CaseActionException {
731  synchronized (caseActionSerializationLock) {
732  if (null == currentCase) {
733  return;
734  }
735  CaseMetadata metadata = currentCase.getMetadata();
737  deleteCase(metadata);
738  }
739  }
740 
752  @Messages({
753  "Case.progressIndicatorTitle.deletingCase=Deleting Case",
754  "Case.exceptionMessage.cannotDeleteCurrentCase=Cannot delete current case, it must be closed first.",
755  "# {0} - case display name", "Case.exceptionMessage.deletionInterrupted=Deletion of the case {0} was cancelled."
756  })
757  public static void deleteCase(CaseMetadata metadata) throws CaseActionException {
758  synchronized (caseActionSerializationLock) {
759  if (null != currentCase) {
760  throw new CaseActionException(Bundle.Case_exceptionMessage_cannotDeleteCurrentCase());
761  }
762  }
763 
764  ProgressIndicator progressIndicator;
766  progressIndicator = new ModalDialogProgressIndicator(mainFrame, Bundle.Case_progressIndicatorTitle_deletingCase());
767  } else {
768  progressIndicator = new LoggingProgressIndicator();
769  }
770  progressIndicator.start(Bundle.Case_progressMessage_preparing());
771  try {
772  if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) {
773  deleteSingleUserCase(metadata, progressIndicator);
774  } else {
775  try {
776  deleteMultiUserCase(metadata, progressIndicator);
777  } catch (InterruptedException ex) {
778  /*
779  * Note that task cancellation is not currently supported
780  * for this code path, so this catch block is not expected
781  * to be executed.
782  */
783  throw new CaseActionException(Bundle.Case_exceptionMessage_deletionInterrupted(metadata.getCaseDisplayName()), ex);
784  }
785  }
786  } finally {
787  progressIndicator.finish();
788  }
789  }
790 
801  @Messages({
802  "Case.exceptionMessage.cannotLocateMainWindow=Cannot locate main application window"
803  })
804  private static void openAsCurrentCase(Case newCurrentCase, boolean isNewCase) throws CaseActionException, CaseActionCancelledException {
805  synchronized (caseActionSerializationLock) {
806  if (null != currentCase) {
807  try {
809  } catch (CaseActionException ex) {
810  /*
811  * Notify the user and continue (the error has already been
812  * logged in closeCurrentCase.
813  */
814  MessageNotifyUtil.Message.error(ex.getLocalizedMessage());
815  }
816  }
817  try {
818  logger.log(Level.INFO, "Opening {0} ({1}) in {2} as the current case", new Object[]{newCurrentCase.getDisplayName(), newCurrentCase.getName(), newCurrentCase.getCaseDirectory()}); //NON-NLS
819  newCurrentCase.open(isNewCase);
820  currentCase = newCurrentCase;
821  logger.log(Level.INFO, "Opened {0} ({1}) in {2} as the current case", new Object[]{newCurrentCase.getDisplayName(), newCurrentCase.getName(), newCurrentCase.getCaseDirectory()}); //NON-NLS
823  updateGUIForCaseOpened(newCurrentCase);
824  }
825  eventPublisher.publishLocally(new AutopsyEvent(Events.CURRENT_CASE.toString(), null, currentCase));
826  } catch (CaseActionCancelledException ex) {
827  logger.log(Level.INFO, String.format("Cancelled opening %s (%s) in %s as the current case", newCurrentCase.getDisplayName(), newCurrentCase.getName(), newCurrentCase.getCaseDirectory())); //NON-NLS
828  throw ex;
829  } catch (CaseActionException ex) {
830  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
831  throw ex;
832  }
833  }
834  }
835 
844  private static String displayNameToUniqueName(String caseDisplayName) {
845  /*
846  * Replace all non-ASCII characters.
847  */
848  String uniqueCaseName = caseDisplayName.replaceAll("[^\\p{ASCII}]", "_"); //NON-NLS
849 
850  /*
851  * Replace all control characters.
852  */
853  uniqueCaseName = uniqueCaseName.replaceAll("[\\p{Cntrl}]", "_"); //NON-NLS
854 
855  /*
856  * Replace /, \, :, ?, space, ' ".
857  */
858  uniqueCaseName = uniqueCaseName.replaceAll("[ /?:'\"\\\\]", "_"); //NON-NLS
859 
860  /*
861  * Make it all lowercase.
862  */
863  uniqueCaseName = uniqueCaseName.toLowerCase();
864 
865  /*
866  * Add a time stamp for uniqueness.
867  */
868  SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
869  Date date = new Date();
870  uniqueCaseName = uniqueCaseName + "_" + dateFormat.format(date);
871 
872  return uniqueCaseName;
873  }
874 
884  public static void createCaseDirectory(String caseDirPath, CaseType caseType) throws CaseActionException {
885  /*
886  * Check the case directory path and permissions. The case directory may
887  * already exist.
888  */
889  File caseDir = new File(caseDirPath);
890  if (caseDir.exists()) {
891  if (caseDir.isFile()) {
892  throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.existNotDir", caseDirPath));
893  } else if (!caseDir.canRead() || !caseDir.canWrite()) {
894  throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.existCantRW", caseDirPath));
895  }
896  }
897 
898  /*
899  * Create the case directory, if it does not already exist.
900  */
901  if (!caseDir.mkdirs()) {
902  throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreate", caseDirPath));
903  }
904 
905  /*
906  * Create the subdirectories of the case directory, if they do not
907  * already exist. Note that multi-user cases get an extra layer of
908  * subdirectories, one subdirectory per application host machine.
909  */
910  String hostPathComponent = "";
911  if (caseType == CaseType.MULTI_USER_CASE) {
912  hostPathComponent = File.separator + NetworkUtils.getLocalHostName();
913  }
914 
915  Path exportDir = Paths.get(caseDirPath, hostPathComponent, EXPORT_FOLDER);
916  if (!exportDir.toFile().mkdirs()) {
917  throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreateCaseDir", exportDir));
918  }
919 
920  Path logsDir = Paths.get(caseDirPath, hostPathComponent, LOG_FOLDER);
921  if (!logsDir.toFile().mkdirs()) {
922  throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreateCaseDir", logsDir));
923  }
924 
925  Path tempDir = Paths.get(caseDirPath, hostPathComponent, TEMP_FOLDER);
926  if (!tempDir.toFile().mkdirs()) {
927  throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreateCaseDir", tempDir));
928  }
929 
930  Path cacheDir = Paths.get(caseDirPath, hostPathComponent, CACHE_FOLDER);
931  if (!cacheDir.toFile().mkdirs()) {
932  throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreateCaseDir", cacheDir));
933  }
934 
935  Path moduleOutputDir = Paths.get(caseDirPath, hostPathComponent, MODULE_FOLDER);
936  if (!moduleOutputDir.toFile().mkdirs()) {
937  throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreateModDir", moduleOutputDir));
938  }
939 
940  Path reportsDir = Paths.get(caseDirPath, hostPathComponent, REPORTS_FOLDER);
941  if (!reportsDir.toFile().mkdirs()) {
942  throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreateReportsDir", reportsDir));
943  }
944  }
945 
953  static Map<Long, String> getImagePaths(SleuthkitCase db) {
954  Map<Long, String> imgPaths = new HashMap<>();
955  try {
956  Map<Long, List<String>> imgPathsList = db.getImagePaths();
957  for (Map.Entry<Long, List<String>> entry : imgPathsList.entrySet()) {
958  if (entry.getValue().size() > 0) {
959  imgPaths.put(entry.getKey(), entry.getValue().get(0));
960  }
961  }
962  } catch (TskCoreException ex) {
963  logger.log(Level.SEVERE, "Error getting image paths", ex); //NON-NLS
964  }
965  return imgPaths;
966  }
967 
978  @Messages({
979  "Case.creationException.couldNotAcquireResourcesLock=Failed to get lock on case resources"
980  })
981  private static CoordinationService.Lock acquireExclusiveCaseResourcesLock(String caseDir) throws CaseActionException {
982  try {
983  Path caseDirPath = Paths.get(caseDir);
984  String resourcesNodeName = CoordinationServiceUtils.getCaseResourcesNodePath(caseDirPath);
985  Lock lock = CoordinationService.getInstance().tryGetExclusiveLock(CategoryNode.CASES, resourcesNodeName, RESOURCES_LOCK_TIMOUT_HOURS, TimeUnit.HOURS);
986  return lock;
987  } catch (InterruptedException ex) {
988  throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser());
989  } catch (CoordinationServiceException ex) {
990  throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireResourcesLock(), ex);
991  }
992  }
993 
994  private static String getNameForTitle() {
995  //Method should become unnecessary once technical debt story 3334 is done.
996  if (UserPreferences.getAppName().equals(Version.getName())) {
997  //Available version number is version number for this application
998  return String.format("%s %s", UserPreferences.getAppName(), Version.getVersion());
999  } else {
1000  return UserPreferences.getAppName();
1001  }
1002  }
1003 
1007  private static void updateGUIForCaseOpened(Case newCurrentCase) {
1009  SwingUtilities.invokeLater(() -> {
1010  /*
1011  * If the case database was upgraded for a new schema and a
1012  * backup database was created, notify the user.
1013  */
1014  SleuthkitCase caseDb = newCurrentCase.getSleuthkitCase();
1015  String backupDbPath = caseDb.getBackupDatabasePath();
1016  if (null != backupDbPath) {
1017  JOptionPane.showMessageDialog(
1018  mainFrame,
1019  NbBundle.getMessage(Case.class, "Case.open.msgDlg.updated.msg", backupDbPath),
1020  NbBundle.getMessage(Case.class, "Case.open.msgDlg.updated.title"),
1021  JOptionPane.INFORMATION_MESSAGE);
1022  }
1023 
1024  /*
1025  * Look for the files for the data sources listed in the case
1026  * database and give the user the opportunity to locate any that
1027  * are missing.
1028  */
1029  Map<Long, String> imgPaths = getImagePaths(caseDb);
1030  for (Map.Entry<Long, String> entry : imgPaths.entrySet()) {
1031  long obj_id = entry.getKey();
1032  String path = entry.getValue();
1033  boolean fileExists = (new File(path).isFile() || DriveUtils.driveExists(path));
1034  if (!fileExists) {
1035  int response = JOptionPane.showConfirmDialog(
1036  mainFrame,
1037  NbBundle.getMessage(Case.class, "Case.checkImgExist.confDlg.doesntExist.msg", path),
1038  NbBundle.getMessage(Case.class, "Case.checkImgExist.confDlg.doesntExist.title"),
1039  JOptionPane.YES_NO_OPTION);
1040  if (response == JOptionPane.YES_OPTION) {
1041  MissingImageDialog.makeDialog(obj_id, caseDb);
1042  } else {
1043  logger.log(Level.SEVERE, "User proceeding with missing image files"); //NON-NLS
1044 
1045  }
1046  }
1047  }
1048 
1049  /*
1050  * Enable the case-specific actions.
1051  */
1052  CallableSystemAction.get(AddImageAction.class).setEnabled(true);
1053  CallableSystemAction.get(CaseCloseAction.class).setEnabled(true);
1054  CallableSystemAction.get(CaseDetailsAction.class).setEnabled(true);
1055  CallableSystemAction.get(DataSourceSummaryAction.class).setEnabled(true);
1056  CallableSystemAction.get(CaseDeleteAction.class).setEnabled(true);
1057  CallableSystemAction.get(OpenTimelineAction.class).setEnabled(true);
1058  CallableSystemAction.get(OpenCommVisualizationToolAction.class).setEnabled(true);
1059  CallableSystemAction.get(CommonAttributeSearchAction.class).setEnabled(true);
1060  CallableSystemAction.get(OpenOutputFolderAction.class).setEnabled(false);
1061 
1062  /*
1063  * Add the case to the recent cases tracker that supplies a list
1064  * of recent cases to the recent cases menu item and the
1065  * open/create case dialog.
1066  */
1067  RecentCases.getInstance().addRecentCase(newCurrentCase.getDisplayName(), newCurrentCase.getMetadata().getFilePath().toString());
1068 
1069  /*
1070  * Open the top components (windows within the main application
1071  * window).
1072  *
1073  * Note: If the core windows are not opened here, they will be
1074  * opened via the DirectoryTreeTopComponent 'propertyChange()'
1075  * method on a DATA_SOURCE_ADDED event.
1076  */
1077  if (newCurrentCase.hasData()) {
1079  }
1080 
1081  /*
1082  * Reset the main window title to:
1083  *
1084  * [curent case display name] - [application name].
1085  */
1086  mainFrame.setTitle(newCurrentCase.getDisplayName() + " - " + getNameForTitle());
1087  });
1088  }
1089  }
1090 
1091  /*
1092  * Update the GUI to to reflect the lack of a current case.
1093  */
1094  private static void updateGUIForCaseClosed() {
1096  SwingUtilities.invokeLater(() -> {
1097  /*
1098  * Close the top components (windows within the main application
1099  * window).
1100  */
1102 
1103  /*
1104  * Disable the case-specific menu items.
1105  */
1106  CallableSystemAction.get(AddImageAction.class).setEnabled(false);
1107  CallableSystemAction.get(CaseCloseAction.class).setEnabled(false);
1108  CallableSystemAction.get(CaseDetailsAction.class).setEnabled(false);
1109  CallableSystemAction.get(DataSourceSummaryAction.class).setEnabled(false);
1110  CallableSystemAction.get(CaseDeleteAction.class).setEnabled(false);
1111  CallableSystemAction.get(OpenTimelineAction.class).setEnabled(false);
1112  CallableSystemAction.get(OpenCommVisualizationToolAction.class).setEnabled(false);
1113  CallableSystemAction.get(OpenOutputFolderAction.class).setEnabled(false);
1114  CallableSystemAction.get(CommonAttributeSearchAction.class).setEnabled(false);
1115 
1116  /*
1117  * Clear the notifications in the notfier component in the lower
1118  * right hand corner of the main application window.
1119  */
1121 
1122  /*
1123  * Reset the main window title to be just the application name,
1124  * instead of [curent case display name] - [application name].
1125  */
1126  mainFrame.setTitle(getNameForTitle());
1127  });
1128  }
1129  }
1130 
1134  private static void clearTempSubDir(String tempSubDirPath) {
1135  File tempFolder = new File(tempSubDirPath);
1136  if (tempFolder.isDirectory()) {
1137  File[] files = tempFolder.listFiles();
1138  if (files.length > 0) {
1139  for (File file : files) {
1140  if (file.isDirectory()) {
1141  FileUtil.deleteDir(file);
1142  } else {
1143  file.delete();
1144  }
1145  }
1146  }
1147  }
1148  }
1149 
1155  public SleuthkitCase getSleuthkitCase() {
1156  return this.caseDb;
1157  }
1158 
1165  return caseServices;
1166  }
1167 
1174  return metadata.getCaseType();
1175  }
1176 
1182  public String getCreatedDate() {
1183  return metadata.getCreatedDate();
1184  }
1185 
1191  public String getName() {
1192  return metadata.getCaseName();
1193  }
1194 
1200  public String getDisplayName() {
1201  return metadata.getCaseDisplayName();
1202  }
1203 
1209  public String getNumber() {
1210  return metadata.getCaseNumber();
1211  }
1212 
1218  public String getExaminer() {
1219  return metadata.getExaminer();
1220  }
1221 
1227  public String getExaminerPhone() {
1228  return metadata.getExaminerPhone();
1229  }
1230 
1236  public String getExaminerEmail() {
1237  return metadata.getExaminerEmail();
1238  }
1239 
1245  public String getCaseNotes() {
1246  return metadata.getCaseNotes();
1247  }
1248 
1254  public String getCaseDirectory() {
1255  return metadata.getCaseDirectory();
1256  }
1257 
1266  public String getOutputDirectory() {
1267  String caseDirectory = getCaseDirectory();
1268  Path hostPath;
1269  if (CaseType.MULTI_USER_CASE == metadata.getCaseType()) {
1270  hostPath = Paths.get(caseDirectory, NetworkUtils.getLocalHostName());
1271  } else {
1272  hostPath = Paths.get(caseDirectory);
1273  }
1274  if (!hostPath.toFile().exists()) {
1275  hostPath.toFile().mkdirs();
1276  }
1277  return hostPath.toString();
1278  }
1279 
1286  public String getTempDirectory() {
1287  return getOrCreateSubdirectory(TEMP_FOLDER);
1288  }
1289 
1296  public String getCacheDirectory() {
1297  return getOrCreateSubdirectory(CACHE_FOLDER);
1298  }
1299 
1306  public String getExportDirectory() {
1307  return getOrCreateSubdirectory(EXPORT_FOLDER);
1308  }
1309 
1316  public String getLogDirectoryPath() {
1317  return getOrCreateSubdirectory(LOG_FOLDER);
1318  }
1319 
1326  public String getReportDirectory() {
1327  return getOrCreateSubdirectory(REPORTS_FOLDER);
1328  }
1329 
1336  public String getConfigDirectory() {
1337  return getOrCreateSubdirectory(CONFIG_FOLDER);
1338  }
1339 
1346  public String getModuleDirectory() {
1347  return getOrCreateSubdirectory(MODULE_FOLDER);
1348  }
1349 
1358  Path path = Paths.get(getModuleDirectory());
1360  return path.subpath(path.getNameCount() - 2, path.getNameCount()).toString();
1361  } else {
1362  return path.subpath(path.getNameCount() - 1, path.getNameCount()).toString();
1363  }
1364  }
1365 
1375  public List<Content> getDataSources() throws TskCoreException {
1376  List<Content> list = caseDb.getRootObjects();
1377  hasDataSources = (list.size() > 0);
1378  return list;
1379  }
1380 
1386  public Set<TimeZone> getTimeZones() {
1387  Set<TimeZone> timezones = new HashSet<>();
1388  try {
1389  for (Content c : getDataSources()) {
1390  final Content dataSource = c.getDataSource();
1391  if ((dataSource != null) && (dataSource instanceof Image)) {
1392  Image image = (Image) dataSource;
1393  timezones.add(TimeZone.getTimeZone(image.getTimeZone()));
1394  }
1395  }
1396  } catch (TskCoreException ex) {
1397  logger.log(Level.SEVERE, "Error getting data source time zones", ex); //NON-NLS
1398  }
1399  return timezones;
1400  }
1401 
1408  public String getTextIndexName() {
1409  return getMetadata().getTextIndexName();
1410  }
1411 
1418  public boolean hasData() {
1419  if (!hasDataSources) {
1420  try {
1421  hasDataSources = (getDataSources().size() > 0);
1422  } catch (TskCoreException ex) {
1423  logger.log(Level.SEVERE, "Error accessing case database", ex); //NON-NLS
1424  }
1425  }
1426  return hasDataSources;
1427  }
1428 
1439  public void notifyAddingDataSource(UUID eventId) {
1440  eventPublisher.publish(new AddingDataSourceEvent(eventId));
1441  }
1442 
1453  public void notifyFailedAddingDataSource(UUID addingDataSourceEventId) {
1454  eventPublisher.publish(new AddingDataSourceFailedEvent(addingDataSourceEventId));
1455  }
1456 
1468  public void notifyDataSourceAdded(Content dataSource, UUID addingDataSourceEventId) {
1469  eventPublisher.publish(new DataSourceAddedEvent(dataSource, addingDataSourceEventId));
1470  }
1471 
1481  public void notifyDataSourceNameChanged(Content dataSource, String newName) {
1482  eventPublisher.publish(new DataSourceNameChangedEvent(dataSource, newName));
1483  }
1484 
1492  public void notifyContentTagAdded(ContentTag newTag) {
1493  eventPublisher.publish(new ContentTagAddedEvent(newTag));
1494  }
1495 
1503  public void notifyContentTagDeleted(ContentTag deletedTag) {
1504  eventPublisher.publish(new ContentTagDeletedEvent(deletedTag));
1505  }
1506 
1514  public void notifyTagDefinitionChanged(String changedTagName) {
1515  //leaving new value of changedTagName as null, because we do not currently support changing the display name of a tag.
1516  eventPublisher.publish(new AutopsyEvent(Events.TAG_DEFINITION_CHANGED.toString(), changedTagName, null));
1517  }
1518 
1529  public void notifyCentralRepoCommentChanged(long contentId, String newComment) {
1530  try {
1531  eventPublisher.publish(new CommentChangedEvent(contentId, newComment));
1532  } catch (NoCurrentCaseException ex) {
1533  logger.log(Level.WARNING, "Unable to send notifcation regarding comment change due to no current case being open", ex);
1534  }
1535  }
1536 
1544  public void notifyBlackBoardArtifactTagAdded(BlackboardArtifactTag newTag) {
1545  eventPublisher.publish(new BlackBoardArtifactTagAddedEvent(newTag));
1546  }
1547 
1555  public void notifyBlackBoardArtifactTagDeleted(BlackboardArtifactTag deletedTag) {
1556  eventPublisher.publish(new BlackBoardArtifactTagDeletedEvent(deletedTag));
1557  }
1558 
1570  public void addReport(String localPath, String srcModuleName, String reportName) throws TskCoreException {
1571  addReport(localPath, srcModuleName, reportName, null);
1572  }
1573 
1588  public Report addReport(String localPath, String srcModuleName, String reportName, Content parent) throws TskCoreException {
1589  String normalizedLocalPath;
1590  try {
1591  if (localPath.toLowerCase().contains("http:")) {
1592  normalizedLocalPath = localPath;
1593  } else {
1594  normalizedLocalPath = Paths.get(localPath).normalize().toString();
1595  }
1596  } catch (InvalidPathException ex) {
1597  String errorMsg = "Invalid local path provided: " + localPath; // NON-NLS
1598  throw new TskCoreException(errorMsg, ex);
1599  }
1600  Report report = this.caseDb.addReport(normalizedLocalPath, srcModuleName, reportName, parent);
1601  eventPublisher.publish(new ReportAddedEvent(report));
1602  return report;
1603  }
1604 
1613  public List<Report> getAllReports() throws TskCoreException {
1614  return this.caseDb.getAllReports();
1615  }
1616 
1625  public void deleteReports(Collection<? extends Report> reports) throws TskCoreException {
1626  for (Report report : reports) {
1627  this.caseDb.deleteReport(report);
1628  eventPublisher.publish(new AutopsyEvent(Events.REPORT_DELETED.toString(), report, null));
1629  }
1630  }
1631 
1637  CaseMetadata getMetadata() {
1638  return metadata;
1639  }
1640 
1648  @Messages({
1649  "Case.exceptionMessage.metadataUpdateError=Failed to update case metadata"
1650  })
1651  void updateCaseDetails(CaseDetails caseDetails) throws CaseActionException {
1652  CaseDetails oldCaseDetails = metadata.getCaseDetails();
1653  try {
1654  metadata.setCaseDetails(caseDetails);
1655  } catch (CaseMetadataException ex) {
1656  throw new CaseActionException(Bundle.Case_exceptionMessage_metadataUpdateError(), ex);
1657  }
1658  if (getCaseType() == CaseType.MULTI_USER_CASE && !oldCaseDetails.getCaseDisplayName().equals(caseDetails.getCaseDisplayName())) {
1659  try {
1660  CaseNodeData nodeData = CaseNodeData.readCaseNodeData(metadata.getCaseDirectory());
1661  nodeData.setDisplayName(caseDetails.getCaseDisplayName());
1662  CaseNodeData.writeCaseNodeData(nodeData);
1663  } catch (CaseNodeDataException | InterruptedException ex) {
1664  throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotUpdateCaseNodeData(ex.getLocalizedMessage()), ex);
1665  }
1666  }
1667  if (!oldCaseDetails.getCaseNumber().equals(caseDetails.getCaseNumber())) {
1668  eventPublisher.publish(new AutopsyEvent(Events.NUMBER.toString(), oldCaseDetails.getCaseNumber(), caseDetails.getCaseNumber()));
1669  }
1670  if (!oldCaseDetails.getExaminerName().equals(caseDetails.getExaminerName())) {
1671  eventPublisher.publish(new AutopsyEvent(Events.NUMBER.toString(), oldCaseDetails.getExaminerName(), caseDetails.getExaminerName()));
1672  }
1673  if (!oldCaseDetails.getCaseDisplayName().equals(caseDetails.getCaseDisplayName())) {
1674  eventPublisher.publish(new AutopsyEvent(Events.NAME.toString(), oldCaseDetails.getCaseDisplayName(), caseDetails.getCaseDisplayName()));
1675  }
1676  eventPublisher.publish(new AutopsyEvent(Events.CASE_DETAILS.toString(), oldCaseDetails, caseDetails));
1677  if (RuntimeProperties.runningWithGUI()) {
1678  SwingUtilities.invokeLater(() -> {
1679  mainFrame.setTitle(caseDetails.getCaseDisplayName() + " - " + getNameForTitle());
1680  try {
1681  RecentCases.getInstance().updateRecentCase(oldCaseDetails.getCaseDisplayName(), metadata.getFilePath().toString(), caseDetails.getCaseDisplayName(), metadata.getFilePath().toString());
1682  } catch (Exception ex) {
1683  logger.log(Level.SEVERE, "Error updating case name in UI", ex); //NON-NLS
1684  }
1685  });
1686  }
1687  }
1688 
1701  private Case(CaseType caseType, String caseDir, CaseDetails caseDetails) {
1702  metadata = new CaseMetadata(caseType, caseDir, displayNameToUniqueName(caseDetails.getCaseDisplayName()), caseDetails);
1703  }
1704 
1710  private Case(CaseMetadata caseMetaData) {
1711  metadata = caseMetaData;
1712  }
1713 
1729  @Messages({
1730  "Case.progressIndicatorTitle.creatingCase=Creating Case",
1731  "Case.progressIndicatorTitle.openingCase=Opening Case",
1732  "Case.progressIndicatorCancelButton.label=Cancel",
1733  "Case.progressMessage.preparing=Preparing...",
1734  "Case.progressMessage.preparingToOpenCaseResources=<html>Preparing to open case resources.<br>This may take time if another user is upgrading the case.</html>",
1735  "Case.progressMessage.cancelling=Cancelling...",
1736  "Case.exceptionMessage.cancelledByUser=Cancelled by user.",
1737  "# {0} - exception message", "Case.exceptionMessage.execExceptionWrapperMessage={0}"
1738  })
1739  private void open(boolean isNewCase) throws CaseActionException {
1740  /*
1741  * Create and start either a GUI progress indicator with a Cancel button
1742  * or a logging progress indicator.
1743  */
1744  CancelButtonListener cancelButtonListener = null;
1745  ProgressIndicator progressIndicator;
1747  cancelButtonListener = new CancelButtonListener(Bundle.Case_progressMessage_cancelling());
1748  String progressIndicatorTitle = isNewCase ? Bundle.Case_progressIndicatorTitle_creatingCase() : Bundle.Case_progressIndicatorTitle_openingCase();
1749  progressIndicator = new ModalDialogProgressIndicator(
1750  mainFrame,
1751  progressIndicatorTitle,
1752  new String[]{Bundle.Case_progressIndicatorCancelButton_label()},
1753  Bundle.Case_progressIndicatorCancelButton_label(),
1754  cancelButtonListener);
1755  } else {
1756  progressIndicator = new LoggingProgressIndicator();
1757  }
1758  progressIndicator.start(Bundle.Case_progressMessage_preparing());
1759 
1760  /*
1761  * Creating/opening a case is always done by creating a task running in
1762  * the same non-UI thread that will be used to close the case, so a
1763  * single-threaded executor service is created here and saved as case
1764  * state (must be volatile for cancellation to work).
1765  *
1766  * --- If the case is a single-user case, this supports cancelling
1767  * opening of the case by cancelling the task.
1768  *
1769  * --- If the case is a multi-user case, this still supports
1770  * cancellation, but it also makes it possible for the shared case
1771  * directory lock held as long as the case is open to be released in the
1772  * same thread in which it was acquired, as is required by the
1773  * coordination service.
1774  */
1775  TaskThreadFactory threadFactory = new TaskThreadFactory(String.format(CASE_ACTION_THREAD_NAME, metadata.getCaseName()));
1776  caseLockingExecutor = Executors.newSingleThreadExecutor(threadFactory);
1777  Future<Void> future = caseLockingExecutor.submit(() -> {
1778  if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) {
1779  open(isNewCase, progressIndicator);
1780  } else {
1781  /*
1782  * First, acquire a shared case directory lock that will be held
1783  * as long as this node has this case open. This will prevent
1784  * deletion of the case by another node. Next, acquire an
1785  * exclusive case resources lock to ensure only one node at a
1786  * time can create/open/upgrade/close the case resources.
1787  */
1788  progressIndicator.progress(Bundle.Case_progressMessage_preparingToOpenCaseResources());
1791  if (null == resourcesLock) {
1792  throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireResourcesLock());
1793  }
1794  open(isNewCase, progressIndicator);
1795  } catch (CaseActionException ex) {
1796  releaseSharedCaseDirLock(getMetadata().getCaseDirectory());
1797  throw ex;
1798  }
1799  }
1800  return null;
1801  });
1802  if (null != cancelButtonListener) {
1803  cancelButtonListener.setCaseActionFuture(future);
1804  }
1805 
1806  /*
1807  * Wait for the case creation/opening task to finish.
1808  */
1809  try {
1810  future.get();
1811  } catch (InterruptedException discarded) {
1812  /*
1813  * The thread this method is running in has been interrupted. Cancel
1814  * the create/open task, wait for it to finish, and shut down the
1815  * executor. This can be done safely because if the task is
1816  * completed with a cancellation condition, the case will have been
1817  * closed and the case directory lock released will have been
1818  * released.
1819  */
1820  if (null != cancelButtonListener) {
1821  cancelButtonListener.actionPerformed(null);
1822  } else {
1823  future.cancel(true);
1824  }
1825  ThreadUtils.shutDownTaskExecutor(caseLockingExecutor);
1826  } catch (CancellationException discarded) {
1827  /*
1828  * The create/open task has been cancelled. Wait for it to finish,
1829  * and shut down the executor. This can be done safely because if
1830  * the task is completed with a cancellation condition, the case
1831  * will have been closed and the case directory lock released will
1832  * have been released.
1833  */
1834  ThreadUtils.shutDownTaskExecutor(caseLockingExecutor);
1835  throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser());
1836  } catch (ExecutionException ex) {
1837  /*
1838  * The create/open task has thrown an exception. Wait for it to
1839  * finish, and shut down the executor. This can be done safely
1840  * because if the task is completed with an execution condition, the
1841  * case will have been closed and the case directory lock released
1842  * will have been released.
1843  */
1844  ThreadUtils.shutDownTaskExecutor(caseLockingExecutor);
1845  throw new CaseActionException(Bundle.Case_exceptionMessage_execExceptionWrapperMessage(ex.getCause().getLocalizedMessage()), ex);
1846  } finally {
1847  progressIndicator.finish();
1848  }
1849  }
1850 
1862  private void open(boolean isNewCase, ProgressIndicator progressIndicator) throws CaseActionException {
1863  try {
1865  createCaseDirectoryIfDoesNotExist(progressIndicator);
1867  switchLoggingToCaseLogsDirectory(progressIndicator);
1869  if (isNewCase) {
1870  saveCaseMetadataToFile(progressIndicator);
1871  }
1873  if (isNewCase) {
1874  createCaseNodeData(progressIndicator);
1875  } else {
1876  updateCaseNodeData(progressIndicator);
1877  }
1879  if (!isNewCase) {
1880  deleteTempfilesFromCaseDirectory(progressIndicator);
1881  }
1883  if (isNewCase) {
1884  createCaseDatabase(progressIndicator);
1885  } else {
1886  openCaseDataBase(progressIndicator);
1887  }
1889  openCaseLevelServices(progressIndicator);
1891  openAppServiceCaseResources(progressIndicator);
1893  openCommunicationChannels(progressIndicator);
1894 
1895  } catch (CaseActionException ex) {
1896  /*
1897  * Cancellation or failure. Clean up by calling the close method.
1898  * The sleep is a little hack to clear the interrupted flag for this
1899  * thread if this is a cancellation scenario, so that the clean up
1900  * can run to completion in the current thread.
1901  */
1902  try {
1903  Thread.sleep(1);
1904  } catch (InterruptedException discarded) {
1905  }
1906  close(progressIndicator);
1907  throw ex;
1908  }
1909  }
1910 
1921  public SleuthkitCase createPortableCase(String caseName, File portableCaseFolder) throws TskCoreException {
1922 
1923  if (portableCaseFolder.exists()) {
1924  throw new TskCoreException("Portable case folder " + portableCaseFolder.toString() + " already exists");
1925  }
1926  if (!portableCaseFolder.mkdirs()) {
1927  throw new TskCoreException("Error creating portable case folder " + portableCaseFolder.toString());
1928  }
1929 
1930  CaseDetails details = new CaseDetails(caseName, getNumber(), getExaminer(),
1932  try {
1933  CaseMetadata portableCaseMetadata = new CaseMetadata(Case.CaseType.SINGLE_USER_CASE, portableCaseFolder.toString(),
1934  caseName, details, metadata);
1935  portableCaseMetadata.setCaseDatabaseName(SINGLE_USER_CASE_DB_NAME);
1936  } catch (CaseMetadataException ex) {
1937  throw new TskCoreException("Error creating case metadata", ex);
1938  }
1939 
1940  // Create the Sleuthkit case
1941  SleuthkitCase portableSleuthkitCase;
1942  String dbFilePath = Paths.get(portableCaseFolder.toString(), SINGLE_USER_CASE_DB_NAME).toString();
1943  portableSleuthkitCase = SleuthkitCase.newCase(dbFilePath);
1944 
1945  return portableSleuthkitCase;
1946  }
1947 
1958  if (Thread.currentThread().isInterrupted()) {
1959  throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser());
1960  }
1961  }
1962 
1976  @Messages({
1977  "Case.progressMessage.creatingCaseDirectory=Creating case directory..."
1978  })
1979  private void createCaseDirectoryIfDoesNotExist(ProgressIndicator progressIndicator) throws CaseActionException {
1980  progressIndicator.progress(Bundle.Case_progressMessage_creatingCaseDirectory());
1981  if (new File(metadata.getCaseDirectory()).exists() == false) {
1982  progressIndicator.progress(Bundle.Case_progressMessage_creatingCaseDirectory());
1983  Case.createCaseDirectory(metadata.getCaseDirectory(), metadata.getCaseType());
1984  }
1985  }
1986 
1993  @Messages({
1994  "Case.progressMessage.switchingLogDirectory=Switching log directory..."
1995  })
1996  private void switchLoggingToCaseLogsDirectory(ProgressIndicator progressIndicator) {
1997  progressIndicator.progress(Bundle.Case_progressMessage_switchingLogDirectory());
1999  }
2000 
2012  @Messages({
2013  "Case.progressMessage.savingCaseMetadata=Saving case metadata to file...",
2014  "# {0} - exception message", "Case.exceptionMessage.couldNotSaveCaseMetadata=Failed to save case metadata:\n{0}."
2015  })
2016  private void saveCaseMetadataToFile(ProgressIndicator progressIndicator) throws CaseActionException {
2017  progressIndicator.progress(Bundle.Case_progressMessage_savingCaseMetadata());
2018  try {
2019  this.metadata.writeToFile();
2020  } catch (CaseMetadataException ex) {
2021  throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotSaveCaseMetadata(ex.getLocalizedMessage()), ex);
2022  }
2023  }
2024 
2036  @Messages({
2037  "Case.progressMessage.creatingCaseNodeData=Creating coordination service node data...",
2038  "# {0} - exception message", "Case.exceptionMessage.couldNotCreateCaseNodeData=Failed to create coordination service node data:\n{0}."
2039  })
2040  private void createCaseNodeData(ProgressIndicator progressIndicator) throws CaseActionException {
2042  progressIndicator.progress(Bundle.Case_progressMessage_creatingCaseNodeData());
2043  try {
2044  CaseNodeData.createCaseNodeData(metadata);
2045  } catch (CaseNodeDataException | InterruptedException ex) {
2046  throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotCreateCaseNodeData(ex.getLocalizedMessage()), ex);
2047  }
2048  }
2049  }
2050 
2062  @Messages({
2063  "Case.progressMessage.updatingCaseNodeData=Updating coordination service node data...",
2064  "# {0} - exception message", "Case.exceptionMessage.couldNotUpdateCaseNodeData=Failed to update coordination service node data:\n{0}."
2065  })
2066  private void updateCaseNodeData(ProgressIndicator progressIndicator) throws CaseActionException {
2068  progressIndicator.progress(Bundle.Case_progressMessage_updatingCaseNodeData());
2069  try {
2071  nodeData.setLastAccessDate(new Date());
2072  CaseNodeData.writeCaseNodeData(nodeData);
2073  } catch (CaseNodeDataException | InterruptedException ex) {
2074  throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotUpdateCaseNodeData(ex.getLocalizedMessage()), ex);
2075  }
2076  }
2077  }
2078 
2084  @Messages({
2085  "Case.progressMessage.clearingTempDirectory=Clearing case temp directory..."
2086  })
2087  private void deleteTempfilesFromCaseDirectory(ProgressIndicator progressIndicator) {
2088  /*
2089  * Clear the temp subdirectory of the case directory.
2090  */
2091  progressIndicator.progress(Bundle.Case_progressMessage_clearingTempDirectory());
2093  }
2094 
2106  @Messages({
2107  "Case.progressMessage.creatingCaseDatabase=Creating case database...",
2108  "# {0} - exception message", "Case.exceptionMessage.couldNotGetDbServerConnectionInfo=Failed to get case database server conneciton info:\n{0}.",
2109  "# {0} - exception message", "Case.exceptionMessage.couldNotCreateCaseDatabase=Failed to create case database:\n{0}.",
2110  "# {0} - exception message", "Case.exceptionMessage.couldNotSaveDbNameToMetadataFile=Failed to save case database name to case metadata file:\n{0}."
2111  })
2112  private void createCaseDatabase(ProgressIndicator progressIndicator) throws CaseActionException {
2113  progressIndicator.progress(Bundle.Case_progressMessage_creatingCaseDatabase());
2114  try {
2115  if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) {
2116  /*
2117  * For single-user cases, the case database is a SQLite database
2118  * with a standard name, physically located in the case
2119  * directory.
2120  */
2121  caseDb = SleuthkitCase.newCase(Paths.get(metadata.getCaseDirectory(), SINGLE_USER_CASE_DB_NAME).toString());
2122  metadata.setCaseDatabaseName(SINGLE_USER_CASE_DB_NAME);
2123  } else {
2124  /*
2125  * For multi-user cases, the case database is a PostgreSQL
2126  * database with a name derived from the case display name,
2127  * physically located on the PostgreSQL database server.
2128  */
2129  caseDb = SleuthkitCase.newCase(metadata.getCaseDisplayName(), UserPreferences.getDatabaseConnectionInfo(), metadata.getCaseDirectory());
2130  metadata.setCaseDatabaseName(caseDb.getDatabaseName());
2131  }
2132  } catch (TskCoreException ex) {
2133  throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotCreateCaseDatabase(ex.getLocalizedMessage()), ex);
2134  } catch (UserPreferencesException ex) {
2135  throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotGetDbServerConnectionInfo(ex.getLocalizedMessage()), ex);
2136  } catch (CaseMetadataException ex) {
2137  throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotSaveDbNameToMetadataFile(ex.getLocalizedMessage()), ex);
2138  }
2139  }
2140 
2152  @Messages({
2153  "Case.progressMessage.openingCaseDatabase=Opening case database...",
2154  "# {0} - exception message", "Case.exceptionMessage.couldNotOpenCaseDatabase=Failed to open case database:\n{0}.",
2155  "# {0} - exception message", "Case.exceptionMessage.unsupportedSchemaVersionMessage=Unsupported case database schema version:\n{0}.",
2156  "Case.open.exception.multiUserCaseNotEnabled=Cannot open a multi-user case if multi-user cases are not enabled. See Tools, Options, Multi-User."
2157  })
2158  private void openCaseDataBase(ProgressIndicator progressIndicator) throws CaseActionException {
2159  progressIndicator.progress(Bundle.Case_progressMessage_openingCaseDatabase());
2160  try {
2161  String databaseName = metadata.getCaseDatabaseName();
2162  if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) {
2163  caseDb = SleuthkitCase.openCase(Paths.get(metadata.getCaseDirectory(), databaseName).toString());
2165  caseDb = SleuthkitCase.openCase(databaseName, UserPreferences.getDatabaseConnectionInfo(), metadata.getCaseDirectory());
2166  } else {
2167  throw new CaseActionException(Bundle.Case_open_exception_multiUserCaseNotEnabled());
2168  }
2169  } catch (TskUnsupportedSchemaVersionException ex) {
2170  throw new CaseActionException(Bundle.Case_exceptionMessage_unsupportedSchemaVersionMessage(ex.getLocalizedMessage()), ex);
2171  } catch (UserPreferencesException ex) {
2172  throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotGetDbServerConnectionInfo(ex.getLocalizedMessage()), ex);
2173  } catch (TskCoreException ex) {
2174  throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotOpenCaseDatabase(ex.getLocalizedMessage()), ex);
2175  }
2176  }
2177 
2184  @Messages({
2185  "Case.progressMessage.openingCaseLevelServices=Opening case-level services...",})
2186  private void openCaseLevelServices(ProgressIndicator progressIndicator) {
2187  progressIndicator.progress(Bundle.Case_progressMessage_openingCaseLevelServices());
2188  this.caseServices = new Services(caseDb);
2189 
2190  caseDb.registerForEvents(tskEventForwarder);
2191  }
2192 
2204  @NbBundle.Messages({
2205  "Case.progressMessage.openingApplicationServiceResources=Opening application service case resources...",
2206  "# {0} - service name", "Case.serviceOpenCaseResourcesProgressIndicator.title={0} Opening Case Resources",
2207  "# {0} - service name", "Case.serviceOpenCaseResourcesProgressIndicator.cancellingMessage=Cancelling opening case resources by {0}...",
2208  "# {0} - service name", "Case.servicesException.notificationTitle={0} Error"
2209  })
2210  private void openAppServiceCaseResources(ProgressIndicator progressIndicator) throws CaseActionException {
2211  /*
2212  * Each service gets its own independently cancellable/interruptible
2213  * task, running in a named thread managed by an executor service, with
2214  * its own progress indicator. This allows for cancellation of the
2215  * opening of case resources for individual services. It also makes it
2216  * possible to ensure that each service task completes before the next
2217  * one starts by awaiting termination of the executor service.
2218  */
2219  progressIndicator.progress(Bundle.Case_progressMessage_openingApplicationServiceResources());
2220  for (AutopsyService service : Lookup.getDefault().lookupAll(AutopsyService.class)) {
2221  /*
2222  * Create a progress indicator for the task and start the task. If
2223  * running with a GUI, the progress indicator will be a dialog box
2224  * with a Cancel button.
2225  */
2226  CancelButtonListener cancelButtonListener = null;
2227  ProgressIndicator appServiceProgressIndicator;
2229  cancelButtonListener = new CancelButtonListener(Bundle.Case_serviceOpenCaseResourcesProgressIndicator_cancellingMessage(service.getServiceName()));
2230  appServiceProgressIndicator = new ModalDialogProgressIndicator(
2231  mainFrame,
2232  Bundle.Case_serviceOpenCaseResourcesProgressIndicator_title(service.getServiceName()),
2233  new String[]{Bundle.Case_progressIndicatorCancelButton_label()},
2234  Bundle.Case_progressIndicatorCancelButton_label(),
2235  cancelButtonListener);
2236  } else {
2237  appServiceProgressIndicator = new LoggingProgressIndicator();
2238  }
2239  appServiceProgressIndicator.start(Bundle.Case_progressMessage_preparing());
2240  AutopsyService.CaseContext context = new AutopsyService.CaseContext(this, appServiceProgressIndicator);
2241  String threadNameSuffix = service.getServiceName().replaceAll("[ ]", "-"); //NON-NLS
2242  threadNameSuffix = threadNameSuffix.toLowerCase();
2243  TaskThreadFactory threadFactory = new TaskThreadFactory(String.format(CASE_RESOURCES_THREAD_NAME, threadNameSuffix));
2244  ExecutorService executor = Executors.newSingleThreadExecutor(threadFactory);
2245  Future<Void> future = executor.submit(() -> {
2246  service.openCaseResources(context);
2247  return null;
2248  });
2249  if (null != cancelButtonListener) {
2250  cancelButtonListener.setCaseContext(context);
2251  cancelButtonListener.setCaseActionFuture(future);
2252  }
2253 
2254  /*
2255  * Wait for the task to either be completed or
2256  * cancelled/interrupted, or for the opening of the case to be
2257  * cancelled.
2258  */
2259  try {
2260  future.get();
2261  } catch (InterruptedException discarded) {
2262  /*
2263  * The parent create/open case task has been cancelled.
2264  */
2265  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()));
2266  future.cancel(true);
2267  } catch (CancellationException discarded) {
2268  /*
2269  * The opening of case resources by the application service has
2270  * been cancelled, so the executor service has thrown. Note that
2271  * there is no guarantee the task itself has responded to the
2272  * cancellation request yet.
2273  */
2274  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()));
2275  } catch (ExecutionException ex) {
2276  /*
2277  * An exception was thrown while executing the task. The
2278  * case-specific application service resources are not
2279  * essential. Log an error and notify the user if running the
2280  * desktop GUI, but do not throw.
2281  */
2282  Case.logger.log(Level.SEVERE, String.format("%s failed to open case resources for %s", service.getServiceName(), this.getDisplayName()), ex);
2284  SwingUtilities.invokeLater(() -> {
2285  MessageNotifyUtil.Notify.error(Bundle.Case_servicesException_notificationTitle(service.getServiceName()), ex.getLocalizedMessage());
2286  });
2287  }
2288  } finally {
2289  /*
2290  * Shut down the executor service and wait for it to finish.
2291  * This ensures that the task has finished. Without this, it
2292  * would be possible to start the next task before the current
2293  * task responded to a cancellation request.
2294  */
2296  appServiceProgressIndicator.finish();
2297  }
2299  }
2300  }
2301 
2313  @Messages({
2314  "Case.progressMessage.settingUpNetworkCommunications=Setting up network communications...",
2315  "# {0} - exception message", "Case.exceptionMessage.couldNotOpenRemoteEventChannel=Failed to open remote events channel:\n{0}.",
2316  "# {0} - exception message", "Case.exceptionMessage.couldNotCreatCollaborationMonitor=Failed to create collaboration monitor:\n{0}."
2317  })
2318  private void openCommunicationChannels(ProgressIndicator progressIndicator) throws CaseActionException {
2319  if (CaseType.MULTI_USER_CASE == metadata.getCaseType()) {
2320  progressIndicator.progress(Bundle.Case_progressMessage_settingUpNetworkCommunications());
2321  try {
2322  eventPublisher.openRemoteEventChannel(String.format(EVENT_CHANNEL_NAME, metadata.getCaseName()));
2324  collaborationMonitor = new CollaborationMonitor(metadata.getCaseName());
2325  } catch (AutopsyEventException ex) {
2326  throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotOpenRemoteEventChannel(ex.getLocalizedMessage()), ex);
2327  } catch (CollaborationMonitor.CollaborationMonitorException ex) {
2328  throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotCreatCollaborationMonitor(ex.getLocalizedMessage()), ex);
2329  }
2330  }
2331  }
2332 
2341  private void close() throws CaseActionException {
2342  /*
2343  * Set up either a GUI progress indicator without a Cancel button or a
2344  * logging progress indicator.
2345  */
2346  ProgressIndicator progressIndicator;
2348  progressIndicator = new ModalDialogProgressIndicator(
2349  mainFrame,
2350  Bundle.Case_progressIndicatorTitle_closingCase());
2351  } else {
2352  progressIndicator = new LoggingProgressIndicator();
2353  }
2354  progressIndicator.start(Bundle.Case_progressMessage_preparing());
2355 
2356  /*
2357  * Closing a case is always done in the same non-UI thread that
2358  * opened/created the case. If the case is a multi-user case, this
2359  * ensures that case directory lock that is held as long as the case is
2360  * open is released in the same thread in which it was acquired, as is
2361  * required by the coordination service.
2362  */
2363  Future<Void> future = caseLockingExecutor.submit(() -> {
2364  if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) {
2365  close(progressIndicator);
2366  } else {
2367  /*
2368  * Acquire an exclusive case resources lock to ensure only one
2369  * node at a time can create/open/upgrade/close the case
2370  * resources.
2371  */
2372  progressIndicator.progress(Bundle.Case_progressMessage_preparing());
2374  if (null == resourcesLock) {
2375  throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireResourcesLock());
2376  }
2377  close(progressIndicator);
2378  } finally {
2379  /*
2380  * Always release the case directory lock that was acquired
2381  * when the case was opened.
2382  */
2384  }
2385  }
2386  return null;
2387  });
2388 
2389  try {
2390  future.get();
2391  } catch (InterruptedException | CancellationException unused) {
2392  /*
2393  * The wait has been interrupted by interrupting the thread running
2394  * this method. Not allowing cancellation of case closing, so ignore
2395  * the interrupt. Likewsie, cancellation of the case closing task is
2396  * not supported.
2397  */
2398  } catch (ExecutionException ex) {
2399  throw new CaseActionException(Bundle.Case_exceptionMessage_execExceptionWrapperMessage(ex.getCause().getMessage()), ex);
2400  } finally {
2401  ThreadUtils.shutDownTaskExecutor(caseLockingExecutor);
2402  progressIndicator.finish();
2403  }
2404  }
2405 
2411  @Messages({
2412  "Case.progressMessage.shuttingDownNetworkCommunications=Shutting down network communications...",
2413  "Case.progressMessage.closingApplicationServiceResources=Closing case-specific application service resources...",
2414  "Case.progressMessage.closingCaseDatabase=Closing case database..."
2415  })
2416  private void close(ProgressIndicator progressIndicator) {
2418 
2419  /*
2420  * Stop sending/receiving case events to and from other nodes if this is
2421  * a multi-user case.
2422  */
2423  if (CaseType.MULTI_USER_CASE == metadata.getCaseType()) {
2424  progressIndicator.progress(Bundle.Case_progressMessage_shuttingDownNetworkCommunications());
2425  if (null != collaborationMonitor) {
2426  collaborationMonitor.shutdown();
2427  }
2428  eventPublisher.closeRemoteEventChannel();
2429  }
2430 
2431  /*
2432  * Allow all registered application services providers to close
2433  * resources related to the case.
2434  */
2435  progressIndicator.progress(Bundle.Case_progressMessage_closingApplicationServiceResources());
2437 
2438  /*
2439  * Close the case database.
2440  */
2441  if (null != caseDb) {
2442  progressIndicator.progress(Bundle.Case_progressMessage_closingCaseDatabase());
2443  caseDb.unregisterForEvents(tskEventForwarder);
2444  caseDb.close();
2445  }
2446 
2447  /*
2448  * Switch the log directory.
2449  */
2450  progressIndicator.progress(Bundle.Case_progressMessage_switchingLogDirectory());
2452  }
2453 
2458  @Messages({
2459  "# {0} - serviceName", "Case.serviceCloseResourcesProgressIndicator.title={0} Closing Case Resources",
2460  "# {0} - service name", "# {1} - exception message", "Case.servicesException.serviceResourcesCloseError=Could not close case resources for {0} service: {1}"
2461  })
2463  /*
2464  * Each service gets its own independently cancellable task, and thus
2465  * its own task progress indicator.
2466  */
2467  for (AutopsyService service : Lookup.getDefault().lookupAll(AutopsyService.class)) {
2468  ProgressIndicator progressIndicator;
2470  progressIndicator = new ModalDialogProgressIndicator(
2471  mainFrame,
2472  Bundle.Case_serviceCloseResourcesProgressIndicator_title(service.getServiceName()));
2473  } else {
2474  progressIndicator = new LoggingProgressIndicator();
2475  }
2476  progressIndicator.start(Bundle.Case_progressMessage_preparing());
2477  AutopsyService.CaseContext context = new AutopsyService.CaseContext(this, progressIndicator);
2478  String threadNameSuffix = service.getServiceName().replaceAll("[ ]", "-"); //NON-NLS
2479  threadNameSuffix = threadNameSuffix.toLowerCase();
2480  TaskThreadFactory threadFactory = new TaskThreadFactory(String.format(CASE_RESOURCES_THREAD_NAME, threadNameSuffix));
2481  ExecutorService executor = Executors.newSingleThreadExecutor(threadFactory);
2482  Future<Void> future = executor.submit(() -> {
2483  service.closeCaseResources(context);
2484  return null;
2485  });
2486  try {
2487  future.get();
2488  } catch (InterruptedException ex) {
2489  Case.logger.log(Level.SEVERE, String.format("Unexpected interrupt while waiting on %s service to close case resources", service.getServiceName()), ex);
2490  } catch (CancellationException ex) {
2491  Case.logger.log(Level.SEVERE, String.format("Unexpected cancellation while waiting on %s service to close case resources", service.getServiceName()), ex);
2492  } catch (ExecutionException ex) {
2493  Case.logger.log(Level.SEVERE, String.format("%s service failed to open case resources", service.getServiceName()), ex);
2495  SwingUtilities.invokeLater(() -> MessageNotifyUtil.Notify.error(
2496  Bundle.Case_servicesException_notificationTitle(service.getServiceName()),
2497  Bundle.Case_servicesException_serviceResourcesCloseError(service.getServiceName(), ex.getLocalizedMessage())));
2498  }
2499  } finally {
2501  progressIndicator.finish();
2502  }
2503  }
2504  }
2505 
2514  @Messages({"Case.creationException.couldNotAcquireDirLock=Failed to get lock on case directory"})
2515  private void acquireSharedCaseDirLock(String caseDir) throws CaseActionException {
2516  try {
2517  caseDirLock = CoordinationService.getInstance().tryGetSharedLock(CategoryNode.CASES, caseDir, DIR_LOCK_TIMOUT_HOURS, TimeUnit.HOURS);
2518  if (null == caseDirLock) {
2519  throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireDirLock());
2520  }
2521  } catch (InterruptedException | CoordinationServiceException ex) {
2522  throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireDirLock(), ex);
2523  }
2524  }
2525 
2531  private void releaseSharedCaseDirLock(String caseDir) {
2532  if (caseDirLock != null) {
2533  try {
2534  caseDirLock.release();
2535  caseDirLock = null;
2537  logger.log(Level.SEVERE, String.format("Failed to release shared case directory lock for %s", caseDir), ex);
2538  }
2539  }
2540  }
2541 
2548  private String getOrCreateSubdirectory(String subDirectoryName) {
2549  File subDirectory = Paths.get(getOutputDirectory(), subDirectoryName).toFile();
2550  if (!subDirectory.exists()) {
2551  subDirectory.mkdirs();
2552  }
2553  return subDirectory.toString();
2554 
2555  }
2556 
2568  @Messages({
2569  "Case.exceptionMessage.errorsDeletingCase=Errors occured while deleting the case. See the application log for details."
2570  })
2571  private static void deleteSingleUserCase(CaseMetadata metadata, ProgressIndicator progressIndicator) throws CaseActionException {
2572  boolean errorsOccurred = false;
2573  try {
2574  deleteTextIndex(metadata, progressIndicator);
2575  } catch (KeywordSearchServiceException ex) {
2576  errorsOccurred = true;
2577  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
2578  }
2579 
2580  try {
2581  deleteCaseDirectory(metadata, progressIndicator);
2582  } catch (CaseActionException ex) {
2583  errorsOccurred = true;
2584  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
2585  }
2586 
2587  deleteFromRecentCases(metadata, progressIndicator);
2588 
2589  if (errorsOccurred) {
2590  throw new CaseActionException(Bundle.Case_exceptionMessage_errorsDeletingCase());
2591  }
2592  }
2593 
2613  @Messages({
2614  "Case.progressMessage.connectingToCoordSvc=Connecting to coordination service...",
2615  "# {0} - exception message", "Case.exceptionMessage.failedToConnectToCoordSvc=Failed to connect to coordination service:\n{0}.",
2616  "Case.exceptionMessage.cannotGetLockToDeleteCase=Cannot delete case because it is open for another user or host.",
2617  "# {0} - exception message", "Case.exceptionMessage.failedToLockCaseForDeletion=Failed to exclusively lock case for deletion:\n{0}.",
2618  "Case.progressMessage.fetchingCoordSvcNodeData=Fetching coordination service node data for the case...",
2619  "# {0} - exception message", "Case.exceptionMessage.failedToFetchCoordSvcNodeData=Failed to fetch coordination service node data:\n{0}.",
2620  "Case.progressMessage.deletingResourcesCoordSvcNode=Deleting case resources coordination service node...",
2621  "Case.progressMessage.deletingCaseDirCoordSvcNode=Deleting case directory coordination service node..."
2622  })
2623  private static void deleteMultiUserCase(CaseMetadata metadata, ProgressIndicator progressIndicator) throws CaseActionException, InterruptedException {
2624  progressIndicator.progress(Bundle.Case_progressMessage_connectingToCoordSvc());
2625  CoordinationService coordinationService;
2626  try {
2627  coordinationService = CoordinationService.getInstance();
2628  } catch (CoordinationServiceException ex) {
2629  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
2630  throw new CaseActionException(Bundle.Case_exceptionMessage_failedToConnectToCoordSvc(ex.getLocalizedMessage()));
2631  }
2632 
2633  CaseNodeData caseNodeData;
2634  boolean errorsOccurred = false;
2635  try (CoordinationService.Lock dirLock = coordinationService.tryGetExclusiveLock(CategoryNode.CASES, metadata.getCaseDirectory())) {
2636  if (dirLock == null) {
2637  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
2638  throw new CaseActionException(Bundle.Case_exceptionMessage_cannotGetLockToDeleteCase());
2639  }
2640 
2641  progressIndicator.progress(Bundle.Case_progressMessage_fetchingCoordSvcNodeData());
2642  try {
2643  caseNodeData = CaseNodeData.readCaseNodeData(metadata.getCaseDirectory());
2644  } catch (CaseNodeDataException | InterruptedException ex) {
2645  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
2646  throw new CaseActionException(Bundle.Case_exceptionMessage_failedToFetchCoordSvcNodeData(ex.getLocalizedMessage()));
2647  }
2648 
2649  errorsOccurred = deleteMultiUserCase(caseNodeData, metadata, progressIndicator, logger);
2650 
2651  progressIndicator.progress(Bundle.Case_progressMessage_deletingResourcesCoordSvcNode());
2652  try {
2653  String resourcesLockNodePath = CoordinationServiceUtils.getCaseResourcesNodePath(caseNodeData.getDirectory());
2654  coordinationService.deleteNode(CategoryNode.CASES, resourcesLockNodePath);
2655  } catch (CoordinationServiceException ex) {
2656  if (!isNoNodeException(ex)) {
2657  errorsOccurred = true;
2658  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
2659  }
2660  } catch (InterruptedException ex) {
2661  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
2662  }
2663 
2664  } catch (CoordinationServiceException ex) {
2665  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
2666  throw new CaseActionException(Bundle.Case_exceptionMessage_failedToLockCaseForDeletion(ex.getLocalizedMessage()));
2667  }
2668 
2669  if (!errorsOccurred) {
2670  progressIndicator.progress(Bundle.Case_progressMessage_deletingCaseDirCoordSvcNode());
2671  try {
2672  String casDirNodePath = CoordinationServiceUtils.getCaseDirectoryNodePath(caseNodeData.getDirectory());
2673  coordinationService.deleteNode(CategoryNode.CASES, casDirNodePath);
2674  } catch (CoordinationServiceException | InterruptedException ex) {
2675  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
2676  errorsOccurred = true;
2677  }
2678  }
2679 
2680  if (errorsOccurred) {
2681  throw new CaseActionException(Bundle.Case_exceptionMessage_errorsDeletingCase());
2682  }
2683  }
2684 
2709  @Beta
2710  public static boolean deleteMultiUserCase(CaseNodeData caseNodeData, CaseMetadata metadata, ProgressIndicator progressIndicator, Logger logger) throws InterruptedException {
2711  boolean errorsOccurred = false;
2712  try {
2713  deleteMultiUserCaseDatabase(caseNodeData, metadata, progressIndicator, logger);
2714  deleteMultiUserCaseTextIndex(caseNodeData, metadata, progressIndicator, logger);
2715  deleteMultiUserCaseDirectory(caseNodeData, metadata, progressIndicator, logger);
2716  deleteFromRecentCases(metadata, progressIndicator);
2717  } catch (UserPreferencesException | ClassNotFoundException | SQLException ex) {
2718  errorsOccurred = true;
2719  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
2720  } catch (KeywordSearchServiceException ex) {
2721  errorsOccurred = true;
2722  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
2723  } catch (CaseActionException ex) {
2724  errorsOccurred = true;
2725  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
2726  }
2727  return errorsOccurred;
2728  }
2729 
2750  @Messages({
2751  "Case.progressMessage.deletingCaseDatabase=Deleting case database..."
2752  })
2753  private static void deleteMultiUserCaseDatabase(CaseNodeData caseNodeData, CaseMetadata metadata, ProgressIndicator progressIndicator, Logger logger) throws UserPreferencesException, ClassNotFoundException, SQLException, InterruptedException {
2754  if (!caseNodeData.isDeletedFlagSet(CaseNodeData.DeletedFlags.CASE_DB)) {
2755  progressIndicator.progress(Bundle.Case_progressMessage_deletingCaseDatabase());
2756  logger.log(Level.INFO, String.format("Deleting case database for %s (%s) in %s", caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory())); //NON-NLS
2757  CaseDbConnectionInfo info = UserPreferences.getDatabaseConnectionInfo();
2758  String url = "jdbc:postgresql://" + info.getHost() + ":" + info.getPort() + "/postgres"; //NON-NLS
2759  Class.forName("org.postgresql.Driver"); //NON-NLS
2760  try (Connection connection = DriverManager.getConnection(url, info.getUserName(), info.getPassword()); Statement statement = connection.createStatement()) {
2761  String dbExistsQuery = "SELECT 1 from pg_database WHERE datname = '" + metadata.getCaseDatabaseName() + "'"; //NON-NLS
2762  try (ResultSet queryResult = statement.executeQuery(dbExistsQuery)) {
2763  if (queryResult.next()) {
2764  String deleteCommand = "DROP DATABASE \"" + metadata.getCaseDatabaseName() + "\""; //NON-NLS
2765  statement.execute(deleteCommand);
2766  }
2767  }
2768  }
2770  }
2771  }
2772 
2788  private static void deleteMultiUserCaseTextIndex(CaseNodeData caseNodeData, CaseMetadata metadata, ProgressIndicator progressIndicator, Logger logger) throws KeywordSearchServiceException, InterruptedException {
2790  logger.log(Level.INFO, String.format("Deleting text index for %s", caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory())); //NON-NLS
2791  deleteTextIndex(metadata, progressIndicator);
2793  }
2794  }
2795 
2805  @Messages({
2806  "Case.progressMessage.deletingTextIndex=Deleting text index..."
2807  })
2808  private static void deleteTextIndex(CaseMetadata metadata, ProgressIndicator progressIndicator) throws KeywordSearchServiceException {
2809  progressIndicator.progress(Bundle.Case_progressMessage_deletingTextIndex());
2810  for (KeywordSearchService searchService : Lookup.getDefault().lookupAll(KeywordSearchService.class)) {
2811  searchService.deleteTextIndex(metadata);
2812  }
2813  }
2814 
2829  private static void deleteMultiUserCaseDirectory(CaseNodeData caseNodeData, CaseMetadata metadata, ProgressIndicator progressIndicator, Logger logger) throws CaseActionException, InterruptedException {
2830  if (!caseNodeData.isDeletedFlagSet(CaseNodeData.DeletedFlags.CASE_DIR)) {
2831  logger.log(Level.INFO, String.format("Deleting case directory for %s", caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory())); //NON-NLS
2832  deleteCaseDirectory(metadata, progressIndicator);
2834  }
2835  }
2836 
2846  @Messages({
2847  "Case.progressMessage.deletingCaseDirectory=Deleting case directory..."
2848  })
2849  private static void deleteCaseDirectory(CaseMetadata metadata, ProgressIndicator progressIndicator) throws CaseActionException {
2850  progressIndicator.progress(Bundle.Case_progressMessage_deletingCaseDirectory());
2851  if (!FileUtil.deleteDir(new File(metadata.getCaseDirectory()))) {
2852  throw new CaseActionException(String.format("Failed to delete %s", metadata.getCaseDirectory())); //NON-NLS
2853  }
2854  }
2855 
2863  @Messages({
2864  "Case.progressMessage.removingCaseFromRecentCases=Removing case from Recent Cases menu..."
2865  })
2866  private static void deleteFromRecentCases(CaseMetadata metadata, ProgressIndicator progressIndicator) {
2868  progressIndicator.progress(Bundle.Case_progressMessage_removingCaseFromRecentCases());
2869  SwingUtilities.invokeLater(() -> {
2870  RecentCases.getInstance().removeRecentCase(metadata.getCaseDisplayName(), metadata.getFilePath().toString());
2871  });
2872  }
2873  }
2874 
2885  boolean isNodeNodeEx = false;
2886  Throwable cause = ex.getCause();
2887  if (cause != null) {
2888  String causeMessage = cause.getMessage();
2889  isNodeNodeEx = causeMessage.contains(NO_NODE_ERROR_MSG_FRAGMENT);
2890  }
2891  return isNodeNodeEx;
2892  }
2893 
2905  private static void setDeletedItemFlag(CaseNodeData caseNodeData, CaseNodeData.DeletedFlags flag) throws InterruptedException {
2906  try {
2907  caseNodeData.setDeletedFlag(flag);
2908  CaseNodeData.writeCaseNodeData(caseNodeData);
2909  } catch (CaseNodeDataException ex) {
2910  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);
2911  }
2912  }
2913 
2918  @ThreadSafe
2919  private final static class CancelButtonListener implements ActionListener {
2920 
2921  private final String cancellationMessage;
2922  @GuardedBy("this")
2923  private boolean cancelRequested;
2924  @GuardedBy("this")
2926  @GuardedBy("this")
2927  private Future<?> caseActionFuture;
2928 
2937  private CancelButtonListener(String cancellationMessage) {
2938  this.cancellationMessage = cancellationMessage;
2939  }
2940 
2946  private synchronized void setCaseContext(CaseContext caseContext) {
2947  this.caseContext = caseContext;
2948  /*
2949  * If the cancel button has already been pressed, pass the
2950  * cancellation on to the case context.
2951  */
2952  if (cancelRequested) {
2953  cancel();
2954  }
2955  }
2956 
2962  private synchronized void setCaseActionFuture(Future<?> caseActionFuture) {
2963  this.caseActionFuture = caseActionFuture;
2964  /*
2965  * If the cancel button has already been pressed, cancel the Future
2966  * of the task.
2967  */
2968  if (cancelRequested) {
2969  cancel();
2970  }
2971  }
2972 
2978  @Override
2979  public synchronized void actionPerformed(ActionEvent event) {
2980  cancel();
2981  }
2982 
2986  private void cancel() {
2987  /*
2988  * At a minimum, set the cancellation requested flag of this
2989  * listener.
2990  */
2991  this.cancelRequested = true;
2992  if (null != this.caseContext) {
2993  /*
2994  * Set the cancellation request flag and display the
2995  * cancellation message in the progress indicator for the case
2996  * context associated with this listener.
2997  */
2999  ProgressIndicator progressIndicator = this.caseContext.getProgressIndicator();
3000  if (progressIndicator instanceof ModalDialogProgressIndicator) {
3001  ((ModalDialogProgressIndicator) progressIndicator).setCancelling(cancellationMessage);
3002  }
3003  }
3004  this.caseContext.requestCancel();
3005  }
3006  if (null != this.caseActionFuture) {
3007  /*
3008  * Cancel the Future of the task associated with this listener.
3009  * Note that the task thread will be interrupted if the task is
3010  * blocked.
3011  */
3012  this.caseActionFuture.cancel(true);
3013  }
3014  }
3015  }
3016 
3020  private static class TaskThreadFactory implements ThreadFactory {
3021 
3022  private final String threadName;
3023 
3024  private TaskThreadFactory(String threadName) {
3025  this.threadName = threadName;
3026  }
3027 
3028  @Override
3029  public Thread newThread(Runnable task) {
3030  return new Thread(task, threadName);
3031  }
3032 
3033  }
3034 
3042  @Deprecated
3043  public static String getAppName() {
3044  return UserPreferences.getAppName();
3045  }
3046 
3066  @Deprecated
3067  public static void create(String caseDir, String caseDisplayName, String caseNumber, String examiner) throws CaseActionException {
3068  createAsCurrentCase(caseDir, caseDisplayName, caseNumber, examiner, CaseType.SINGLE_USER_CASE);
3069  }
3070 
3091  @Deprecated
3092  public static void create(String caseDir, String caseDisplayName, String caseNumber, String examiner, CaseType caseType) throws CaseActionException {
3093  createAsCurrentCase(caseDir, caseDisplayName, caseNumber, examiner, caseType);
3094  }
3095 
3107  @Deprecated
3108  public static void open(String caseMetadataFilePath) throws CaseActionException {
3109  openAsCurrentCase(caseMetadataFilePath);
3110  }
3111 
3121  @Deprecated
3122  public void closeCase() throws CaseActionException {
3123  closeCurrentCase();
3124  }
3125 
3131  @Deprecated
3132  public static void invokeStartupDialog() {
3134  }
3135 
3149  @Deprecated
3150  public static String convertTimeZone(String timeZoneId) {
3151  return TimeZoneUtils.convertToAlphaNumericFormat(timeZoneId);
3152  }
3153 
3163  @Deprecated
3164  public static boolean pathExists(String filePath) {
3165  return new File(filePath).isFile();
3166  }
3167 
3176  @Deprecated
3177  public static String getAutopsyVersion() {
3178  return Version.getVersion();
3179  }
3180 
3188  @Deprecated
3189  public static boolean existsCurrentCase() {
3190  return isCaseOpen();
3191  }
3192 
3202  @Deprecated
3203  public static String getModulesOutputDirRelPath() {
3204  return "ModuleOutput"; //NON-NLS
3205  }
3206 
3216  @Deprecated
3217  public static PropertyChangeSupport
3219  return new PropertyChangeSupport(Case.class
3220  );
3221  }
3222 
3231  @Deprecated
3232  public String getModulesOutputDirAbsPath() {
3233  return getModuleDirectory();
3234  }
3235 
3250  @Deprecated
3251  public Image addImage(String imgPath, long imgId, String timeZone) throws CaseActionException {
3252  try {
3253  Image newDataSource = caseDb.getImageById(imgId);
3254  notifyDataSourceAdded(newDataSource, UUID.randomUUID());
3255  return newDataSource;
3256  } catch (TskCoreException ex) {
3257  throw new CaseActionException(NbBundle.getMessage(this.getClass(), "Case.addImg.exception.msg"), ex);
3258  }
3259  }
3260 
3268  @Deprecated
3269  public Set<TimeZone> getTimeZone() {
3270  return getTimeZones();
3271  }
3272 
3283  @Deprecated
3284  public void deleteReports(Collection<? extends Report> reports, boolean deleteFromDisk) throws TskCoreException {
3285  deleteReports(reports);
3286  }
3287 
3288 }
static final AutopsyEventPublisher eventPublisher
Definition: Case.java:151
List< Content > getDataSources()
Definition: Case.java:1375
static CaseNodeData createCaseNodeData(final CaseMetadata metadata)
void notifyContentTagDeleted(ContentTag deletedTag)
Definition: Case.java:1503
Case(CaseMetadata caseMetaData)
Definition: Case.java:1710
static CaseType fromString(String typeName)
Definition: Case.java:195
static final String CASE_ACTION_THREAD_NAME
Definition: Case.java:147
static void setDeletedItemFlag(CaseNodeData caseNodeData, CaseNodeData.DeletedFlags flag)
Definition: Case.java:2905
static void createAsCurrentCase(CaseType caseType, String caseDir, CaseDetails caseDetails)
Definition: Case.java:594
void notifyBlackBoardArtifactTagDeleted(BlackboardArtifactTag deletedTag)
Definition: Case.java:1555
static boolean deleteMultiUserCase(CaseNodeData caseNodeData, CaseMetadata metadata, ProgressIndicator progressIndicator, Logger logger)
Definition: Case.java:2710
Image addImage(String imgPath, long imgId, String timeZone)
Definition: Case.java:3251
static synchronized IngestManager getInstance()
void deleteNode(CategoryNode category, String nodePath)
static final String NO_NODE_ERROR_MSG_FRAGMENT
Definition: Case.java:149
static CoordinationService.Lock acquireExclusiveCaseResourcesLock(String caseDir)
Definition: Case.java:981
static boolean existsCurrentCase()
Definition: Case.java:3189
static void removePropertyChangeListener(PropertyChangeListener listener)
Definition: Case.java:452
void start(String message, int totalWorkUnits)
static final Logger logger
Definition: Case.java:150
static final int RESOURCES_LOCK_TIMOUT_HOURS
Definition: Case.java:137
static final String EXPORT_FOLDER
Definition: Case.java:141
static void createCaseDirectory(String caseDirPath, CaseType caseType)
Definition: Case.java:884
void notifyTagDefinitionChanged(String changedTagName)
Definition: Case.java:1514
static volatile Frame mainFrame
Definition: Case.java:153
static String convertTimeZone(String timeZoneId)
Definition: Case.java:3150
static boolean driveExists(String path)
Definition: DriveUtils.java:66
static void deleteSingleUserCase(CaseMetadata metadata, ProgressIndicator progressIndicator)
Definition: Case.java:2571
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:1529
static final String CACHE_FOLDER
Definition: Case.java:140
static void deleteMultiUserCaseDirectory(CaseNodeData caseNodeData, CaseMetadata metadata, ProgressIndicator progressIndicator, Logger logger)
Definition: Case.java:2829
Case(CaseType caseType, String caseDir, CaseDetails caseDetails)
Definition: Case.java:1701
void addReport(String localPath, String srcModuleName, String reportName)
Definition: Case.java:1570
static void updateGUIForCaseOpened(Case newCurrentCase)
Definition: Case.java:1007
void notifyDataSourceNameChanged(Content dataSource, String newName)
Definition: Case.java:1481
static CaseDbConnectionInfo getDatabaseConnectionInfo()
static void create(String caseDir, String caseDisplayName, String caseNumber, String examiner, CaseType caseType)
Definition: Case.java:3092
static final String SINGLE_USER_CASE_DB_NAME
Definition: Case.java:138
static void deleteCase(CaseMetadata metadata)
Definition: Case.java:757
void deleteReports(Collection<?extends Report > reports, boolean deleteFromDisk)
Definition: Case.java:3284
volatile ExecutorService caseLockingExecutor
Definition: Case.java:156
synchronized void setCaseContext(CaseContext caseContext)
Definition: Case.java:2946
static void clearTempSubDir(String tempSubDirPath)
Definition: Case.java:1134
void rebroadcastArtifactsPosted(Blackboard.ArtifactsPostedEvent event)
Definition: Case.java:418
static boolean isValidName(String caseName)
Definition: Case.java:536
void acquireSharedCaseDirLock(String caseDir)
Definition: Case.java:2515
void openCaseDataBase(ProgressIndicator progressIndicator)
Definition: Case.java:2158
void createCaseDirectoryIfDoesNotExist(ProgressIndicator progressIndicator)
Definition: Case.java:1979
CollaborationMonitor collaborationMonitor
Definition: Case.java:159
void openCommunicationChannels(ProgressIndicator progressIndicator)
Definition: Case.java:2318
void releaseSharedCaseDirLock(String caseDir)
Definition: Case.java:2531
static void shutDownTaskExecutor(ExecutorService executor)
static String getModulesOutputDirRelPath()
Definition: Case.java:3203
void saveCaseMetadataToFile(ProgressIndicator progressIndicator)
Definition: Case.java:2016
static void deleteMultiUserCaseTextIndex(CaseNodeData caseNodeData, CaseMetadata metadata, ProgressIndicator progressIndicator, Logger logger)
Definition: Case.java:2788
synchronized void actionPerformed(ActionEvent event)
Definition: Case.java:2979
static final String MODULE_FOLDER
Definition: Case.java:146
void openCaseLevelServices(ProgressIndicator progressIndicator)
Definition: Case.java:2186
static void create(String caseDir, String caseDisplayName, String caseNumber, String examiner)
Definition: Case.java:3067
synchronized void openRemoteEventChannel(String channelName)
void rebroadcastTimelineEventCreated(TimelineManager.TimelineEventAddedEvent event)
Definition: Case.java:412
void createCaseNodeData(ProgressIndicator progressIndicator)
Definition: Case.java:2040
static String displayNameToUniqueName(String caseDisplayName)
Definition: Case.java:844
static void deleteFromRecentCases(CaseMetadata metadata, ProgressIndicator progressIndicator)
Definition: Case.java:2866
void open(boolean isNewCase)
Definition: Case.java:1739
static void openAsCurrentCase(String caseMetadataFilePath)
Definition: Case.java:621
static void removeEventSubscriber(Set< String > eventNames, PropertyChangeListener subscriber)
Definition: Case.java:512
Lock tryGetExclusiveLock(CategoryNode category, String nodePath, int timeOut, TimeUnit timeUnit)
static boolean isNoNodeException(CoordinationServiceException ex)
Definition: Case.java:2884
void fireModuleDataEvent(ModuleDataEvent moduleDataEvent)
default void setCancelling(String cancellingMessage)
static final int DIR_LOCK_TIMOUT_HOURS
Definition: Case.java:136
void close(ProgressIndicator progressIndicator)
Definition: Case.java:2416
void notifyBlackBoardArtifactTagAdded(BlackboardArtifactTag newTag)
Definition: Case.java:1544
static PropertyChangeSupport getPropertyChangeSupport()
Definition: Case.java:3218
static void addPropertyChangeListener(PropertyChangeListener listener)
Definition: Case.java:440
synchronized void setCaseActionFuture(Future<?> caseActionFuture)
Definition: Case.java:2962
void removeSubscriber(Set< String > eventNames, PropertyChangeListener subscriber)
void createCaseDatabase(ProgressIndicator progressIndicator)
Definition: Case.java:2112
void openAppServiceCaseResources(ProgressIndicator progressIndicator)
Definition: Case.java:2210
static void removeEventSubscriber(String eventName, PropertyChangeListener subscriber)
Definition: Case.java:502
void switchLoggingToCaseLogsDirectory(ProgressIndicator progressIndicator)
Definition: Case.java:1996
static void deleteTextIndex(CaseMetadata metadata, ProgressIndicator progressIndicator)
Definition: Case.java:2808
void deleteTempfilesFromCaseDirectory(ProgressIndicator progressIndicator)
Definition: Case.java:2087
void deleteReports(Collection<?extends Report > reports)
Definition: Case.java:1625
void notifyDataSourceAdded(Content dataSource, UUID addingDataSourceEventId)
Definition: Case.java:1468
Report addReport(String localPath, String srcModuleName, String reportName, Content parent)
Definition: Case.java:1588
static boolean pathExists(String filePath)
Definition: Case.java:3164
SleuthkitCase createPortableCase(String caseName, File portableCaseFolder)
Definition: Case.java:1921
static void open(String caseMetadataFilePath)
Definition: Case.java:3108
static void openAsCurrentCase(Case newCurrentCase, boolean isNewCase)
Definition: Case.java:804
static void error(String title, String message)
String getOrCreateSubdirectory(String subDirectoryName)
Definition: Case.java:2548
boolean equalsName(String otherTypeName)
Definition: Case.java:253
static final String EVENT_CHANNEL_NAME
Definition: Case.java:139
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
static void addEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:477
static String convertToAlphaNumericFormat(String timeZoneId)
static final String LOG_FOLDER
Definition: Case.java:142
Lock tryGetSharedLock(CategoryNode category, String nodePath, int timeOut, TimeUnit timeUnit)
static volatile Case currentCase
Definition: Case.java:154
void notifyAddingDataSource(UUID eventId)
Definition: Case.java:1439
CoordinationService.Lock caseDirLock
Definition: Case.java:157
static void addEventSubscriber(String eventName, PropertyChangeListener subscriber)
Definition: Case.java:492
void notifyContentTagAdded(ContentTag newTag)
Definition: Case.java:1492
void cancelAllIngestJobs(IngestJob.CancellationReason reason)
static final String CASE_RESOURCES_THREAD_NAME
Definition: Case.java:148
static boolean deleteDir(File dirPath)
Definition: FileUtil.java:49
static void createAsCurrentCase(String caseDir, String caseDisplayName, String caseNumber, String examiner, CaseType caseType)
Definition: Case.java:567
static void deleteCaseDirectory(CaseMetadata metadata, ProgressIndicator progressIndicator)
Definition: Case.java:2849
void notifyFailedAddingDataSource(UUID addingDataSourceEventId)
Definition: Case.java:1453
static void deleteMultiUserCase(CaseMetadata metadata, ProgressIndicator progressIndicator)
Definition: Case.java:2623
static final String CONFIG_FOLDER
Definition: Case.java:144
static void removeEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:522
static final Object caseActionSerializationLock
Definition: Case.java:152
void open(boolean isNewCase, ProgressIndicator progressIndicator)
Definition: Case.java:1862
static void deleteMultiUserCaseDatabase(CaseNodeData caseNodeData, CaseMetadata metadata, ProgressIndicator progressIndicator, Logger logger)
Definition: Case.java:2753
final TSKCaseRepublisher tskEventForwarder
Definition: Case.java:162
static final String REPORTS_FOLDER
Definition: Case.java:143
static final String TEMP_FOLDER
Definition: Case.java:145
static void addEventSubscriber(Set< String > eventNames, PropertyChangeListener subscriber)
Definition: Case.java:467
void updateCaseNodeData(ProgressIndicator progressIndicator)
Definition: Case.java:2066
static synchronized IngestServices getInstance()

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