19 package org.sleuthkit.autopsy.casemodule;
21 import java.awt.Cursor;
22 import java.awt.Frame;
23 import java.beans.PropertyChangeListener;
24 import java.beans.PropertyChangeSupport;
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;
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;
88 public class Case implements SleuthkitCase.ErrorObserver {
93 @NbBundle.Messages({
"Case_caseType_singleUser=Single-user case",
"Case_caseType_multiUser=Multi-user case"})
107 this.typeName = typeName;
126 if (fromString(typeName) == SINGLE_USER_CASE) {
127 return Bundle.Case_caseType_singleUser();
129 return Bundle.Case_caseType_multiUser();
141 if (typeName != null) {
143 if (typeName.equalsIgnoreCase(c.typeName)) {
163 return (otherTypeName == null) ?
false : typeName.equals(otherTypeName);
277 static final String MODULE_FOLDER =
"ModuleOutput";
284 private final SleuthkitCase
db;
308 .map(Events::toString)
309 .collect(Collectors.toSet()), listener);
320 .map(Events::toString)
321 .collect(Collectors.toSet()), listener);
370 return currentCase != null;
381 if (currentCase != null) {
384 throw new IllegalStateException(NbBundle.getMessage(
Case.class,
"Case.getCurCase.exception.noneOpen"));
430 return getCaseMetadata().getCreatedDate();
452 void updateCaseName(String oldCaseName, String oldPath, String newCaseName, String newPath)
throws CaseActionException {
454 caseMetadata.setCaseName(newCaseName);
455 eventPublisher.
publish(
new AutopsyEvent(Events.NAME.toString(), oldCaseName, newCaseName));
456 SwingUtilities.invokeLater(() -> {
458 RecentCases.getInstance().updateRecentCase(oldCaseName, oldPath, newCaseName, newPath);
460 }
catch (Exception ex) {
461 Logger.getLogger(
Case.class.getName()).log(Level.SEVERE,
"Error updating case name in UI", ex);
464 }
catch (Exception ex) {
465 throw new CaseActionException(NbBundle.getMessage(
this.getClass(),
"Case.updateCaseName.exception.msg"), ex);
510 hostPath = Paths.get(caseDirectory);
512 if (!hostPath.toFile().exists()) {
513 hostPath.toFile().mkdirs();
515 return hostPath.toString();
588 return path.subpath(path.getNameCount() - 2, path.getNameCount()).toString();
590 return path.subpath(path.getNameCount() - 1, path.getNameCount()).toString();
602 if (!subDirectory.exists()) {
603 subDirectory.mkdirs();
605 return subDirectory.toString();
618 List<Content> list = db.getRootObjects();
619 hasDataSources = (list.size() > 0);
629 Set<TimeZone> timezones =
new HashSet<>();
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()));
638 }
catch (TskCoreException ex) {
639 logger.log(Level.SEVERE,
"Error getting data source time zones", ex);
660 if (!hasDataSources) {
663 }
catch (TskCoreException ex) {
664 logger.log(Level.SEVERE,
"Error accessing case database", ex);
768 public void addReport(String localPath, String srcModuleName, String reportName)
throws TskCoreException {
769 String normalizedLocalPath;
771 normalizedLocalPath = Paths.get(localPath).normalize().toString();
772 }
catch (InvalidPathException ex) {
773 String errorMsg =
"Invalid local path provided: " + localPath;
774 throw new TskCoreException(errorMsg, ex);
776 Report report = this.db.addReport(normalizedLocalPath, srcModuleName, reportName);
789 return this.db.getAllReports();
800 public void deleteReports(Collection<? extends Report> reports)
throws TskCoreException {
801 for (Report report : reports) {
802 this.db.deleteReport(report);
821 if (null != tskErrorReporter) {
822 tskErrorReporter.addProblems(context, errorMessage);
839 }
catch (Exception e) {
840 throw new CaseActionException(NbBundle.getMessage(
this.getClass(),
"Case.closeCase.exception.msg"), e);
853 logger.log(Level.INFO,
"Deleting case.\ncaseDir: {0}", caseDir);
856 boolean result = deleteCaseDirectory(caseDir);
858 RecentCases.getInstance().removeRecentCase(this.caseMetadata.
getCaseName(), this.caseMetadata.getFilePath().toString());
860 if (result ==
false) {
862 NbBundle.getMessage(
this.getClass(),
"Case.deleteCase.exception.msg", caseDir));
865 }
catch (Exception ex) {
866 logger.log(Level.SEVERE,
"Error deleting the current case dir: " + caseDir, ex);
867 throw new CaseActionException(
868 NbBundle.getMessage(
this.getClass(),
"Case.deleteCase.exception.msg2", caseDir), ex);
878 if ((appName == null) || appName.equals(
"")) {
879 appName = WindowManager.getDefault().getMainWindow().getTitle();
895 return !(caseName.contains(
"\\") || caseName.contains(
"/") || caseName.contains(
":")
896 || caseName.contains(
"*") || caseName.contains(
"?") || caseName.contains(
"\"")
897 || caseName.contains(
"<") || caseName.contains(
">") || caseName.contains(
"|"));
939 @Messages({
"Case.creationException=Could not create case: failed to create case metadata file."})
941 logger.log(Level.INFO,
"Attempting to create case {0} in directory = {1}",
new Object[]{caseName, caseDir});
946 if (
new File(caseDir).exists() ==
false) {
947 Case.createCaseDirectory(caseDir, caseType);
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;
961 dbName = caseDir + File.separator +
"autopsy.db";
971 metadata =
new CaseMetadata(caseDir, caseType, caseName, caseNumber, examiner, dbName, indexName);
979 SleuthkitCase db = null;
982 db = SleuthkitCase.newCase(dbName);
986 }
catch (TskCoreException ex) {
987 logger.log(Level.SEVERE, String.format(
"Error creating a case %s in %s ", caseName, caseDir), ex);
988 SwingUtilities.invokeLater(() -> {
989 WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
997 logger.log(Level.SEVERE,
"Error accessing case database connection info", ex);
998 SwingUtilities.invokeLater(() -> {
999 WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
1004 Case newCase =
new Case(metadata, db);
1007 logger.log(Level.INFO,
"Created case {0} in directory = {1}",
new Object[]{caseName, caseDir});
1035 static String sanitizeCaseName(String caseName) {
1040 result = caseName.replaceAll(
"[^\\p{ASCII}]",
"_");
1043 result = result.replaceAll(
"[\\p{Cntrl}]",
"_");
1046 result = result.replaceAll(
"[ /?:'\"\\\\]",
"_");
1049 result = result.toLowerCase();
1052 if (result.length() > 0 && !(Character.isLetter(result.codePointAt(0))) && !(result.codePointAt(0) ==
'_')) {
1053 result =
"_" + result;
1058 result = result.substring(0, MAX_SANITIZED_CASE_NAME_LEN);
1061 if (result.isEmpty()) {
1076 static void createCaseDirectory(String caseDir, CaseType caseType)
throws CaseActionException {
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));
1090 boolean result = (caseDirF).mkdirs();
1091 if (result ==
false) {
1092 throw new CaseActionException(
1093 NbBundle.getMessage(
Case.class,
"Case.createCaseDir.exception.cantCreate", caseDir));
1097 String hostClause =
"";
1100 hostClause = File.separator + NetworkUtils.getLocalHostName();
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();
1107 if (result ==
false) {
1108 throw new CaseActionException(
1109 NbBundle.getMessage(
Case.class,
"Case.createCaseDir.exception.cantCreateCaseDir", caseDir));
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",
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",
1128 }
catch (Exception e) {
1129 throw new CaseActionException(
1130 NbBundle.getMessage(
Case.class,
"Case.createCaseDir.exception.gen", caseDir), e);
1145 logger.log(Level.INFO,
"Opening case with metadata file path {0}", caseMetadataFilePath);
1167 db = SleuthkitCase.openCase(dbPath);
1170 throw new CaseActionException(NbBundle.getMessage(
Case.class,
"Case.open.exception.multiUserCaseNotEnabled"));
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);
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));
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);
1217 logger.log(Level.WARNING,
"Selected image files don't match old files!");
1222 Case openedCase =
new Case(metadata, db);
1227 }
catch (TskCoreException ex) {
1228 SwingUtilities.invokeLater(() -> {
1229 WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
1246 static Map<Long, String> getImagePaths(SleuthkitCase db) {
1247 Map<Long, String> imgPaths =
new HashMap<>();
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));
1255 }
catch (TskException ex) {
1256 logger.log(Level.SEVERE,
"Error getting image paths", ex);
1272 if (oldCase != null) {
1273 SwingUtilities.invokeLater(() -> {
1274 WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
1291 if (newCase != null) {
1292 currentCase = newCase;
1299 currentCase.
tskErrorReporter =
new IntervalErrorReportData(currentCase, MIN_SECS_BETWEEN_TSK_ERROR_REPORTS,
1300 NbBundle.getMessage(
Case.class,
"IntervalErrorReport.ErrorText"));
1302 SwingUtilities.invokeLater(() -> {
1303 RecentCases.getInstance().addRecentCase(currentCase.
getName(), currentCase.getCaseMetadata().getFilePath().toString());
1316 logger.log(Level.SEVERE,
"Failed to setup for collaboration", ex);
1317 MessageNotifyUtil.
Notify.
error(NbBundle.getMessage(
Case.class,
"Case.CollaborationSetup.FailNotify.Title"), NbBundle.getMessage(
Case.class,
"Case.CollaborationSetup.FailNotify.ErrMsg"));
1325 SwingUtilities.invokeLater(() -> {
1326 WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
1338 logger.log(Level.INFO,
"Changing Case to: {0}", newCase);
1339 if (newCase != null) {
1346 SwingUtilities.invokeLater(() -> {
1349 CallableSystemAction.get(CasePropertiesAction.class).setEnabled(
true);
1350 CallableSystemAction.get(CaseDeleteAction.class).setEnabled(
true);
1363 SwingUtilities.invokeLater(() -> {
1364 Frame f = WindowManager.getDefault().getMainWindow();
1370 SwingUtilities.invokeLater(() -> {
1377 CallableSystemAction.get(
AddImageAction.class).setEnabled(
false);
1379 CallableSystemAction.get(CasePropertiesAction.class).setEnabled(
false);
1380 CallableSystemAction.get(CaseDeleteAction.class).setEnabled(
false);
1386 Frame f = WindowManager.getDefault().getMainWindow();
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);
1425 if (!newCaseName.equals(
"")) {
1426 Frame f = WindowManager.getDefault().getMainWindow();
1427 f.setTitle(newCaseName +
" - " +
getAppName());
1438 static boolean deleteCaseDirectory(File casePath) {
1439 logger.log(Level.INFO,
"Deleting case directory: {0}", casePath.getAbsolutePath());
1466 static boolean isPhysicalDrive(String path) {
1479 static boolean isPartition(String path) {
1480 return DriveUtils.isPartition(path);
1495 static boolean driveExists(String path) {
1496 return DriveUtils.driveExists(path);
1538 return new File(filePath).isFile();
1565 static void createCaseDirectory(String caseDir, String caseName)
throws CaseActionException {
1578 return currentCase != null;
1605 return "ModuleOutput";
1619 return new PropertyChangeSupport(
Case.class);
1630 String getConfigFilePath() {
1631 return getCaseMetadata().getFilePath().toString();
1647 Image newDataSource = db.getImageById(imgId);
1649 return newDataSource;
1650 }
catch (Exception ex) {
1651 throw new CaseActionException(NbBundle.getMessage(
this.getClass(),
"Case.addImg.exception.msg"), ex);
1666 void addLocalDataSource(Content newDataSource) {
1681 public void deleteReports(Collection<? extends Report> reports,
boolean deleteFromDisk)
throws TskCoreException {
String getLogDirectoryPath()
static final AutopsyEventPublisher eventPublisher
List< Content > getDataSources()
String getModuleOutputDirectoryRelativePath()
void notifyContentTagDeleted(ContentTag deletedTag)
static final int MIN_SECS_BETWEEN_TSK_ERROR_REPORTS
static CaseType fromString(String typeName)
static void addCaseNameToMainWindowTitle(String newCaseName)
void publishLocally(AutopsyEvent event)
void notifyBlackBoardArtifactTagDeleted(BlackboardArtifactTag deletedTag)
Set< TimeZone > getTimeZones()
Image addImage(String imgPath, long imgId, String timeZone)
static boolean isPhysicalDrive(String path)
static final String propStartup
static synchronized IngestManager getInstance()
String getTempDirectory()
void cancelAllIngestJobs()
static boolean existsCurrentCase()
static void removePropertyChangeListener(PropertyChangeListener listener)
static final Logger logger
void publish(AutopsyEvent event)
String getLocalizedDisplayName()
ADDING_DATA_SOURCE_FAILED
static final String EXPORT_FOLDER
String getCaseDirectory()
static String convertTimeZone(String timeZoneId)
static void openCoreWindows()
void addSubscriber(Set< String > eventNames, PropertyChangeListener subscriber)
synchronized static void setLogDirectory(String directoryPath)
static final String CACHE_FOLDER
static String getAutopsyVersion()
CaseType(String typeName)
static final int MAX_SANITIZED_CASE_NAME_LEN
String getReportDirectory()
static boolean getIsMultiUserModeEnabled()
void addReport(String localPath, String srcModuleName, String reportName)
static CaseDbConnectionInfo getDatabaseConnectionInfo()
String getModulesOutputDirAbsPath()
void deleteReports(Collection<?extends Report > reports, boolean deleteFromDisk)
Case(CaseMetadata caseMetadata, SleuthkitCase db)
volatile IntervalErrorReportData tskErrorReporter
List< Report > getAllReports()
static boolean isValidName(String caseName)
static void changeCurrentCase(Case newCase)
CollaborationMonitor collaborationMonitor
static void closeCoreWindows()
static String getModulesOutputDirRelPath()
static void completeCaseChange(Case newCase)
Set< TimeZone > getTimeZone()
static void invokeStartupDialog()
void openRemoteEventChannel(String channelName)
static void removeEventSubscriber(Set< String > eventNames, PropertyChangeListener subscriber)
SleuthkitCase getSleuthkitCase()
void closeRemoteEventChannel()
void notifyBlackBoardArtifactTagAdded(BlackboardArtifactTag newTag)
static PropertyChangeSupport getPropertyChangeSupport()
String getCacheDirectory()
static void addPropertyChangeListener(PropertyChangeListener listener)
static synchronized boolean coreComponentsAreActive()
void removeSubscriber(Set< String > eventNames, PropertyChangeListener subscriber)
String getModuleDirectory()
static void removeEventSubscriber(String eventName, PropertyChangeListener subscriber)
void deleteReports(Collection<?extends Report > reports)
void notifyDataSourceAdded(Content dataSource, UUID addingDataSourceEventId)
static boolean pathExists(String filePath)
BLACKBOARD_ARTIFACT_TAG_ADDED
static void open(String caseMetadataFilePath)
final CaseMetadata caseMetadata
String getOutputDirectory()
static void error(String title, String message)
String getOrCreateSubdirectory(String subDirectoryName)
static void create(String caseDir, String caseName, String caseNumber, String examiner)
boolean equalsName(String otherTypeName)
static final String EVENT_CHANNEL_NAME
static Case getCurrentCase()
static String getLocalHostName()
synchronized static Logger getLogger(String name)
static String convertToAlphaNumericFormat(String timeZoneId)
static final String LOG_FOLDER
void receiveError(String context, String errorMessage)
static String getAppName()
static String getVersion()
String getExportDirectory()
void notifyAddingDataSource(UUID eventId)
static void clearTempFolder()
static void addEventSubscriber(String eventName, PropertyChangeListener subscriber)
void notifyContentTagAdded(ContentTag newTag)
static StartupWindowProvider getInstance()
static boolean deleteDir(File dirPath)
static void create(String caseDir, String caseName, String caseNumber, String examiner, CaseType caseType)
void notifyFailedAddingDataSource(UUID addingDataSourceEventId)
static boolean isCaseOpen()
String getTextIndexName()
static final String REPORTS_FOLDER
static final String TEMP_FOLDER
BLACKBOARD_ARTIFACT_TAG_DELETED
static void addEventSubscriber(Set< String > eventNames, PropertyChangeListener subscriber)