Autopsy  4.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-2016 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.Cursor;
22 import java.awt.Frame;
23 import java.beans.PropertyChangeListener;
24 import java.beans.PropertyChangeSupport;
25 import java.io.File;
26 import java.nio.file.InvalidPathException;
27 import java.nio.file.Path;
28 import java.nio.file.Paths;
29 import java.text.SimpleDateFormat;
30 import java.util.Collection;
31 import java.util.Date;
32 import java.util.HashMap;
33 import java.util.HashSet;
34 import java.util.List;
35 import java.util.Map;
36 import java.util.Set;
37 import java.util.TimeZone;
38 import java.util.UUID;
39 import java.util.logging.Level;
40 import java.util.stream.Collectors;
41 import java.util.stream.Stream;
42 import javax.swing.JOptionPane;
43 import javax.swing.SwingUtilities;
44 import org.openide.util.NbBundle;
45 import org.openide.util.NbBundle.Messages;
46 import org.openide.util.actions.CallableSystemAction;
47 import org.openide.windows.WindowManager;
76 import org.sleuthkit.datamodel.BlackboardArtifactTag;
77 import org.sleuthkit.datamodel.Content;
78 import org.sleuthkit.datamodel.ContentTag;
79 import org.sleuthkit.datamodel.Image;
80 import org.sleuthkit.datamodel.Report;
81 import org.sleuthkit.datamodel.SleuthkitCase;
82 import org.sleuthkit.datamodel.TskCoreException;
83 import org.sleuthkit.datamodel.TskException;
84 
88 public class Case implements SleuthkitCase.ErrorObserver {
89 
93  @NbBundle.Messages({"Case_caseType_singleUser=Single-user case", "Case_caseType_multiUser=Multi-user case"})
94  public enum CaseType {
95 
96  SINGLE_USER_CASE("Single-user case"), //NON-NLS
97  MULTI_USER_CASE("Multi-user case"); //NON-NLS
98 
99  private final String typeName;
100 
106  private CaseType(String typeName) {
107  this.typeName = typeName;
108  }
109 
115  @Override
116  public String toString() {
117  return typeName;
118  }
119 
126  if (fromString(typeName) == SINGLE_USER_CASE) {
127  return Bundle.Case_caseType_singleUser();
128  } else {
129  return Bundle.Case_caseType_multiUser();
130  }
131  }
132 
140  public static CaseType fromString(String typeName) {
141  if (typeName != null) {
142  for (CaseType c : CaseType.values()) {
143  if (typeName.equalsIgnoreCase(c.typeName)) {
144  return c;
145  }
146  }
147  }
148  return null;
149  }
150 
161  @Deprecated
162  public boolean equalsName(String otherTypeName) {
163  return (otherTypeName == null) ? false : typeName.equals(otherTypeName);
164  }
165 
166  };
167 
172  public enum Events {
173 
268  };
269 
270  private static final int MAX_SANITIZED_CASE_NAME_LEN = 47;
271  private static final String EVENT_CHANNEL_NAME = "%s-Case-Events"; //NON-NLS
272  private static final String CACHE_FOLDER = "Cache"; //NON-NLS
273  private static final String EXPORT_FOLDER = "Export"; //NON-NLS
274  private static final String LOG_FOLDER = "Log"; //NON-NLS
275  private static final String REPORTS_FOLDER = "Reports"; //NON-NLS
276  private static final String TEMP_FOLDER = "Temp"; //NON-NLS
277  static final String MODULE_FOLDER = "ModuleOutput"; //NON-NLS
278  private static final int MIN_SECS_BETWEEN_TSK_ERROR_REPORTS = 60;
279  private static final Logger logger = Logger.getLogger(Case.class.getName());
281  private static String appName;
282  private static Case currentCase;
283  private final CaseMetadata caseMetadata;
284  private final SleuthkitCase db;
285  private final Services services;
286  private CollaborationMonitor collaborationMonitor;
287  private boolean hasDataSources;
288  private volatile IntervalErrorReportData tskErrorReporter;
289 
294  private Case(CaseMetadata caseMetadata, SleuthkitCase db) {
295  this.caseMetadata = caseMetadata;
296  this.db = db;
297  this.services = new Services(db);
298  }
299 
306  public static void addPropertyChangeListener(PropertyChangeListener listener) {
307  addEventSubscriber(Stream.of(Events.values())
308  .map(Events::toString)
309  .collect(Collectors.toSet()), listener);
310  }
311 
318  public static void removePropertyChangeListener(PropertyChangeListener listener) {
319  removeEventSubscriber(Stream.of(Events.values())
320  .map(Events::toString)
321  .collect(Collectors.toSet()), listener);
322  }
323 
330  public static void addEventSubscriber(Set<String> eventNames, PropertyChangeListener subscriber) {
331  eventPublisher.addSubscriber(eventNames, subscriber);
332  }
333 
340  public static void addEventSubscriber(String eventName, PropertyChangeListener subscriber) {
341  eventPublisher.addSubscriber(eventName, subscriber);
342  }
343 
350  public static void removeEventSubscriber(String eventName, PropertyChangeListener subscriber) {
351  eventPublisher.removeSubscriber(eventName, subscriber);
352  }
353 
360  public static void removeEventSubscriber(Set<String> eventNames, PropertyChangeListener subscriber) {
361  eventPublisher.removeSubscriber(eventNames, subscriber);
362  }
363 
369  public static boolean isCaseOpen() {
370  return currentCase != null;
371  }
372 
380  public static Case getCurrentCase() {
381  if (currentCase != null) {
382  return currentCase;
383  } else {
384  throw new IllegalStateException(NbBundle.getMessage(Case.class, "Case.getCurCase.exception.noneOpen"));
385  }
386  }
387 
393  public SleuthkitCase getSleuthkitCase() {
394  return this.db;
395  }
396 
403  return services;
404  }
405 
411  CaseMetadata getCaseMetadata() {
412  return caseMetadata;
413  }
414 
421  return getCaseMetadata().getCaseType();
422  }
423 
429  public String getCreatedDate() {
430  return getCaseMetadata().getCreatedDate();
431  }
432 
438  public String getName() {
439  return getCaseMetadata().getCaseName();
440  }
441 
452  void updateCaseName(String oldCaseName, String oldPath, String newCaseName, String newPath) throws CaseActionException {
453  try {
454  caseMetadata.setCaseName(newCaseName);
455  eventPublisher.publish(new AutopsyEvent(Events.NAME.toString(), oldCaseName, newCaseName));
456  SwingUtilities.invokeLater(() -> {
457  try {
458  RecentCases.getInstance().updateRecentCase(oldCaseName, oldPath, newCaseName, newPath); // update the recent case
459  addCaseNameToMainWindowTitle(newCaseName);
460  } catch (Exception ex) {
461  Logger.getLogger(Case.class.getName()).log(Level.SEVERE, "Error updating case name in UI", ex); //NON-NLS
462  }
463  });
464  } catch (Exception ex) {
465  throw new CaseActionException(NbBundle.getMessage(this.getClass(), "Case.updateCaseName.exception.msg"), ex);
466  }
467  }
468 
474  public String getNumber() {
475  return caseMetadata.getCaseNumber();
476  }
477 
483  public String getExaminer() {
484  return caseMetadata.getExaminer();
485  }
486 
492  public String getCaseDirectory() {
493  return caseMetadata.getCaseDirectory();
494  }
495 
504  public String getOutputDirectory() {
505  String caseDirectory = getCaseDirectory();
506  Path hostPath;
507  if (getCaseMetadata().getCaseType() == CaseType.MULTI_USER_CASE) {
508  hostPath = Paths.get(caseDirectory, NetworkUtils.getLocalHostName());
509  } else {
510  hostPath = Paths.get(caseDirectory);
511  }
512  if (!hostPath.toFile().exists()) {
513  hostPath.toFile().mkdirs();
514  }
515  return hostPath.toString();
516  }
517 
524  public String getTempDirectory() {
525  return getOrCreateSubdirectory(TEMP_FOLDER);
526  }
527 
534  public String getCacheDirectory() {
535  return getOrCreateSubdirectory(CACHE_FOLDER);
536  }
537 
544  public String getExportDirectory() {
545  return getOrCreateSubdirectory(EXPORT_FOLDER);
546  }
547 
554  public String getLogDirectoryPath() {
555  return getOrCreateSubdirectory(LOG_FOLDER);
556  }
557 
564  public String getReportDirectory() {
565  return getOrCreateSubdirectory(REPORTS_FOLDER);
566  }
567 
574  public String getModuleDirectory() {
575  return getOrCreateSubdirectory(MODULE_FOLDER);
576  }
577 
586  Path path = Paths.get(getModuleDirectory());
588  return path.subpath(path.getNameCount() - 2, path.getNameCount()).toString();
589  } else {
590  return path.subpath(path.getNameCount() - 1, path.getNameCount()).toString();
591  }
592  }
593 
600  private String getOrCreateSubdirectory(String subDirectoryName) {
601  File subDirectory = Paths.get(getOutputDirectory(), subDirectoryName).toFile();
602  if (!subDirectory.exists()) {
603  subDirectory.mkdirs();
604  }
605  return subDirectory.toString();
606  }
607 
617  public List<Content> getDataSources() throws TskCoreException {
618  List<Content> list = db.getRootObjects();
619  hasDataSources = (list.size() > 0);
620  return list;
621  }
622 
628  public Set<TimeZone> getTimeZones() {
629  Set<TimeZone> timezones = new HashSet<>();
630  try {
631  for (Content c : getDataSources()) {
632  final Content dataSource = c.getDataSource();
633  if ((dataSource != null) && (dataSource instanceof Image)) {
634  Image image = (Image) dataSource;
635  timezones.add(TimeZone.getTimeZone(image.getTimeZone()));
636  }
637  }
638  } catch (TskCoreException ex) {
639  logger.log(Level.SEVERE, "Error getting data source time zones", ex); //NON-NLS
640  }
641  return timezones;
642  }
643 
649  public String getTextIndexName() {
650  return getCaseMetadata().getTextIndexName();
651  }
652 
659  public boolean hasData() {
660  if (!hasDataSources) {
661  try {
662  hasDataSources = (getDataSources().size() > 0);
663  } catch (TskCoreException ex) {
664  logger.log(Level.SEVERE, "Error accessing case database", ex); //NON-NLS
665  }
666  }
667  return hasDataSources;
668  }
669 
680  public void notifyAddingDataSource(UUID eventId) {
681  eventPublisher.publish(new AddingDataSourceEvent(eventId));
682  }
683 
694  public void notifyFailedAddingDataSource(UUID addingDataSourceEventId) {
695  eventPublisher.publish(new AddingDataSourceFailedEvent(addingDataSourceEventId));
696  }
697 
709  public void notifyDataSourceAdded(Content dataSource, UUID addingDataSourceEventId) {
710  eventPublisher.publish(new DataSourceAddedEvent(dataSource, addingDataSourceEventId));
711  }
712 
720  public void notifyContentTagAdded(ContentTag newTag) {
721  eventPublisher.publish(new ContentTagAddedEvent(newTag));
722  }
723 
731  public void notifyContentTagDeleted(ContentTag deletedTag) {
732  eventPublisher.publish(new ContentTagDeletedEvent(deletedTag));
733  }
734 
742  public void notifyBlackBoardArtifactTagAdded(BlackboardArtifactTag newTag) {
743  eventPublisher.publish(new BlackBoardArtifactTagAddedEvent(newTag));
744  }
745 
753  public void notifyBlackBoardArtifactTagDeleted(BlackboardArtifactTag deletedTag) {
754  eventPublisher.publish(new BlackBoardArtifactTagDeletedEvent(deletedTag));
755  }
756 
768  public void addReport(String localPath, String srcModuleName, String reportName) throws TskCoreException {
769  String normalizedLocalPath;
770  try {
771  normalizedLocalPath = Paths.get(localPath).normalize().toString();
772  } catch (InvalidPathException ex) {
773  String errorMsg = "Invalid local path provided: " + localPath; // NON-NLS
774  throw new TskCoreException(errorMsg, ex);
775  }
776  Report report = this.db.addReport(normalizedLocalPath, srcModuleName, reportName);
777  eventPublisher.publish(new ReportAddedEvent(report));
778  }
779 
788  public List<Report> getAllReports() throws TskCoreException {
789  return this.db.getAllReports();
790  }
791 
800  public void deleteReports(Collection<? extends Report> reports) throws TskCoreException {
801  for (Report report : reports) {
802  this.db.deleteReport(report);
803  eventPublisher.publish(new AutopsyEvent(Events.REPORT_DELETED.toString(), report, null));
804  }
805  }
806 
814  @Override
815  public void receiveError(String context, String errorMessage) {
816  /*
817  * NOTE: We are accessing tskErrorReporter from two different threads.
818  * This is ok as long as we only read the value of tskErrorReporter
819  * because tskErrorReporter is declared as volatile.
820  */
821  if (null != tskErrorReporter) {
822  tskErrorReporter.addProblems(context, errorMessage);
823  }
824  }
825 
834  public void closeCase() throws CaseActionException {
835  changeCurrentCase(null);
836  try {
837  services.close();
838  this.db.close();
839  } catch (Exception e) {
840  throw new CaseActionException(NbBundle.getMessage(this.getClass(), "Case.closeCase.exception.msg"), e);
841  }
842  }
843 
852  void deleteCase(File caseDir) throws CaseActionException {
853  logger.log(Level.INFO, "Deleting case.\ncaseDir: {0}", caseDir); //NON-NLS
854 
855  try {
856  boolean result = deleteCaseDirectory(caseDir);
857 
858  RecentCases.getInstance().removeRecentCase(this.caseMetadata.getCaseName(), this.caseMetadata.getFilePath().toString()); // remove it from the recent case
859  Case.changeCurrentCase(null);
860  if (result == false) {
861  throw new CaseActionException(
862  NbBundle.getMessage(this.getClass(), "Case.deleteCase.exception.msg", caseDir));
863  }
864 
865  } catch (Exception ex) {
866  logger.log(Level.SEVERE, "Error deleting the current case dir: " + caseDir, ex); //NON-NLS
867  throw new CaseActionException(
868  NbBundle.getMessage(this.getClass(), "Case.deleteCase.exception.msg2", caseDir), ex);
869  }
870  }
871 
877  public static String getAppName() {
878  if ((appName == null) || appName.equals("")) {
879  appName = WindowManager.getDefault().getMainWindow().getTitle();
880  }
881  return appName;
882  }
883 
894  public static boolean isValidName(String caseName) {
895  return !(caseName.contains("\\") || caseName.contains("/") || caseName.contains(":")
896  || caseName.contains("*") || caseName.contains("?") || caseName.contains("\"")
897  || caseName.contains("<") || caseName.contains(">") || caseName.contains("|"));
898  }
899 
917  public static void create(String caseDir, String caseName, String caseNumber, String examiner) throws CaseActionException {
918  create(caseDir, caseName, caseNumber, examiner, CaseType.SINGLE_USER_CASE);
919  }
920 
939  @Messages({"Case.creationException=Could not create case: failed to create case metadata file."})
940  public static void create(String caseDir, String caseName, String caseNumber, String examiner, CaseType caseType) throws CaseActionException {
941  logger.log(Level.INFO, "Attempting to create case {0} in directory = {1}", new Object[]{caseName, caseDir}); //NON-NLS
942 
943  /*
944  * Create case directory if it doesn't already exist.
945  */
946  if (new File(caseDir).exists() == false) {
947  Case.createCaseDirectory(caseDir, caseType);
948  }
949 
950  /*
951  * Sanitize the case name, create a unique keyword search index name,
952  * and create a standard (single-user) or unique (multi-user) case
953  * database name.
954  */
955  String santizedCaseName = sanitizeCaseName(caseName);
956  SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
957  Date date = new Date();
958  String indexName = santizedCaseName + "_" + dateFormat.format(date);
959  String dbName = null;
960  if (caseType == CaseType.SINGLE_USER_CASE) {
961  dbName = caseDir + File.separator + "autopsy.db"; //NON-NLS
962  } else if (caseType == CaseType.MULTI_USER_CASE) {
963  dbName = indexName;
964  }
965 
966  /*
967  * Create the case metadata (.aut) file.
968  */
969  CaseMetadata metadata;
970  try {
971  metadata = new CaseMetadata(caseDir, caseType, caseName, caseNumber, examiner, dbName, indexName);
972  } catch (CaseMetadataException ex) {
973  throw new CaseActionException(Bundle.Case_creationException(), ex);
974  }
975 
976  /*
977  * Create the case database.
978  */
979  SleuthkitCase db = null;
980  try {
981  if (caseType == CaseType.SINGLE_USER_CASE) {
982  db = SleuthkitCase.newCase(dbName);
983  } else if (caseType == CaseType.MULTI_USER_CASE) {
984  db = SleuthkitCase.newCase(dbName, UserPreferences.getDatabaseConnectionInfo(), caseDir);
985  }
986  } catch (TskCoreException ex) {
987  logger.log(Level.SEVERE, String.format("Error creating a case %s in %s ", caseName, caseDir), ex); //NON-NLS
988  SwingUtilities.invokeLater(() -> {
989  WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
990  });
991  /*
992  * SleuthkitCase.newCase throws TskCoreExceptions with user-friendly
993  * messages, so propagate the exception message.
994  */
995  throw new CaseActionException(ex.getMessage(), ex); //NON-NLS
996  } catch (UserPreferencesException ex) {
997  logger.log(Level.SEVERE, "Error accessing case database connection info", ex); //NON-NLS
998  SwingUtilities.invokeLater(() -> {
999  WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
1000  });
1001  throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.databaseConnectionInfo.error.msg"), ex);
1002  }
1003 
1004  Case newCase = new Case(metadata, db);
1005  changeCurrentCase(newCase);
1006 
1007  logger.log(Level.INFO, "Created case {0} in directory = {1}", new Object[]{caseName, caseDir}); //NON-NLS
1008  }
1009 
1035  static String sanitizeCaseName(String caseName) {
1036 
1037  String result;
1038 
1039  // Remove all non-ASCII characters
1040  result = caseName.replaceAll("[^\\p{ASCII}]", "_"); //NON-NLS
1041 
1042  // Remove all control characters
1043  result = result.replaceAll("[\\p{Cntrl}]", "_"); //NON-NLS
1044 
1045  // Remove / \ : ? space ' "
1046  result = result.replaceAll("[ /?:'\"\\\\]", "_"); //NON-NLS
1047 
1048  // Make it all lowercase
1049  result = result.toLowerCase();
1050 
1051  // Must start with letter or underscore for PostgreSQL. If not, prepend an underscore.
1052  if (result.length() > 0 && !(Character.isLetter(result.codePointAt(0))) && !(result.codePointAt(0) == '_')) {
1053  result = "_" + result;
1054  }
1055 
1056  // Chop to 63-16=47 left (63 max for PostgreSQL, taking 16 for the date _20151225_123456)
1057  if (result.length() > MAX_SANITIZED_CASE_NAME_LEN) {
1058  result = result.substring(0, MAX_SANITIZED_CASE_NAME_LEN);
1059  }
1060 
1061  if (result.isEmpty()) {
1062  result = "case"; //NON-NLS
1063  }
1064 
1065  return result;
1066  }
1067 
1076  static void createCaseDirectory(String caseDir, CaseType caseType) throws CaseActionException {
1077 
1078  File caseDirF = new File(caseDir);
1079  if (caseDirF.exists()) {
1080  if (caseDirF.isFile()) {
1081  throw new CaseActionException(
1082  NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.existNotDir", caseDir));
1083  } else if (!caseDirF.canRead() || !caseDirF.canWrite()) {
1084  throw new CaseActionException(
1085  NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.existCantRW", caseDir));
1086  }
1087  }
1088 
1089  try {
1090  boolean result = (caseDirF).mkdirs(); // create root case Directory
1091  if (result == false) {
1092  throw new CaseActionException(
1093  NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreate", caseDir));
1094  }
1095 
1096  // create the folders inside the case directory
1097  String hostClause = "";
1098 
1099  if (caseType == CaseType.MULTI_USER_CASE) {
1100  hostClause = File.separator + NetworkUtils.getLocalHostName();
1101  }
1102  result = result && (new File(caseDir + hostClause + File.separator + EXPORT_FOLDER)).mkdirs()
1103  && (new File(caseDir + hostClause + File.separator + LOG_FOLDER)).mkdirs()
1104  && (new File(caseDir + hostClause + File.separator + TEMP_FOLDER)).mkdirs()
1105  && (new File(caseDir + hostClause + File.separator + CACHE_FOLDER)).mkdirs();
1106 
1107  if (result == false) {
1108  throw new CaseActionException(
1109  NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreateCaseDir", caseDir));
1110  }
1111 
1112  final String modulesOutDir = caseDir + hostClause + File.separator + MODULE_FOLDER;
1113  result = new File(modulesOutDir).mkdir();
1114  if (result == false) {
1115  throw new CaseActionException(
1116  NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreateModDir",
1117  modulesOutDir));
1118  }
1119 
1120  final String reportsOutDir = caseDir + hostClause + File.separator + REPORTS_FOLDER;
1121  result = new File(reportsOutDir).mkdir();
1122  if (result == false) {
1123  throw new CaseActionException(
1124  NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.cantCreateReportsDir",
1125  modulesOutDir));
1126  }
1127 
1128  } catch (Exception e) {
1129  throw new CaseActionException(
1130  NbBundle.getMessage(Case.class, "Case.createCaseDir.exception.gen", caseDir), e);
1131  }
1132  }
1133 
1144  public static void open(String caseMetadataFilePath) throws CaseActionException {
1145  logger.log(Level.INFO, "Opening case with metadata file path {0}", caseMetadataFilePath); //NON-NLS
1146 
1147  /*
1148  * Verify the extension of the case metadata file.
1149  */
1150  if (!caseMetadataFilePath.endsWith(CaseMetadata.getFileExtension())) {
1151  throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.open.exception.checkFile.msg", CaseMetadata.getFileExtension()));
1152  }
1153 
1154  try {
1155  /*
1156  * Get the case metadata required to open the case database.
1157  */
1158  CaseMetadata metadata = new CaseMetadata(Paths.get(caseMetadataFilePath));
1159  CaseType caseType = metadata.getCaseType();
1160 
1161  /*
1162  * Open the case database.
1163  */
1164  SleuthkitCase db;
1165  if (caseType == CaseType.SINGLE_USER_CASE) {
1166  String dbPath = metadata.getCaseDatabasePath(); //NON-NLS
1167  db = SleuthkitCase.openCase(dbPath);
1168  } else {
1170  throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.open.exception.multiUserCaseNotEnabled"));
1171  }
1172  try {
1173  db = SleuthkitCase.openCase(metadata.getCaseDatabaseName(), UserPreferences.getDatabaseConnectionInfo(), metadata.getCaseDirectory());
1174  } catch (UserPreferencesException ex) {
1175  throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.databaseConnectionInfo.error.msg"), ex);
1176  }
1177  }
1178 
1179  /*
1180  * Check for the presence of the UI and do things that can only be
1181  * done with user interaction.
1182  */
1184  /*
1185  * If the case database was upgraded for a new schema, notify
1186  * the user.
1187  */
1188  if (null != db.getBackupDatabasePath()) {
1189  SwingUtilities.invokeLater(() -> {
1190  JOptionPane.showMessageDialog(
1191  WindowManager.getDefault().getMainWindow(),
1192  NbBundle.getMessage(Case.class, "Case.open.msgDlg.updated.msg", db.getBackupDatabasePath()),
1193  NbBundle.getMessage(Case.class, "Case.open.msgDlg.updated.title"),
1194  JOptionPane.INFORMATION_MESSAGE);
1195  });
1196  }
1197 
1198  /*
1199  * Look for the files for the data sources listed in the case
1200  * database and give the user the opportunity to locate any that
1201  * are missing.
1202  */
1203  Map<Long, String> imgPaths = getImagePaths(db);
1204  for (Map.Entry<Long, String> entry : imgPaths.entrySet()) {
1205  long obj_id = entry.getKey();
1206  String path = entry.getValue();
1207  boolean fileExists = (new File(path).isFile() || driveExists(path));
1208  if (!fileExists) {
1209  int ret = JOptionPane.showConfirmDialog(
1210  WindowManager.getDefault().getMainWindow(),
1211  NbBundle.getMessage(Case.class, "Case.checkImgExist.confDlg.doesntExist.msg", getAppName(), path),
1212  NbBundle.getMessage(Case.class, "Case.checkImgExist.confDlg.doesntExist.title"),
1213  JOptionPane.YES_NO_OPTION);
1214  if (ret == JOptionPane.YES_OPTION) {
1215  MissingImageDialog.makeDialog(obj_id, db);
1216  } else {
1217  logger.log(Level.WARNING, "Selected image files don't match old files!"); //NON-NLS
1218  }
1219  }
1220  }
1221  }
1222  Case openedCase = new Case(metadata, db);
1223  changeCurrentCase(openedCase);
1224 
1225  } catch (CaseMetadataException ex) {
1226  throw new CaseActionException(NbBundle.getMessage(Case.class, "Case.metaDataFileCorrupt.exception.msg"), ex); //NON-NLS
1227  } catch (TskCoreException ex) {
1228  SwingUtilities.invokeLater(() -> {
1229  WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
1230  });
1231  /*
1232  * SleuthkitCase.openCase throws TskCoreExceptions with
1233  * user-friendly messages, so propagate the exception message.
1234  */
1235  throw new CaseActionException(ex.getMessage(), ex);
1236  }
1237  }
1238 
1246  static Map<Long, String> getImagePaths(SleuthkitCase db) {
1247  Map<Long, String> imgPaths = new HashMap<>();
1248  try {
1249  Map<Long, List<String>> imgPathsList = db.getImagePaths();
1250  for (Map.Entry<Long, List<String>> entry : imgPathsList.entrySet()) {
1251  if (entry.getValue().size() > 0) {
1252  imgPaths.put(entry.getKey(), entry.getValue().get(0));
1253  }
1254  }
1255  } catch (TskException ex) {
1256  logger.log(Level.SEVERE, "Error getting image paths", ex); //NON-NLS
1257  }
1258  return imgPaths;
1259  }
1260 
1268  private static void changeCurrentCase(Case newCase) {
1269  // close the existing case
1270  Case oldCase = Case.currentCase;
1271  Case.currentCase = null;
1272  if (oldCase != null) {
1273  SwingUtilities.invokeLater(() -> {
1274  WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
1275  });
1277  completeCaseChange(null); //closes windows, etc
1278  if (null != oldCase.tskErrorReporter) {
1279  oldCase.tskErrorReporter.shutdown(); // stop listening for TSK errors for the old case
1280  oldCase.tskErrorReporter = null;
1281  }
1282  eventPublisher.publishLocally(new AutopsyEvent(Events.CURRENT_CASE.toString(), oldCase, null));
1283  if (CaseType.MULTI_USER_CASE == oldCase.getCaseType()) {
1284  if (null != oldCase.collaborationMonitor) {
1285  oldCase.collaborationMonitor.shutdown();
1286  }
1287  eventPublisher.closeRemoteEventChannel();
1288  }
1289  }
1290 
1291  if (newCase != null) {
1292  currentCase = newCase;
1294  // sanity check
1295  if (null != currentCase.tskErrorReporter) {
1296  currentCase.tskErrorReporter.shutdown();
1297  }
1298  // start listening for TSK errors for the new case
1299  currentCase.tskErrorReporter = new IntervalErrorReportData(currentCase, MIN_SECS_BETWEEN_TSK_ERROR_REPORTS,
1300  NbBundle.getMessage(Case.class, "IntervalErrorReport.ErrorText"));
1301  completeCaseChange(currentCase);
1302  SwingUtilities.invokeLater(() -> {
1303  RecentCases.getInstance().addRecentCase(currentCase.getName(), currentCase.getCaseMetadata().getFilePath().toString()); // update the recent cases
1304  });
1305  if (CaseType.MULTI_USER_CASE == newCase.getCaseType()) {
1306  try {
1313  eventPublisher.openRemoteEventChannel(String.format(EVENT_CHANNEL_NAME, newCase.getTextIndexName()));
1314  currentCase.collaborationMonitor = new CollaborationMonitor();
1315  } catch (AutopsyEventException | CollaborationMonitor.CollaborationMonitorException ex) {
1316  logger.log(Level.SEVERE, "Failed to setup for collaboration", ex); //NON-NLS
1317  MessageNotifyUtil.Notify.error(NbBundle.getMessage(Case.class, "Case.CollaborationSetup.FailNotify.Title"), NbBundle.getMessage(Case.class, "Case.CollaborationSetup.FailNotify.ErrMsg"));
1318  }
1319  }
1320  eventPublisher.publishLocally(new AutopsyEvent(Events.CURRENT_CASE.toString(), null, currentCase));
1321 
1322  } else {
1324  }
1325  SwingUtilities.invokeLater(() -> {
1326  WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
1327  });
1328  }
1329 
1337  private static void completeCaseChange(Case newCase) {
1338  logger.log(Level.INFO, "Changing Case to: {0}", newCase); //NON-NLS
1339  if (newCase != null) { // new case is open
1340 
1341  // clear the temp folder when the case is created / opened
1343 
1345  // enable these menus
1346  SwingUtilities.invokeLater(() -> {
1347  CallableSystemAction.get(AddImageAction.class).setEnabled(true);
1348  CallableSystemAction.get(CaseCloseAction.class).setEnabled(true);
1349  CallableSystemAction.get(CasePropertiesAction.class).setEnabled(true);
1350  CallableSystemAction.get(CaseDeleteAction.class).setEnabled(true); // Delete Case menu
1351  CallableSystemAction.get(OpenTimelineAction.class).setEnabled(true);
1352 
1353  if (newCase.hasData()) {
1354  // open all top components
1356  } else {
1357  // close all top components
1359  }
1360  addCaseNameToMainWindowTitle(currentCase.getName());
1361  });
1362  } else {
1363  SwingUtilities.invokeLater(() -> {
1364  Frame f = WindowManager.getDefault().getMainWindow();
1365  f.setTitle(getAppName()); // set the window name to just application name
1366  });
1367  }
1368 
1369  } else { // case is closed
1370  SwingUtilities.invokeLater(() -> {
1372 
1373  // close all top components first
1375 
1376  // disable these menus
1377  CallableSystemAction.get(AddImageAction.class).setEnabled(false); // Add Image menu
1378  CallableSystemAction.get(CaseCloseAction.class).setEnabled(false); // Case Close menu
1379  CallableSystemAction.get(CasePropertiesAction.class).setEnabled(false); // Case Properties menu
1380  CallableSystemAction.get(CaseDeleteAction.class).setEnabled(false); // Delete Case menu
1381  CallableSystemAction.get(OpenTimelineAction.class).setEnabled(false);
1382  }
1383 
1384  //clear pending notifications
1386  Frame f = WindowManager.getDefault().getMainWindow();
1387  f.setTitle(getAppName()); // set the window name to just application name
1388  });
1389 
1390  //try to force gc to happen
1391  System.gc();
1392  System.gc();
1393  }
1394 
1395  //log memory usage after case changed
1396  logger.log(Level.INFO, PlatformUtil.getAllMemUsageInfo());
1397 
1398  }
1399 
1403  private static void clearTempFolder() {
1404  File tempFolder = new File(currentCase.getTempDirectory());
1405  if (tempFolder.isDirectory()) {
1406  File[] files = tempFolder.listFiles();
1407  if (files.length > 0) {
1408  for (File file : files) {
1409  if (file.isDirectory()) {
1410  deleteCaseDirectory(file);
1411  } else {
1412  file.delete();
1413  }
1414  }
1415  }
1416  }
1417  }
1418 
1424  private static void addCaseNameToMainWindowTitle(String newCaseName) {
1425  if (!newCaseName.equals("")) {
1426  Frame f = WindowManager.getDefault().getMainWindow();
1427  f.setTitle(newCaseName + " - " + getAppName());
1428  }
1429  }
1430 
1438  static boolean deleteCaseDirectory(File casePath) {
1439  logger.log(Level.INFO, "Deleting case directory: {0}", casePath.getAbsolutePath()); //NON-NLS
1440  return FileUtil.deleteDir(casePath);
1441  }
1442 
1450  @Deprecated
1451  public Set<TimeZone> getTimeZone() {
1452  return getTimeZones();
1453  }
1454 
1465  @Deprecated
1466  static boolean isPhysicalDrive(String path) {
1467  return DriveUtils.isPhysicalDrive(path);
1468  }
1469 
1478  @Deprecated
1479  static boolean isPartition(String path) {
1480  return DriveUtils.isPartition(path);
1481  }
1482 
1494  @Deprecated
1495  static boolean driveExists(String path) {
1496  return DriveUtils.driveExists(path);
1497  }
1498 
1504  @Deprecated
1505  public static void invokeStartupDialog() {
1507  }
1508 
1522  @Deprecated
1523  public static String convertTimeZone(String timeZoneId) {
1524  return TimeZoneUtils.convertToAlphaNumericFormat(timeZoneId);
1525  }
1526 
1536  @Deprecated
1537  public static boolean pathExists(String filePath) {
1538  return new File(filePath).isFile();
1539  }
1540 
1549  @Deprecated
1550  public static String getAutopsyVersion() {
1551  return Version.getVersion();
1552  }
1553 
1564  @Deprecated
1565  static void createCaseDirectory(String caseDir, String caseName) throws CaseActionException {
1566  createCaseDirectory(caseDir, CaseType.SINGLE_USER_CASE);
1567  }
1568 
1576  @Deprecated
1577  public static boolean existsCurrentCase() {
1578  return currentCase != null;
1579  }
1580 
1589  @Deprecated
1590  public String getModulesOutputDirAbsPath() {
1591  return getModuleDirectory();
1592  }
1593 
1603  @Deprecated
1604  public static String getModulesOutputDirRelPath() {
1605  return "ModuleOutput"; //NON-NLS
1606  }
1607 
1617  @Deprecated
1618  public static PropertyChangeSupport getPropertyChangeSupport() {
1619  return new PropertyChangeSupport(Case.class);
1620  }
1621 
1629  @Deprecated
1630  String getConfigFilePath() {
1631  return getCaseMetadata().getFilePath().toString();
1632  }
1633 
1644  @Deprecated
1645  public Image addImage(String imgPath, long imgId, String timeZone) throws CaseActionException {
1646  try {
1647  Image newDataSource = db.getImageById(imgId);
1648  notifyDataSourceAdded(newDataSource, UUID.randomUUID());
1649  return newDataSource;
1650  } catch (Exception ex) {
1651  throw new CaseActionException(NbBundle.getMessage(this.getClass(), "Case.addImg.exception.msg"), ex);
1652  }
1653  }
1654 
1665  @Deprecated
1666  void addLocalDataSource(Content newDataSource) {
1667  notifyDataSourceAdded(newDataSource, UUID.randomUUID());
1668  }
1669 
1680  @Deprecated
1681  public void deleteReports(Collection<? extends Report> reports, boolean deleteFromDisk) throws TskCoreException {
1682  deleteReports(reports);
1683  }
1684 
1685  @Deprecated
1686  public static final String propStartup = "LBL_StartupDialog"; //NON-NLS
1687 
1688 }
static final AutopsyEventPublisher eventPublisher
Definition: Case.java:280
List< Content > getDataSources()
Definition: Case.java:617
void notifyContentTagDeleted(ContentTag deletedTag)
Definition: Case.java:731
static final int MIN_SECS_BETWEEN_TSK_ERROR_REPORTS
Definition: Case.java:278
static CaseType fromString(String typeName)
Definition: Case.java:140
static void addCaseNameToMainWindowTitle(String newCaseName)
Definition: Case.java:1424
void notifyBlackBoardArtifactTagDeleted(BlackboardArtifactTag deletedTag)
Definition: Case.java:753
Set< TimeZone > getTimeZones()
Definition: Case.java:628
Image addImage(String imgPath, long imgId, String timeZone)
Definition: Case.java:1645
static boolean isPhysicalDrive(String path)
Definition: DriveUtils.java:43
static final String propStartup
Definition: Case.java:1686
static synchronized IngestManager getInstance()
static boolean existsCurrentCase()
Definition: Case.java:1577
static void removePropertyChangeListener(PropertyChangeListener listener)
Definition: Case.java:318
static final Logger logger
Definition: Case.java:279
static final String EXPORT_FOLDER
Definition: Case.java:273
static String convertTimeZone(String timeZoneId)
Definition: Case.java:1523
void addSubscriber(Set< String > eventNames, PropertyChangeListener subscriber)
synchronized static void setLogDirectory(String directoryPath)
Definition: Logger.java:121
static final String CACHE_FOLDER
Definition: Case.java:272
static final int MAX_SANITIZED_CASE_NAME_LEN
Definition: Case.java:270
void addReport(String localPath, String srcModuleName, String reportName)
Definition: Case.java:768
static CaseDbConnectionInfo getDatabaseConnectionInfo()
void deleteReports(Collection<?extends Report > reports, boolean deleteFromDisk)
Definition: Case.java:1681
Case(CaseMetadata caseMetadata, SleuthkitCase db)
Definition: Case.java:294
volatile IntervalErrorReportData tskErrorReporter
Definition: Case.java:288
static boolean isValidName(String caseName)
Definition: Case.java:894
static void changeCurrentCase(Case newCase)
Definition: Case.java:1268
CollaborationMonitor collaborationMonitor
Definition: Case.java:286
static String getModulesOutputDirRelPath()
Definition: Case.java:1604
static void completeCaseChange(Case newCase)
Definition: Case.java:1337
static void removeEventSubscriber(Set< String > eventNames, PropertyChangeListener subscriber)
Definition: Case.java:360
void notifyBlackBoardArtifactTagAdded(BlackboardArtifactTag newTag)
Definition: Case.java:742
static PropertyChangeSupport getPropertyChangeSupport()
Definition: Case.java:1618
static void addPropertyChangeListener(PropertyChangeListener listener)
Definition: Case.java:306
static synchronized boolean coreComponentsAreActive()
void removeSubscriber(Set< String > eventNames, PropertyChangeListener subscriber)
static void removeEventSubscriber(String eventName, PropertyChangeListener subscriber)
Definition: Case.java:350
void deleteReports(Collection<?extends Report > reports)
Definition: Case.java:800
void notifyDataSourceAdded(Content dataSource, UUID addingDataSourceEventId)
Definition: Case.java:709
static boolean pathExists(String filePath)
Definition: Case.java:1537
static void open(String caseMetadataFilePath)
Definition: Case.java:1144
final CaseMetadata caseMetadata
Definition: Case.java:283
static void error(String title, String message)
String getOrCreateSubdirectory(String subDirectoryName)
Definition: Case.java:600
static void create(String caseDir, String caseName, String caseNumber, String examiner)
Definition: Case.java:917
boolean equalsName(String otherTypeName)
Definition: Case.java:162
static final String EVENT_CHANNEL_NAME
Definition: Case.java:271
synchronized static Logger getLogger(String name)
Definition: Logger.java:161
static String convertToAlphaNumericFormat(String timeZoneId)
static final String LOG_FOLDER
Definition: Case.java:274
void receiveError(String context, String errorMessage)
Definition: Case.java:815
void notifyAddingDataSource(UUID eventId)
Definition: Case.java:680
static void addEventSubscriber(String eventName, PropertyChangeListener subscriber)
Definition: Case.java:340
void notifyContentTagAdded(ContentTag newTag)
Definition: Case.java:720
static boolean deleteDir(File dirPath)
Definition: FileUtil.java:47
static void create(String caseDir, String caseName, String caseNumber, String examiner, CaseType caseType)
Definition: Case.java:940
void notifyFailedAddingDataSource(UUID addingDataSourceEventId)
Definition: Case.java:694
static final String REPORTS_FOLDER
Definition: Case.java:275
static final String TEMP_FOLDER
Definition: Case.java:276
static void addEventSubscriber(Set< String > eventNames, PropertyChangeListener subscriber)
Definition: Case.java:330

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