Autopsy  4.9.1
Graphical digital forensics platform for The Sleuth Kit and other tools.
Case.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2011-2018 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.casemodule;
20 
21 import java.awt.Frame;
22 import java.awt.event.ActionEvent;
23 import java.awt.event.ActionListener;
24 import java.beans.PropertyChangeListener;
25 import java.beans.PropertyChangeSupport;
26 import java.io.File;
27 import java.io.IOException;
28 import java.nio.file.InvalidPathException;
29 import java.nio.file.Path;
30 import java.nio.file.Paths;
31 import java.sql.Connection;
32 import java.sql.DriverManager;
33 import java.sql.SQLException;
34 import java.sql.Statement;
35 import java.text.SimpleDateFormat;
36 import java.util.Collection;
37 import java.util.Date;
38 import java.util.HashMap;
39 import java.util.HashSet;
40 import java.util.List;
41 import java.util.Map;
42 import java.util.MissingResourceException;
43 import java.util.Set;
44 import java.util.TimeZone;
45 import java.util.UUID;
46 import java.util.concurrent.CancellationException;
47 import java.util.concurrent.ExecutionException;
48 import java.util.concurrent.ExecutorService;
49 import java.util.concurrent.Executors;
50 import java.util.concurrent.Future;
51 import java.util.concurrent.ThreadFactory;
52 import java.util.concurrent.TimeUnit;
53 import java.util.logging.Level;
54 import java.util.stream.Collectors;
55 import java.util.stream.Stream;
56 import javax.annotation.concurrent.GuardedBy;
57 import javax.annotation.concurrent.ThreadSafe;
58 import javax.swing.JOptionPane;
59 import javax.swing.SwingUtilities;
60 import org.openide.util.Lookup;
61 import org.openide.util.NbBundle;
62 import org.openide.util.NbBundle.Messages;
63 import org.openide.util.actions.CallableSystemAction;
64 import org.openide.windows.WindowManager;
68 import static org.sleuthkit.autopsy.casemodule.Bundle.*;
110 import org.sleuthkit.datamodel.BlackboardArtifactTag;
111 import org.sleuthkit.datamodel.CaseDbConnectionInfo;
112 import org.sleuthkit.datamodel.Content;
113 import org.sleuthkit.datamodel.ContentTag;
114 import org.sleuthkit.datamodel.Image;
115 import org.sleuthkit.datamodel.Report;
116 import org.sleuthkit.datamodel.SleuthkitCase;
117 import org.sleuthkit.datamodel.TskCoreException;
118 import org.sleuthkit.datamodel.TskUnsupportedSchemaVersionException;
120 
124 public class Case {
125 
126  private static final int DIR_LOCK_TIMOUT_HOURS = 12;
127  private static final int RESOURCES_LOCK_TIMOUT_HOURS = 12;
128  private static final String SINGLE_USER_CASE_DB_NAME = "autopsy.db";
129  private static final String EVENT_CHANNEL_NAME = "%s-Case-Events"; //NON-NLS
130  private static final String CACHE_FOLDER = "Cache"; //NON-NLS
131  private static final String EXPORT_FOLDER = "Export"; //NON-NLS
132  private static final String LOG_FOLDER = "Log"; //NON-NLS
133  private static final String REPORTS_FOLDER = "Reports"; //NON-NLS
134  private static final String CONFIG_FOLDER = "Config"; // NON-NLS
135  private static final String TEMP_FOLDER = "Temp"; //NON-NLS
136  private static final String MODULE_FOLDER = "ModuleOutput"; //NON-NLS
137  private static final String CASE_ACTION_THREAD_NAME = "%s-case-action";
138  private static final String CASE_RESOURCES_THREAD_NAME = "%s-manage-case-resources";
139  private static final Logger logger = Logger.getLogger(Case.class.getName());
141  private static final Object caseActionSerializationLock = new Object();
142  private static volatile Frame mainFrame;
143  private static volatile Case currentCase;
144  private final CaseMetadata metadata;
145  private volatile ExecutorService caseLockingExecutor;
147  private SleuthkitCase caseDb;
148  private CollaborationMonitor collaborationMonitor;
150  private boolean hasDataSources;
151 
152  /*
153  * Get a reference to the main window of the desktop application to use to
154  * parent pop up dialogs and initialize the application name for use in
155  * changing the main window title.
156  */
157  static {
158  WindowManager.getDefault().invokeWhenUIReady(new Runnable() {
159  @Override
160  public void run() {
161  mainFrame = WindowManager.getDefault().getMainWindow();
162  }
163  });
164  }
165 
169  public enum CaseType {
170 
171  SINGLE_USER_CASE("Single-user case"), //NON-NLS
172  MULTI_USER_CASE("Multi-user case"); //NON-NLS
173 
174  private final String typeName;
175 
183  public static CaseType fromString(String typeName) {
184  if (typeName != null) {
185  for (CaseType c : CaseType.values()) {
186  if (typeName.equalsIgnoreCase(c.toString())) {
187  return c;
188  }
189  }
190  }
191  return null;
192  }
193 
199  @Override
200  public String toString() {
201  return typeName;
202  }
203 
209  @Messages({
210  "Case_caseType_singleUser=Single-user case",
211  "Case_caseType_multiUser=Multi-user case"
212  })
214  if (fromString(typeName) == SINGLE_USER_CASE) {
215  return Bundle.Case_caseType_singleUser();
216  } else {
217  return Bundle.Case_caseType_multiUser();
218  }
219  }
220 
226  private CaseType(String typeName) {
227  this.typeName = typeName;
228  }
229 
240  @Deprecated
241  public boolean equalsName(String otherTypeName) {
242  return (otherTypeName == null) ? false : typeName.equals(otherTypeName);
243  }
244 
245  };
246 
251  public enum Events {
252 
260  @Deprecated
269  @Deprecated
278  @Deprecated
383 
384  };
385 
392  public static void addPropertyChangeListener(PropertyChangeListener listener) {
393  addEventSubscriber(Stream.of(Events.values())
394  .map(Events::toString)
395  .collect(Collectors.toSet()), listener);
396  }
397 
404  public static void removePropertyChangeListener(PropertyChangeListener listener) {
405  removeEventSubscriber(Stream.of(Events.values())
406  .map(Events::toString)
407  .collect(Collectors.toSet()), listener);
408  }
409 
418  @Deprecated
419  public static void addEventSubscriber(Set<String> eventNames, PropertyChangeListener subscriber) {
420  eventPublisher.addSubscriber(eventNames, subscriber);
421  }
422 
429  public static void addEventTypeSubscriber(Set<Events> eventTypes, PropertyChangeListener subscriber) {
430  eventTypes.forEach((Events event) -> {
431  eventPublisher.addSubscriber(event.toString(), subscriber);
432  });
433  }
434 
443  @Deprecated
444  public static void addEventSubscriber(String eventName, PropertyChangeListener subscriber) {
445  eventPublisher.addSubscriber(eventName, subscriber);
446  }
447 
454  public static void removeEventSubscriber(String eventName, PropertyChangeListener subscriber) {
455  eventPublisher.removeSubscriber(eventName, subscriber);
456  }
457 
464  public static void removeEventSubscriber(Set<String> eventNames, PropertyChangeListener subscriber) {
465  eventPublisher.removeSubscriber(eventNames, subscriber);
466  }
467 
474  public static void removeEventTypeSubscriber(Set<Events> eventTypes, PropertyChangeListener subscriber) {
475  eventTypes.forEach((Events event) -> {
476  eventPublisher.removeSubscriber(event.toString(), subscriber);
477  });
478  }
479 
488  public static boolean isValidName(String caseName) {
489  return !(caseName.contains("\\") || caseName.contains("/") || caseName.contains(":")
490  || caseName.contains("*") || caseName.contains("?") || caseName.contains("\"")
491  || caseName.contains("<") || caseName.contains(">") || caseName.contains("|"));
492  }
493 
518  @Deprecated
519  public static void createAsCurrentCase(String caseDir, String caseDisplayName, String caseNumber, String examiner, CaseType caseType) throws CaseActionException, CaseActionCancelledException {
520  createAsCurrentCase(caseType, caseDir, new CaseDetails(caseDisplayName, caseNumber, examiner, "", "", ""));
521  }
522 
542  @Messages({
543  "Case.exceptionMessage.emptyCaseName=Must specify a case name.",
544  "Case.exceptionMessage.emptyCaseDir=Must specify a case directory path."
545  })
546  public static void createAsCurrentCase(CaseType caseType, String caseDir, CaseDetails caseDetails) throws CaseActionException, CaseActionCancelledException {
547  if (caseDetails.getCaseDisplayName().isEmpty()) {
548  throw new CaseActionException(Bundle.Case_exceptionMessage_emptyCaseName());
549  }
550  if (caseDir.isEmpty()) {
551  throw new CaseActionException(Bundle.Case_exceptionMessage_emptyCaseDir());
552  }
553  openAsCurrentCase(new Case(caseType, caseDir, caseDetails), true);
554  }
555 
569  @Messages({
570  "Case.exceptionMessage.failedToReadMetadata=Failed to read case metadata.",
571  "Case.exceptionMessage.cannotOpenMultiUserCaseNoSettings=Multi-user settings are missing (see Tools, Options, Multi-user tab), cannot open a multi-user case."
572  })
573  public static void openAsCurrentCase(String caseMetadataFilePath) throws CaseActionException {
575  try {
576  metadata = new CaseMetadata(Paths.get(caseMetadataFilePath));
577  } catch (CaseMetadataException ex) {
578  throw new CaseActionException(Bundle.Case_exceptionMessage_failedToReadMetadata(), ex);
579  }
581  throw new CaseActionException(Bundle.Case_exceptionMessage_cannotOpenMultiUserCaseNoSettings());
582  }
583  openAsCurrentCase(new Case(metadata), false);
584  }
585 
591  public static boolean isCaseOpen() {
592  return currentCase != null;
593  }
594 
602  public static Case getCurrentCase() {
603  try {
604  return getCurrentCaseThrows();
605  } catch (NoCurrentCaseException ex) {
606  /*
607  * Throw a runtime exception, since this is a programming error.
608  */
609  throw new IllegalStateException(NbBundle.getMessage(Case.class, "Case.getCurCase.exception.noneOpen"), ex);
610  }
611  }
612 
631  Case openCase = currentCase;
632  if (openCase == null) {
633  throw new NoCurrentCaseException(NbBundle.getMessage(Case.class, "Case.getCurCase.exception.noneOpen"));
634  } else {
635  return openCase;
636  }
637  }
638 
647  @Messages({
648  "# {0} - exception message", "Case.closeException.couldNotCloseCase=Error closing case: {0}",
649  "Case.progressIndicatorTitle.closingCase=Closing Case"
650  })
651  public static void closeCurrentCase() throws CaseActionException {
652  synchronized (caseActionSerializationLock) {
653  if (null == currentCase) {
654  return;
655  }
656  Case closedCase = currentCase;
657  try {
658  eventPublisher.publishLocally(new AutopsyEvent(Events.CURRENT_CASE.toString(), closedCase, null));
659  logger.log(Level.INFO, "Closing current case {0} ({1}) in {2}", new Object[]{closedCase.getDisplayName(), closedCase.getName(), closedCase.getCaseDirectory()}); //NON-NLS
660  currentCase = null;
661  closedCase.close();
662  logger.log(Level.INFO, "Closed current case {0} ({1}) in {2}", new Object[]{closedCase.getDisplayName(), closedCase.getName(), closedCase.getCaseDirectory()}); //NON-NLS
663  } catch (CaseActionException ex) {
664  logger.log(Level.SEVERE, String.format("Error closing current case %s (%s) in %s", closedCase.getDisplayName(), closedCase.getName(), closedCase.getCaseDirectory()), ex); //NON-NLS
665  throw ex;
666  } finally {
669  }
670  }
671  }
672  }
673 
682  public static void deleteCurrentCase() throws CaseActionException {
683  synchronized (caseActionSerializationLock) {
684  if (null == currentCase) {
685  return;
686  }
687  CaseMetadata metadata = currentCase.getMetadata();
689  deleteCase(metadata);
690  }
691  }
692 
704  @Messages({
705  "Case.progressIndicatorTitle.deletingCase=Deleting Case",
706  "Case.exceptionMessage.cannotDeleteCurrentCase=Cannot delete current case, it must be closed first.",
707  "Case.progressMessage.checkingForOtherUser=Checking to see if another user has the case open...",
708  "Case.exceptionMessage.cannotGetLockToDeleteCase=Cannot delete case because it is open for another user or there is a problem with the coordination service."
709  })
710  public static void deleteCase(CaseMetadata metadata) throws CaseActionException {
711  StopWatch stopWatch = new StopWatch();
712  stopWatch.start();
713  synchronized (caseActionSerializationLock) {
714  if (null != currentCase) {
715  throw new CaseActionException(Bundle.Case_exceptionMessage_cannotDeleteCurrentCase());
716  }
717  }
718  stopWatch.stop();
719  logger.log(Level.INFO, String.format("Used %d s to acquire caseActionSerializationLock (Java monitor in Case class) for %s (%s) in %s", stopWatch.getElapsedTimeSecs(), metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()));
720 
721  /*
722  * Set up either a GUI progress indicator without a cancel button (can't
723  * cancel deleting a case) or a logging progress indicator.
724  */
725  ProgressIndicator progressIndicator;
727  progressIndicator = new ModalDialogProgressIndicator(mainFrame, Bundle.Case_progressIndicatorTitle_deletingCase());
728  } else {
729  progressIndicator = new LoggingProgressIndicator();
730  }
731  progressIndicator.start(Bundle.Case_progressMessage_preparing());
732  try {
733  if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) {
734  deleteCase(metadata, progressIndicator);
735  } else {
736  /*
737  * First, acquire an exclusive case directory lock. The case
738  * cannot be deleted if another node has it open.
739  */
740  progressIndicator.progress(Bundle.Case_progressMessage_checkingForOtherUser());
741  stopWatch.reset();
742  stopWatch.start();
744  stopWatch.stop();
745  logger.log(Level.INFO, String.format("Used %d s to acquire case directory coordination service lock for %s (%s) in %s", stopWatch.getElapsedTimeSecs(), metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()));
746  if (dirLock != null) {
747  deleteCase(metadata, progressIndicator);
748  } else {
749  throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireDirLock());
750  }
751  } catch (CoordinationServiceException ex) {
752  stopWatch.stop();
753  logger.log(Level.INFO, String.format("Used %d s to fail to acquire case directory coordination service lock for %s (%s) in %s", stopWatch.getElapsedTimeSecs(), metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()));
754  throw new CaseActionException(Bundle.Case_exceptionMessage_cannotGetLockToDeleteCase(), ex);
755  }
756  }
757  } finally {
758  progressIndicator.finish();
759  }
760  }
761 
772  @Messages({
773  "Case.exceptionMessage.cannotLocateMainWindow=Cannot locate main application window"
774  })
775  private static void openAsCurrentCase(Case newCurrentCase, boolean isNewCase) throws CaseActionException, CaseActionCancelledException {
776  synchronized (caseActionSerializationLock) {
777  if (null != currentCase) {
778  try {
780  } catch (CaseActionException ex) {
781  /*
782  * Notify the user and continue (the error has already been
783  * logged in closeCurrentCase.
784  */
785  MessageNotifyUtil.Message.error(ex.getLocalizedMessage());
786  }
787  }
788  try {
789  logger.log(Level.INFO, "Opening {0} ({1}) in {2} as the current case", new Object[]{newCurrentCase.getDisplayName(), newCurrentCase.getName(), newCurrentCase.getCaseDirectory()}); //NON-NLS
790  newCurrentCase.open(isNewCase);
791  currentCase = newCurrentCase;
792  logger.log(Level.INFO, "Opened {0} ({1}) in {2} as the current case", new Object[]{newCurrentCase.getDisplayName(), newCurrentCase.getName(), newCurrentCase.getCaseDirectory()}); //NON-NLS
794  updateGUIForCaseOpened(newCurrentCase);
795  }
796  eventPublisher.publishLocally(new AutopsyEvent(Events.CURRENT_CASE.toString(), null, currentCase));
797  } catch (CaseActionCancelledException ex) {
798  logger.log(Level.INFO, String.format("Cancelled opening %s (%s) in %s as the current case", newCurrentCase.getDisplayName(), newCurrentCase.getName(), newCurrentCase.getCaseDirectory())); //NON-NLS
799  throw ex;
800  } catch (CaseActionException ex) {
801  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
802  throw ex;
803  }
804  }
805  }
806 
815  private static String displayNameToUniqueName(String caseDisplayName) {
816  /*
817  * Replace all non-ASCII characters.
818  */
819  String uniqueCaseName = caseDisplayName.replaceAll("[^\\p{ASCII}]", "_"); //NON-NLS
820 
821  /*
822  * Replace all control characters.
823  */
824  uniqueCaseName = uniqueCaseName.replaceAll("[\\p{Cntrl}]", "_"); //NON-NLS
825 
826  /*
827  * Replace /, \, :, ?, space, ' ".
828  */
829  uniqueCaseName = uniqueCaseName.replaceAll("[ /?:'\"\\\\]", "_"); //NON-NLS
830 
831  /*
832  * Make it all lowercase.
833  */
834  uniqueCaseName = uniqueCaseName.toLowerCase();
835 
836  /*
837  * Add a time stamp for uniqueness.
838  */
839  SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
840  Date date = new Date();
841  uniqueCaseName = uniqueCaseName + "_" + dateFormat.format(date);
842 
843  return uniqueCaseName;
844  }
845 
854  public static void createCaseDirectory(String caseDir, CaseType caseType) throws CaseActionException {
855 
856  File caseDirF = new File(caseDir);
857 
858  if (caseDirF.exists()) {
859  if (caseDirF.isFile()) {
860  throw new CaseActionException(
861  NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.existNotDir", caseDir));
862 
863  } else if (!caseDirF.canRead() || !caseDirF.canWrite()) {
864  throw new CaseActionException(
865  NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.existCantRW", caseDir));
866  }
867  }
868 
869  try {
870  boolean result = (caseDirF).mkdirs(); // create root case Directory
871 
872  if (result == false) {
873  throw new CaseActionException(
874  NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreate", caseDir));
875  }
876 
877  // create the folders inside the case directory
878  String hostClause = "";
879 
880  if (caseType == CaseType.MULTI_USER_CASE) {
881  hostClause = File.separator + NetworkUtils.getLocalHostName();
882  }
883  result = result && (new File(caseDir + hostClause + File.separator + EXPORT_FOLDER)).mkdirs()
884  && (new File(caseDir + hostClause + File.separator + LOG_FOLDER)).mkdirs()
885  && (new File(caseDir + hostClause + File.separator + TEMP_FOLDER)).mkdirs()
886  && (new File(caseDir + hostClause + File.separator + CACHE_FOLDER)).mkdirs();
887 
888  if (result == false) {
889  throw new CaseActionException(
890  NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreateCaseDir", caseDir));
891  }
892 
893  final String modulesOutDir = caseDir + hostClause + File.separator + MODULE_FOLDER;
894  result = new File(modulesOutDir).mkdir();
895 
896  if (result == false) {
897  throw new CaseActionException(
898  NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreateModDir",
899  modulesOutDir));
900  }
901 
902  final String reportsOutDir = caseDir + hostClause + File.separator + REPORTS_FOLDER;
903  result = new File(reportsOutDir).mkdir();
904 
905  if (result == false) {
906  throw new CaseActionException(
907  NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreateReportsDir",
908  modulesOutDir));
909 
910  }
911 
912  } catch (MissingResourceException | CaseActionException e) {
913  throw new CaseActionException(
914  NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.gen", caseDir), e);
915  }
916  }
917 
925  static Map<Long, String> getImagePaths(SleuthkitCase db) {
926  Map<Long, String> imgPaths = new HashMap<>();
927  try {
928  Map<Long, List<String>> imgPathsList = db.getImagePaths();
929  for (Map.Entry<Long, List<String>> entry : imgPathsList.entrySet()) {
930  if (entry.getValue().size() > 0) {
931  imgPaths.put(entry.getKey(), entry.getValue().get(0));
932  }
933  }
934  } catch (TskCoreException ex) {
935  logger.log(Level.SEVERE, "Error getting image paths", ex); //NON-NLS
936  }
937  return imgPaths;
938  }
939 
956  @Messages({
957  "Case.progressMessage.deletingTextIndex=Deleting text index...",
958  "Case.progressMessage.deletingCaseDatabase=Deleting case database...",
959  "Case.progressMessage.deletingCaseDirectory=Deleting case directory...",
960  "Case.exceptionMessage.errorsDeletingCase=Errors occured while deleting the case. See the application log for details"
961  })
962  private static void deleteCase(CaseMetadata metadata, ProgressIndicator progressIndicator) throws CaseActionException {
963  StopWatch stopWatch = new StopWatch();
964  boolean errorsOccurred = false;
965  if (CaseType.MULTI_USER_CASE == metadata.getCaseType()) {
966  /*
967  * Delete the case database from the database server.
968  */
969  stopWatch.start();
970  try {
971  progressIndicator.progress(Bundle.Case_progressMessage_deletingCaseDatabase());
972  CaseDbConnectionInfo db;
974  Class.forName("org.postgresql.Driver"); //NON-NLS
975  try (Connection connection = DriverManager.getConnection("jdbc:postgresql://" + db.getHost() + ":" + db.getPort() + "/postgres", db.getUserName(), db.getPassword()); //NON-NLS
976  Statement statement = connection.createStatement();) {
977  String deleteCommand = "DROP DATABASE \"" + metadata.getCaseDatabaseName() + "\""; //NON-NLS
978  statement.execute(deleteCommand);
979  stopWatch.stop();
980  logger.log(Level.INFO, String.format("Used %d s to delete case database for %s (%s) in %s", stopWatch.getElapsedTimeSecs(), metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()));
981  }
982  } catch (UserPreferencesException | ClassNotFoundException | SQLException ex) {
983  logger.log(Level.SEVERE, String.format("Failed to delete case database %s for %s (%s) in %s", metadata.getCaseDatabaseName(), metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex);
984  errorsOccurred = true;
985  stopWatch.stop();
986  logger.log(Level.INFO, String.format("Used %d s to fail delete case database for %s (%s) in %s", stopWatch.getElapsedTimeSecs(), metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()));
987  }
988  }
989 
990  /*
991  * Delete the text index.
992  */
993  progressIndicator.progress(Bundle.Case_progressMessage_deletingTextIndex());
994  for (KeywordSearchService searchService : Lookup.getDefault().lookupAll(KeywordSearchService.class)) {
995  try {
996  stopWatch.reset();
997  stopWatch.start();
998  searchService.deleteTextIndex(metadata);
999  stopWatch.stop();
1000  logger.log(Level.INFO, String.format("Used %d s to delete text index for %s (%s) in %s", stopWatch.getElapsedTimeSecs(), metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()));
1001  } catch (KeywordSearchServiceException ex) {
1002  logger.log(Level.SEVERE, String.format("Failed to delete text index for %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex);
1003  errorsOccurred = true;
1004  stopWatch.stop();
1005  logger.log(Level.INFO, String.format("Used %d s to fail to delete text index for %s (%s) in %s", stopWatch.getElapsedTimeSecs(), metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()));
1006  }
1007  }
1008 
1009  /*
1010  * Delete the case directory.
1011  */
1012  progressIndicator.progress(Bundle.Case_progressMessage_deletingCaseDirectory());
1013  stopWatch.reset();
1014  stopWatch.start();
1015  if (!FileUtil.deleteDir(new File(metadata.getCaseDirectory()))) {
1016  logger.log(Level.SEVERE, String.format("Failed to delete case directory for %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()));
1017  errorsOccurred = true;
1018  stopWatch.stop();
1019  logger.log(Level.INFO, String.format("Used %d s to fail to delete case directory for %s (%s) in %s", stopWatch.getElapsedTimeSecs(), metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()));
1020  } else {
1021  stopWatch.stop();
1022  logger.log(Level.INFO, String.format("Used %d s to delete case directory for %s (%s) in %s", stopWatch.getElapsedTimeSecs(), metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()));
1023  }
1024 
1025  /*
1026  * If running in a GUI, remove the case from the Recent Cases menu
1027  */
1029  SwingUtilities.invokeLater(() -> {
1030  RecentCases.getInstance().removeRecentCase(metadata.getCaseDisplayName(), metadata.getFilePath().toString());
1031  });
1032  }
1033 
1034  if (errorsOccurred) {
1035  throw new CaseActionException(Bundle.Case_exceptionMessage_errorsDeletingCase());
1036  }
1037  }
1038 
1049  @Messages({"Case.creationException.couldNotAcquireResourcesLock=Failed to get lock on case resources"})
1050  private static CoordinationService.Lock acquireExclusiveCaseResourcesLock(String caseDir) throws CaseActionException {
1051  try {
1052  String resourcesNodeName = caseDir + "_resources";
1053  Lock lock = CoordinationService.getInstance().tryGetExclusiveLock(CategoryNode.CASES, resourcesNodeName, RESOURCES_LOCK_TIMOUT_HOURS, TimeUnit.HOURS);
1054  if (null == lock) {
1055  throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireResourcesLock());
1056  }
1057  return lock;
1058  } catch (InterruptedException ex) {
1059  throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser());
1060  } catch (CoordinationServiceException ex) {
1061  throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireResourcesLock(), ex);
1062  }
1063  }
1064 
1065  private static String getNameForTitle() {
1066  //Method should become unnecessary once technical debt story 3334 is done.
1067  if (UserPreferences.getAppName().equals(Version.getName())) {
1068  //Available version number is version number for this application
1069  return String.format("%s %s", UserPreferences.getAppName(), Version.getVersion());
1070  } else {
1071  return UserPreferences.getAppName();
1072  }
1073  }
1074 
1078  private static void updateGUIForCaseOpened(Case newCurrentCase) {
1080  SwingUtilities.invokeLater(() -> {
1081  /*
1082  * If the case database was upgraded for a new schema and a
1083  * backup database was created, notify the user.
1084  */
1085  SleuthkitCase caseDb = newCurrentCase.getSleuthkitCase();
1086  String backupDbPath = caseDb.getBackupDatabasePath();
1087  if (null != backupDbPath) {
1088  JOptionPane.showMessageDialog(
1089  mainFrame,
1090  NbBundle.getMessage(Case.class, "Case.open.msgDlg.updated.msg", backupDbPath),
1091  NbBundle.getMessage(Case.class, "Case.open.msgDlg.updated.title"),
1092  JOptionPane.INFORMATION_MESSAGE);
1093  }
1094 
1095  /*
1096  * Look for the files for the data sources listed in the case
1097  * database and give the user the opportunity to locate any that
1098  * are missing.
1099  */
1100  Map<Long, String> imgPaths = getImagePaths(caseDb);
1101  for (Map.Entry<Long, String> entry : imgPaths.entrySet()) {
1102  long obj_id = entry.getKey();
1103  String path = entry.getValue();
1104  boolean fileExists = (new File(path).isFile() || DriveUtils.driveExists(path));
1105  if (!fileExists) {
1106  int response = JOptionPane.showConfirmDialog(
1107  mainFrame,
1108  NbBundle.getMessage(Case.class, "Case.checkImgExist.confDlg.doesntExist.msg", path),
1109  NbBundle.getMessage(Case.class, "Case.checkImgExist.confDlg.doesntExist.title"),
1110  JOptionPane.YES_NO_OPTION);
1111  if (response == JOptionPane.YES_OPTION) {
1112  MissingImageDialog.makeDialog(obj_id, caseDb);
1113  } else {
1114  logger.log(Level.SEVERE, "User proceeding with missing image files"); //NON-NLS
1115 
1116  }
1117  }
1118  }
1119 
1120  /*
1121  * Enable the case-specific actions.
1122  */
1123 
1124  //Deny ability to add a data source if the special admin access file is present.
1125  File denyAddDataSourcePermissions = new File(PlatformUtil.getUserConfigDirectory(), "_ndsp");
1126  if(!denyAddDataSourcePermissions.exists()) {
1127  CallableSystemAction.get(AddImageAction.class).setEnabled(true);
1128  }
1129  CallableSystemAction.get(CaseCloseAction.class).setEnabled(true);
1130  CallableSystemAction.get(CasePropertiesAction.class).setEnabled(true);
1131  CallableSystemAction.get(CaseDeleteAction.class).setEnabled(true);
1132  CallableSystemAction.get(OpenTimelineAction.class).setEnabled(true);
1133  CallableSystemAction.get(OpenCommVisualizationToolAction.class).setEnabled(true);
1134  CallableSystemAction.get(CommonAttributeSearchAction.class).setEnabled(true);
1135  CallableSystemAction.get(OpenOutputFolderAction.class).setEnabled(false);
1136 
1137  /*
1138  * Add the case to the recent cases tracker that supplies a list
1139  * of recent cases to the recent cases menu item and the
1140  * open/create case dialog.
1141  */
1142  RecentCases.getInstance().addRecentCase(newCurrentCase.getDisplayName(), newCurrentCase.getMetadata().getFilePath().toString());
1143 
1144  /*
1145  * Open the top components (windows within the main application
1146  * window).
1147  *
1148  * Note: If the core windows are not opened here, they will be
1149  * opened via the DirectoryTreeTopComponent 'propertyChange()'
1150  * method on a DATA_SOURCE_ADDED event.
1151  */
1152  if (newCurrentCase.hasData()) {
1154  }
1155 
1156  /*
1157  * Reset the main window title to:
1158  *
1159  * [curent case display name] - [application name].
1160  */
1161  mainFrame.setTitle(newCurrentCase.getDisplayName() + " - " + getNameForTitle());
1162  });
1163  }
1164  }
1165 
1166  /*
1167  * Update the GUI to to reflect the lack of a current case.
1168  */
1169  private static void updateGUIForCaseClosed() {
1171  SwingUtilities.invokeLater(() -> {
1172  /*
1173  * Close the top components (windows within the main application
1174  * window).
1175  */
1177 
1178  /*
1179  * Disable the case-specific menu items.
1180  */
1181  CallableSystemAction.get(AddImageAction.class).setEnabled(false);
1182  CallableSystemAction.get(CaseCloseAction.class).setEnabled(false);
1183  CallableSystemAction.get(CasePropertiesAction.class).setEnabled(false);
1184  CallableSystemAction.get(CaseDeleteAction.class).setEnabled(false);
1185  CallableSystemAction.get(OpenTimelineAction.class).setEnabled(false);
1186  CallableSystemAction.get(OpenCommVisualizationToolAction.class).setEnabled(false);
1187  CallableSystemAction.get(OpenOutputFolderAction.class).setEnabled(false);
1188  CallableSystemAction.get(CommonAttributeSearchAction.class).setEnabled(false);
1189 
1190  /*
1191  * Clear the notifications in the notfier component in the lower
1192  * right hand corner of the main application window.
1193  */
1195 
1196  /*
1197  * Reset the main window title to be just the application name,
1198  * instead of [curent case display name] - [application name].
1199  */
1200  mainFrame.setTitle(getNameForTitle());
1201  });
1202  }
1203  }
1204 
1208  private static void clearTempSubDir(String tempSubDirPath) {
1209  File tempFolder = new File(tempSubDirPath);
1210  if (tempFolder.isDirectory()) {
1211  File[] files = tempFolder.listFiles();
1212  if (files.length > 0) {
1213  for (File file : files) {
1214  if (file.isDirectory()) {
1215  FileUtil.deleteDir(file);
1216  } else {
1217  file.delete();
1218  }
1219  }
1220  }
1221  }
1222  }
1223 
1229  public SleuthkitCase getSleuthkitCase() {
1230  return this.caseDb;
1231  }
1232 
1239  return caseServices;
1240  }
1241 
1248  return metadata.getCaseType();
1249  }
1250 
1256  public String getCreatedDate() {
1257  return metadata.getCreatedDate();
1258  }
1259 
1265  public String getName() {
1266  return metadata.getCaseName();
1267  }
1268 
1274  public String getDisplayName() {
1275  return metadata.getCaseDisplayName();
1276  }
1277 
1283  public String getNumber() {
1284  return metadata.getCaseNumber();
1285  }
1286 
1292  public String getExaminer() {
1293  return metadata.getExaminer();
1294  }
1295 
1301  public String getExaminerPhone() {
1302  return metadata.getExaminerPhone();
1303  }
1304 
1310  public String getExaminerEmail() {
1311  return metadata.getExaminerEmail();
1312  }
1313 
1319  public String getCaseNotes() {
1320  return metadata.getCaseNotes();
1321  }
1322 
1328  public String getCaseDirectory() {
1329  return metadata.getCaseDirectory();
1330  }
1331 
1340  public String getOutputDirectory() {
1341  String caseDirectory = getCaseDirectory();
1342  Path hostPath;
1343  if (CaseType.MULTI_USER_CASE == metadata.getCaseType()) {
1344  hostPath = Paths.get(caseDirectory, NetworkUtils.getLocalHostName());
1345  } else {
1346  hostPath = Paths.get(caseDirectory);
1347  }
1348  if (!hostPath.toFile().exists()) {
1349  hostPath.toFile().mkdirs();
1350  }
1351  return hostPath.toString();
1352  }
1353 
1360  public String getTempDirectory() {
1361  return getOrCreateSubdirectory(TEMP_FOLDER);
1362  }
1363 
1370  public String getCacheDirectory() {
1371  return getOrCreateSubdirectory(CACHE_FOLDER);
1372  }
1373 
1380  public String getExportDirectory() {
1381  return getOrCreateSubdirectory(EXPORT_FOLDER);
1382  }
1383 
1390  public String getLogDirectoryPath() {
1391  return getOrCreateSubdirectory(LOG_FOLDER);
1392  }
1393 
1400  public String getReportDirectory() {
1401  return getOrCreateSubdirectory(REPORTS_FOLDER);
1402  }
1403 
1410  public String getConfigDirectory() {
1411  return getOrCreateSubdirectory(CONFIG_FOLDER);
1412  }
1413 
1420  public String getModuleDirectory() {
1421  return getOrCreateSubdirectory(MODULE_FOLDER);
1422  }
1423 
1432  Path path = Paths.get(getModuleDirectory());
1434  return path.subpath(path.getNameCount() - 2, path.getNameCount()).toString();
1435  } else {
1436  return path.subpath(path.getNameCount() - 1, path.getNameCount()).toString();
1437  }
1438  }
1439 
1449  public List<Content> getDataSources() throws TskCoreException {
1450  List<Content> list = caseDb.getRootObjects();
1451  hasDataSources = (list.size() > 0);
1452  return list;
1453  }
1454 
1460  public Set<TimeZone> getTimeZones() {
1461  Set<TimeZone> timezones = new HashSet<>();
1462  try {
1463  for (Content c : getDataSources()) {
1464  final Content dataSource = c.getDataSource();
1465  if ((dataSource != null) && (dataSource instanceof Image)) {
1466  Image image = (Image) dataSource;
1467  timezones.add(TimeZone.getTimeZone(image.getTimeZone()));
1468  }
1469  }
1470  } catch (TskCoreException ex) {
1471  logger.log(Level.SEVERE, "Error getting data source time zones", ex); //NON-NLS
1472  }
1473  return timezones;
1474  }
1475 
1482  public String getTextIndexName() {
1483  return getMetadata().getTextIndexName();
1484  }
1485 
1492  public boolean hasData() {
1493  if (!hasDataSources) {
1494  try {
1495  hasDataSources = (getDataSources().size() > 0);
1496  } catch (TskCoreException ex) {
1497  logger.log(Level.SEVERE, "Error accessing case database", ex); //NON-NLS
1498  }
1499  }
1500  return hasDataSources;
1501  }
1502 
1513  public void notifyAddingDataSource(UUID eventId) {
1514  eventPublisher.publish(new AddingDataSourceEvent(eventId));
1515  }
1516 
1527  public void notifyFailedAddingDataSource(UUID addingDataSourceEventId) {
1528  eventPublisher.publish(new AddingDataSourceFailedEvent(addingDataSourceEventId));
1529  }
1530 
1542  public void notifyDataSourceAdded(Content dataSource, UUID addingDataSourceEventId) {
1543  eventPublisher.publish(new DataSourceAddedEvent(dataSource, addingDataSourceEventId));
1544  }
1545 
1553  public void notifyContentTagAdded(ContentTag newTag) {
1554  eventPublisher.publish(new ContentTagAddedEvent(newTag));
1555  }
1556 
1564  public void notifyContentTagDeleted(ContentTag deletedTag) {
1565  eventPublisher.publish(new ContentTagDeletedEvent(deletedTag));
1566  }
1567 
1575  public void notifyTagDefinitionChanged(String changedTagName) {
1576  //leaving new value of changedTagName as null, because we do not currently support changing the display name of a tag.
1577  eventPublisher.publish(new AutopsyEvent(Events.TAG_DEFINITION_CHANGED.toString(), changedTagName, null));
1578  }
1579 
1590  public void notifyCentralRepoCommentChanged(long contentId, String newComment) {
1591  try {
1592  eventPublisher.publish(new CommentChangedEvent(contentId, newComment));
1593  } catch (NoCurrentCaseException ex) {
1594  logger.log(Level.WARNING, "Unable to send notifcation regarding comment change due to no current case being open", ex);
1595  }
1596  }
1597 
1605  public void notifyBlackBoardArtifactTagAdded(BlackboardArtifactTag newTag) {
1606  eventPublisher.publish(new BlackBoardArtifactTagAddedEvent(newTag));
1607  }
1608 
1616  public void notifyBlackBoardArtifactTagDeleted(BlackboardArtifactTag deletedTag) {
1617  eventPublisher.publish(new BlackBoardArtifactTagDeletedEvent(deletedTag));
1618  }
1619 
1631  public void addReport(String localPath, String srcModuleName, String reportName) throws TskCoreException {
1632  addReport(localPath, srcModuleName, reportName, null);
1633  }
1634 
1649  public Report addReport(String localPath, String srcModuleName, String reportName, Content parent) throws TskCoreException {
1650  String normalizedLocalPath;
1651  try {
1652  if (localPath.toLowerCase().contains("http:")) {
1653  normalizedLocalPath = localPath;
1654  } else {
1655  normalizedLocalPath = Paths.get(localPath).normalize().toString();
1656  }
1657  } catch (InvalidPathException ex) {
1658  String errorMsg = "Invalid local path provided: " + localPath; // NON-NLS
1659  throw new TskCoreException(errorMsg, ex);
1660  }
1661  Report report = this.caseDb.addReport(normalizedLocalPath, srcModuleName, reportName, parent);
1662  eventPublisher.publish(new ReportAddedEvent(report));
1663  return report;
1664  }
1665 
1674  public List<Report> getAllReports() throws TskCoreException {
1675  return this.caseDb.getAllReports();
1676  }
1677 
1686  public void deleteReports(Collection<? extends Report> reports) throws TskCoreException {
1687  for (Report report : reports) {
1688  this.caseDb.deleteReport(report);
1689  eventPublisher.publish(new AutopsyEvent(Events.REPORT_DELETED.toString(), report, null));
1690  }
1691  }
1692 
1698  CaseMetadata getMetadata() {
1699  return metadata;
1700  }
1701 
1709  @Messages({
1710  "Case.exceptionMessage.metadataUpdateError=Failed to update case metadata"
1711  })
1712  void updateCaseDetails(CaseDetails caseDetails) throws CaseActionException {
1713  CaseDetails oldCaseDetails = metadata.getCaseDetails();
1714  try {
1715  metadata.setCaseDetails(caseDetails);
1716  } catch (CaseMetadataException ex) {
1717  throw new CaseActionException(Bundle.Case_exceptionMessage_metadataUpdateError(), ex);
1718  }
1719  if (!oldCaseDetails.getCaseNumber().equals(caseDetails.getCaseNumber())) {
1720  eventPublisher.publish(new AutopsyEvent(Events.NUMBER.toString(), oldCaseDetails.getCaseNumber(), caseDetails.getCaseNumber()));
1721  }
1722  if (!oldCaseDetails.getExaminerName().equals(caseDetails.getExaminerName())) {
1723  eventPublisher.publish(new AutopsyEvent(Events.NUMBER.toString(), oldCaseDetails.getExaminerName(), caseDetails.getExaminerName()));
1724  }
1725  if (!oldCaseDetails.getCaseDisplayName().equals(caseDetails.getCaseDisplayName())) {
1726  eventPublisher.publish(new AutopsyEvent(Events.NAME.toString(), oldCaseDetails.getCaseDisplayName(), caseDetails.getCaseDisplayName()));
1727  }
1728  eventPublisher.publish(new AutopsyEvent(Events.CASE_DETAILS.toString(), oldCaseDetails, caseDetails));
1729  if (RuntimeProperties.runningWithGUI()) {
1730  SwingUtilities.invokeLater(() -> {
1731  mainFrame.setTitle(caseDetails.getCaseDisplayName() + " - " + getNameForTitle());
1732  try {
1733  RecentCases.getInstance().updateRecentCase(oldCaseDetails.getCaseDisplayName(), metadata.getFilePath().toString(), caseDetails.getCaseDisplayName(), metadata.getFilePath().toString());
1734  } catch (Exception ex) {
1735  logger.log(Level.SEVERE, "Error updating case name in UI", ex); //NON-NLS
1736  }
1737  });
1738  }
1739  }
1740 
1753  private Case(CaseType caseType, String caseDir, CaseDetails caseDetails) {
1754  metadata = new CaseMetadata(caseType, caseDir, displayNameToUniqueName(caseDetails.getCaseDisplayName()), caseDetails);
1755  }
1756 
1762  private Case(CaseMetadata caseMetaData) {
1763  metadata = caseMetaData;
1764  }
1765 
1781  @Messages({
1782  "Case.progressIndicatorTitle.creatingCase=Creating Case",
1783  "Case.progressIndicatorTitle.openingCase=Opening Case",
1784  "Case.progressIndicatorCancelButton.label=Cancel",
1785  "Case.progressMessage.preparing=Preparing...",
1786  "Case.progressMessage.preparingToOpenCaseResources=<html>Preparing to open case resources.<br>This may take time if another user is upgrading the case.</html>",
1787  "Case.progressMessage.cancelling=Cancelling...",
1788  "Case.exceptionMessage.cancelledByUser=Cancelled by user.",
1789  "# {0} - exception message", "Case.exceptionMessage.execExceptionWrapperMessage={0}"
1790  })
1791  private void open(boolean isNewCase) throws CaseActionException {
1792  /*
1793  * Create and start either a GUI progress indicator with a Cancel button
1794  * or a logging progress indicator.
1795  */
1796  CancelButtonListener cancelButtonListener = null;
1797  ProgressIndicator progressIndicator;
1799  cancelButtonListener = new CancelButtonListener(Bundle.Case_progressMessage_cancelling());
1800  String progressIndicatorTitle = isNewCase ? Bundle.Case_progressIndicatorTitle_creatingCase() : Bundle.Case_progressIndicatorTitle_openingCase();
1801  progressIndicator = new ModalDialogProgressIndicator(
1802  mainFrame,
1803  progressIndicatorTitle,
1804  new String[]{Bundle.Case_progressIndicatorCancelButton_label()},
1805  Bundle.Case_progressIndicatorCancelButton_label(),
1806  cancelButtonListener);
1807  } else {
1808  progressIndicator = new LoggingProgressIndicator();
1809  }
1810  progressIndicator.start(Bundle.Case_progressMessage_preparing());
1811 
1812  /*
1813  * Creating/opening a case is always done by creating a task running in
1814  * the same non-UI thread that will be used to close the case, so a
1815  * single-threaded executor service is created here and saved as case
1816  * state (must be volatile for cancellation to work).
1817  *
1818  * --- If the case is a single-user case, this supports cancelling
1819  * opening of the case by cancelling the task.
1820  *
1821  * --- If the case is a multi-user case, this still supports
1822  * cancellation, but it also makes it possible for the shared case
1823  * directory lock held as long as the case is open to be released in the
1824  * same thread in which it was acquired, as is required by the
1825  * coordination service.
1826  */
1827  TaskThreadFactory threadFactory = new TaskThreadFactory(String.format(CASE_ACTION_THREAD_NAME, metadata.getCaseName()));
1828  caseLockingExecutor = Executors.newSingleThreadExecutor(threadFactory);
1829  Future<Void> future = caseLockingExecutor.submit(() -> {
1830  if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) {
1831  open(isNewCase, progressIndicator);
1832  } else {
1833  /*
1834  * First, acquire a shared case directory lock that will be held
1835  * as long as this node has this case open. This will prevent
1836  * deletion of the case by another node. Next, acquire an
1837  * exclusive case resources lock to ensure only one node at a
1838  * time can create/open/upgrade/close the case resources.
1839  */
1840  progressIndicator.progress(Bundle.Case_progressMessage_preparingToOpenCaseResources());
1843  assert(resourcesLock != null); // Use reference to avoid compile time warning.
1844  open(isNewCase, progressIndicator);
1845  } catch (CaseActionException ex) {
1846  releaseSharedCaseDirLock(getMetadata().getCaseDirectory());
1847  throw ex;
1848  }
1849  }
1850  return null;
1851  });
1852  if (null != cancelButtonListener) {
1853  cancelButtonListener.setCaseActionFuture(future);
1854  }
1855 
1856  /*
1857  * Wait for the case creation/opening task to finish.
1858  */
1859  try {
1860  future.get();
1861  } catch (InterruptedException discarded) {
1862  /*
1863  * The thread this method is running in has been interrupted. Cancel
1864  * the create/open task, wait for it to finish, and shut down the
1865  * executor. This can be done safely because if the task is
1866  * completed with a cancellation condition, the case will have been
1867  * closed and the case directory lock released will have been
1868  * released.
1869  */
1870  if (null != cancelButtonListener) {
1871  cancelButtonListener.actionPerformed(null);
1872  } else {
1873  future.cancel(true);
1874  }
1875  ThreadUtils.shutDownTaskExecutor(caseLockingExecutor);
1876  } catch (CancellationException discarded) {
1877  /*
1878  * The create/open task has been cancelled. Wait for it to finish,
1879  * and shut down the executor. This can be done safely because if
1880  * the task is completed with a cancellation condition, the case
1881  * will have been closed and the case directory lock released will
1882  * have been released.
1883  */
1884  ThreadUtils.shutDownTaskExecutor(caseLockingExecutor);
1885  throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser());
1886  } catch (ExecutionException ex) {
1887  /*
1888  * The create/open task has thrown an exception. Wait for it to
1889  * finish, and shut down the executor. This can be done safely
1890  * because if the task is completed with an execution condition, the
1891  * case will have been closed and the case directory lock released
1892  * will have been released.
1893  */
1894  ThreadUtils.shutDownTaskExecutor(caseLockingExecutor);
1895  throw new CaseActionException(Bundle.Case_exceptionMessage_execExceptionWrapperMessage(ex.getCause().getLocalizedMessage()), ex);
1896  } finally {
1897  progressIndicator.finish();
1898  }
1899  }
1900 
1912  private void open(boolean isNewCase, ProgressIndicator progressIndicator) throws CaseActionException {
1913  try {
1914  if (Thread.currentThread().isInterrupted()) {
1915  throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser());
1916  }
1917 
1918  if (isNewCase) {
1919  createCaseData(progressIndicator);
1920  } else {
1921  openCaseData(progressIndicator);
1922  }
1923 
1924  if (Thread.currentThread().isInterrupted()) {
1925  throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser());
1926  }
1927 
1928  openServices(progressIndicator);
1929 
1930  if (Thread.currentThread().isInterrupted()) {
1931  throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser());
1932  }
1933  } catch (CaseActionException ex) {
1934  /*
1935  * Cancellation or failure. Clean up. The sleep is a little hack to
1936  * clear the interrupted flag for this thread if this is a
1937  * cancellation scenario, so that the clean up can run to completion
1938  * in this thread.
1939  */
1940  try {
1941  Thread.sleep(1);
1942  } catch (InterruptedException discarded) {
1943  }
1944  close(progressIndicator);
1945  throw ex;
1946  }
1947  }
1948 
1959  @Messages({
1960  "Case.progressMessage.creatingCaseDirectory=Creating case directory...",
1961  "Case.progressMessage.creatingCaseDatabase=Creating case database...",
1962  "# {0} - exception message", "Case.exceptionMessage.couldNotCreateCaseDatabase=Failed to create case database:\n{0}",
1963  "Case.exceptionMessage.couldNotCreateMetadataFile=Failed to create case metadata file."
1964  })
1965  private void createCaseData(ProgressIndicator progressIndicator) throws CaseActionException {
1966  /*
1967  * Create the case directory, if it does not already exist.
1968  *
1969  * TODO (JIRA-2180): Always create the case directory as part of the
1970  * case creation process.
1971  */
1972  if (new File(metadata.getCaseDirectory()).exists() == false) {
1973  progressIndicator.progress(Bundle.Case_progressMessage_creatingCaseDirectory());
1974  Case.createCaseDirectory(metadata.getCaseDirectory(), metadata.getCaseType());
1975  }
1976 
1977  /*
1978  * Create the case database.
1979  */
1980  progressIndicator.progress(Bundle.Case_progressMessage_creatingCaseDatabase());
1981  try {
1982  if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) {
1983  /*
1984  * For single-user cases, the case database is a SQLite database
1985  * with a standard name, physically located in the root of the
1986  * case directory.
1987  */
1988  caseDb = SleuthkitCase.newCase(Paths.get(metadata.getCaseDirectory(), SINGLE_USER_CASE_DB_NAME).toString());
1989  metadata.setCaseDatabaseName(SINGLE_USER_CASE_DB_NAME);
1990  } else {
1991  /*
1992  * For multi-user cases, the case database is a PostgreSQL
1993  * database with a name derived from the case display name,
1994  * physically located on a database server.
1995  */
1996  caseDb = SleuthkitCase.newCase(metadata.getCaseDisplayName(), UserPreferences.getDatabaseConnectionInfo(), metadata.getCaseDirectory());
1997  metadata.setCaseDatabaseName(caseDb.getDatabaseName());
1998  }
1999  } catch (TskCoreException ex) {
2000  throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotCreateCaseDatabase(ex.getLocalizedMessage()), ex);
2001  } catch (UserPreferencesException ex) {
2002  throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.databaseConnectionInfo.error.msg"), ex);
2003  } catch (CaseMetadataException ex) {
2004  throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotCreateMetadataFile(), ex);
2005  }
2006  }
2007 
2018  @Messages({
2019  "Case.progressMessage.openingCaseDatabase=Opening case database...",
2020  "Case.exceptionMessage.couldNotOpenCaseDatabase=Failed to open case database.",
2021  "Case.unsupportedSchemaVersionMessage=Unsupported DB schema version - see log for details",
2022  "Case.databaseConnectionInfo.error.msg=Error accessing database server connection info. See Tools, Options, Multi-User.",
2023  "Case.open.exception.multiUserCaseNotEnabled=Cannot open a multi-user case if multi-user cases are not enabled. "
2024  + "See Tools, Options, Multi-user."
2025  })
2026  private void openCaseData(ProgressIndicator progressIndicator) throws CaseActionException {
2027  try {
2028  progressIndicator.progress(Bundle.Case_progressMessage_openingCaseDatabase());
2029  String databaseName = metadata.getCaseDatabaseName();
2030  if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) {
2031  caseDb = SleuthkitCase.openCase(Paths.get(metadata.getCaseDirectory(), databaseName).toString());
2033  try {
2034  caseDb = SleuthkitCase.openCase(databaseName, UserPreferences.getDatabaseConnectionInfo(), metadata.getCaseDirectory());
2035  } catch (UserPreferencesException ex) {
2036  throw new CaseActionException(Case_databaseConnectionInfo_error_msg(), ex);
2037  }
2038  } else {
2039  throw new CaseActionException(Case_open_exception_multiUserCaseNotEnabled());
2040  }
2041  } catch (TskUnsupportedSchemaVersionException ex) {
2042  throw new CaseActionException(Bundle.Case_unsupportedSchemaVersionMessage(), ex);
2043  } catch (TskCoreException ex) {
2044  throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotOpenCaseDatabase(), ex);
2045  }
2046  }
2047 
2056  @Messages({
2057  "Case.progressMessage.switchingLogDirectory=Switching log directory...",
2058  "Case.progressMessage.clearingTempDirectory=Clearing case temp directory...",
2059  "Case.progressMessage.openingCaseLevelServices=Opening case-level services...",
2060  "Case.progressMessage.openingApplicationServiceResources=Opening application service case resources...",
2061  "Case.progressMessage.settingUpNetworkCommunications=Setting up network communications...",})
2062  private void openServices(ProgressIndicator progressIndicator) throws CaseActionException {
2063  /*
2064  * Switch to writing to the application logs in the logs subdirectory of
2065  * the case directory.
2066  */
2067  progressIndicator.progress(Bundle.Case_progressMessage_switchingLogDirectory());
2069  if (Thread.currentThread().isInterrupted()) {
2070  throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser());
2071  }
2072 
2073  /*
2074  * Clear the temp subdirectory of the case directory.
2075  */
2076  progressIndicator.progress(Bundle.Case_progressMessage_clearingTempDirectory());
2078  if (Thread.currentThread().isInterrupted()) {
2079  throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser());
2080  }
2081 
2082  /*
2083  * Open the case-level services.
2084  */
2085  progressIndicator.progress(Bundle.Case_progressMessage_openingCaseLevelServices());
2086  this.caseServices = new Services(caseDb);
2087  if (Thread.currentThread().isInterrupted()) {
2088  throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser());
2089  }
2090 
2091  /*
2092  * Allow any registered application services to open any resources
2093  * specific to this case.
2094  */
2095  progressIndicator.progress(Bundle.Case_progressMessage_openingApplicationServiceResources());
2097  if (Thread.currentThread().isInterrupted()) {
2098  throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser());
2099  }
2100 
2101  /*
2102  * If this case is a multi-user case, set up for communication with
2103  * other nodes.
2104  */
2105  if (CaseType.MULTI_USER_CASE == metadata.getCaseType()) {
2106  progressIndicator.progress(Bundle.Case_progressMessage_settingUpNetworkCommunications());
2107  try {
2108  eventPublisher.openRemoteEventChannel(String.format(EVENT_CHANNEL_NAME, metadata.getCaseName()));
2109  if (Thread.currentThread().isInterrupted()) {
2110  throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser());
2111  }
2112  collaborationMonitor = new CollaborationMonitor(metadata.getCaseName());
2113  } catch (AutopsyEventException | CollaborationMonitor.CollaborationMonitorException ex) {
2114  /*
2115  * The collaboration monitor and event channel are not
2116  * essential. Log an error and notify the user, but do not
2117  * throw.
2118  */
2119  logger.log(Level.SEVERE, "Failed to setup network communications", ex); //NON-NLS
2121  SwingUtilities.invokeLater(() -> MessageNotifyUtil.Notify.error(
2122  NbBundle.getMessage(Case.class, "Case.CollaborationSetup.FailNotify.Title"),
2123  NbBundle.getMessage(Case.class, "Case.CollaborationSetup.FailNotify.ErrMsg")));
2124  }
2125  }
2126  }
2127  }
2128 
2133  @NbBundle.Messages({
2134  "# {0} - service name", "Case.serviceOpenCaseResourcesProgressIndicator.title={0} Opening Case Resources",
2135  "# {0} - service name", "Case.serviceOpenCaseResourcesProgressIndicator.cancellingMessage=Cancelling opening case resources by {0}...",
2136  "# {0} - service name", "Case.servicesException.notificationTitle={0} Error"
2137  })
2138  private void openAppServiceCaseResources() throws CaseActionException {
2139  /*
2140  * Each service gets its own independently cancellable/interruptible
2141  * task, running in a named thread managed by an executor service, with
2142  * its own progress indicator. This allows for cancellation of the
2143  * opening of case resources for individual services. It also makes it
2144  * possible to ensure that each service task completes before the next
2145  * one starts by awaiting termination of the executor service.
2146  */
2147  for (AutopsyService service : Lookup.getDefault().lookupAll(AutopsyService.class)) {
2148  /*
2149  * Create a progress indicator for the task and start the task. If
2150  * running with a GUI, the progress indicator will be a dialog box
2151  * with a Cancel button.
2152  */
2153  CancelButtonListener cancelButtonListener = null;
2154  ProgressIndicator progressIndicator;
2156  cancelButtonListener = new CancelButtonListener(Bundle.Case_serviceOpenCaseResourcesProgressIndicator_cancellingMessage(service.getServiceName()));
2157  progressIndicator = new ModalDialogProgressIndicator(
2158  mainFrame,
2159  Bundle.Case_serviceOpenCaseResourcesProgressIndicator_title(service.getServiceName()),
2160  new String[]{Bundle.Case_progressIndicatorCancelButton_label()},
2161  Bundle.Case_progressIndicatorCancelButton_label(),
2162  cancelButtonListener);
2163  } else {
2164  progressIndicator = new LoggingProgressIndicator();
2165  }
2166  progressIndicator.start(Bundle.Case_progressMessage_preparing());
2167  AutopsyService.CaseContext context = new AutopsyService.CaseContext(this, progressIndicator);
2168  String threadNameSuffix = service.getServiceName().replaceAll("[ ]", "-"); //NON-NLS
2169  threadNameSuffix = threadNameSuffix.toLowerCase();
2170  TaskThreadFactory threadFactory = new TaskThreadFactory(String.format(CASE_RESOURCES_THREAD_NAME, threadNameSuffix));
2171  ExecutorService executor = Executors.newSingleThreadExecutor(threadFactory);
2172  Future<Void> future = executor.submit(() -> {
2173  service.openCaseResources(context);
2174  return null;
2175  });
2176  if (null != cancelButtonListener) {
2177  cancelButtonListener.setCaseContext(context);
2178  cancelButtonListener.setCaseActionFuture(future);
2179  }
2180 
2181  /*
2182  * Wait for the task to either be completed or
2183  * cancelled/interrupted, or for the opening of the case to be
2184  * cancelled.
2185  */
2186  try {
2187  future.get();
2188  } catch (InterruptedException discarded) {
2189  /*
2190  * The parent create/open case task has been cancelled.
2191  */
2192  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()));
2193  future.cancel(true);
2194  } catch (CancellationException discarded) {
2195  /*
2196  * The opening of case resources by the application service has
2197  * been cancelled, so the executor service has thrown. Note that
2198  * there is no guarantee the task itself has responded to the
2199  * cancellation request yet.
2200  */
2201  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()));
2202  } catch (ExecutionException ex) {
2203  /*
2204  * An exception was thrown while executing the task. The
2205  * case-specific application service resources are not
2206  * essential. Log an error and notify the user if running the
2207  * desktop GUI, but do not throw.
2208  */
2209  Case.logger.log(Level.SEVERE, String.format("%s failed to open case resources for %s", service.getServiceName(), this.getDisplayName()), ex);
2211  SwingUtilities.invokeLater(() -> {
2212  MessageNotifyUtil.Notify.error(Bundle.Case_servicesException_notificationTitle(service.getServiceName()), ex.getLocalizedMessage());
2213  });
2214  }
2215  } finally {
2216  /*
2217  * Shut down the executor service and wait for it to finish.
2218  * This ensures that the task has finished. Without this, it
2219  * would be possible to start the next task before the current
2220  * task responded to a cancellation request.
2221  */
2223  progressIndicator.finish();
2224  }
2225 
2226  if (Thread.currentThread().isInterrupted()) {
2227  throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser());
2228  }
2229  }
2230  }
2231 
2235  private void close() throws CaseActionException {
2236  /*
2237  * Set up either a GUI progress indicator without a Cancel button or a
2238  * logging progress indicator.
2239  */
2240  ProgressIndicator progressIndicator;
2242  progressIndicator = new ModalDialogProgressIndicator(
2243  mainFrame,
2244  Bundle.Case_progressIndicatorTitle_closingCase());
2245  } else {
2246  progressIndicator = new LoggingProgressIndicator();
2247  }
2248  progressIndicator.start(Bundle.Case_progressMessage_preparing());
2249 
2250  /*
2251  * Closing a case is always done in the same non-UI thread that
2252  * opened/created the case. If the case is a multi-user case, this
2253  * ensures that case directory lock that is held as long as the case is
2254  * open is released in the same thread in which it was acquired, as is
2255  * required by the coordination service.
2256  */
2257  Future<Void> future = caseLockingExecutor.submit(() -> {
2258  if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) {
2259  close(progressIndicator);
2260  } else {
2261  /*
2262  * Acquire an exclusive case resources lock to ensure only one
2263  * node at a time can create/open/upgrade/close the case
2264  * resources.
2265  */
2266  progressIndicator.progress(Bundle.Case_progressMessage_preparing());
2268  assert (null != resourcesLock);
2269  close(progressIndicator);
2270  } finally {
2271  /*
2272  * Always release the case directory lock that was acquired
2273  * when the case was opened.
2274  */
2276  }
2277  }
2278  return null;
2279  });
2280 
2281  try {
2282  future.get();
2283  } catch (InterruptedException | CancellationException unused) {
2284  /*
2285  * The wait has been interrupted by interrupting the thread running
2286  * this method. Not allowing cancellation of case closing, so ignore
2287  * the interrupt. Likewsie, cancellation of the case closing task is
2288  * not supported.
2289  */
2290  } catch (ExecutionException ex) {
2291  throw new CaseActionException(Bundle.Case_exceptionMessage_execExceptionWrapperMessage(ex.getCause().getMessage()), ex);
2292  } finally {
2293  ThreadUtils.shutDownTaskExecutor(caseLockingExecutor);
2294  progressIndicator.finish();
2295  }
2296  }
2297 
2303  @Messages({
2304  "Case.progressMessage.shuttingDownNetworkCommunications=Shutting down network communications...",
2305  "Case.progressMessage.closingApplicationServiceResources=Closing case-specific application service resources...",
2306  "Case.progressMessage.closingCaseLevelServices=Closing case-level services...",
2307  "Case.progressMessage.closingCaseDatabase=Closing case database..."
2308  })
2309  private void close(ProgressIndicator progressIndicator) {
2311 
2312  /*
2313  * Stop sending/receiving case events to and from other nodes if this is
2314  * a multi-user case.
2315  */
2316  if (CaseType.MULTI_USER_CASE == metadata.getCaseType()) {
2317  progressIndicator.progress(Bundle.Case_progressMessage_shuttingDownNetworkCommunications());
2318  if (null != collaborationMonitor) {
2319  collaborationMonitor.shutdown();
2320  }
2321  eventPublisher.closeRemoteEventChannel();
2322  }
2323 
2324  /*
2325  * Allow all registered application services providers to close
2326  * resources related to the case.
2327  */
2328  progressIndicator.progress(Bundle.Case_progressMessage_closingApplicationServiceResources());
2330 
2331  /*
2332  * Close the case-level services.
2333  */
2334  if (null != caseServices) {
2335  progressIndicator.progress(Bundle.Case_progressMessage_closingCaseLevelServices());
2336  try {
2337  this.caseServices.close();
2338  } catch (IOException ex) {
2339  logger.log(Level.SEVERE, String.format("Error closing internal case services for %s at %s", this.getName(), this.getCaseDirectory()), ex);
2340  }
2341  }
2342 
2343  /*
2344  * Close the case database
2345  */
2346  if (null != caseDb) {
2347  progressIndicator.progress(Bundle.Case_progressMessage_closingCaseDatabase());
2348  caseDb.close();
2349  }
2350 
2351  /*
2352  * Switch the log directory.
2353  */
2354  progressIndicator.progress(Bundle.Case_progressMessage_switchingLogDirectory());
2356  }
2357 
2362  @Messages({
2363  "# {0} - serviceName", "Case.serviceCloseResourcesProgressIndicator.title={0} Closing Case Resources",
2364  "# {0} - service name", "# {1} - exception message", "Case.servicesException.serviceResourcesCloseError=Could not close case resources for {0} service: {1}"
2365  })
2367  /*
2368  * Each service gets its own independently cancellable task, and thus
2369  * its own task progress indicator.
2370  */
2371  for (AutopsyService service : Lookup.getDefault().lookupAll(AutopsyService.class)) {
2372  ProgressIndicator progressIndicator;
2374  progressIndicator = new ModalDialogProgressIndicator(
2375  mainFrame,
2376  Bundle.Case_serviceCloseResourcesProgressIndicator_title(service.getServiceName()));
2377  } else {
2378  progressIndicator = new LoggingProgressIndicator();
2379  }
2380  progressIndicator.start(Bundle.Case_progressMessage_preparing());
2381  AutopsyService.CaseContext context = new AutopsyService.CaseContext(this, progressIndicator);
2382  String threadNameSuffix = service.getServiceName().replaceAll("[ ]", "-"); //NON-NLS
2383  threadNameSuffix = threadNameSuffix.toLowerCase();
2384  TaskThreadFactory threadFactory = new TaskThreadFactory(String.format(CASE_RESOURCES_THREAD_NAME, threadNameSuffix));
2385  ExecutorService executor = Executors.newSingleThreadExecutor(threadFactory);
2386  Future<Void> future = executor.submit(() -> {
2387  service.closeCaseResources(context);
2388  return null;
2389  });
2390  try {
2391  future.get();
2392  } catch (InterruptedException ex) {
2393  Case.logger.log(Level.SEVERE, String.format("Unexpected interrupt while waiting on %s service to close case resources", service.getServiceName()), ex);
2394  } catch (CancellationException ex) {
2395  Case.logger.log(Level.SEVERE, String.format("Unexpected cancellation while waiting on %s service to close case resources", service.getServiceName()), ex);
2396  } catch (ExecutionException ex) {
2397  Case.logger.log(Level.SEVERE, String.format("%s service failed to open case resources", service.getServiceName()), ex);
2399  SwingUtilities.invokeLater(() -> MessageNotifyUtil.Notify.error(
2400  Bundle.Case_servicesException_notificationTitle(service.getServiceName()),
2401  Bundle.Case_servicesException_serviceResourcesCloseError(service.getServiceName(), ex.getLocalizedMessage())));
2402  }
2403  } finally {
2405  progressIndicator.finish();
2406  }
2407  }
2408  }
2409 
2418  @Messages({"Case.creationException.couldNotAcquireDirLock=Failed to get lock on case directory"})
2419  private void acquireSharedCaseDirLock(String caseDir) throws CaseActionException {
2420  try {
2421  caseDirLock = CoordinationService.getInstance().tryGetSharedLock(CategoryNode.CASES, caseDir, DIR_LOCK_TIMOUT_HOURS, TimeUnit.HOURS);
2422  if (null == caseDirLock) {
2423  throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireDirLock());
2424  }
2425  } catch (InterruptedException | CoordinationServiceException ex) {
2426  throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireDirLock(), ex);
2427  }
2428  }
2429 
2435  private void releaseSharedCaseDirLock(String caseDir) {
2436  if (caseDirLock != null) {
2437  try {
2438  caseDirLock.release();
2439  caseDirLock = null;
2441  logger.log(Level.SEVERE, String.format("Failed to release shared case directory lock for %s", caseDir), ex);
2442  }
2443  }
2444  }
2445 
2452  private String getOrCreateSubdirectory(String subDirectoryName) {
2453  File subDirectory = Paths.get(getOutputDirectory(), subDirectoryName).toFile();
2454  if (!subDirectory.exists()) {
2455  subDirectory.mkdirs();
2456  }
2457  return subDirectory.toString();
2458 
2459  }
2460 
2465  @ThreadSafe
2466  private final static class CancelButtonListener implements ActionListener {
2467 
2468  private final String cancellationMessage;
2469  @GuardedBy("this")
2470  private boolean cancelRequested;
2471  @GuardedBy("this")
2473  @GuardedBy("this")
2474  private Future<?> caseActionFuture;
2475 
2484  private CancelButtonListener(String cancellationMessage) {
2485  this.cancellationMessage = cancellationMessage;
2486  }
2487 
2493  private synchronized void setCaseContext(CaseContext caseContext) {
2494  this.caseContext = caseContext;
2495  /*
2496  * If the cancel button has already been pressed, pass the
2497  * cancellation on to the case context.
2498  */
2499  if (cancelRequested) {
2500  cancel();
2501  }
2502  }
2503 
2509  private synchronized void setCaseActionFuture(Future<?> caseActionFuture) {
2510  this.caseActionFuture = caseActionFuture;
2511  /*
2512  * If the cancel button has already been pressed, cancel the Future
2513  * of the task.
2514  */
2515  if (cancelRequested) {
2516  cancel();
2517  }
2518  }
2519 
2525  @Override
2526  public synchronized void actionPerformed(ActionEvent event) {
2527  cancel();
2528  }
2529 
2533  private void cancel() {
2534  /*
2535  * At a minimum, set the cancellation requested flag of this
2536  * listener.
2537  */
2538  this.cancelRequested = true;
2539  if (null != this.caseContext) {
2540  /*
2541  * Set the cancellation request flag and display the
2542  * cancellation message in the progress indicator for the case
2543  * context associated with this listener.
2544  */
2546  ProgressIndicator progressIndicator = this.caseContext.getProgressIndicator();
2547  if (progressIndicator instanceof ModalDialogProgressIndicator) {
2548  ((ModalDialogProgressIndicator) progressIndicator).setCancelling(cancellationMessage);
2549  }
2550  }
2551  this.caseContext.requestCancel();
2552  }
2553  if (null != this.caseActionFuture) {
2554  /*
2555  * Cancel the Future of the task associated with this listener.
2556  * Note that the task thread will be interrupted if the task is
2557  * blocked.
2558  */
2559  this.caseActionFuture.cancel(true);
2560  }
2561  }
2562  }
2563 
2567  private static class TaskThreadFactory implements ThreadFactory {
2568 
2569  private final String threadName;
2570 
2571  private TaskThreadFactory(String threadName) {
2572  this.threadName = threadName;
2573  }
2574 
2575  @Override
2576  public Thread newThread(Runnable task) {
2577  return new Thread(task, threadName);
2578  }
2579 
2580  }
2581 
2589  @Deprecated
2590  public static String getAppName() {
2591  return UserPreferences.getAppName();
2592  }
2593 
2613  @Deprecated
2614  public static void create(String caseDir, String caseDisplayName, String caseNumber, String examiner) throws CaseActionException {
2615  createAsCurrentCase(caseDir, caseDisplayName, caseNumber, examiner, CaseType.SINGLE_USER_CASE);
2616  }
2617 
2638  @Deprecated
2639  public static void create(String caseDir, String caseDisplayName, String caseNumber, String examiner, CaseType caseType) throws CaseActionException {
2640  createAsCurrentCase(caseDir, caseDisplayName, caseNumber, examiner, caseType);
2641  }
2642 
2654  @Deprecated
2655  public static void open(String caseMetadataFilePath) throws CaseActionException {
2656  openAsCurrentCase(caseMetadataFilePath);
2657  }
2658 
2668  @Deprecated
2669  public void closeCase() throws CaseActionException {
2670  closeCurrentCase();
2671  }
2672 
2678  @Deprecated
2679  public static void invokeStartupDialog() {
2681  }
2682 
2696  @Deprecated
2697  public static String convertTimeZone(String timeZoneId) {
2698  return TimeZoneUtils.convertToAlphaNumericFormat(timeZoneId);
2699  }
2700 
2710  @Deprecated
2711  public static boolean pathExists(String filePath) {
2712  return new File(filePath).isFile();
2713  }
2714 
2723  @Deprecated
2724  public static String getAutopsyVersion() {
2725  return Version.getVersion();
2726  }
2727 
2735  @Deprecated
2736  public static boolean existsCurrentCase() {
2737  return isCaseOpen();
2738  }
2739 
2749  @Deprecated
2750  public static String getModulesOutputDirRelPath() {
2751  return "ModuleOutput"; //NON-NLS
2752  }
2753 
2763  @Deprecated
2764  public static PropertyChangeSupport
2766  return new PropertyChangeSupport(Case.class
2767  );
2768  }
2769 
2778  @Deprecated
2779  public String getModulesOutputDirAbsPath() {
2780  return getModuleDirectory();
2781  }
2782 
2797  @Deprecated
2798  public Image addImage(String imgPath, long imgId, String timeZone) throws CaseActionException {
2799  try {
2800  Image newDataSource = caseDb.getImageById(imgId);
2801  notifyDataSourceAdded(newDataSource, UUID.randomUUID());
2802  return newDataSource;
2803  } catch (TskCoreException ex) {
2804  throw new CaseActionException(NbBundle.getMessage(this.getClass(), "Case.addImg.exception.msg"), ex);
2805  }
2806  }
2807 
2815  @Deprecated
2816  public Set<TimeZone> getTimeZone() {
2817  return getTimeZones();
2818  }
2819 
2830  @Deprecated
2831  public void deleteReports(Collection<? extends Report> reports, boolean deleteFromDisk) throws TskCoreException {
2832  deleteReports(reports);
2833  }
2834 
2835 }
static final AutopsyEventPublisher eventPublisher
Definition: Case.java:140
List< Content > getDataSources()
Definition: Case.java:1449
void notifyContentTagDeleted(ContentTag deletedTag)
Definition: Case.java:1564
Case(CaseMetadata caseMetaData)
Definition: Case.java:1762
static CaseType fromString(String typeName)
Definition: Case.java:183
static final String CASE_ACTION_THREAD_NAME
Definition: Case.java:137
static void createAsCurrentCase(CaseType caseType, String caseDir, CaseDetails caseDetails)
Definition: Case.java:546
void notifyBlackBoardArtifactTagDeleted(BlackboardArtifactTag deletedTag)
Definition: Case.java:1616
Image addImage(String imgPath, long imgId, String timeZone)
Definition: Case.java:2798
static synchronized IngestManager getInstance()
static CoordinationService.Lock acquireExclusiveCaseResourcesLock(String caseDir)
Definition: Case.java:1050
static boolean existsCurrentCase()
Definition: Case.java:2736
static void removePropertyChangeListener(PropertyChangeListener listener)
Definition: Case.java:404
void start(String message, int totalWorkUnits)
static final Logger logger
Definition: Case.java:139
static final int RESOURCES_LOCK_TIMOUT_HOURS
Definition: Case.java:127
static final String EXPORT_FOLDER
Definition: Case.java:131
void notifyTagDefinitionChanged(String changedTagName)
Definition: Case.java:1575
static volatile Frame mainFrame
Definition: Case.java:142
static String convertTimeZone(String timeZoneId)
Definition: Case.java:2697
static boolean driveExists(String path)
Definition: DriveUtils.java:66
void addSubscriber(Set< String > eventNames, PropertyChangeListener subscriber)
synchronized static void setLogDirectory(String directoryPath)
Definition: Logger.java:89
void notifyCentralRepoCommentChanged(long contentId, String newComment)
Definition: Case.java:1590
static final String CACHE_FOLDER
Definition: Case.java:130
Case(CaseType caseType, String caseDir, CaseDetails caseDetails)
Definition: Case.java:1753
void createCaseData(ProgressIndicator progressIndicator)
Definition: Case.java:1965
void addReport(String localPath, String srcModuleName, String reportName)
Definition: Case.java:1631
static void updateGUIForCaseOpened(Case newCurrentCase)
Definition: Case.java:1078
static CaseDbConnectionInfo getDatabaseConnectionInfo()
static void create(String caseDir, String caseDisplayName, String caseNumber, String examiner, CaseType caseType)
Definition: Case.java:2639
static final String SINGLE_USER_CASE_DB_NAME
Definition: Case.java:128
static void deleteCase(CaseMetadata metadata)
Definition: Case.java:710
void deleteReports(Collection<?extends Report > reports, boolean deleteFromDisk)
Definition: Case.java:2831
volatile ExecutorService caseLockingExecutor
Definition: Case.java:145
synchronized void setCaseContext(CaseContext caseContext)
Definition: Case.java:2493
static void clearTempSubDir(String tempSubDirPath)
Definition: Case.java:1208
static boolean isValidName(String caseName)
Definition: Case.java:488
void acquireSharedCaseDirLock(String caseDir)
Definition: Case.java:2419
CollaborationMonitor collaborationMonitor
Definition: Case.java:148
void releaseSharedCaseDirLock(String caseDir)
Definition: Case.java:2435
static void shutDownTaskExecutor(ExecutorService executor)
static String getModulesOutputDirRelPath()
Definition: Case.java:2750
void openCaseData(ProgressIndicator progressIndicator)
Definition: Case.java:2026
synchronized void actionPerformed(ActionEvent event)
Definition: Case.java:2526
static final String MODULE_FOLDER
Definition: Case.java:136
static void create(String caseDir, String caseDisplayName, String caseNumber, String examiner)
Definition: Case.java:2614
synchronized void openRemoteEventChannel(String channelName)
void openServices(ProgressIndicator progressIndicator)
Definition: Case.java:2062
static String displayNameToUniqueName(String caseDisplayName)
Definition: Case.java:815
void open(boolean isNewCase)
Definition: Case.java:1791
static void openAsCurrentCase(String caseMetadataFilePath)
Definition: Case.java:573
static void removeEventSubscriber(Set< String > eventNames, PropertyChangeListener subscriber)
Definition: Case.java:464
Lock tryGetExclusiveLock(CategoryNode category, String nodePath, int timeOut, TimeUnit timeUnit)
static final int DIR_LOCK_TIMOUT_HOURS
Definition: Case.java:126
void close(ProgressIndicator progressIndicator)
Definition: Case.java:2309
void notifyBlackBoardArtifactTagAdded(BlackboardArtifactTag newTag)
Definition: Case.java:1605
static PropertyChangeSupport getPropertyChangeSupport()
Definition: Case.java:2765
static void addPropertyChangeListener(PropertyChangeListener listener)
Definition: Case.java:392
synchronized void setCaseActionFuture(Future<?> caseActionFuture)
Definition: Case.java:2509
void removeSubscriber(Set< String > eventNames, PropertyChangeListener subscriber)
static void removeEventSubscriber(String eventName, PropertyChangeListener subscriber)
Definition: Case.java:454
void deleteReports(Collection<?extends Report > reports)
Definition: Case.java:1686
void notifyDataSourceAdded(Content dataSource, UUID addingDataSourceEventId)
Definition: Case.java:1542
Report addReport(String localPath, String srcModuleName, String reportName, Content parent)
Definition: Case.java:1649
static boolean pathExists(String filePath)
Definition: Case.java:2711
static void open(String caseMetadataFilePath)
Definition: Case.java:2655
static void openAsCurrentCase(Case newCurrentCase, boolean isNewCase)
Definition: Case.java:775
static void error(String title, String message)
String getOrCreateSubdirectory(String subDirectoryName)
Definition: Case.java:2452
boolean equalsName(String otherTypeName)
Definition: Case.java:241
static final String EVENT_CHANNEL_NAME
Definition: Case.java:129
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
static void addEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:429
static String convertToAlphaNumericFormat(String timeZoneId)
static final String LOG_FOLDER
Definition: Case.java:132
Lock tryGetSharedLock(CategoryNode category, String nodePath, int timeOut, TimeUnit timeUnit)
static volatile Case currentCase
Definition: Case.java:143
void notifyAddingDataSource(UUID eventId)
Definition: Case.java:1513
CoordinationService.Lock caseDirLock
Definition: Case.java:146
static void addEventSubscriber(String eventName, PropertyChangeListener subscriber)
Definition: Case.java:444
void notifyContentTagAdded(ContentTag newTag)
Definition: Case.java:1553
void cancelAllIngestJobs(IngestJob.CancellationReason reason)
static final String CASE_RESOURCES_THREAD_NAME
Definition: Case.java:138
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:519
void notifyFailedAddingDataSource(UUID addingDataSourceEventId)
Definition: Case.java:1527
static final String CONFIG_FOLDER
Definition: Case.java:134
static void removeEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:474
static final Object caseActionSerializationLock
Definition: Case.java:141
void open(boolean isNewCase, ProgressIndicator progressIndicator)
Definition: Case.java:1912
static final String REPORTS_FOLDER
Definition: Case.java:133
static void createCaseDirectory(String caseDir, CaseType caseType)
Definition: Case.java:854
static final String TEMP_FOLDER
Definition: Case.java:135
static void addEventSubscriber(Set< String > eventNames, PropertyChangeListener subscriber)
Definition: Case.java:419
static void deleteCase(CaseMetadata metadata, ProgressIndicator progressIndicator)
Definition: Case.java:962

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