Autopsy  4.14.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-2020 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 
22 import com.google.common.annotations.Beta;
23 import com.google.common.eventbus.Subscribe;
25 import java.awt.Frame;
26 import java.awt.event.ActionEvent;
27 import java.awt.event.ActionListener;
28 import java.beans.PropertyChangeListener;
29 import java.beans.PropertyChangeSupport;
30 import java.io.File;
31 import java.nio.file.InvalidPathException;
32 import java.nio.file.Path;
33 import java.nio.file.Paths;
34 import java.sql.Connection;
35 import java.sql.DriverManager;
36 import java.sql.ResultSet;
37 import java.sql.SQLException;
38 import java.sql.Statement;
39 import java.text.SimpleDateFormat;
40 import java.util.Collection;
41 import java.util.Date;
42 import java.util.HashMap;
43 import java.util.HashSet;
44 import java.util.List;
45 import java.util.Map;
46 import java.util.Set;
47 import java.util.TimeZone;
48 import java.util.UUID;
49 import java.util.concurrent.CancellationException;
50 import java.util.concurrent.ExecutionException;
51 import java.util.concurrent.ExecutorService;
52 import java.util.concurrent.Executors;
53 import java.util.concurrent.Future;
54 import java.util.concurrent.ThreadFactory;
55 import java.util.concurrent.TimeUnit;
56 import java.util.logging.Level;
57 import java.util.stream.Collectors;
58 import java.util.stream.Stream;
59 import javax.annotation.concurrent.GuardedBy;
60 import javax.annotation.concurrent.ThreadSafe;
61 import javax.swing.JOptionPane;
62 import javax.swing.SwingUtilities;
63 import org.openide.util.Lookup;
64 import org.openide.util.NbBundle;
65 import org.openide.util.NbBundle.Messages;
66 import org.openide.util.actions.CallableSystemAction;
67 import org.openide.windows.WindowManager;
121 import org.sleuthkit.datamodel.Blackboard;
122 import org.sleuthkit.datamodel.BlackboardArtifact;
123 import org.sleuthkit.datamodel.BlackboardArtifactTag;
124 import org.sleuthkit.datamodel.CaseDbConnectionInfo;
125 import org.sleuthkit.datamodel.Content;
126 import org.sleuthkit.datamodel.ContentTag;
127 import org.sleuthkit.datamodel.DataSource;
128 import org.sleuthkit.datamodel.Image;
129 import org.sleuthkit.datamodel.Report;
130 import org.sleuthkit.datamodel.SleuthkitCase;
131 import org.sleuthkit.datamodel.TimelineManager;
132 import org.sleuthkit.datamodel.SleuthkitCaseAdminUtil;
133 import org.sleuthkit.datamodel.TskCoreException;
134 import org.sleuthkit.datamodel.TskDataException;
135 import org.sleuthkit.datamodel.TskUnsupportedSchemaVersionException;
136 
140 public class Case {
141 
142  private static final int CASE_LOCK_TIMEOUT_MINS = 1;
143  private static final int CASE_RESOURCES_LOCK_TIMEOUT_HOURS = 1;
144  private static final String SINGLE_USER_CASE_DB_NAME = "autopsy.db";
145  private static final String EVENT_CHANNEL_NAME = "%s-Case-Events"; //NON-NLS
146  private static final String CACHE_FOLDER = "Cache"; //NON-NLS
147  private static final String EXPORT_FOLDER = "Export"; //NON-NLS
148  private static final String LOG_FOLDER = "Log"; //NON-NLS
149  private static final String REPORTS_FOLDER = "Reports"; //NON-NLS
150  private static final String CONFIG_FOLDER = "Config"; // NON-NLS
151  private static final String TEMP_FOLDER = "Temp"; //NON-NLS
152  private static final String MODULE_FOLDER = "ModuleOutput"; //NON-NLS
153  private static final String CASE_ACTION_THREAD_NAME = "%s-case-action";
154  private static final String CASE_RESOURCES_THREAD_NAME = "%s-manage-case-resources";
155  private static final String NO_NODE_ERROR_MSG_FRAGMENT = "KeeperErrorCode = NoNode";
156  private static final Logger logger = Logger.getLogger(Case.class.getName());
158  private static final Object caseActionSerializationLock = new Object();
159  private static volatile Frame mainFrame;
160  private static volatile Case currentCase;
161  private final CaseMetadata metadata;
162  private volatile ExecutorService caseActionExecutor;
164  private SleuthkitCase caseDb;
166  private CollaborationMonitor collaborationMonitor;
168 
169  /*
170  * Get a reference to the main window of the desktop application to use to
171  * parent pop up dialogs and initialize the application name for use in
172  * changing the main window title.
173  */
174  static {
175  WindowManager.getDefault().invokeWhenUIReady(() -> {
176  mainFrame = WindowManager.getDefault().getMainWindow();
177  });
178  }
179 
183  public enum CaseType {
184 
185  SINGLE_USER_CASE("Single-user case"), //NON-NLS
186  MULTI_USER_CASE("Multi-user case"); //NON-NLS
187 
188  private final String typeName;
189 
197  public static CaseType fromString(String typeName) {
198  if (typeName != null) {
199  for (CaseType c : CaseType.values()) {
200  if (typeName.equalsIgnoreCase(c.toString())) {
201  return c;
202  }
203  }
204  }
205  return null;
206  }
207 
213  @Override
214  public String toString() {
215  return typeName;
216  }
217 
223  @Messages({
224  "Case_caseType_singleUser=Single-user case",
225  "Case_caseType_multiUser=Multi-user case"
226  })
228  if (fromString(typeName) == SINGLE_USER_CASE) {
229  return Bundle.Case_caseType_singleUser();
230  } else {
231  return Bundle.Case_caseType_multiUser();
232  }
233  }
234 
240  private CaseType(String typeName) {
241  this.typeName = typeName;
242  }
243 
254  @Deprecated
255  public boolean equalsName(String otherTypeName) {
256  return (otherTypeName == null) ? false : typeName.equals(otherTypeName);
257  }
258 
259  };
260 
265  public enum Events {
266 
274  @Deprecated
283  @Deprecated
292  @Deprecated
403  /*
404  * An item in the central repository has had its comment modified. The
405  * old value is null, the new value is string for current comment.
406  */
408 
409  };
410 
416  private final class SleuthkitEventListener {
417 
418  @Subscribe
419  public void publishTimelineEventAddedEvent(TimelineManager.TimelineEventAddedEvent event) {
420  eventPublisher.publish(new TimelineEventAddedEvent(event));
421  }
422 
423  @SuppressWarnings("deprecation")
424  @Subscribe
425  public void publishArtifactsPostedEvent(Blackboard.ArtifactsPostedEvent event) {
426  for (BlackboardArtifact.Type artifactType : event.getArtifactTypes()) {
427  /*
428  * IngestServices.fireModuleDataEvent is deprecated to
429  * discourage ingest module writers from using it (they should
430  * use org.sleuthkit.datamodel.Blackboard.postArtifact(s)
431  * instead), but a way to publish
432  * Blackboard.ArtifactsPostedEvents from the SleuthKit layer as
433  * Autopsy ModuleDataEvents is still needed.
434  */
436  event.getModuleName(),
437  artifactType,
438  event.getArtifacts(artifactType)));
439  }
440  }
441  }
442 
449  public static void addPropertyChangeListener(PropertyChangeListener listener) {
450  addEventSubscriber(Stream.of(Events.values())
451  .map(Events::toString)
452  .collect(Collectors.toSet()), listener);
453  }
454 
461  public static void removePropertyChangeListener(PropertyChangeListener listener) {
462  removeEventSubscriber(Stream.of(Events.values())
463  .map(Events::toString)
464  .collect(Collectors.toSet()), listener);
465  }
466 
475  @Deprecated
476  public static void addEventSubscriber(Set<String> eventNames, PropertyChangeListener subscriber) {
477  eventPublisher.addSubscriber(eventNames, subscriber);
478  }
479 
486  public static void addEventTypeSubscriber(Set<Events> eventTypes, PropertyChangeListener subscriber) {
487  eventTypes.forEach((Events event) -> {
488  eventPublisher.addSubscriber(event.toString(), subscriber);
489  });
490  }
491 
500  @Deprecated
501  public static void addEventSubscriber(String eventName, PropertyChangeListener subscriber) {
502  eventPublisher.addSubscriber(eventName, subscriber);
503  }
504 
511  public static void removeEventSubscriber(String eventName, PropertyChangeListener subscriber) {
512  eventPublisher.removeSubscriber(eventName, subscriber);
513  }
514 
521  public static void removeEventSubscriber(Set<String> eventNames, PropertyChangeListener subscriber) {
522  eventPublisher.removeSubscriber(eventNames, subscriber);
523  }
524 
531  public static void removeEventTypeSubscriber(Set<Events> eventTypes, PropertyChangeListener subscriber) {
532  eventTypes.forEach((Events event) -> {
533  eventPublisher.removeSubscriber(event.toString(), subscriber);
534  });
535  }
536 
545  public static boolean isValidName(String caseName) {
546  return !(caseName.contains("\\") || caseName.contains("/") || caseName.contains(":")
547  || caseName.contains("*") || caseName.contains("?") || caseName.contains("\"")
548  || caseName.contains("<") || caseName.contains(">") || caseName.contains("|"));
549  }
550 
575  @Deprecated
576  public static void createAsCurrentCase(String caseDir, String caseDisplayName, String caseNumber, String examiner, CaseType caseType) throws CaseActionException, CaseActionCancelledException {
577  createAsCurrentCase(caseType, caseDir, new CaseDetails(caseDisplayName, caseNumber, examiner, "", "", ""));
578  }
579 
599  @Messages({
600  "Case.exceptionMessage.emptyCaseName=Must specify a case name.",
601  "Case.exceptionMessage.emptyCaseDir=Must specify a case directory path."
602  })
603  public static void createAsCurrentCase(CaseType caseType, String caseDir, CaseDetails caseDetails) throws CaseActionException, CaseActionCancelledException {
604  if (caseDetails.getCaseDisplayName().isEmpty()) {
605  throw new CaseActionException(Bundle.Case_exceptionMessage_emptyCaseName());
606  }
607  if (caseDir.isEmpty()) {
608  throw new CaseActionException(Bundle.Case_exceptionMessage_emptyCaseDir());
609  }
610  openAsCurrentCase(new Case(caseType, caseDir, caseDetails), true);
611  }
612 
626  @Messages({
627  "# {0} - exception message", "Case.exceptionMessage.failedToReadMetadata=Failed to read case metadata:\n{0}.",
628  "Case.exceptionMessage.cannotOpenMultiUserCaseNoSettings=Multi-user settings are missing (see Tools, Options, Multi-user tab), cannot open a multi-user case."
629  })
630  public static void openAsCurrentCase(String caseMetadataFilePath) throws CaseActionException {
632  try {
633  metadata = new CaseMetadata(Paths.get(caseMetadataFilePath));
634  } catch (CaseMetadataException ex) {
635  throw new CaseActionException(Bundle.Case_exceptionMessage_failedToReadMetadata(ex.getLocalizedMessage()), ex);
636  }
638  throw new CaseActionException(Bundle.Case_exceptionMessage_cannotOpenMultiUserCaseNoSettings());
639  }
640  openAsCurrentCase(new Case(metadata), false);
641  }
642 
648  public static boolean isCaseOpen() {
649  return currentCase != null;
650  }
651 
659  public static Case getCurrentCase() {
660  try {
661  return getCurrentCaseThrows();
662  } catch (NoCurrentCaseException ex) {
663  /*
664  * Throw a runtime exception, since this is a programming error.
665  */
666  throw new IllegalStateException(NbBundle.getMessage(Case.class, "Case.getCurCase.exception.noneOpen"), ex);
667  }
668  }
669 
685  /*
686  * TODO (JIRA-3825): Introduce a reference counting scheme for this get
687  * case method.
688  */
689  Case openCase = currentCase;
690  if (openCase == null) {
691  throw new NoCurrentCaseException(NbBundle.getMessage(Case.class, "Case.getCurCase.exception.noneOpen"));
692  } else {
693  return openCase;
694  }
695  }
696 
705  @Messages({
706  "# {0} - exception message", "Case.closeException.couldNotCloseCase=Error closing case: {0}",
707  "Case.progressIndicatorTitle.closingCase=Closing Case"
708  })
709  public static void closeCurrentCase() throws CaseActionException {
710  synchronized (caseActionSerializationLock) {
711  if (null == currentCase) {
712  return;
713  }
714  Case closedCase = currentCase;
715  try {
716  eventPublisher.publishLocally(new AutopsyEvent(Events.CURRENT_CASE.toString(), closedCase, null));
717  logger.log(Level.INFO, "Closing current case {0} ({1}) in {2}", new Object[]{closedCase.getDisplayName(), closedCase.getName(), closedCase.getCaseDirectory()}); //NON-NLS
718  currentCase = null;
719  closedCase.doCloseCaseAction();
720  logger.log(Level.INFO, "Closed current case {0} ({1}) in {2}", new Object[]{closedCase.getDisplayName(), closedCase.getName(), closedCase.getCaseDirectory()}); //NON-NLS
721  } catch (CaseActionException ex) {
722  logger.log(Level.SEVERE, String.format("Error closing current case %s (%s) in %s", closedCase.getDisplayName(), closedCase.getName(), closedCase.getCaseDirectory()), ex); //NON-NLS
723  throw ex;
724  } finally {
727  }
728  }
729  }
730  }
731 
740  public static void deleteCurrentCase() throws CaseActionException {
741  synchronized (caseActionSerializationLock) {
742  if (null == currentCase) {
743  return;
744  }
745  CaseMetadata metadata = currentCase.getMetadata();
747  deleteCase(metadata);
748  }
749  }
750 
761  @Messages({
762  "Case.progressIndicatorTitle.deletingDataSource=Removing Data Source"
763  })
764  static void deleteDataSourceFromCurrentCase(Long dataSourceObjectID) throws CaseActionException {
765  synchronized (caseActionSerializationLock) {
766  if (null == currentCase) {
767  return;
768  }
769 
770  /*
771  * Close the current case to release the shared case lock.
772  */
773  CaseMetadata caseMetadata = currentCase.getMetadata();
775 
776  /*
777  * Re-open the case with an exclusive case lock, delete the data
778  * source, and close the case again, releasing the exclusive case
779  * lock.
780  */
781  Case theCase = new Case(caseMetadata);
782  theCase.doOpenCaseAction(Bundle.Case_progressIndicatorTitle_deletingDataSource(), theCase::deleteDataSource, CaseLockType.EXCLUSIVE, false, dataSourceObjectID);
783 
784  /*
785  * Re-open the case with a shared case lock.
786  */
787  openAsCurrentCase(new Case(caseMetadata), false);
788  }
789  }
790 
802  @Messages({
803  "Case.progressIndicatorTitle.deletingCase=Deleting Case",
804  "Case.exceptionMessage.cannotDeleteCurrentCase=Cannot delete current case, it must be closed first.",
805  "# {0} - case display name", "Case.exceptionMessage.deletionInterrupted=Deletion of the case {0} was cancelled."
806  })
807  public static void deleteCase(CaseMetadata metadata) throws CaseActionException {
808  synchronized (caseActionSerializationLock) {
809  if (null != currentCase) {
810  throw new CaseActionException(Bundle.Case_exceptionMessage_cannotDeleteCurrentCase());
811  }
812  }
813 
814  ProgressIndicator progressIndicator;
816  progressIndicator = new ModalDialogProgressIndicator(mainFrame, Bundle.Case_progressIndicatorTitle_deletingCase());
817  } else {
818  progressIndicator = new LoggingProgressIndicator();
819  }
820  progressIndicator.start(Bundle.Case_progressMessage_preparing());
821  try {
822  if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) {
823  deleteSingleUserCase(metadata, progressIndicator);
824  } else {
825  try {
826  deleteMultiUserCase(metadata, progressIndicator);
827  } catch (InterruptedException ex) {
828  /*
829  * Note that task cancellation is not currently supported
830  * for this code path, so this catch block is not expected
831  * to be executed.
832  */
833  throw new CaseActionException(Bundle.Case_exceptionMessage_deletionInterrupted(metadata.getCaseDisplayName()), ex);
834  }
835  }
836  } finally {
837  progressIndicator.finish();
838  }
839  }
840 
851  @Messages({
852  "Case.progressIndicatorTitle.creatingCase=Creating Case",
853  "Case.progressIndicatorTitle.openingCase=Opening Case",
854  "Case.exceptionMessage.cannotLocateMainWindow=Cannot locate main application window"
855  })
856  private static void openAsCurrentCase(Case newCurrentCase, boolean isNewCase) throws CaseActionException, CaseActionCancelledException {
857  synchronized (caseActionSerializationLock) {
858  if (null != currentCase) {
859  try {
861  } catch (CaseActionException ex) {
862  /*
863  * Notify the user and continue (the error has already been
864  * logged in closeCurrentCase.
865  */
866  MessageNotifyUtil.Message.error(ex.getLocalizedMessage());
867  }
868  }
869  try {
870  logger.log(Level.INFO, "Opening {0} ({1}) in {2} as the current case", new Object[]{newCurrentCase.getDisplayName(), newCurrentCase.getName(), newCurrentCase.getCaseDirectory()}); //NON-NLS
871  String progressIndicatorTitle;
873  if (isNewCase) {
874  progressIndicatorTitle = Bundle.Case_progressIndicatorTitle_creatingCase();
875  openCaseAction = newCurrentCase::create;
876  } else {
877  progressIndicatorTitle = Bundle.Case_progressIndicatorTitle_openingCase();
878  openCaseAction = newCurrentCase::open;
879  }
880  newCurrentCase.doOpenCaseAction(progressIndicatorTitle, openCaseAction, CaseLockType.SHARED, true, null);
881  currentCase = newCurrentCase;
882  logger.log(Level.INFO, "Opened {0} ({1}) in {2} as the current case", new Object[]{newCurrentCase.getDisplayName(), newCurrentCase.getName(), newCurrentCase.getCaseDirectory()}); //NON-NLS
884  updateGUIForCaseOpened(newCurrentCase);
885  }
886  eventPublisher.publishLocally(new AutopsyEvent(Events.CURRENT_CASE.toString(), null, currentCase));
887  } catch (CaseActionCancelledException ex) {
888  logger.log(Level.INFO, String.format("Cancelled opening %s (%s) in %s as the current case", newCurrentCase.getDisplayName(), newCurrentCase.getName(), newCurrentCase.getCaseDirectory())); //NON-NLS
889  throw ex;
890  } catch (CaseActionException ex) {
891  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
892  throw ex;
893  }
894  }
895  }
896 
905  private static String displayNameToUniqueName(String caseDisplayName) {
906  /*
907  * Replace all non-ASCII characters.
908  */
909  String uniqueCaseName = caseDisplayName.replaceAll("[^\\p{ASCII}]", "_"); //NON-NLS
910 
911  /*
912  * Replace all control characters.
913  */
914  uniqueCaseName = uniqueCaseName.replaceAll("[\\p{Cntrl}]", "_"); //NON-NLS
915 
916  /*
917  * Replace /, \, :, ?, space, ' ".
918  */
919  uniqueCaseName = uniqueCaseName.replaceAll("[ /?:'\"\\\\]", "_"); //NON-NLS
920 
921  /*
922  * Make it all lowercase.
923  */
924  uniqueCaseName = uniqueCaseName.toLowerCase();
925 
926  /*
927  * Add a time stamp for uniqueness.
928  */
929  SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
930  Date date = new Date();
931  uniqueCaseName = uniqueCaseName + "_" + dateFormat.format(date);
932 
933  return uniqueCaseName;
934  }
935 
945  public static void createCaseDirectory(String caseDirPath, CaseType caseType) throws CaseActionException {
946  /*
947  * Check the case directory path and permissions. The case directory may
948  * already exist.
949  */
950  File caseDir = new File(caseDirPath);
951  if (caseDir.exists()) {
952  if (caseDir.isFile()) {
953  throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.existNotDir", caseDirPath));
954  } else if (!caseDir.canRead() || !caseDir.canWrite()) {
955  throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.existCantRW", caseDirPath));
956  }
957  }
958 
959  /*
960  * Create the case directory, if it does not already exist.
961  */
962  if (!caseDir.mkdirs()) {
963  throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreate", caseDirPath));
964  }
965 
966  /*
967  * Create the subdirectories of the case directory, if they do not
968  * already exist. Note that multi-user cases get an extra layer of
969  * subdirectories, one subdirectory per application host machine.
970  */
971  String hostPathComponent = "";
972  if (caseType == CaseType.MULTI_USER_CASE) {
973  hostPathComponent = File.separator + NetworkUtils.getLocalHostName();
974  }
975 
976  Path exportDir = Paths.get(caseDirPath, hostPathComponent, EXPORT_FOLDER);
977  if (!exportDir.toFile().mkdirs()) {
978  throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreateCaseDir", exportDir));
979  }
980 
981  Path logsDir = Paths.get(caseDirPath, hostPathComponent, LOG_FOLDER);
982  if (!logsDir.toFile().mkdirs()) {
983  throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreateCaseDir", logsDir));
984  }
985 
986  Path tempDir = Paths.get(caseDirPath, hostPathComponent, TEMP_FOLDER);
987  if (!tempDir.toFile().mkdirs()) {
988  throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreateCaseDir", tempDir));
989  }
990 
991  Path cacheDir = Paths.get(caseDirPath, hostPathComponent, CACHE_FOLDER);
992  if (!cacheDir.toFile().mkdirs()) {
993  throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreateCaseDir", cacheDir));
994  }
995 
996  Path moduleOutputDir = Paths.get(caseDirPath, hostPathComponent, MODULE_FOLDER);
997  if (!moduleOutputDir.toFile().mkdirs()) {
998  throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreateModDir", moduleOutputDir));
999  }
1000 
1001  Path reportsDir = Paths.get(caseDirPath, hostPathComponent, REPORTS_FOLDER);
1002  if (!reportsDir.toFile().mkdirs()) {
1003  throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreateReportsDir", reportsDir));
1004  }
1005  }
1006 
1014  static Map<Long, String> getImagePaths(SleuthkitCase db) {
1015  Map<Long, String> imgPaths = new HashMap<>();
1016  try {
1017  Map<Long, List<String>> imgPathsList = db.getImagePaths();
1018  for (Map.Entry<Long, List<String>> entry : imgPathsList.entrySet()) {
1019  if (entry.getValue().size() > 0) {
1020  imgPaths.put(entry.getKey(), entry.getValue().get(0));
1021  }
1022  }
1023  } catch (TskCoreException ex) {
1024  logger.log(Level.SEVERE, "Error getting image paths", ex); //NON-NLS
1025  }
1026  return imgPaths;
1027  }
1028 
1039  @Messages({
1040  "Case.creationException.couldNotAcquireResourcesLock=Failed to get lock on case resources"
1041  })
1042  private static CoordinationService.Lock acquireCaseResourcesLock(String caseDir) throws CaseActionException {
1043  try {
1044  Path caseDirPath = Paths.get(caseDir);
1045  String resourcesNodeName = CoordinationServiceUtils.getCaseResourcesNodePath(caseDirPath);
1046  Lock lock = CoordinationService.getInstance().tryGetExclusiveLock(CategoryNode.CASES, resourcesNodeName, CASE_RESOURCES_LOCK_TIMEOUT_HOURS, TimeUnit.HOURS);
1047  return lock;
1048  } catch (InterruptedException ex) {
1049  throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelled());
1050  } catch (CoordinationServiceException ex) {
1051  throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireResourcesLock(), ex);
1052  }
1053  }
1054 
1055  private static String getNameForTitle() {
1056  //Method should become unnecessary once technical debt story 3334 is done.
1057  if (UserPreferences.getAppName().equals(Version.getName())) {
1058  //Available version number is version number for this application
1059  return String.format("%s %s", UserPreferences.getAppName(), Version.getVersion());
1060  } else {
1061  return UserPreferences.getAppName();
1062  }
1063  }
1064 
1068  private static void updateGUIForCaseOpened(Case newCurrentCase) {
1070  SwingUtilities.invokeLater(() -> {
1071  /*
1072  * If the case database was upgraded for a new schema and a
1073  * backup database was created, notify the user.
1074  */
1075  SleuthkitCase caseDb = newCurrentCase.getSleuthkitCase();
1076  String backupDbPath = caseDb.getBackupDatabasePath();
1077  if (null != backupDbPath) {
1078  JOptionPane.showMessageDialog(
1079  mainFrame,
1080  NbBundle.getMessage(Case.class, "Case.open.msgDlg.updated.msg", backupDbPath),
1081  NbBundle.getMessage(Case.class, "Case.open.msgDlg.updated.title"),
1082  JOptionPane.INFORMATION_MESSAGE);
1083  }
1084 
1085  /*
1086  * Look for the files for the data sources listed in the case
1087  * database and give the user the opportunity to locate any that
1088  * are missing.
1089  */
1090  Map<Long, String> imgPaths = getImagePaths(caseDb);
1091  for (Map.Entry<Long, String> entry : imgPaths.entrySet()) {
1092  long obj_id = entry.getKey();
1093  String path = entry.getValue();
1094  boolean fileExists = (new File(path).isFile() || DriveUtils.driveExists(path));
1095  if (!fileExists) {
1096  int response = JOptionPane.showConfirmDialog(
1097  mainFrame,
1098  NbBundle.getMessage(Case.class, "Case.checkImgExist.confDlg.doesntExist.msg", path),
1099  NbBundle.getMessage(Case.class, "Case.checkImgExist.confDlg.doesntExist.title"),
1100  JOptionPane.YES_NO_OPTION);
1101  if (response == JOptionPane.YES_OPTION) {
1102  MissingImageDialog.makeDialog(obj_id, caseDb);
1103  } else {
1104  logger.log(Level.SEVERE, "User proceeding with missing image files"); //NON-NLS
1105 
1106  }
1107  }
1108  }
1109 
1110  /*
1111  * Enable the case-specific actions.
1112  */
1113  CallableSystemAction.get(AddImageAction.class).setEnabled(FeatureAccessUtils.canAddDataSources());
1114  CallableSystemAction.get(CaseCloseAction.class).setEnabled(true);
1115  CallableSystemAction.get(CaseDetailsAction.class).setEnabled(true);
1116  CallableSystemAction.get(DataSourceSummaryAction.class).setEnabled(true);
1117  CallableSystemAction.get(CaseDeleteAction.class).setEnabled(FeatureAccessUtils.canDeleteCurrentCase());
1118  CallableSystemAction.get(OpenTimelineAction.class).setEnabled(true);
1119  CallableSystemAction.get(OpenCommVisualizationToolAction.class).setEnabled(true);
1120  CallableSystemAction.get(CommonAttributeSearchAction.class).setEnabled(true);
1121  CallableSystemAction.get(OpenOutputFolderAction.class).setEnabled(false);
1122  CallableSystemAction.get(OpenFileDiscoveryAction.class).setEnabled(true);
1123 
1124  /*
1125  * Add the case to the recent cases tracker that supplies a list
1126  * of recent cases to the recent cases menu item and the
1127  * open/create case dialog.
1128  */
1129  RecentCases.getInstance().addRecentCase(newCurrentCase.getDisplayName(), newCurrentCase.getMetadata().getFilePath().toString());
1130 
1131  /*
1132  * Open the top components (windows within the main application
1133  * window).
1134  *
1135  * Note: If the core windows are not opened here, they will be
1136  * opened via the DirectoryTreeTopComponent 'propertyChange()'
1137  * method on a DATA_SOURCE_ADDED event.
1138  */
1139  if (newCurrentCase.hasData()) {
1141  }
1142 
1143  /*
1144  * Reset the main window title to:
1145  *
1146  * [curent case display name] - [application name].
1147  */
1148  mainFrame.setTitle(newCurrentCase.getDisplayName() + " - " + getNameForTitle());
1149  });
1150  }
1151  }
1152 
1153  /*
1154  * Update the GUI to to reflect the lack of a current case.
1155  */
1156  private static void updateGUIForCaseClosed() {
1158  SwingUtilities.invokeLater(() -> {
1159  /*
1160  * Close the top components (windows within the main application
1161  * window).
1162  */
1164 
1165  /*
1166  * Disable the case-specific menu items.
1167  */
1168  CallableSystemAction.get(AddImageAction.class).setEnabled(false);
1169  CallableSystemAction.get(CaseCloseAction.class).setEnabled(false);
1170  CallableSystemAction.get(CaseDetailsAction.class).setEnabled(false);
1171  CallableSystemAction.get(DataSourceSummaryAction.class).setEnabled(false);
1172  CallableSystemAction.get(CaseDeleteAction.class).setEnabled(false);
1173  CallableSystemAction.get(OpenTimelineAction.class).setEnabled(false);
1174  CallableSystemAction.get(OpenCommVisualizationToolAction.class).setEnabled(false);
1175  CallableSystemAction.get(OpenOutputFolderAction.class).setEnabled(false);
1176  CallableSystemAction.get(CommonAttributeSearchAction.class).setEnabled(false);
1177  CallableSystemAction.get(OpenFileDiscoveryAction.class).setEnabled(false);
1178 
1179  /*
1180  * Clear the notifications in the notfier component in the lower
1181  * right hand corner of the main application window.
1182  */
1184 
1185  /*
1186  * Reset the main window title to be just the application name,
1187  * instead of [curent case display name] - [application name].
1188  */
1189  mainFrame.setTitle(getNameForTitle());
1190  });
1191  }
1192  }
1193 
1197  private static void clearTempSubDir(String tempSubDirPath) {
1198  File tempFolder = new File(tempSubDirPath);
1199  if (tempFolder.isDirectory()) {
1200  File[] files = tempFolder.listFiles();
1201  if (files.length > 0) {
1202  for (File file : files) {
1203  if (file.isDirectory()) {
1204  FileUtil.deleteDir(file);
1205  } else {
1206  file.delete();
1207  }
1208  }
1209  }
1210  }
1211  }
1212 
1218  public SleuthkitCase getSleuthkitCase() {
1219  return this.caseDb;
1220  }
1221 
1228  return caseServices;
1229  }
1230 
1237  return metadata.getCaseType();
1238  }
1239 
1245  public String getCreatedDate() {
1246  return metadata.getCreatedDate();
1247  }
1248 
1254  public String getName() {
1255  return metadata.getCaseName();
1256  }
1257 
1263  public String getDisplayName() {
1264  return metadata.getCaseDisplayName();
1265  }
1266 
1272  public String getNumber() {
1273  return metadata.getCaseNumber();
1274  }
1275 
1281  public String getExaminer() {
1282  return metadata.getExaminer();
1283  }
1284 
1290  public String getExaminerPhone() {
1291  return metadata.getExaminerPhone();
1292  }
1293 
1299  public String getExaminerEmail() {
1300  return metadata.getExaminerEmail();
1301  }
1302 
1308  public String getCaseNotes() {
1309  return metadata.getCaseNotes();
1310  }
1311 
1317  public String getCaseDirectory() {
1318  return metadata.getCaseDirectory();
1319  }
1320 
1329  public String getOutputDirectory() {
1330  String caseDirectory = getCaseDirectory();
1331  Path hostPath;
1332  if (CaseType.MULTI_USER_CASE == metadata.getCaseType()) {
1333  hostPath = Paths.get(caseDirectory, NetworkUtils.getLocalHostName());
1334  } else {
1335  hostPath = Paths.get(caseDirectory);
1336  }
1337  if (!hostPath.toFile().exists()) {
1338  hostPath.toFile().mkdirs();
1339  }
1340  return hostPath.toString();
1341  }
1342 
1349  public String getTempDirectory() {
1350  return getOrCreateSubdirectory(TEMP_FOLDER);
1351  }
1352 
1359  public String getCacheDirectory() {
1360  return getOrCreateSubdirectory(CACHE_FOLDER);
1361  }
1362 
1369  public String getExportDirectory() {
1370  return getOrCreateSubdirectory(EXPORT_FOLDER);
1371  }
1372 
1379  public String getLogDirectoryPath() {
1380  return getOrCreateSubdirectory(LOG_FOLDER);
1381  }
1382 
1389  public String getReportDirectory() {
1390  return getOrCreateSubdirectory(REPORTS_FOLDER);
1391  }
1392 
1399  public String getConfigDirectory() {
1400  return getOrCreateSubdirectory(CONFIG_FOLDER);
1401  }
1402 
1409  public String getModuleDirectory() {
1410  return getOrCreateSubdirectory(MODULE_FOLDER);
1411  }
1412 
1421  Path path = Paths.get(getModuleDirectory());
1423  return path.subpath(path.getNameCount() - 2, path.getNameCount()).toString();
1424  } else {
1425  return path.subpath(path.getNameCount() - 1, path.getNameCount()).toString();
1426  }
1427  }
1428 
1438  public List<Content> getDataSources() throws TskCoreException {
1439  return caseDb.getRootObjects();
1440  }
1441 
1447  public Set<TimeZone> getTimeZones() {
1448  Set<TimeZone> timezones = new HashSet<>();
1449  try {
1450  for (Content c : getDataSources()) {
1451  final Content dataSource = c.getDataSource();
1452  if ((dataSource != null) && (dataSource instanceof Image)) {
1453  Image image = (Image) dataSource;
1454  timezones.add(TimeZone.getTimeZone(image.getTimeZone()));
1455  }
1456  }
1457  } catch (TskCoreException ex) {
1458  logger.log(Level.SEVERE, "Error getting data source time zones", ex); //NON-NLS
1459  }
1460  return timezones;
1461  }
1462 
1469  public String getTextIndexName() {
1470  return getMetadata().getTextIndexName();
1471  }
1472 
1479  public boolean hasData() {
1480  boolean hasDataSources = false;
1481  try {
1482  hasDataSources = (getDataSources().size() > 0);
1483  } catch (TskCoreException ex) {
1484  logger.log(Level.SEVERE, "Error accessing case database", ex); //NON-NLS
1485  }
1486  return hasDataSources;
1487  }
1488 
1499  public void notifyAddingDataSource(UUID eventId) {
1500  eventPublisher.publish(new AddingDataSourceEvent(eventId));
1501  }
1502 
1513  public void notifyFailedAddingDataSource(UUID addingDataSourceEventId) {
1514  eventPublisher.publish(new AddingDataSourceFailedEvent(addingDataSourceEventId));
1515  }
1516 
1528  public void notifyDataSourceAdded(Content dataSource, UUID addingDataSourceEventId) {
1529  eventPublisher.publish(new DataSourceAddedEvent(dataSource, addingDataSourceEventId));
1530  }
1531 
1541  public void notifyDataSourceNameChanged(Content dataSource, String newName) {
1542  eventPublisher.publish(new DataSourceNameChangedEvent(dataSource, newName));
1543  }
1544 
1552  public void notifyContentTagAdded(ContentTag newTag) {
1553  eventPublisher.publish(new ContentTagAddedEvent(newTag));
1554  }
1555 
1563  public void notifyContentTagDeleted(ContentTag deletedTag) {
1564  eventPublisher.publish(new ContentTagDeletedEvent(deletedTag));
1565  }
1566 
1574  public void notifyTagDefinitionChanged(String changedTagName) {
1575  //leaving new value of changedTagName as null, because we do not currently support changing the display name of a tag.
1576  eventPublisher.publish(new AutopsyEvent(Events.TAG_DEFINITION_CHANGED.toString(), changedTagName, null));
1577  }
1578 
1589  public void notifyCentralRepoCommentChanged(long contentId, String newComment) {
1590  try {
1591  eventPublisher.publish(new CommentChangedEvent(contentId, newComment));
1592  } catch (NoCurrentCaseException ex) {
1593  logger.log(Level.WARNING, "Unable to send notifcation regarding comment change due to no current case being open", ex);
1594  }
1595  }
1596 
1604  public void notifyBlackBoardArtifactTagAdded(BlackboardArtifactTag newTag) {
1605  eventPublisher.publish(new BlackBoardArtifactTagAddedEvent(newTag));
1606  }
1607 
1615  public void notifyBlackBoardArtifactTagDeleted(BlackboardArtifactTag deletedTag) {
1616  eventPublisher.publish(new BlackBoardArtifactTagDeletedEvent(deletedTag));
1617  }
1618 
1630  public void addReport(String localPath, String srcModuleName, String reportName) throws TskCoreException {
1631  addReport(localPath, srcModuleName, reportName, null);
1632  }
1633 
1648  public Report addReport(String localPath, String srcModuleName, String reportName, Content parent) throws TskCoreException {
1649  String normalizedLocalPath;
1650  try {
1651  if (localPath.toLowerCase().contains("http:")) {
1652  normalizedLocalPath = localPath;
1653  } else {
1654  normalizedLocalPath = Paths.get(localPath).normalize().toString();
1655  }
1656  } catch (InvalidPathException ex) {
1657  String errorMsg = "Invalid local path provided: " + localPath; // NON-NLS
1658  throw new TskCoreException(errorMsg, ex);
1659  }
1660  Report report = this.caseDb.addReport(normalizedLocalPath, srcModuleName, reportName, parent);
1661  eventPublisher.publish(new ReportAddedEvent(report));
1662  return report;
1663  }
1664 
1673  public List<Report> getAllReports() throws TskCoreException {
1674  return this.caseDb.getAllReports();
1675  }
1676 
1685  public void deleteReports(Collection<? extends Report> reports) throws TskCoreException {
1686  for (Report report : reports) {
1687  this.caseDb.deleteReport(report);
1688  eventPublisher.publish(new AutopsyEvent(Events.REPORT_DELETED.toString(), report, null));
1689  }
1690  }
1691 
1697  CaseMetadata getMetadata() {
1698  return metadata;
1699  }
1700 
1708  @Messages({
1709  "Case.exceptionMessage.metadataUpdateError=Failed to update case metadata"
1710  })
1711  void updateCaseDetails(CaseDetails caseDetails) throws CaseActionException {
1712  CaseDetails oldCaseDetails = metadata.getCaseDetails();
1713  try {
1714  metadata.setCaseDetails(caseDetails);
1715  } catch (CaseMetadataException ex) {
1716  throw new CaseActionException(Bundle.Case_exceptionMessage_metadataUpdateError(), ex);
1717  }
1718  if (getCaseType() == CaseType.MULTI_USER_CASE && !oldCaseDetails.getCaseDisplayName().equals(caseDetails.getCaseDisplayName())) {
1719  try {
1720  CaseNodeData nodeData = CaseNodeData.readCaseNodeData(metadata.getCaseDirectory());
1721  nodeData.setDisplayName(caseDetails.getCaseDisplayName());
1722  CaseNodeData.writeCaseNodeData(nodeData);
1723  } catch (CaseNodeDataException | InterruptedException ex) {
1724  throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotUpdateCaseNodeData(ex.getLocalizedMessage()), ex);
1725  }
1726  }
1727  if (!oldCaseDetails.getCaseNumber().equals(caseDetails.getCaseNumber())) {
1728  eventPublisher.publish(new AutopsyEvent(Events.NUMBER.toString(), oldCaseDetails.getCaseNumber(), caseDetails.getCaseNumber()));
1729  }
1730  if (!oldCaseDetails.getExaminerName().equals(caseDetails.getExaminerName())) {
1731  eventPublisher.publish(new AutopsyEvent(Events.NUMBER.toString(), oldCaseDetails.getExaminerName(), caseDetails.getExaminerName()));
1732  }
1733  if (!oldCaseDetails.getCaseDisplayName().equals(caseDetails.getCaseDisplayName())) {
1734  eventPublisher.publish(new AutopsyEvent(Events.NAME.toString(), oldCaseDetails.getCaseDisplayName(), caseDetails.getCaseDisplayName()));
1735  }
1736  eventPublisher.publish(new AutopsyEvent(Events.CASE_DETAILS.toString(), oldCaseDetails, caseDetails));
1737  if (RuntimeProperties.runningWithGUI()) {
1738  SwingUtilities.invokeLater(() -> {
1739  mainFrame.setTitle(caseDetails.getCaseDisplayName() + " - " + getNameForTitle());
1740  try {
1741  RecentCases.getInstance().updateRecentCase(oldCaseDetails.getCaseDisplayName(), metadata.getFilePath().toString(), caseDetails.getCaseDisplayName(), metadata.getFilePath().toString());
1742  } catch (Exception ex) {
1743  logger.log(Level.SEVERE, "Error updating case name in UI", ex); //NON-NLS
1744  }
1745  });
1746  }
1747  }
1748 
1761  private Case(CaseType caseType, String caseDir, CaseDetails caseDetails) {
1762  this(new CaseMetadata(caseType, caseDir, displayNameToUniqueName(caseDetails.getCaseDisplayName()), caseDetails));
1763  }
1764 
1770  private Case(CaseMetadata caseMetaData) {
1771  metadata = caseMetaData;
1772  sleuthkitEventListener = new SleuthkitEventListener();
1773  }
1774 
1804  @Messages({
1805  "Case.progressIndicatorCancelButton.label=Cancel",
1806  "Case.progressMessage.preparing=Preparing...",
1807  "Case.progressMessage.cancelling=Cancelling...",
1808  "Case.exceptionMessage.cancelled=Cancelled.",
1809  "# {0} - exception message", "Case.exceptionMessage.execExceptionWrapperMessage={0}"
1810  })
1811  private void doOpenCaseAction(String progressIndicatorTitle, CaseAction<ProgressIndicator, Object, Void> caseAction, CaseLockType caseLockType, boolean allowCancellation, Object additionalParams) throws CaseActionException {
1812  /*
1813  * Create and start either a GUI progress indicator (with or without a
1814  * cancel button) or a logging progress indicator.
1815  */
1816  CancelButtonListener cancelButtonListener = null;
1817  ProgressIndicator progressIndicator;
1819  if (allowCancellation) {
1820  cancelButtonListener = new CancelButtonListener(Bundle.Case_progressMessage_cancelling());
1821  progressIndicator = new ModalDialogProgressIndicator(
1822  mainFrame,
1823  progressIndicatorTitle,
1824  new String[]{Bundle.Case_progressIndicatorCancelButton_label()},
1825  Bundle.Case_progressIndicatorCancelButton_label(),
1826  cancelButtonListener);
1827  } else {
1828  progressIndicator = new ModalDialogProgressIndicator(
1829  mainFrame,
1830  progressIndicatorTitle);
1831  }
1832  } else {
1833  progressIndicator = new LoggingProgressIndicator();
1834  }
1835  progressIndicator.start(Bundle.Case_progressMessage_preparing());
1836 
1837  /*
1838  * Do the case action in the single thread in the case action executor.
1839  * If the case is a multi-user case, a case lock is acquired and held
1840  * until explictly released and an exclusive case resources lock is
1841  * aquired and held for the duration of the action.
1842  */
1843  TaskThreadFactory threadFactory = new TaskThreadFactory(String.format(CASE_ACTION_THREAD_NAME, metadata.getCaseName()));
1844  caseActionExecutor = Executors.newSingleThreadExecutor(threadFactory);
1845  Future<Void> future = caseActionExecutor.submit(() -> {
1846  if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) {
1847  caseAction.execute(progressIndicator, additionalParams);
1848  } else {
1849  acquireCaseLock(caseLockType);
1850  try (CoordinationService.Lock resourcesLock = acquireCaseResourcesLock(metadata.getCaseDirectory())) {
1851  if (null == resourcesLock) {
1852  throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireResourcesLock());
1853  }
1854  caseAction.execute(progressIndicator, additionalParams);
1855  } catch (CaseActionException ex) {
1856  releaseCaseLock();
1857  throw ex;
1858  }
1859  }
1860  return null;
1861  });
1862  if (null != cancelButtonListener) {
1863  cancelButtonListener.setCaseActionFuture(future);
1864  }
1865 
1866  /*
1867  * Wait for the case action task to finish.
1868  */
1869  try {
1870  future.get();
1871  } catch (InterruptedException discarded) {
1872  /*
1873  * The thread this method is running in has been interrupted.
1874  */
1875  if (null != cancelButtonListener) {
1876  cancelButtonListener.actionPerformed(null);
1877  } else {
1878  future.cancel(true);
1879  }
1880  ThreadUtils.shutDownTaskExecutor(caseActionExecutor);
1881  } catch (CancellationException discarded) {
1882  /*
1883  * The case action has been cancelled.
1884  */
1885  ThreadUtils.shutDownTaskExecutor(caseActionExecutor);
1886  throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelled());
1887  } catch (ExecutionException ex) {
1888  /*
1889  * The case action has thrown an exception.
1890  */
1891  ThreadUtils.shutDownTaskExecutor(caseActionExecutor);
1892  throw new CaseActionException(Bundle.Case_exceptionMessage_execExceptionWrapperMessage(ex.getCause().getLocalizedMessage()), ex);
1893  } finally {
1894  progressIndicator.finish();
1895  }
1896  }
1897 
1913  private Void create(ProgressIndicator progressIndicator, Object additionalParams) throws CaseActionException {
1914  assert (additionalParams == null);
1915  try {
1917  createCaseDirectoryIfDoesNotExist(progressIndicator);
1919  switchLoggingToCaseLogsDirectory(progressIndicator);
1921  saveCaseMetadataToFile(progressIndicator);
1923  createCaseNodeData(progressIndicator);
1926  createCaseDatabase(progressIndicator);
1928  openCaseLevelServices(progressIndicator);
1930  openAppServiceCaseResources(progressIndicator);
1932  openCommunicationChannels(progressIndicator);
1933  return null;
1934 
1935  } catch (CaseActionException ex) {
1936  /*
1937  * Cancellation or failure. The sleep is a little hack to clear the
1938  * interrupted flag for this thread if this is a cancellation
1939  * scenario, so that the clean up can run to completion in the
1940  * current thread.
1941  */
1942  try {
1943  Thread.sleep(1);
1944  } catch (InterruptedException discarded) {
1945  }
1946  close(progressIndicator);
1947  throw ex;
1948  }
1949  }
1950 
1965  private Void open(ProgressIndicator progressIndicator, Object additionalParams) throws CaseActionException {
1966  assert (additionalParams == null);
1967  try {
1969  switchLoggingToCaseLogsDirectory(progressIndicator);
1971  updateCaseNodeData(progressIndicator);
1973  deleteTempfilesFromCaseDirectory(progressIndicator);
1975  openCaseDataBase(progressIndicator);
1977  openCaseLevelServices(progressIndicator);
1979  openAppServiceCaseResources(progressIndicator);
1981  openCommunicationChannels(progressIndicator);
1982  return null;
1983 
1984  } catch (CaseActionException ex) {
1985  /*
1986  * Cancellation or failure. The sleep is a little hack to clear the
1987  * interrupted flag for this thread if this is a cancellation
1988  * scenario, so that the clean up can run to completion in the
1989  * current thread.
1990  */
1991  try {
1992  Thread.sleep(1);
1993  } catch (InterruptedException discarded) {
1994  }
1995  close(progressIndicator);
1996  throw ex;
1997  }
1998  }
1999 
2014  @Messages({
2015  "Case.progressMessage.deletingDataSource=Removing the data source from the case...",
2016  "Case.exceptionMessage.dataSourceNotFound=The data source was not found.",
2017  "Case.exceptionMessage.errorDeletingDataSourceFromCaseDb=An error occurred while removing the data source from the case database.",
2018  "Case.exceptionMessage.errorDeletingDataSourceFromTextIndex=An error occurred while removing the data source from the text index.",})
2019  Void deleteDataSource(ProgressIndicator progressIndicator, Object additionalParams) throws CaseActionException {
2020  assert (additionalParams instanceof Long);
2021  open(progressIndicator, null);
2022  try {
2023  progressIndicator.progress(Bundle.Case_progressMessage_deletingDataSource());
2024  Long dataSourceObjectID = (Long) additionalParams;
2025  try {
2026  DataSource dataSource = this.caseDb.getDataSource(dataSourceObjectID);
2027  if (dataSource == null) {
2028  throw new CaseActionException(Bundle.Case_exceptionMessage_dataSourceNotFound());
2029  }
2030  SleuthkitCaseAdminUtil.deleteDataSource(this.caseDb, dataSourceObjectID);
2031  } catch (TskDataException | TskCoreException ex) {
2032  throw new CaseActionException(Bundle.Case_exceptionMessage_errorDeletingDataSourceFromCaseDb(), ex);
2033  }
2034  try {
2035  this.caseServices.getKeywordSearchService().deleteDataSource(dataSourceObjectID);
2036  } catch (KeywordSearchServiceException ex) {
2037  throw new CaseActionException(Bundle.Case_exceptionMessage_errorDeletingDataSourceFromTextIndex(), ex);
2038  }
2039  eventPublisher.publish(new DataSourceDeletedEvent(dataSourceObjectID));
2040  return null;
2041  } finally {
2042  close(progressIndicator);
2043  releaseCaseLock();
2044  }
2045  }
2046 
2057  public SleuthkitCase createPortableCase(String caseName, File portableCaseFolder) throws TskCoreException {
2058 
2059  if (portableCaseFolder.exists()) {
2060  throw new TskCoreException("Portable case folder " + portableCaseFolder.toString() + " already exists");
2061  }
2062  if (!portableCaseFolder.mkdirs()) {
2063  throw new TskCoreException("Error creating portable case folder " + portableCaseFolder.toString());
2064  }
2065 
2066  CaseDetails details = new CaseDetails(caseName, getNumber(), getExaminer(),
2068  try {
2069  CaseMetadata portableCaseMetadata = new CaseMetadata(Case.CaseType.SINGLE_USER_CASE, portableCaseFolder.toString(),
2070  caseName, details, metadata);
2071  portableCaseMetadata.setCaseDatabaseName(SINGLE_USER_CASE_DB_NAME);
2072  } catch (CaseMetadataException ex) {
2073  throw new TskCoreException("Error creating case metadata", ex);
2074  }
2075 
2076  // Create the Sleuthkit case
2077  SleuthkitCase portableSleuthkitCase;
2078  String dbFilePath = Paths.get(portableCaseFolder.toString(), SINGLE_USER_CASE_DB_NAME).toString();
2079  portableSleuthkitCase = SleuthkitCase.newCase(dbFilePath);
2080 
2081  return portableSleuthkitCase;
2082  }
2083 
2094  if (Thread.currentThread().isInterrupted()) {
2095  throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelled());
2096  }
2097  }
2098 
2109  @Messages({
2110  "Case.progressMessage.creatingCaseDirectory=Creating case directory..."
2111  })
2112  private void createCaseDirectoryIfDoesNotExist(ProgressIndicator progressIndicator) throws CaseActionException {
2113  /*
2114  * TODO (JIRA-2180): Always create the case directory as part of the
2115  * case creation process.
2116  */
2117  progressIndicator.progress(Bundle.Case_progressMessage_creatingCaseDirectory());
2118  if (new File(metadata.getCaseDirectory()).exists() == false) {
2119  progressIndicator.progress(Bundle.Case_progressMessage_creatingCaseDirectory());
2120  Case.createCaseDirectory(metadata.getCaseDirectory(), metadata.getCaseType());
2121  }
2122  }
2123 
2130  @Messages({
2131  "Case.progressMessage.switchingLogDirectory=Switching log directory..."
2132  })
2133  private void switchLoggingToCaseLogsDirectory(ProgressIndicator progressIndicator) {
2134  progressIndicator.progress(Bundle.Case_progressMessage_switchingLogDirectory());
2136  }
2137 
2149  @Messages({
2150  "Case.progressMessage.savingCaseMetadata=Saving case metadata to file...",
2151  "# {0} - exception message", "Case.exceptionMessage.couldNotSaveCaseMetadata=Failed to save case metadata:\n{0}."
2152  })
2153  private void saveCaseMetadataToFile(ProgressIndicator progressIndicator) throws CaseActionException {
2154  progressIndicator.progress(Bundle.Case_progressMessage_savingCaseMetadata());
2155  try {
2156  this.metadata.writeToFile();
2157  } catch (CaseMetadataException ex) {
2158  throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotSaveCaseMetadata(ex.getLocalizedMessage()), ex);
2159  }
2160  }
2161 
2173  @Messages({
2174  "Case.progressMessage.creatingCaseNodeData=Creating coordination service node data...",
2175  "# {0} - exception message", "Case.exceptionMessage.couldNotCreateCaseNodeData=Failed to create coordination service node data:\n{0}."
2176  })
2177  private void createCaseNodeData(ProgressIndicator progressIndicator) throws CaseActionException {
2179  progressIndicator.progress(Bundle.Case_progressMessage_creatingCaseNodeData());
2180  try {
2181  CaseNodeData.createCaseNodeData(metadata);
2182  } catch (CaseNodeDataException | InterruptedException ex) {
2183  throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotCreateCaseNodeData(ex.getLocalizedMessage()), ex);
2184  }
2185  }
2186  }
2187 
2199  @Messages({
2200  "Case.progressMessage.updatingCaseNodeData=Updating coordination service node data...",
2201  "# {0} - exception message", "Case.exceptionMessage.couldNotUpdateCaseNodeData=Failed to update coordination service node data:\n{0}."
2202  })
2203  private void updateCaseNodeData(ProgressIndicator progressIndicator) throws CaseActionException {
2205  progressIndicator.progress(Bundle.Case_progressMessage_updatingCaseNodeData());
2206  try {
2208  nodeData.setLastAccessDate(new Date());
2209  CaseNodeData.writeCaseNodeData(nodeData);
2210  } catch (CaseNodeDataException | InterruptedException ex) {
2211  throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotUpdateCaseNodeData(ex.getLocalizedMessage()), ex);
2212  }
2213  }
2214  }
2215 
2221  @Messages({
2222  "Case.progressMessage.clearingTempDirectory=Clearing case temp directory..."
2223  })
2224  private void deleteTempfilesFromCaseDirectory(ProgressIndicator progressIndicator) {
2225  /*
2226  * Clear the temp subdirectory of the case directory.
2227  */
2228  progressIndicator.progress(Bundle.Case_progressMessage_clearingTempDirectory());
2230  }
2231 
2243  @Messages({
2244  "Case.progressMessage.creatingCaseDatabase=Creating case database...",
2245  "# {0} - exception message", "Case.exceptionMessage.couldNotGetDbServerConnectionInfo=Failed to get case database server conneciton info:\n{0}.",
2246  "# {0} - exception message", "Case.exceptionMessage.couldNotCreateCaseDatabase=Failed to create case database:\n{0}.",
2247  "# {0} - exception message", "Case.exceptionMessage.couldNotSaveDbNameToMetadataFile=Failed to save case database name to case metadata file:\n{0}."
2248  })
2249  private void createCaseDatabase(ProgressIndicator progressIndicator) throws CaseActionException {
2250  progressIndicator.progress(Bundle.Case_progressMessage_creatingCaseDatabase());
2251  try {
2252  if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) {
2253  /*
2254  * For single-user cases, the case database is a SQLite database
2255  * with a standard name, physically located in the case
2256  * directory.
2257  */
2258  caseDb = SleuthkitCase.newCase(Paths.get(metadata.getCaseDirectory(), SINGLE_USER_CASE_DB_NAME).toString());
2259  metadata.setCaseDatabaseName(SINGLE_USER_CASE_DB_NAME);
2260  } else {
2261  /*
2262  * For multi-user cases, the case database is a PostgreSQL
2263  * database with a name derived from the case display name,
2264  * physically located on the PostgreSQL database server.
2265  */
2266  caseDb = SleuthkitCase.newCase(metadata.getCaseDisplayName(), UserPreferences.getDatabaseConnectionInfo(), metadata.getCaseDirectory());
2267  metadata.setCaseDatabaseName(caseDb.getDatabaseName());
2268  }
2269  } catch (TskCoreException ex) {
2270  throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotCreateCaseDatabase(ex.getLocalizedMessage()), ex);
2271  } catch (UserPreferencesException ex) {
2272  throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotGetDbServerConnectionInfo(ex.getLocalizedMessage()), ex);
2273  } catch (CaseMetadataException ex) {
2274  throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotSaveDbNameToMetadataFile(ex.getLocalizedMessage()), ex);
2275  }
2276  }
2277 
2289  @Messages({
2290  "Case.progressMessage.openingCaseDatabase=Opening case database...",
2291  "# {0} - exception message", "Case.exceptionMessage.couldNotOpenCaseDatabase=Failed to open case database:\n{0}.",
2292  "# {0} - exception message", "Case.exceptionMessage.unsupportedSchemaVersionMessage=Unsupported case database schema version:\n{0}.",
2293  "Case.open.exception.multiUserCaseNotEnabled=Cannot open a multi-user case if multi-user cases are not enabled. See Tools, Options, Multi-User."
2294  })
2295  private void openCaseDataBase(ProgressIndicator progressIndicator) throws CaseActionException {
2296  progressIndicator.progress(Bundle.Case_progressMessage_openingCaseDatabase());
2297  try {
2298  String databaseName = metadata.getCaseDatabaseName();
2299  if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) {
2300  caseDb = SleuthkitCase.openCase(Paths.get(metadata.getCaseDirectory(), databaseName).toString());
2302  caseDb = SleuthkitCase.openCase(databaseName, UserPreferences.getDatabaseConnectionInfo(), metadata.getCaseDirectory());
2303  } else {
2304  throw new CaseActionException(Bundle.Case_open_exception_multiUserCaseNotEnabled());
2305  }
2306  } catch (TskUnsupportedSchemaVersionException ex) {
2307  throw new CaseActionException(Bundle.Case_exceptionMessage_unsupportedSchemaVersionMessage(ex.getLocalizedMessage()), ex);
2308  } catch (UserPreferencesException ex) {
2309  throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotGetDbServerConnectionInfo(ex.getLocalizedMessage()), ex);
2310  } catch (TskCoreException ex) {
2311  throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotOpenCaseDatabase(ex.getLocalizedMessage()), ex);
2312  }
2313  }
2314 
2321  @Messages({
2322  "Case.progressMessage.openingCaseLevelServices=Opening case-level services...",})
2323  private void openCaseLevelServices(ProgressIndicator progressIndicator) {
2324  progressIndicator.progress(Bundle.Case_progressMessage_openingCaseLevelServices());
2325  this.caseServices = new Services(caseDb);
2326  /*
2327  * RC Note: JM put this initialization here. I'm not sure why. However,
2328  * my attempt to put it in the openCaseDatabase method seems to lead to
2329  * intermittent unchecked exceptions concerning a missing subscriber.
2330  */
2331  caseDb.registerForEvents(sleuthkitEventListener);
2332  }
2333 
2345  @NbBundle.Messages({
2346  "Case.progressMessage.openingApplicationServiceResources=Opening application service case resources...",
2347  "# {0} - service name", "Case.serviceOpenCaseResourcesProgressIndicator.title={0} Opening Case Resources",
2348  "# {0} - service name", "Case.serviceOpenCaseResourcesProgressIndicator.cancellingMessage=Cancelling opening case resources by {0}...",
2349  "# {0} - service name", "Case.servicesException.notificationTitle={0} Error"
2350  })
2351  private void openAppServiceCaseResources(ProgressIndicator progressIndicator) throws CaseActionException {
2352  /*
2353  * Each service gets its own independently cancellable/interruptible
2354  * task, running in a named thread managed by an executor service, with
2355  * its own progress indicator. This allows for cancellation of the
2356  * opening of case resources for individual services. It also makes it
2357  * possible to ensure that each service task completes before the next
2358  * one starts by awaiting termination of the executor service.
2359  */
2360  progressIndicator.progress(Bundle.Case_progressMessage_openingApplicationServiceResources());
2361 
2362  for (AutopsyService service : Lookup.getDefault().lookupAll(AutopsyService.class
2363  )) {
2364  /*
2365  * Create a progress indicator for the task and start the task. If
2366  * running with a GUI, the progress indicator will be a dialog box
2367  * with a Cancel button.
2368  */
2369  CancelButtonListener cancelButtonListener = null;
2370  ProgressIndicator appServiceProgressIndicator;
2372  cancelButtonListener = new CancelButtonListener(Bundle.Case_serviceOpenCaseResourcesProgressIndicator_cancellingMessage(service.getServiceName()));
2373  appServiceProgressIndicator = new ModalDialogProgressIndicator(
2374  mainFrame,
2375  Bundle.Case_serviceOpenCaseResourcesProgressIndicator_title(service.getServiceName()),
2376  new String[]{Bundle.Case_progressIndicatorCancelButton_label()},
2377  Bundle.Case_progressIndicatorCancelButton_label(),
2378  cancelButtonListener);
2379  } else {
2380  appServiceProgressIndicator = new LoggingProgressIndicator();
2381  }
2382  appServiceProgressIndicator.start(Bundle.Case_progressMessage_preparing());
2383  AutopsyService.CaseContext context = new AutopsyService.CaseContext(this, appServiceProgressIndicator);
2384  String threadNameSuffix = service.getServiceName().replaceAll("[ ]", "-"); //NON-NLS
2385  threadNameSuffix = threadNameSuffix.toLowerCase();
2386  TaskThreadFactory threadFactory = new TaskThreadFactory(String.format(CASE_RESOURCES_THREAD_NAME, threadNameSuffix));
2387  ExecutorService executor = Executors.newSingleThreadExecutor(threadFactory);
2388  Future<Void> future = executor.submit(() -> {
2389  service.openCaseResources(context);
2390  return null;
2391  });
2392  if (null != cancelButtonListener) {
2393  cancelButtonListener.setCaseContext(context);
2394  cancelButtonListener.setCaseActionFuture(future);
2395  }
2396 
2397  /*
2398  * Wait for the task to either be completed or
2399  * cancelled/interrupted, or for the opening of the case to be
2400  * cancelled.
2401  */
2402  try {
2403  future.get();
2404  } catch (InterruptedException discarded) {
2405  /*
2406  * The parent create/open case task has been cancelled.
2407  */
2408  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()));
2409  future.cancel(true);
2410  } catch (CancellationException discarded) {
2411  /*
2412  * The opening of case resources by the application service has
2413  * been cancelled, so the executor service has thrown. Note that
2414  * there is no guarantee the task itself has responded to the
2415  * cancellation request yet.
2416  */
2417  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()));
2418  } catch (ExecutionException ex) {
2419  /*
2420  * An exception was thrown while executing the task. The
2421  * case-specific application service resources are not
2422  * essential. Log an error and notify the user if running the
2423  * desktop GUI, but do not throw.
2424  */
2425  Case.logger.log(Level.SEVERE, String.format("%s failed to open case resources for %s", service.getServiceName(), this.getDisplayName()), ex);
2427  SwingUtilities.invokeLater(() -> {
2428  MessageNotifyUtil.Notify.error(Bundle.Case_servicesException_notificationTitle(service.getServiceName()), ex.getLocalizedMessage());
2429  });
2430  }
2431  } finally {
2432  /*
2433  * Shut down the executor service and wait for it to finish.
2434  * This ensures that the task has finished. Without this, it
2435  * would be possible to start the next task before the current
2436  * task responded to a cancellation request.
2437  */
2439  appServiceProgressIndicator.finish();
2440  }
2442  }
2443  }
2444 
2456  @Messages({
2457  "Case.progressMessage.settingUpNetworkCommunications=Setting up network communications...",
2458  "# {0} - exception message", "Case.exceptionMessage.couldNotOpenRemoteEventChannel=Failed to open remote events channel:\n{0}.",
2459  "# {0} - exception message", "Case.exceptionMessage.couldNotCreatCollaborationMonitor=Failed to create collaboration monitor:\n{0}."
2460  })
2461  private void openCommunicationChannels(ProgressIndicator progressIndicator) throws CaseActionException {
2462  if (CaseType.MULTI_USER_CASE == metadata.getCaseType()) {
2463  progressIndicator.progress(Bundle.Case_progressMessage_settingUpNetworkCommunications());
2464  try {
2465  eventPublisher.openRemoteEventChannel(String.format(EVENT_CHANNEL_NAME, metadata.getCaseName()));
2467  collaborationMonitor = new CollaborationMonitor(metadata.getCaseName());
2468  } catch (AutopsyEventException ex) {
2469  throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotOpenRemoteEventChannel(ex.getLocalizedMessage()), ex);
2470  } catch (CollaborationMonitor.CollaborationMonitorException ex) {
2471  throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotCreatCollaborationMonitor(ex.getLocalizedMessage()), ex);
2472  }
2473  }
2474  }
2475 
2490  private void doCloseCaseAction() throws CaseActionException {
2491  /*
2492  * Set up either a GUI progress indicator without a Cancel button or a
2493  * logging progress indicator.
2494  */
2495  ProgressIndicator progressIndicator;
2497  progressIndicator = new ModalDialogProgressIndicator(
2498  mainFrame,
2499  Bundle.Case_progressIndicatorTitle_closingCase());
2500  } else {
2501  progressIndicator = new LoggingProgressIndicator();
2502  }
2503  progressIndicator.start(Bundle.Case_progressMessage_preparing());
2504 
2505  /*
2506  * Closing a case is always done in the same non-UI thread that
2507  * opened/created the case. If the case is a multi-user case, this
2508  * ensures that case lock that is held as long as the case is open is
2509  * released in the same thread in which it was acquired, as is required
2510  * by the coordination service.
2511  */
2512  Future<Void> future = caseActionExecutor.submit(() -> {
2513  if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) {
2514  close(progressIndicator);
2515  } else {
2516  /*
2517  * Acquire an exclusive case resources lock to ensure only one
2518  * node at a time can create/open/upgrade/close the case
2519  * resources.
2520  */
2521  progressIndicator.progress(Bundle.Case_progressMessage_preparing());
2522  try (CoordinationService.Lock resourcesLock = acquireCaseResourcesLock(metadata.getCaseDirectory())) {
2523  if (null == resourcesLock) {
2524  throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireResourcesLock());
2525  }
2526  close(progressIndicator);
2527  } finally {
2528  /*
2529  * Always release the case directory lock that was acquired
2530  * when the case was opened.
2531  */
2532  releaseCaseLock();
2533  }
2534  }
2535  return null;
2536  });
2537 
2538  try {
2539  future.get();
2540  } catch (InterruptedException | CancellationException unused) {
2541  /*
2542  * The wait has been interrupted by interrupting the thread running
2543  * this method. Not allowing cancellation of case closing, so ignore
2544  * the interrupt. Likewise, cancellation of the case closing task is
2545  * not supported.
2546  */
2547  } catch (ExecutionException ex) {
2548  throw new CaseActionException(Bundle.Case_exceptionMessage_execExceptionWrapperMessage(ex.getCause().getMessage()), ex);
2549  } finally {
2550  ThreadUtils.shutDownTaskExecutor(caseActionExecutor);
2551  progressIndicator.finish();
2552  }
2553  }
2554 
2560  @Messages({
2561  "Case.progressMessage.shuttingDownNetworkCommunications=Shutting down network communications...",
2562  "Case.progressMessage.closingApplicationServiceResources=Closing case-specific application service resources...",
2563  "Case.progressMessage.closingCaseDatabase=Closing case database..."
2564  })
2565  private void close(ProgressIndicator progressIndicator) {
2567 
2568  /*
2569  * Stop sending/receiving case events to and from other nodes if this is
2570  * a multi-user case.
2571  */
2572  if (CaseType.MULTI_USER_CASE == metadata.getCaseType()) {
2573  progressIndicator.progress(Bundle.Case_progressMessage_shuttingDownNetworkCommunications());
2574  if (null != collaborationMonitor) {
2575  collaborationMonitor.shutdown();
2576  }
2577  eventPublisher.closeRemoteEventChannel();
2578  }
2579 
2580  /*
2581  * Allow all registered application services providers to close
2582  * resources related to the case.
2583  */
2584  progressIndicator.progress(Bundle.Case_progressMessage_closingApplicationServiceResources());
2586 
2587  /*
2588  * Close the case database.
2589  */
2590  if (null != caseDb) {
2591  progressIndicator.progress(Bundle.Case_progressMessage_closingCaseDatabase());
2592  caseDb.unregisterForEvents(sleuthkitEventListener);
2593  caseDb.close();
2594  }
2595 
2596  /*
2597  * Switch the log directory.
2598  */
2599  progressIndicator.progress(Bundle.Case_progressMessage_switchingLogDirectory());
2601  }
2602 
2607  @Messages({
2608  "# {0} - serviceName", "Case.serviceCloseResourcesProgressIndicator.title={0} Closing Case Resources",
2609  "# {0} - service name", "# {1} - exception message", "Case.servicesException.serviceResourcesCloseError=Could not close case resources for {0} service: {1}"
2610  })
2612  /*
2613  * Each service gets its own independently cancellable task, and thus
2614  * its own task progress indicator.
2615  */
2616  for (AutopsyService service : Lookup.getDefault().lookupAll(AutopsyService.class
2617  )) {
2618  ProgressIndicator progressIndicator;
2620  progressIndicator = new ModalDialogProgressIndicator(
2621  mainFrame,
2622  Bundle.Case_serviceCloseResourcesProgressIndicator_title(service.getServiceName()));
2623  } else {
2624  progressIndicator = new LoggingProgressIndicator();
2625  }
2626  progressIndicator.start(Bundle.Case_progressMessage_preparing());
2627  AutopsyService.CaseContext context = new AutopsyService.CaseContext(this, progressIndicator);
2628  String threadNameSuffix = service.getServiceName().replaceAll("[ ]", "-"); //NON-NLS
2629  threadNameSuffix = threadNameSuffix.toLowerCase();
2630  TaskThreadFactory threadFactory = new TaskThreadFactory(String.format(CASE_RESOURCES_THREAD_NAME, threadNameSuffix));
2631  ExecutorService executor = Executors.newSingleThreadExecutor(threadFactory);
2632  Future<Void> future = executor.submit(() -> {
2633  service.closeCaseResources(context);
2634  return null;
2635  });
2636  try {
2637  future.get();
2638  } catch (InterruptedException ex) {
2639  Case.logger.log(Level.SEVERE, String.format("Unexpected interrupt while waiting on %s service to close case resources", service.getServiceName()), ex);
2640  } catch (CancellationException ex) {
2641  Case.logger.log(Level.SEVERE, String.format("Unexpected cancellation while waiting on %s service to close case resources", service.getServiceName()), ex);
2642  } catch (ExecutionException ex) {
2643  Case.logger.log(Level.SEVERE, String.format("%s service failed to open case resources", service.getServiceName()), ex);
2645  SwingUtilities.invokeLater(() -> MessageNotifyUtil.Notify.error(
2646  Bundle.Case_servicesException_notificationTitle(service.getServiceName()),
2647  Bundle.Case_servicesException_serviceResourcesCloseError(service.getServiceName(), ex.getLocalizedMessage())));
2648  }
2649  } finally {
2651  progressIndicator.finish();
2652  }
2653  }
2654  }
2655 
2661  @Messages({
2662  "Case.lockingException.couldNotAcquireSharedLock=Failed to get an shared lock on the case.",
2663  "Case.lockingException.couldNotAcquireExclusiveLock=Failed to get a exclusive lock on the case."
2664  })
2665  private void acquireCaseLock(CaseLockType lockType) throws CaseActionException {
2666  String caseDir = metadata.getCaseDirectory();
2667  try {
2668  CoordinationService coordinationService = CoordinationService.getInstance();
2669  caseLock = lockType == CaseLockType.SHARED
2670  ? coordinationService.tryGetSharedLock(CategoryNode.CASES, caseDir, CASE_LOCK_TIMEOUT_MINS, TimeUnit.MINUTES)
2671  : coordinationService.tryGetExclusiveLock(CategoryNode.CASES, caseDir, CASE_LOCK_TIMEOUT_MINS, TimeUnit.MINUTES);
2672  if (caseLock == null) {
2673  if (lockType == CaseLockType.SHARED) {
2674  throw new CaseActionException(Bundle.Case_lockingException_couldNotAcquireSharedLock());
2675  } else {
2676  throw new CaseActionException(Bundle.Case_lockingException_couldNotAcquireExclusiveLock());
2677  }
2678  }
2679  } catch (InterruptedException | CoordinationServiceException ex) {
2680  if (lockType == CaseLockType.SHARED) {
2681  throw new CaseActionException(Bundle.Case_lockingException_couldNotAcquireSharedLock(), ex);
2682  } else {
2683  throw new CaseActionException(Bundle.Case_lockingException_couldNotAcquireExclusiveLock(), ex);
2684  }
2685  }
2686  }
2687 
2691  private void releaseCaseLock() {
2692  if (caseLock != null) {
2693  try {
2694  caseLock.release();
2695  caseLock = null;
2697  logger.log(Level.SEVERE, String.format("Failed to release shared case directory lock for %s", getMetadata().getCaseDirectory()), ex);
2698  }
2699  }
2700  }
2701 
2708  private String getOrCreateSubdirectory(String subDirectoryName) {
2709  File subDirectory = Paths.get(getOutputDirectory(), subDirectoryName).toFile();
2710  if (!subDirectory.exists()) {
2711  subDirectory.mkdirs();
2712  }
2713  return subDirectory.toString();
2714 
2715  }
2716 
2728  @Messages({
2729  "Case.exceptionMessage.errorsDeletingCase=Errors occured while deleting the case. See the application log for details."
2730  })
2731  private static void deleteSingleUserCase(CaseMetadata metadata, ProgressIndicator progressIndicator) throws CaseActionException {
2732  boolean errorsOccurred = false;
2733  try {
2734  deleteTextIndex(metadata, progressIndicator);
2735  } catch (KeywordSearchServiceException ex) {
2736  errorsOccurred = true;
2737  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
2738  }
2739 
2740  try {
2741  deleteCaseDirectory(metadata, progressIndicator);
2742  } catch (CaseActionException ex) {
2743  errorsOccurred = true;
2744  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
2745  }
2746 
2747  deleteFromRecentCases(metadata, progressIndicator);
2748 
2749  if (errorsOccurred) {
2750  throw new CaseActionException(Bundle.Case_exceptionMessage_errorsDeletingCase());
2751  }
2752  }
2753 
2773  @Messages({
2774  "Case.progressMessage.connectingToCoordSvc=Connecting to coordination service...",
2775  "# {0} - exception message", "Case.exceptionMessage.failedToConnectToCoordSvc=Failed to connect to coordination service:\n{0}.",
2776  "Case.exceptionMessage.cannotGetLockToDeleteCase=Cannot delete case because it is open for another user or host.",
2777  "# {0} - exception message", "Case.exceptionMessage.failedToLockCaseForDeletion=Failed to exclusively lock case for deletion:\n{0}.",
2778  "Case.progressMessage.fetchingCoordSvcNodeData=Fetching coordination service node data for the case...",
2779  "# {0} - exception message", "Case.exceptionMessage.failedToFetchCoordSvcNodeData=Failed to fetch coordination service node data:\n{0}.",
2780  "Case.progressMessage.deletingResourcesCoordSvcNode=Deleting case resources coordination service node...",
2781  "Case.progressMessage.deletingCaseDirCoordSvcNode=Deleting case directory coordination service node..."
2782  })
2783  private static void deleteMultiUserCase(CaseMetadata metadata, ProgressIndicator progressIndicator) throws CaseActionException, InterruptedException {
2784  progressIndicator.progress(Bundle.Case_progressMessage_connectingToCoordSvc());
2785  CoordinationService coordinationService;
2786  try {
2787  coordinationService = CoordinationService.getInstance();
2788  } catch (CoordinationServiceException ex) {
2789  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
2790  throw new CaseActionException(Bundle.Case_exceptionMessage_failedToConnectToCoordSvc(ex.getLocalizedMessage()));
2791  }
2792 
2793  CaseNodeData caseNodeData;
2794  boolean errorsOccurred = false;
2795  try (CoordinationService.Lock dirLock = coordinationService.tryGetExclusiveLock(CategoryNode.CASES, metadata.getCaseDirectory())) {
2796  if (dirLock == null) {
2797  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
2798  throw new CaseActionException(Bundle.Case_exceptionMessage_cannotGetLockToDeleteCase());
2799  }
2800 
2801  progressIndicator.progress(Bundle.Case_progressMessage_fetchingCoordSvcNodeData());
2802  try {
2803  caseNodeData = CaseNodeData.readCaseNodeData(metadata.getCaseDirectory());
2804  } catch (CaseNodeDataException | InterruptedException ex) {
2805  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
2806  throw new CaseActionException(Bundle.Case_exceptionMessage_failedToFetchCoordSvcNodeData(ex.getLocalizedMessage()));
2807  }
2808 
2809  errorsOccurred = deleteMultiUserCase(caseNodeData, metadata, progressIndicator, logger);
2810 
2811  progressIndicator.progress(Bundle.Case_progressMessage_deletingResourcesCoordSvcNode());
2812  try {
2813  String resourcesLockNodePath = CoordinationServiceUtils.getCaseResourcesNodePath(caseNodeData.getDirectory());
2814  coordinationService.deleteNode(CategoryNode.CASES, resourcesLockNodePath);
2815  } catch (CoordinationServiceException ex) {
2816  if (!isNoNodeException(ex)) {
2817  errorsOccurred = true;
2818  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
2819  }
2820  } catch (InterruptedException ex) {
2821  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
2822  }
2823 
2824  } catch (CoordinationServiceException ex) {
2825  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
2826  throw new CaseActionException(Bundle.Case_exceptionMessage_failedToLockCaseForDeletion(ex.getLocalizedMessage()));
2827  }
2828 
2829  if (!errorsOccurred) {
2830  progressIndicator.progress(Bundle.Case_progressMessage_deletingCaseDirCoordSvcNode());
2831  try {
2832  String casDirNodePath = CoordinationServiceUtils.getCaseDirectoryNodePath(caseNodeData.getDirectory());
2833  coordinationService.deleteNode(CategoryNode.CASES, casDirNodePath);
2834  } catch (CoordinationServiceException | InterruptedException ex) {
2835  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
2836  errorsOccurred = true;
2837  }
2838  }
2839 
2840  if (errorsOccurred) {
2841  throw new CaseActionException(Bundle.Case_exceptionMessage_errorsDeletingCase());
2842  }
2843  }
2844 
2869  @Beta
2870  public static boolean deleteMultiUserCase(CaseNodeData caseNodeData, CaseMetadata metadata, ProgressIndicator progressIndicator, Logger logger) throws InterruptedException {
2871  boolean errorsOccurred = false;
2872  try {
2873  deleteMultiUserCaseDatabase(caseNodeData, metadata, progressIndicator, logger);
2874  deleteMultiUserCaseTextIndex(caseNodeData, metadata, progressIndicator, logger);
2875  deleteMultiUserCaseDirectory(caseNodeData, metadata, progressIndicator, logger);
2876  deleteFromRecentCases(metadata, progressIndicator);
2877  } catch (UserPreferencesException | ClassNotFoundException | SQLException ex) {
2878  errorsOccurred = true;
2879  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
2880  } catch (KeywordSearchServiceException ex) {
2881  errorsOccurred = true;
2882  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
2883  } catch (CaseActionException ex) {
2884  errorsOccurred = true;
2885  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
2886  }
2887  return errorsOccurred;
2888  }
2889 
2910  @Messages({
2911  "Case.progressMessage.deletingCaseDatabase=Deleting case database..."
2912  })
2913  private static void deleteMultiUserCaseDatabase(CaseNodeData caseNodeData, CaseMetadata metadata, ProgressIndicator progressIndicator, Logger logger) throws UserPreferencesException, ClassNotFoundException, SQLException, InterruptedException {
2914  if (!caseNodeData.isDeletedFlagSet(CaseNodeData.DeletedFlags.CASE_DB)) {
2915  progressIndicator.progress(Bundle.Case_progressMessage_deletingCaseDatabase());
2916  logger.log(Level.INFO, String.format("Deleting case database for %s (%s) in %s", caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory())); //NON-NLS
2917  CaseDbConnectionInfo info = UserPreferences.getDatabaseConnectionInfo();
2918  String url = "jdbc:postgresql://" + info.getHost() + ":" + info.getPort() + "/postgres"; //NON-NLS
2919  Class.forName("org.postgresql.Driver"); //NON-NLS
2920  try (Connection connection = DriverManager.getConnection(url, info.getUserName(), info.getPassword()); Statement statement = connection.createStatement()) {
2921  String dbExistsQuery = "SELECT 1 from pg_database WHERE datname = '" + metadata.getCaseDatabaseName() + "'"; //NON-NLS
2922  try (ResultSet queryResult = statement.executeQuery(dbExistsQuery)) {
2923  if (queryResult.next()) {
2924  String deleteCommand = "DROP DATABASE \"" + metadata.getCaseDatabaseName() + "\""; //NON-NLS
2925  statement.execute(deleteCommand);
2926  }
2927  }
2928  }
2930  }
2931  }
2932 
2948  private static void deleteMultiUserCaseTextIndex(CaseNodeData caseNodeData, CaseMetadata metadata, ProgressIndicator progressIndicator, Logger logger) throws KeywordSearchServiceException, InterruptedException {
2950  logger.log(Level.INFO, String.format("Deleting text index for %s", caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory())); //NON-NLS
2951  deleteTextIndex(metadata, progressIndicator);
2953  }
2954  }
2955 
2965  @Messages({
2966  "Case.progressMessage.deletingTextIndex=Deleting text index..."
2967  })
2968  private static void deleteTextIndex(CaseMetadata metadata, ProgressIndicator progressIndicator) throws KeywordSearchServiceException {
2969  progressIndicator.progress(Bundle.Case_progressMessage_deletingTextIndex());
2970 
2971  for (KeywordSearchService searchService : Lookup.getDefault().lookupAll(KeywordSearchService.class
2972  )) {
2973  searchService.deleteTextIndex(metadata);
2974  }
2975  }
2976 
2991  private static void deleteMultiUserCaseDirectory(CaseNodeData caseNodeData, CaseMetadata metadata, ProgressIndicator progressIndicator, Logger logger) throws CaseActionException, InterruptedException {
2992  if (!caseNodeData.isDeletedFlagSet(CaseNodeData.DeletedFlags.CASE_DIR)) {
2993  logger.log(Level.INFO, String.format("Deleting case directory for %s", caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory())); //NON-NLS
2994  deleteCaseDirectory(metadata, progressIndicator);
2996  }
2997  }
2998 
3008  @Messages({
3009  "Case.progressMessage.deletingCaseDirectory=Deleting case directory..."
3010  })
3011  private static void deleteCaseDirectory(CaseMetadata metadata, ProgressIndicator progressIndicator) throws CaseActionException {
3012  progressIndicator.progress(Bundle.Case_progressMessage_deletingCaseDirectory());
3013  if (!FileUtil.deleteDir(new File(metadata.getCaseDirectory()))) {
3014  throw new CaseActionException(String.format("Failed to delete %s", metadata.getCaseDirectory())); //NON-NLS
3015  }
3016  }
3017 
3025  @Messages({
3026  "Case.progressMessage.removingCaseFromRecentCases=Removing case from Recent Cases menu..."
3027  })
3028  private static void deleteFromRecentCases(CaseMetadata metadata, ProgressIndicator progressIndicator) {
3030  progressIndicator.progress(Bundle.Case_progressMessage_removingCaseFromRecentCases());
3031  SwingUtilities.invokeLater(() -> {
3032  RecentCases.getInstance().removeRecentCase(metadata.getCaseDisplayName(), metadata.getFilePath().toString());
3033  });
3034  }
3035  }
3036 
3047  boolean isNodeNodeEx = false;
3048  Throwable cause = ex.getCause();
3049  if (cause != null) {
3050  String causeMessage = cause.getMessage();
3051  isNodeNodeEx = causeMessage.contains(NO_NODE_ERROR_MSG_FRAGMENT);
3052  }
3053  return isNodeNodeEx;
3054  }
3055 
3067  private static void setDeletedItemFlag(CaseNodeData caseNodeData, CaseNodeData.DeletedFlags flag) throws InterruptedException {
3068  try {
3069  caseNodeData.setDeletedFlag(flag);
3070  CaseNodeData.writeCaseNodeData(caseNodeData);
3071  } catch (CaseNodeDataException ex) {
3072  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);
3073 
3074  }
3075  }
3076 
3085  private interface CaseAction<T, V, R> {
3086 
3097  R execute(T progressIndicator, V additionalParams) throws CaseActionException;
3098  }
3099 
3104  private enum CaseLockType {
3105  SHARED, EXCLUSIVE;
3106  }
3107 
3112  @ThreadSafe
3113  private final static class CancelButtonListener implements ActionListener {
3114 
3115  private final String cancellationMessage;
3116  @GuardedBy("this")
3117  private boolean cancelRequested;
3118  @GuardedBy("this")
3120  @GuardedBy("this")
3121  private Future<?> caseActionFuture;
3122 
3131  private CancelButtonListener(String cancellationMessage) {
3132  this.cancellationMessage = cancellationMessage;
3133  }
3134 
3140  private synchronized void setCaseContext(CaseContext caseContext) {
3141  this.caseContext = caseContext;
3142  /*
3143  * If the cancel button has already been pressed, pass the
3144  * cancellation on to the case context.
3145  */
3146  if (cancelRequested) {
3147  cancel();
3148  }
3149  }
3150 
3156  private synchronized void setCaseActionFuture(Future<?> caseActionFuture) {
3157  this.caseActionFuture = caseActionFuture;
3158  /*
3159  * If the cancel button has already been pressed, cancel the Future
3160  * of the task.
3161  */
3162  if (cancelRequested) {
3163  cancel();
3164  }
3165  }
3166 
3172  @Override
3173  public synchronized void actionPerformed(ActionEvent event) {
3174  cancel();
3175  }
3176 
3180  private void cancel() {
3181  /*
3182  * At a minimum, set the cancellation requested flag of this
3183  * listener.
3184  */
3185  this.cancelRequested = true;
3186  if (null != this.caseContext) {
3187  /*
3188  * Set the cancellation request flag and display the
3189  * cancellation message in the progress indicator for the case
3190  * context associated with this listener.
3191  */
3193  ProgressIndicator progressIndicator = this.caseContext.getProgressIndicator();
3194  if (progressIndicator instanceof ModalDialogProgressIndicator) {
3195  ((ModalDialogProgressIndicator) progressIndicator).setCancelling(cancellationMessage);
3196  }
3197  }
3198  this.caseContext.requestCancel();
3199  }
3200  if (null != this.caseActionFuture) {
3201  /*
3202  * Cancel the Future of the task associated with this listener.
3203  * Note that the task thread will be interrupted if the task is
3204  * blocked.
3205  */
3206  this.caseActionFuture.cancel(true);
3207  }
3208  }
3209  }
3210 
3214  private static class TaskThreadFactory implements ThreadFactory {
3215 
3216  private final String threadName;
3217 
3218  private TaskThreadFactory(String threadName) {
3219  this.threadName = threadName;
3220  }
3221 
3222  @Override
3223  public Thread newThread(Runnable task) {
3224  return new Thread(task, threadName);
3225  }
3226 
3227  }
3228 
3236  @Deprecated
3237  public static String getAppName() {
3238  return UserPreferences.getAppName();
3239  }
3240 
3260  @Deprecated
3261  public static void create(String caseDir, String caseDisplayName, String caseNumber, String examiner) throws CaseActionException {
3262  createAsCurrentCase(caseDir, caseDisplayName, caseNumber, examiner, CaseType.SINGLE_USER_CASE);
3263  }
3264 
3285  @Deprecated
3286  public static void create(String caseDir, String caseDisplayName, String caseNumber, String examiner, CaseType caseType) throws CaseActionException {
3287  createAsCurrentCase(caseDir, caseDisplayName, caseNumber, examiner, caseType);
3288  }
3289 
3301  @Deprecated
3302  public static void open(String caseMetadataFilePath) throws CaseActionException {
3303  openAsCurrentCase(caseMetadataFilePath);
3304  }
3305 
3315  @Deprecated
3316  public void closeCase() throws CaseActionException {
3317  closeCurrentCase();
3318  }
3319 
3325  @Deprecated
3326  public static void invokeStartupDialog() {
3328  }
3329 
3343  @Deprecated
3344  public static String convertTimeZone(String timeZoneId) {
3345  return TimeZoneUtils.convertToAlphaNumericFormat(timeZoneId);
3346  }
3347 
3357  @Deprecated
3358  public static boolean pathExists(String filePath) {
3359  return new File(filePath).isFile();
3360  }
3361 
3370  @Deprecated
3371  public static String getAutopsyVersion() {
3372  return Version.getVersion();
3373  }
3374 
3382  @Deprecated
3383  public static boolean existsCurrentCase() {
3384  return isCaseOpen();
3385  }
3386 
3396  @Deprecated
3397  public static String getModulesOutputDirRelPath() {
3398  return "ModuleOutput"; //NON-NLS
3399  }
3400 
3410  @Deprecated
3411  public static PropertyChangeSupport
3413  return new PropertyChangeSupport(Case.class
3414  );
3415  }
3416 
3425  @Deprecated
3426  public String getModulesOutputDirAbsPath() {
3427  return getModuleDirectory();
3428  }
3429 
3444  @Deprecated
3445  public Image addImage(String imgPath, long imgId, String timeZone) throws CaseActionException {
3446  try {
3447  Image newDataSource = caseDb.getImageById(imgId);
3448  notifyDataSourceAdded(newDataSource, UUID.randomUUID());
3449  return newDataSource;
3450  } catch (TskCoreException ex) {
3451  throw new CaseActionException(NbBundle.getMessage(this.getClass(), "Case.addImg.exception.msg"), ex);
3452  }
3453  }
3454 
3462  @Deprecated
3463  public Set<TimeZone> getTimeZone() {
3464  return getTimeZones();
3465  }
3466 
3477  @Deprecated
3478  public void deleteReports(Collection<? extends Report> reports, boolean deleteFromDisk) throws TskCoreException {
3479  deleteReports(reports);
3480  }
3481 
3482 }
static final AutopsyEventPublisher eventPublisher
Definition: Case.java:157
List< Content > getDataSources()
Definition: Case.java:1438
static CaseNodeData createCaseNodeData(final CaseMetadata metadata)
void notifyContentTagDeleted(ContentTag deletedTag)
Definition: Case.java:1563
Case(CaseMetadata caseMetaData)
Definition: Case.java:1770
static CaseType fromString(String typeName)
Definition: Case.java:197
final SleuthkitEventListener sleuthkitEventListener
Definition: Case.java:165
static final String CASE_ACTION_THREAD_NAME
Definition: Case.java:153
static void setDeletedItemFlag(CaseNodeData caseNodeData, CaseNodeData.DeletedFlags flag)
Definition: Case.java:3067
static void createAsCurrentCase(CaseType caseType, String caseDir, CaseDetails caseDetails)
Definition: Case.java:603
void notifyBlackBoardArtifactTagDeleted(BlackboardArtifactTag deletedTag)
Definition: Case.java:1615
Void open(ProgressIndicator progressIndicator, Object additionalParams)
Definition: Case.java:1965
CoordinationService.Lock caseLock
Definition: Case.java:163
static boolean deleteMultiUserCase(CaseNodeData caseNodeData, CaseMetadata metadata, ProgressIndicator progressIndicator, Logger logger)
Definition: Case.java:2870
Image addImage(String imgPath, long imgId, String timeZone)
Definition: Case.java:3445
static synchronized IngestManager getInstance()
void deleteNode(CategoryNode category, String nodePath)
static final String NO_NODE_ERROR_MSG_FRAGMENT
Definition: Case.java:155
void acquireCaseLock(CaseLockType lockType)
Definition: Case.java:2665
static boolean existsCurrentCase()
Definition: Case.java:3383
static void removePropertyChangeListener(PropertyChangeListener listener)
Definition: Case.java:461
void start(String message, int totalWorkUnits)
static final Logger logger
Definition: Case.java:156
static final String EXPORT_FOLDER
Definition: Case.java:147
static void createCaseDirectory(String caseDirPath, CaseType caseType)
Definition: Case.java:945
void notifyTagDefinitionChanged(String changedTagName)
Definition: Case.java:1574
static volatile Frame mainFrame
Definition: Case.java:159
static String convertTimeZone(String timeZoneId)
Definition: Case.java:3344
static boolean driveExists(String path)
Definition: DriveUtils.java:66
static void deleteSingleUserCase(CaseMetadata metadata, ProgressIndicator progressIndicator)
Definition: Case.java:2731
void addSubscriber(Set< String > eventNames, PropertyChangeListener subscriber)
void publishTimelineEventAddedEvent(TimelineManager.TimelineEventAddedEvent event)
Definition: Case.java:419
synchronized static void setLogDirectory(String directoryPath)
Definition: Logger.java:89
volatile ExecutorService caseActionExecutor
Definition: Case.java:162
void notifyCentralRepoCommentChanged(long contentId, String newComment)
Definition: Case.java:1589
static final String CACHE_FOLDER
Definition: Case.java:146
static void deleteMultiUserCaseDirectory(CaseNodeData caseNodeData, CaseMetadata metadata, ProgressIndicator progressIndicator, Logger logger)
Definition: Case.java:2991
Case(CaseType caseType, String caseDir, CaseDetails caseDetails)
Definition: Case.java:1761
void addReport(String localPath, String srcModuleName, String reportName)
Definition: Case.java:1630
static void updateGUIForCaseOpened(Case newCurrentCase)
Definition: Case.java:1068
void notifyDataSourceNameChanged(Content dataSource, String newName)
Definition: Case.java:1541
static CaseDbConnectionInfo getDatabaseConnectionInfo()
static void create(String caseDir, String caseDisplayName, String caseNumber, String examiner, CaseType caseType)
Definition: Case.java:3286
static final int CASE_LOCK_TIMEOUT_MINS
Definition: Case.java:142
static final String SINGLE_USER_CASE_DB_NAME
Definition: Case.java:144
static void deleteCase(CaseMetadata metadata)
Definition: Case.java:807
void deleteReports(Collection<?extends Report > reports, boolean deleteFromDisk)
Definition: Case.java:3478
synchronized void setCaseContext(CaseContext caseContext)
Definition: Case.java:3140
static final int CASE_RESOURCES_LOCK_TIMEOUT_HOURS
Definition: Case.java:143
static void clearTempSubDir(String tempSubDirPath)
Definition: Case.java:1197
static boolean isValidName(String caseName)
Definition: Case.java:545
void openCaseDataBase(ProgressIndicator progressIndicator)
Definition: Case.java:2295
void createCaseDirectoryIfDoesNotExist(ProgressIndicator progressIndicator)
Definition: Case.java:2112
CollaborationMonitor collaborationMonitor
Definition: Case.java:166
void openCommunicationChannels(ProgressIndicator progressIndicator)
Definition: Case.java:2461
static void shutDownTaskExecutor(ExecutorService executor)
static String getModulesOutputDirRelPath()
Definition: Case.java:3397
void saveCaseMetadataToFile(ProgressIndicator progressIndicator)
Definition: Case.java:2153
static void deleteMultiUserCaseTextIndex(CaseNodeData caseNodeData, CaseMetadata metadata, ProgressIndicator progressIndicator, Logger logger)
Definition: Case.java:2948
void doOpenCaseAction(String progressIndicatorTitle, CaseAction< ProgressIndicator, Object, Void > caseAction, CaseLockType caseLockType, boolean allowCancellation, Object additionalParams)
Definition: Case.java:1811
synchronized void actionPerformed(ActionEvent event)
Definition: Case.java:3173
static final String MODULE_FOLDER
Definition: Case.java:152
void openCaseLevelServices(ProgressIndicator progressIndicator)
Definition: Case.java:2323
static void create(String caseDir, String caseDisplayName, String caseNumber, String examiner)
Definition: Case.java:3261
synchronized void openRemoteEventChannel(String channelName)
void createCaseNodeData(ProgressIndicator progressIndicator)
Definition: Case.java:2177
void publishArtifactsPostedEvent(Blackboard.ArtifactsPostedEvent event)
Definition: Case.java:425
static String displayNameToUniqueName(String caseDisplayName)
Definition: Case.java:905
static void deleteFromRecentCases(CaseMetadata metadata, ProgressIndicator progressIndicator)
Definition: Case.java:3028
static void openAsCurrentCase(String caseMetadataFilePath)
Definition: Case.java:630
static void removeEventSubscriber(Set< String > eventNames, PropertyChangeListener subscriber)
Definition: Case.java:521
Lock tryGetExclusiveLock(CategoryNode category, String nodePath, int timeOut, TimeUnit timeUnit)
static boolean isNoNodeException(CoordinationServiceException ex)
Definition: Case.java:3046
void fireModuleDataEvent(ModuleDataEvent moduleDataEvent)
default void setCancelling(String cancellingMessage)
void close(ProgressIndicator progressIndicator)
Definition: Case.java:2565
void notifyBlackBoardArtifactTagAdded(BlackboardArtifactTag newTag)
Definition: Case.java:1604
static PropertyChangeSupport getPropertyChangeSupport()
Definition: Case.java:3412
static void addPropertyChangeListener(PropertyChangeListener listener)
Definition: Case.java:449
synchronized void setCaseActionFuture(Future<?> caseActionFuture)
Definition: Case.java:3156
void removeSubscriber(Set< String > eventNames, PropertyChangeListener subscriber)
void createCaseDatabase(ProgressIndicator progressIndicator)
Definition: Case.java:2249
void openAppServiceCaseResources(ProgressIndicator progressIndicator)
Definition: Case.java:2351
static void removeEventSubscriber(String eventName, PropertyChangeListener subscriber)
Definition: Case.java:511
void switchLoggingToCaseLogsDirectory(ProgressIndicator progressIndicator)
Definition: Case.java:2133
static void deleteTextIndex(CaseMetadata metadata, ProgressIndicator progressIndicator)
Definition: Case.java:2968
void deleteTempfilesFromCaseDirectory(ProgressIndicator progressIndicator)
Definition: Case.java:2224
void deleteReports(Collection<?extends Report > reports)
Definition: Case.java:1685
void notifyDataSourceAdded(Content dataSource, UUID addingDataSourceEventId)
Definition: Case.java:1528
Report addReport(String localPath, String srcModuleName, String reportName, Content parent)
Definition: Case.java:1648
static boolean pathExists(String filePath)
Definition: Case.java:3358
SleuthkitCase createPortableCase(String caseName, File portableCaseFolder)
Definition: Case.java:2057
R execute(T progressIndicator, V additionalParams)
static void open(String caseMetadataFilePath)
Definition: Case.java:3302
static void openAsCurrentCase(Case newCurrentCase, boolean isNewCase)
Definition: Case.java:856
static CoordinationService.Lock acquireCaseResourcesLock(String caseDir)
Definition: Case.java:1042
static void error(String title, String message)
String getOrCreateSubdirectory(String subDirectoryName)
Definition: Case.java:2708
boolean equalsName(String otherTypeName)
Definition: Case.java:255
static final String EVENT_CHANNEL_NAME
Definition: Case.java:145
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
static void addEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:486
static String convertToAlphaNumericFormat(String timeZoneId)
static final String LOG_FOLDER
Definition: Case.java:148
Void create(ProgressIndicator progressIndicator, Object additionalParams)
Definition: Case.java:1913
Lock tryGetSharedLock(CategoryNode category, String nodePath, int timeOut, TimeUnit timeUnit)
static volatile Case currentCase
Definition: Case.java:160
void notifyAddingDataSource(UUID eventId)
Definition: Case.java:1499
static void addEventSubscriber(String eventName, PropertyChangeListener subscriber)
Definition: Case.java:501
void notifyContentTagAdded(ContentTag newTag)
Definition: Case.java:1552
void cancelAllIngestJobs(IngestJob.CancellationReason reason)
static final String CASE_RESOURCES_THREAD_NAME
Definition: Case.java:154
static boolean deleteDir(File dirPath)
Definition: FileUtil.java:47
static void createAsCurrentCase(String caseDir, String caseDisplayName, String caseNumber, String examiner, CaseType caseType)
Definition: Case.java:576
static void deleteCaseDirectory(CaseMetadata metadata, ProgressIndicator progressIndicator)
Definition: Case.java:3011
void notifyFailedAddingDataSource(UUID addingDataSourceEventId)
Definition: Case.java:1513
static void deleteMultiUserCase(CaseMetadata metadata, ProgressIndicator progressIndicator)
Definition: Case.java:2783
static final String CONFIG_FOLDER
Definition: Case.java:150
static void removeEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:531
static final Object caseActionSerializationLock
Definition: Case.java:158
static void deleteMultiUserCaseDatabase(CaseNodeData caseNodeData, CaseMetadata metadata, ProgressIndicator progressIndicator, Logger logger)
Definition: Case.java:2913
static final String REPORTS_FOLDER
Definition: Case.java:149
static final String TEMP_FOLDER
Definition: Case.java:151
static void addEventSubscriber(Set< String > eventNames, PropertyChangeListener subscriber)
Definition: Case.java:476
void updateCaseNodeData(ProgressIndicator progressIndicator)
Definition: Case.java:2203
static synchronized IngestServices getInstance()

Copyright © 2012-2020 Basis Technology. Generated on: Wed Apr 8 2020
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.