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 boolean coreComponentsAreActive()
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)
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)