From 2e8444d6a6e728c11559f0dd9ee0f23e3a3091b8 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Fri, 22 Nov 2013 08:05:46 -0500 Subject: [PATCH 01/12] Assorted hash database module improvements --- .../AddContentToHashDbAction.java | 10 +++- .../autopsy/hashdatabase/Bundle.properties | 2 +- .../autopsy/hashdatabase/HashDb.java | 5 +- .../hashdatabase/HashDbConfigPanel.form | 19 +++++-- .../hashdatabase/HashDbConfigPanel.java | 14 +++-- .../hashdatabase/HashDbIngestModule.java | 57 ++++++++----------- 6 files changed, 56 insertions(+), 51 deletions(-) diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/AddContentToHashDbAction.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/AddContentToHashDbAction.java index bef2c3e889..fca2ac8ab0 100755 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/AddContentToHashDbAction.java +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/AddContentToHashDbAction.java @@ -124,7 +124,7 @@ public class AddContentToHashDbAction extends AbstractAction implements Presente // Add a "New Hash Set..." menu item. addSeparator(); - JMenuItem newHashSetItem = new JMenuItem("New Hash Set..."); + JMenuItem newHashSetItem = new JMenuItem("Create database..."); newHashSetItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -150,9 +150,13 @@ public class AddContentToHashDbAction extends AbstractAction implements Presente } catch (TskCoreException ex) { Logger.getLogger(AddContentToHashDbAction.class.getName()).log(Level.SEVERE, "Error adding to hash database", ex); - JOptionPane.showMessageDialog(null, "Unable to add " + file.getName() + " to hash database.", "Add to Hash Database Error", JOptionPane.ERROR_MESSAGE); + JOptionPane.showMessageDialog(null, "Unable to add " + file.getName() + " to the hash database.", "Add to Hash Database Error", JOptionPane.ERROR_MESSAGE); } - } + } + else { + JOptionPane.showMessageDialog(null, "Unable to add the " + (selectedFiles.size() > 1 ? "files" : "file") + " to the hash database. Hashes have not been calculated. Please configure and run an appropriate ingest module.", "Add to Hash Database Error", JOptionPane.ERROR_MESSAGE); + break; + } } } } diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/Bundle.properties b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/Bundle.properties index 891a9e4358..28c94254f8 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/Bundle.properties +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/Bundle.properties @@ -62,7 +62,7 @@ HashDbConfigPanel.showInboxMessagesCheckBox.text=Enable sending messages to inbo HashDbConfigPanel.useForIngestCheckbox.text=Enable for ingest HashDbConfigPanel.informationLabel.text=Information HashDbSimpleConfigPanel.calcHashesButton.text=Calculate hashes even if no hash database is selected -HashDbConfigPanel.newDatabaseButton.text=New Database +HashDbConfigPanel.newDatabaseButton.text=Create Database HashDbConfigPanel.importDatabaseButton.text=Import Database HashDbConfigPanel.deleteDatabaseButton.text=Delete Database HashDbConfigPanel.indexPathLabelLabel.text=Index Path: diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDb.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDb.java index ec6e84435f..a7b5ffcdd4 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDb.java +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDb.java @@ -31,7 +31,6 @@ import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.HashInfo; import org.sleuthkit.datamodel.SleuthkitJNI; import org.sleuthkit.datamodel.TskCoreException; -import org.sleuthkit.datamodel.TskData; /** * Instances of this class represent hash databases used to classify files as @@ -209,7 +208,7 @@ public class HashDb { } } - public boolean lookUp(Content content) throws TskCoreException { + public boolean hasHashOfContent(Content content) throws TskCoreException { boolean result = false; // TODO: This only works for AbstractFiles at present. Change when Content can be queried for hashes. assert content instanceof AbstractFile; @@ -223,7 +222,7 @@ public class HashDb { return result; } - public HashInfo lookUpVerbose(Content content) throws TskCoreException { + public HashInfo lookUp(Content content) throws TskCoreException { HashInfo result = null; // TODO: This only works for AbstractFiles at present. Change when Content can be queried for hashes. assert content instanceof AbstractFile; diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbConfigPanel.form b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbConfigPanel.form index 2cef194731..cb38db0b8b 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbConfigPanel.form +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbConfigPanel.form @@ -60,7 +60,6 @@ - @@ -106,12 +105,20 @@ - - - - + + + + + + + + + + + + + - diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbConfigPanel.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbConfigPanel.java index 34f9bca200..aa4a09c3dd 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbConfigPanel.java +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbConfigPanel.java @@ -558,7 +558,6 @@ public final class HashDbConfigPanel extends javax.swing.JPanel implements Optio .addGroup(layout.createSequentialGroup() .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(hashDatabasesLabel) .addGroup(layout.createSequentialGroup() .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 275, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) @@ -595,10 +594,15 @@ public final class HashDbConfigPanel extends javax.swing.JPanel implements Optio .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(optionsSeparator, javax.swing.GroupLayout.PREFERRED_SIZE, 324, javax.swing.GroupLayout.PREFERRED_SIZE)))) .addGroup(layout.createSequentialGroup() - .addComponent(newDatabaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 133, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(importDatabaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 133, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addComponent(deleteDatabaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 133, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(hashDatabasesLabel) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) + .addComponent(deleteDatabaseButton, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE) + .addComponent(newDatabaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 137, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(importDatabaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 133, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addGap(0, 0, Short.MAX_VALUE))) .addGap(24, 24, 24)) ); layout.setVerticalGroup( diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbIngestModule.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbIngestModule.java index c3e2340a51..403790a86c 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbIngestModule.java +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbIngestModule.java @@ -206,14 +206,24 @@ public class HashDbIngestModule extends IngestModuleAbstractFile { } // look up in known bad first - TskData.FileKnown status = TskData.FileKnown.UNKNOWN; boolean foundBad = false; ProcessResult ret = ProcessResult.OK; for (HashDb db : knownBadHashSets) { try { long lookupstart = System.currentTimeMillis(); - if (db.lookUp(file)) { - status = TskData.FileKnown.BAD; + if (db.hasHashOfContent(file)) { + foundBad = true; + knownBadCount += 1; + try { + skCase.setKnown(file, TskData.FileKnown.BAD); + } catch (TskException ex) { + logger.log(Level.WARNING, "Couldn't set known bad state for file " + name + " - see sleuthkit log for details", ex); + services.postMessage(IngestMessage.createErrorMessage(++messageId, HashDbIngestModule.this, "Hash Lookup Error: " + name, + "Error encountered while setting known bad state for " + name + ".")); + ret = ProcessResult.ERROR; + } + String hashSetName = db.getHashSetName(); + postHashSetHitToBlackboard(file, md5Hash, hashSetName, db.getShowInboxMessages()); } lookuptime += (System.currentTimeMillis() - lookupstart); } catch (TskException ex) { @@ -222,21 +232,6 @@ public class HashDbIngestModule extends IngestModuleAbstractFile { "Error encountered while looking up known bad hash value for " + name + ".")); ret = ProcessResult.ERROR; } - - if (status.equals(TskData.FileKnown.BAD)) { - foundBad = true; - knownBadCount += 1; - try { - skCase.setKnown(file, TskData.FileKnown.BAD); - } catch (TskException ex) { - logger.log(Level.WARNING, "Couldn't set known bad state for file " + name + " - see sleuthkit log for details", ex); - services.postMessage(IngestMessage.createErrorMessage(++messageId, HashDbIngestModule.this, "Hash Lookup Error: " + name, - "Error encountered while setting known bad state for " + name + ".")); - ret = ProcessResult.ERROR; - } - String hashSetName = db.getHashSetName(); - processBadFile(file, md5Hash, hashSetName, db.getShowInboxMessages()); - } } // If the file is not in the known bad sets, search for it in the known sets. @@ -246,8 +241,16 @@ public class HashDbIngestModule extends IngestModuleAbstractFile { for (HashDb db : knownHashSets) { try { long lookupstart = System.currentTimeMillis(); - if (db.lookUp(file)) { - status = TskData.FileKnown.KNOWN; + if (db.hasHashOfContent(file)) { + try { + skCase.setKnown(file, TskData.FileKnown.KNOWN); + break; + } catch (TskException ex) { + logger.log(Level.WARNING, "Couldn't set known state for file " + name + " - see sleuthkit log for details", ex); + services.postMessage(IngestMessage.createErrorMessage(++messageId, HashDbIngestModule.this, "Hash Lookup Error: " + name, + "Error encountered while setting known state for " + name + ".")); + ret = ProcessResult.ERROR; + } } lookuptime += (System.currentTimeMillis() - lookupstart); } catch (TskException ex) { @@ -256,25 +259,13 @@ public class HashDbIngestModule extends IngestModuleAbstractFile { "Error encountered while looking up known hash value for " + name + ".")); ret = ProcessResult.ERROR; } - - if (status.equals(TskData.FileKnown.KNOWN)) { - try { - skCase.setKnown(file, TskData.FileKnown.KNOWN); - break; - } catch (TskException ex) { - logger.log(Level.WARNING, "Couldn't set known state for file " + name + " - see sleuthkit log for details", ex); - services.postMessage(IngestMessage.createErrorMessage(++messageId, HashDbIngestModule.this, "Hash Lookup Error: " + name, - "Error encountered while setting known state for " + name + ".")); - ret = ProcessResult.ERROR; - } - } } } return ret; } - private void processBadFile(AbstractFile abstractFile, String md5Hash, String hashSetName, boolean showInboxMessage) { + private void postHashSetHitToBlackboard(AbstractFile abstractFile, String md5Hash, String hashSetName, boolean showInboxMessage) { try { BlackboardArtifact badFile = abstractFile.newArtifact(ARTIFACT_TYPE.TSK_HASHSET_HIT); //TODO Revisit usage of deprecated constructor as per TSK-583 From b602bdbf46c8567355a01ff22e90129a550086e7 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Fri, 22 Nov 2013 16:08:31 -0500 Subject: [PATCH 02/12] Hash database configuration management fixes --- .../autopsy/hashdatabase/HashDbManager.java | 132 ++++++++++++------ 1 file changed, 89 insertions(+), 43 deletions(-) diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbManager.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbManager.java index 687903e96b..d7cf3293f1 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbManager.java +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbManager.java @@ -56,6 +56,7 @@ public class HashDbManager { private static final String ENCODING = "UTF-8"; private static final String SET_CALC = "hash_calculate"; private static final String SET_VALUE = "value"; + private static final String LEGACY_INDEX_FILE_EXTENSION = "-md5.idx"; private static final Logger logger = Logger.getLogger(HashDbManager.class.getName()); private static HashDbManager instance; private String xmlFilePath = PlatformUtil.getUserConfigDirectory() + File.separator + CUR_HASHSETS_FILE_NAME; @@ -272,89 +273,137 @@ public class HashDbManager { // TODO: The return value from this function is never checked. Failure is not indicated to the user. Is this desired? private boolean readHashSetsConfigurationFromDisk() { + // Open the XML document that implements the configuration file. final Document doc = XMLUtil.loadDoc(HashDbManager.class, xmlFilePath, XSDFILE); if (doc == null) { return false; } + // Get the root element. Element root = doc.getDocumentElement(); if (root == null) { logger.log(Level.SEVERE, "Error loading hash sets: invalid file format."); return false; } + // Get the hash set elements. NodeList setsNList = root.getElementsByTagName(SET_EL); int numSets = setsNList.getLength(); if(numSets == 0) { logger.log(Level.WARNING, "No element hash_set exists."); } + // Create HashDb objects for each hash set element. + // TODO: Does this code implement the correct policy for handling a malformed config file? + String attributeErrorMessage = " attribute was not set for hash_set at index {0}, cannot make instance of HashDb class"; + String elementErrorMessage = " element was not set for hash_set at index {0}, cannot make instance of HashDb class"; for (int i = 0; i < numSets; ++i) { Element setEl = (Element) setsNList.item(i); - final String name = setEl.getAttribute(SET_NAME_ATTR); - final String type = setEl.getAttribute(SET_TYPE_ATTR); + + final String hashSetName = setEl.getAttribute(SET_NAME_ATTR); + if (hashSetName.isEmpty()) { + logger.log(Level.SEVERE, SET_NAME_ATTR + attributeErrorMessage, i); + continue; + } + + String knownFilesType = setEl.getAttribute(SET_TYPE_ATTR); + if(knownFilesType.isEmpty()) { + logger.log(Level.SEVERE, SET_TYPE_ATTR + attributeErrorMessage, i); + continue; + } + if (knownFilesType.equals("NSRL")) { + knownFilesType = KnownFilesType.KNOWN.toString(); + } + final String useForIngest = setEl.getAttribute(SET_USE_FOR_INGEST_ATTR); - final String showInboxMessages = setEl.getAttribute(SET_SHOW_INBOX_MESSAGES); - Boolean useForIngestBool = Boolean.parseBoolean(useForIngest); - Boolean showInboxMessagesBool = Boolean.parseBoolean(showInboxMessages); + if (useForIngest.isEmpty()) { + logger.log(Level.SEVERE, SET_USE_FOR_INGEST_ATTR + attributeErrorMessage, i); + continue; + } + Boolean useForIngestFlag = Boolean.parseBoolean(useForIngest); - String path = null; + final String showInboxMessages = setEl.getAttribute(SET_SHOW_INBOX_MESSAGES); + if (useForIngest.isEmpty()) { + logger.log(Level.SEVERE, SET_SHOW_INBOX_MESSAGES + attributeErrorMessage, i); + continue; + } + Boolean showInboxMessagesFlag = Boolean.parseBoolean(showInboxMessages); + + String dbPath; NodeList pathsNList = setEl.getElementsByTagName(PATH_EL); - if (pathsNList.getLength() > 0) { - // Shouldn't be more than 1 - Element pathEl = (Element) pathsNList.item(0); - path = pathEl.getTextContent(); - File database = new File(path); - if(!database.exists() && JOptionPane.showConfirmDialog(null, "Database " + name + " could not be found at location\n" + path + "\nWould you like to search for the file?", "Missing Database", JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { - path = searchForFile(); - } + if (pathsNList.getLength() > 0) { + Element pathEl = (Element) pathsNList.item(0); // Shouldn't be more than one. + dbPath = pathEl.getTextContent(); + if (dbPath.isEmpty()) { + logger.log(Level.SEVERE, PATH_EL + elementErrorMessage, i); + continue; + } } - - if(name.isEmpty()) { - logger.log(Level.WARNING, "Name was not set for hash_set at index {0}.", i); - } - - if(type.isEmpty()) { - logger.log(Level.SEVERE, "Type was not set for hash_set at index {0}, cannot make instance of HashDb class.", i); - return false; - } - - if(useForIngest.isEmpty()) { - logger.log(Level.WARNING, "UseForIngest was not set for hash_set at index {0}.", i); - } - - if(showInboxMessages.isEmpty()) { - logger.log(Level.WARNING, "ShowInboxMessages was not set for hash_set at index {0}.", i); - } - - if(path == null) { - logger.log(Level.WARNING, "No path for hash_set at index {0}, cannot make instance of HashDb class.", i); - } else { + logger.log(Level.SEVERE, PATH_EL + elementErrorMessage, i); + continue; + } + dbPath = getValidFilePath(hashSetName, dbPath); + + if (null != dbPath) { try { - addHashSet(HashDb.openHashDatabase(name, path, useForIngestBool, showInboxMessagesBool, KnownFilesType.valueOf(type))); + addHashSet(HashDb.openHashDatabase(hashSetName, dbPath, useForIngestFlag, showInboxMessagesFlag, KnownFilesType.valueOf(knownFilesType))); } catch (TskCoreException ex) { Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error opening hash database", ex); - JOptionPane.showMessageDialog(null, "Unable to open " + path + " hash database.", "Open Hash Database Error", JOptionPane.ERROR_MESSAGE); + JOptionPane.showMessageDialog(null, "Unable to open " + dbPath + " hash database.", "Open Hash Database Error", JOptionPane.ERROR_MESSAGE); } + } + else { + logger.log(Level.WARNING, "No valid path for hash_set at index {0}, cannot make instance of HashDb class", i); } } + // Get the element that stores the always calculate hashes flag. NodeList calcList = root.getElementsByTagName(SET_CALC); if (calcList.getLength() > 0) { - Element calcEl = (Element) calcList.item(0); // Shouldn't be more than 1 + Element calcEl = (Element) calcList.item(0); // Shouldn't be more than one. final String value = calcEl.getAttribute(SET_VALUE); alwaysCalculateHashes = Boolean.parseBoolean(value); } else { - logger.log(Level.WARNING, "No element hash_calculate exists."); + logger.log(Level.WARNING, " element "); + alwaysCalculateHashes = false; } return true; } + + private String getValidFilePath(String hashSetName, String configuredPath) { + // Check the configured path. + File database = new File(configuredPath); + if (database.exists()) { + return configuredPath; + } + + // Try a path that could be in an older version of the configuration file. + String legacyPath = configuredPath + LEGACY_INDEX_FILE_EXTENSION; + database = new File(legacyPath); + if (database.exists()) { + return legacyPath; + } + + // Give the user an opportunity to find the desired file. + String newPath = null; + if (JOptionPane.showConfirmDialog(null, "Database " + hashSetName + " could not be found at location\n" + configuredPath + "\nWould you like to search for the file?", "Missing Database", JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { + newPath = searchForFile(); + if (null != newPath && !newPath.isEmpty()) { + database = new File(newPath); + if (!database.exists()) { + newPath = null; + } + } + } + return newPath; + } private String searchForFile() { + String filePath = null; JFileChooser fc = new JFileChooser(); fc.setDragEnabled(false); fc.setFileSelectionMode(JFileChooser.FILES_ONLY); @@ -362,8 +411,6 @@ public class HashDbManager { FileNameExtensionFilter filter = new FileNameExtensionFilter("Hash Database File", EXTENSION); fc.setFileFilter(filter); fc.setMultiSelectionEnabled(false); - - String filePath = null; if (fc.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) { File f = fc.getSelectedFile(); try { @@ -372,8 +419,7 @@ public class HashDbManager { catch (IOException ex) { logger.log(Level.WARNING, "Couldn't get selected file path", ex); } - } - + } return filePath; } } \ No newline at end of file From 96f23c2079b06eed982723d4c486bf7e64a50464 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Mon, 25 Nov 2013 13:42:59 -0500 Subject: [PATCH 03/12] Normalize line endings --- docs/QuickStartGuide/index.html | 442 ++++++++++++++++---------------- docs/doxygen/needs_a_home.dox | 60 ++--- docs/doxygen/workflow.dox | 106 ++++---- 3 files changed, 304 insertions(+), 304 deletions(-) diff --git a/docs/QuickStartGuide/index.html b/docs/QuickStartGuide/index.html index 7fe6d0867b..7bafa3b452 100644 --- a/docs/QuickStartGuide/index.html +++ b/docs/QuickStartGuide/index.html @@ -1,221 +1,221 @@ - - - - - - Autopsy 3 Quick Start Guide - - - -

Autopsy 3 Quick Start Guide

-

June 2013

-

www.sleuthkit.org/autopsy/

- - -

Installation

-

- The current version of Autopsy 3 runs only on Microsoft Windows. - We have gotten it to run on other platforms, such as Linux and OS X, but we do not have it in a state that makes it easy to distribute and find the needed libraries. -

-

- The Windows installer will make a directory for Autopsy and place all of the needed files inside of it. - The installer includes all dependencies, including Sleuth Kit and Java. -

-

Note that Autopsy 3 is a complete rewrite from Autopsy 2 and none of this document is relevant to Autopsy 2.

- -

Adding a Data Source (image, local disk, logical files)

-

- Data sources are added to a case. A case can have a single data source or it can have multiple data source if they are related. - Currently, a single report is generated for an entire case, so if you need to report on individual data sources, then you should use one data source per case. -

- -

Creating a Case

-

- To create a case, use either the "Create New Case" option on the Welcome screen or from the "File" menu. - This will start the New Case Wizard. You will need to supply it with the name of the case and a directory to store the case results into. - You can optionally provide case numbers and other details. -

- - -

Adding a Data Source

-

- The next step is to add input data source to the case. - The Add Data Source Wizard will start automatically after the case is created or you can manually start it from the "File" menu or toolbar. - You will need to choose the type of input data source to add (image, local disk or logical files and folders). - Next, supply it with the location of the source to add. -

-
    -
  • For a disk image, browse to the first file in the set (Autopsy will find the rest of the files). Autopsy currently supports E01 and raw (dd) files. -
  • -
  • - For local disk, select one of the detected disks. - Autopsy will add the current view of the disk to the case (i.e. snapshot of the meta-data). - However, the individual file content (not meta-data) does get updated with the changes made to the disk. - Note, you may need run Autopsy as an Administrator to detect all disks. -
  • -
  • For logical files (a single file or folder of files), use the "Add" button to add one or more files or folders on your system to the case. Folders will be recursively added to the case.
  • -
- - -

- There are a couple of options in the wizard that will allow you to make the ingest process faster. - These typically deal with deleted files. - It will take longer if unallocated space is analyzed and the entire drive is searched for deleted files. - In some scenarios, these recovery steps must be performed and in other scenarios these steps are not needed and instead fast results on the allocated files are needed. - Use these options to control how long the analysis will take. -

- -

- Autopsy will start to analyze these data sources and add them to the case and internal database. While it is doing that, it will prompt you to configure the Ingest Modules.

- - -

Ingest Modules

-

- You will next be prompted to configure the Ingest Modules. - Ingest modules will run in the background and perform specific tasks. - The Ingest Modules analyze files in a prioritized order so that files in a user's directory are analyzed before files in other folders. - Ingest modules can be developed by third-parties and here are some of the standard ingest modules that come with Autopsy: -

-
    -
  • Recent Activity - extracts user activity as saved by web browsers and the OS. Also runs regripper on the registry hive. -
  • -
  • Hash Lookup - uses hash databases to ignore known files from the NIST NSRL and flag known bad files. - Use the "Advanced" button to add and configure the hash databases to use during this process. - You will get updates on known bad file hits as the ingest occurs. You can later add hash databases - via the Tools -> Options menu in the main UI. You can download an index of the NIST NSRL from - here. -
  • -
  • Keyword Search - uses keyword lists to identify files with specific words in them. - You can select the keyword lists to search for automatically and you can create new lists using the "Advanced" button. - Note that with keyword search, you can always conduct searches after ingest has finished. - The keyword lists that you select during ingest will be searched for at periodic intervals and you will get the results in real-time. - You do not need to wait for all files to be indexed. -
  • -
  • Archive Extractor opens ZIP, RAR, and other archive formats and sends the files from those archive files back - through the pipelines for analysis.
  • -
  • Exif Image Parser extracts EXIF information from JPEG files and posts the results into the tree in the main UI.
  • -
  • Thunderbird Parser Identifies Thunderbird MBOX files and extracts the e-mails from them.
  • -
-

- When you select a module, you will have the option to change its settings. - For example, you can configure which keyword search lists to use during ingest and which hash databases to use. - Refer to the help system inside of Autopsy for details on configuring each module. -

-

- While ingest modules are running in the background, you will see a progress bar in the lower right. - You can use the GUI to review incoming results and perform other tasks while ingest at that time. -

- - -

Analysis Basics

- Autopsy Screenshot -

You will start all of your analysis techniques from the tree on the left.

-
    -
  • The Data Sources root node shows all data in the case.
  • -
      -
    • The individual image nodes show the file system structure of the disk images or local disks in the case.
    • -
    • The LogicalFileSet nodes show the logical files in the case.
    • -
    -
  • The Views node shows the same data from a file type or timeline perspective.
  • -
  • The Results node shows the output from the ingest modules.
  • -
- -

- When you select a node from the tree on the left, a list of files will be shown in the upper right. - You can use the Thumbnail view in the upper right to view the pictures. - When you select a file from the upper right, its contents will be shown in the lower right. - You can use the tabs in the lower right to view the text of the file, an image, or the hex data. -

- -

- If you are viewing files from the Views and Results nodes, you can right-click on a file to go to its file system location. - This feature is useful to see what else the user stored in the same folder as the file that you are currently looking at. - You can also right click on a file to extract it to the local system. -

-

- If you want to search for single keywords, then you can use the search box in the upper right of the program. - The results will be shown in a table in the upper right. -

- -

You can tag (or bookmark) arbitrary files so that you can more quickly find them later or so that you can include them specifically in a report.

- -

Ingest Inbox

-

- As you are going through the results in the tree, the ingest modules are running in the background. - The results are shown in the tree as soon as the ingest modules find them and report them. -

-

- The Ingest Inbox receives messages from the ingest modules as they find results. - You can open the inbox to see what has been recently found. - It keeps track of what messages you have read. -

-

- The intended use of this inbox is that you can focus on some data for a while and then check back on the inbox at a time that is convenient for them. - You can then see what else was found while you were focused on the previous task. - You may learn that a known bad file was found or that a file was found with a relevant keyword and then decide to focus on that for a while. -

-

When you select a message, you can then jump to the Results tree where more details can be found or jump to the file's location in the filesystem.

- -

Timeline (Beta)

-

There is a basic timeline view that you can access via the Tools -> Make Timeline feature. This will take a few minutes to create the timeline for analysis. Its features are still in development.

- - -

Example Use Cases

-

In this section, we will provide examples of how to do common analysis tasks.

- -

Web Artifacts

-

- If you want to view the user's recent web activity, make sure that the Recent Activity ingest module was enabled. - You can then go to the "Results " node in the tree on the left and then into the "Extracted Data" node. - There, you can find bookmarks, cookies, downloads, and history. -

- -

Known Bad Hash Files

-

- If you want to see if the data source had known bad files, make sure that the Hash Lookup ingest module was enabled. - You can then view the "Hashset Hits" section in the "Results" area of the tree on the left. - Note that hash lookup can take a long time, so this section will be updated as long as the ingest process is occurring. - Use the Ingest Inbox to keep track of what known bad files were recently found. -

-

- When you find a known bad file in this interface, you may want to right click on the file to also view the file's original location. - You may find additional files that are relevant and stored in the same folder as this file. -

- -

Media: Images and Videos

-

- If you want to see all images and video on the disk image, then go to the "Views" section in the tree on the left and then "File Types". - Select either "Images" or "Videos". - You can use the thumbnail option in the upper right to view thumbnails of all images. -

-
    -
  • Note: - We are working on making this more efficient when there are lots of images and we are working on the feature to display video thumbnails. -
  • -
-

You can select an image or video from the upper right and view the video or image in the lower right. Video will be played with sound.

- - -

Reporting

-

- A final report can be generated that will include all analysis results. - Use the "Generate Report" button to create this. - It will create an HTML or XLS report in the Reports folder of the case folder. - If you forgot the location of your case folder, you can determine it using the "Case Properties" option in the "File" menu. - There is also an option to export report files to a separate folder outside of the case folder. -

- -
-

Copyright © 2012-2013 Basis Technology.

-

- This work is licensed under a - Creative Commons Attribution-Share Alike 3.0 United States License. -

- - + + + + + + Autopsy 3 Quick Start Guide + + + +

Autopsy 3 Quick Start Guide

+

June 2013

+

www.sleuthkit.org/autopsy/

+ + +

Installation

+

+ The current version of Autopsy 3 runs only on Microsoft Windows. + We have gotten it to run on other platforms, such as Linux and OS X, but we do not have it in a state that makes it easy to distribute and find the needed libraries. +

+

+ The Windows installer will make a directory for Autopsy and place all of the needed files inside of it. + The installer includes all dependencies, including Sleuth Kit and Java. +

+

Note that Autopsy 3 is a complete rewrite from Autopsy 2 and none of this document is relevant to Autopsy 2.

+ +

Adding a Data Source (image, local disk, logical files)

+

+ Data sources are added to a case. A case can have a single data source or it can have multiple data source if they are related. + Currently, a single report is generated for an entire case, so if you need to report on individual data sources, then you should use one data source per case. +

+ +

Creating a Case

+

+ To create a case, use either the "Create New Case" option on the Welcome screen or from the "File" menu. + This will start the New Case Wizard. You will need to supply it with the name of the case and a directory to store the case results into. + You can optionally provide case numbers and other details. +

+ + +

Adding a Data Source

+

+ The next step is to add input data source to the case. + The Add Data Source Wizard will start automatically after the case is created or you can manually start it from the "File" menu or toolbar. + You will need to choose the type of input data source to add (image, local disk or logical files and folders). + Next, supply it with the location of the source to add. +

+
    +
  • For a disk image, browse to the first file in the set (Autopsy will find the rest of the files). Autopsy currently supports E01 and raw (dd) files. +
  • +
  • + For local disk, select one of the detected disks. + Autopsy will add the current view of the disk to the case (i.e. snapshot of the meta-data). + However, the individual file content (not meta-data) does get updated with the changes made to the disk. + Note, you may need run Autopsy as an Administrator to detect all disks. +
  • +
  • For logical files (a single file or folder of files), use the "Add" button to add one or more files or folders on your system to the case. Folders will be recursively added to the case.
  • +
+ + +

+ There are a couple of options in the wizard that will allow you to make the ingest process faster. + These typically deal with deleted files. + It will take longer if unallocated space is analyzed and the entire drive is searched for deleted files. + In some scenarios, these recovery steps must be performed and in other scenarios these steps are not needed and instead fast results on the allocated files are needed. + Use these options to control how long the analysis will take. +

+ +

+ Autopsy will start to analyze these data sources and add them to the case and internal database. While it is doing that, it will prompt you to configure the Ingest Modules.

+ + +

Ingest Modules

+

+ You will next be prompted to configure the Ingest Modules. + Ingest modules will run in the background and perform specific tasks. + The Ingest Modules analyze files in a prioritized order so that files in a user's directory are analyzed before files in other folders. + Ingest modules can be developed by third-parties and here are some of the standard ingest modules that come with Autopsy: +

+
    +
  • Recent Activity + extracts user activity as saved by web browsers and the OS. Also runs regripper on the registry hive. +
  • +
  • Hash Lookup + uses hash databases to ignore known files from the NIST NSRL and flag known bad files. + Use the "Advanced" button to add and configure the hash databases to use during this process. + You will get updates on known bad file hits as the ingest occurs. You can later add hash databases + via the Tools -> Options menu in the main UI. You can download an index of the NIST NSRL from + here. +
  • +
  • Keyword Search + uses keyword lists to identify files with specific words in them. + You can select the keyword lists to search for automatically and you can create new lists using the "Advanced" button. + Note that with keyword search, you can always conduct searches after ingest has finished. + The keyword lists that you select during ingest will be searched for at periodic intervals and you will get the results in real-time. + You do not need to wait for all files to be indexed. +
  • +
  • Archive Extractor opens ZIP, RAR, and other archive formats and sends the files from those archive files back + through the pipelines for analysis.
  • +
  • Exif Image Parser extracts EXIF information from JPEG files and posts the results into the tree in the main UI.
  • +
  • Thunderbird Parser Identifies Thunderbird MBOX files and extracts the e-mails from them.
  • +
+

+ When you select a module, you will have the option to change its settings. + For example, you can configure which keyword search lists to use during ingest and which hash databases to use. + Refer to the help system inside of Autopsy for details on configuring each module. +

+

+ While ingest modules are running in the background, you will see a progress bar in the lower right. + You can use the GUI to review incoming results and perform other tasks while ingest at that time. +

+ + +

Analysis Basics

+ Autopsy Screenshot +

You will start all of your analysis techniques from the tree on the left.

+
    +
  • The Data Sources root node shows all data in the case.
  • +
      +
    • The individual image nodes show the file system structure of the disk images or local disks in the case.
    • +
    • The LogicalFileSet nodes show the logical files in the case.
    • +
    +
  • The Views node shows the same data from a file type or timeline perspective.
  • +
  • The Results node shows the output from the ingest modules.
  • +
+ +

+ When you select a node from the tree on the left, a list of files will be shown in the upper right. + You can use the Thumbnail view in the upper right to view the pictures. + When you select a file from the upper right, its contents will be shown in the lower right. + You can use the tabs in the lower right to view the text of the file, an image, or the hex data. +

+ +

+ If you are viewing files from the Views and Results nodes, you can right-click on a file to go to its file system location. + This feature is useful to see what else the user stored in the same folder as the file that you are currently looking at. + You can also right click on a file to extract it to the local system. +

+

+ If you want to search for single keywords, then you can use the search box in the upper right of the program. + The results will be shown in a table in the upper right. +

+ +

You can tag (or bookmark) arbitrary files so that you can more quickly find them later or so that you can include them specifically in a report.

+ +

Ingest Inbox

+

+ As you are going through the results in the tree, the ingest modules are running in the background. + The results are shown in the tree as soon as the ingest modules find them and report them. +

+

+ The Ingest Inbox receives messages from the ingest modules as they find results. + You can open the inbox to see what has been recently found. + It keeps track of what messages you have read. +

+

+ The intended use of this inbox is that you can focus on some data for a while and then check back on the inbox at a time that is convenient for them. + You can then see what else was found while you were focused on the previous task. + You may learn that a known bad file was found or that a file was found with a relevant keyword and then decide to focus on that for a while. +

+

When you select a message, you can then jump to the Results tree where more details can be found or jump to the file's location in the filesystem.

+ +

Timeline (Beta)

+

There is a basic timeline view that you can access via the Tools -> Make Timeline feature. This will take a few minutes to create the timeline for analysis. Its features are still in development.

+ + +

Example Use Cases

+

In this section, we will provide examples of how to do common analysis tasks.

+ +

Web Artifacts

+

+ If you want to view the user's recent web activity, make sure that the Recent Activity ingest module was enabled. + You can then go to the "Results " node in the tree on the left and then into the "Extracted Data" node. + There, you can find bookmarks, cookies, downloads, and history. +

+ +

Known Bad Hash Files

+

+ If you want to see if the data source had known bad files, make sure that the Hash Lookup ingest module was enabled. + You can then view the "Hashset Hits" section in the "Results" area of the tree on the left. + Note that hash lookup can take a long time, so this section will be updated as long as the ingest process is occurring. + Use the Ingest Inbox to keep track of what known bad files were recently found. +

+

+ When you find a known bad file in this interface, you may want to right click on the file to also view the file's original location. + You may find additional files that are relevant and stored in the same folder as this file. +

+ +

Media: Images and Videos

+

+ If you want to see all images and video on the disk image, then go to the "Views" section in the tree on the left and then "File Types". + Select either "Images" or "Videos". + You can use the thumbnail option in the upper right to view thumbnails of all images. +

+
    +
  • Note: + We are working on making this more efficient when there are lots of images and we are working on the feature to display video thumbnails. +
  • +
+

You can select an image or video from the upper right and view the video or image in the lower right. Video will be played with sound.

+ + +

Reporting

+

+ A final report can be generated that will include all analysis results. + Use the "Generate Report" button to create this. + It will create an HTML or XLS report in the Reports folder of the case folder. + If you forgot the location of your case folder, you can determine it using the "Case Properties" option in the "File" menu. + There is also an option to export report files to a separate folder outside of the case folder. +

+ +
+

Copyright © 2012-2013 Basis Technology.

+

+ This work is licensed under a + Creative Commons Attribution-Share Alike 3.0 United States License. +

+ + diff --git a/docs/doxygen/needs_a_home.dox b/docs/doxygen/needs_a_home.dox index c6badf6b36..b0a2b42d4f 100755 --- a/docs/doxygen/needs_a_home.dox +++ b/docs/doxygen/needs_a_home.dox @@ -1,30 +1,30 @@ - - - -The component is by default registered with the ingest manager as an ingest event listener. -The viewer first loads all the viewer-supported data currently in the blackboard when Autopsy starts. -During the ingest process the viewer receives events from ingest modules -(relayed by ingest manager) and it selectively refreshes parts of the tree providing real-time updates to the user. -When ingest is completed, the viewer responds to the final ingest data event generated by the ingest manager, -and performs a final refresh of all viewer-supported data in the blackboard. - - -Node content support capabilities are registered in the node's Lookup. - - - - -\section design_data_flow Data Flow - -\subsection design_data_flow_create Creating Nodes in DataExplorer - -Data flows between the UI zones using a NetBeans node. The DataExplorer modules create the NetBeans nodes. They query the SQLite database or do whatever they want to identify the set of files that are of interest. They create the NetBeans nodes based on Sleuthkit data model objects. See the org.sleuthkit.autopsy.datamodel package for more details on this. - -\subsection design_data_flow_toResult Getting Nodes to DataResult - -Each DataExplorer TopComponent is responsible for creating its own DataResult TopComponent to display its results. It can choose to re-use the same TopComponent for multiple searches (as DirectoryTree does) or it can choose to make a new one each time (as FileSearch does). The setNode() method on the DataResult object is used to set the root node to display. A dummy root node must be created as the parent if a parent does not already exist. - -The DataExplorer is responsible for setting the double-click and right-click actions associated with the node. The default single click action is to pass data to DataContent. To override this, you must create a new DataResultViewer instance that overrides the propertyChange() method. The DataExplorer adds actions to wrapping the node in a FilterNode variant. The FilterNode then defines the actions for the node by overriding the getPreferredAction() and getActions() methods. As an example, org.sleuthkit.autopsy.directorytree.DataResultFilterNode and org.sleuthkit.autopsy.directorytree.DataResultFilterChildren wraps the nodes that are passed over by the DirectoryTree DataExplorer. - -DataResult can send data back to its DataExplorer by making a custom action that looks up it's instance (DataExplorer.getInstance()). + + + +The component is by default registered with the ingest manager as an ingest event listener. +The viewer first loads all the viewer-supported data currently in the blackboard when Autopsy starts. +During the ingest process the viewer receives events from ingest modules +(relayed by ingest manager) and it selectively refreshes parts of the tree providing real-time updates to the user. +When ingest is completed, the viewer responds to the final ingest data event generated by the ingest manager, +and performs a final refresh of all viewer-supported data in the blackboard. + + +Node content support capabilities are registered in the node's Lookup. + + + + +\section design_data_flow Data Flow + +\subsection design_data_flow_create Creating Nodes in DataExplorer + +Data flows between the UI zones using a NetBeans node. The DataExplorer modules create the NetBeans nodes. They query the SQLite database or do whatever they want to identify the set of files that are of interest. They create the NetBeans nodes based on Sleuthkit data model objects. See the org.sleuthkit.autopsy.datamodel package for more details on this. + +\subsection design_data_flow_toResult Getting Nodes to DataResult + +Each DataExplorer TopComponent is responsible for creating its own DataResult TopComponent to display its results. It can choose to re-use the same TopComponent for multiple searches (as DirectoryTree does) or it can choose to make a new one each time (as FileSearch does). The setNode() method on the DataResult object is used to set the root node to display. A dummy root node must be created as the parent if a parent does not already exist. + +The DataExplorer is responsible for setting the double-click and right-click actions associated with the node. The default single click action is to pass data to DataContent. To override this, you must create a new DataResultViewer instance that overrides the propertyChange() method. The DataExplorer adds actions to wrapping the node in a FilterNode variant. The FilterNode then defines the actions for the node by overriding the getPreferredAction() and getActions() methods. As an example, org.sleuthkit.autopsy.directorytree.DataResultFilterNode and org.sleuthkit.autopsy.directorytree.DataResultFilterChildren wraps the nodes that are passed over by the DirectoryTree DataExplorer. + +DataResult can send data back to its DataExplorer by making a custom action that looks up it's instance (DataExplorer.getInstance()). diff --git a/docs/doxygen/workflow.dox b/docs/doxygen/workflow.dox index e7e3b9c882..c9bdf78486 100644 --- a/docs/doxygen/workflow.dox +++ b/docs/doxygen/workflow.dox @@ -1,53 +1,53 @@ -/*! \page workflow_page General Workflow and Design - -\section design_overview Overview -This section outlines the internal Autopsy design from the typical analysis work flow perspective. -This page is organized based on these phases: -- A Case is created. -- Images are added to the case and ingest modules are run. -- Results are manually reviewed and searched. -- Reports are generated. - -\section design_case Creating a Case -The first step in Autopsy work flow is creating a case. This is done in the org.sleuthkit.autopsy.casemodule package (see \ref casemodule_overview for details). This module contains the wizards needed and deals with how to store the information. You should not need to do much modifications in this package. But, you will want to use the org.sleuthkit.autopsy.casemodule.Case object to access all data related to this case. - - -\section design_image Adding an Image and Running Ingest Modules - -After case is created, one or more disk images can be added to the case. There is a wizard to guide that process and it is located in the org.sleuthkit.autopsy.casemodule package. Refer to the package section \ref casemodule_add_image for more details on the wizard. Most developers will not need to touch this code though. An important concept though is that adding an image to a case means that Autopsy uses The Sleuth Kit to enumerate all of the files in the file system and make a database entry for them in the embedded SQLite database that was created for the case. The database will be used for all further analysis. - -After image has been added to the case, the user can select one or more ingest modules to be executed on the image. Ingest modules focus on a specific type of analysis task and run in the background. They either analyze the entire disk image or individual files. The user will see the results from the modules in the result tree and in the ingest inbox. - -The org.sleuthkit.autopsy.ingest package provides the basic infrastructure for the ingest module management. - -If you want to develop a module that analyzes drive data, then this is probably the type of module that you want to build. See \ref mod_ingest_page for more details on making an ingest module. - - -\section design_view Viewing Results - -The UI has three main areas. The tree on the left-hand side, the result viewers in the upper right, and the content viewers in the lower right. Data passes between these areas by encapsulating them in Netbeans Node objects (see org.openide.nodes.Node). These allow Autopsy to generically handle all types of data. The org.sleuthkit.autopsy.datamodel package wraps the generic org.sleuthkit.datamodel Sleuth Kit objects as Netbeans Nodes. - -Nodes are modeled in a parent-child hierarchy with other nodes. All data within a Case is represented in a hierarchy with the disk images being one level below the case and volumes and such below the image. - -The tree on the left hand-side shows the analysis results. -Its contents are populated from the central database. -This is where you can browse the file system contents and see the results from the blackboard. - -The tree is implemented in the org.sleuthkit.autopsy.directorytree package. - -The area in the upper right is the result viewer area. When a node is selected from the tree, the node and its children are sent to this area. This area is used to view a set of nodes. The viewer is itself a framework with modules that display the data in different layouts. For example, the standard version comes with a table viewer and a thumbnail viewer. Refer to \ref mod_result_page for details on building a data result module. - -When an item is selected from the result viewer area, it is passed to the bottom right content viewers. It too is a framework with many modules that know how to show information about a specific file in different ways. For example, there are viewers that show the data in a hex dump format, extract the strings, and display pictures and movies. -See \ref mod_content_page for details on building new content viewers. - -\section design_report Report generation - -When ingest is complete, the user can generate reports. -There is a reporting framework to enable many different formats. Autopsy currently comes with generic html, xml and Excel reports. See the org.sleuthkit.autopsy.report package for details on the framework and -\ref mod_report_page for details on building a new report module. - - - - - -*/ +/*! \page workflow_page General Workflow and Design + +\section design_overview Overview +This section outlines the internal Autopsy design from the typical analysis work flow perspective. +This page is organized based on these phases: +- A Case is created. +- Images are added to the case and ingest modules are run. +- Results are manually reviewed and searched. +- Reports are generated. + +\section design_case Creating a Case +The first step in Autopsy work flow is creating a case. This is done in the org.sleuthkit.autopsy.casemodule package (see \ref casemodule_overview for details). This module contains the wizards needed and deals with how to store the information. You should not need to do much modifications in this package. But, you will want to use the org.sleuthkit.autopsy.casemodule.Case object to access all data related to this case. + + +\section design_image Adding an Image and Running Ingest Modules + +After case is created, one or more disk images can be added to the case. There is a wizard to guide that process and it is located in the org.sleuthkit.autopsy.casemodule package. Refer to the package section \ref casemodule_add_image for more details on the wizard. Most developers will not need to touch this code though. An important concept though is that adding an image to a case means that Autopsy uses The Sleuth Kit to enumerate all of the files in the file system and make a database entry for them in the embedded SQLite database that was created for the case. The database will be used for all further analysis. + +After image has been added to the case, the user can select one or more ingest modules to be executed on the image. Ingest modules focus on a specific type of analysis task and run in the background. They either analyze the entire disk image or individual files. The user will see the results from the modules in the result tree and in the ingest inbox. + +The org.sleuthkit.autopsy.ingest package provides the basic infrastructure for the ingest module management. + +If you want to develop a module that analyzes drive data, then this is probably the type of module that you want to build. See \ref mod_ingest_page for more details on making an ingest module. + + +\section design_view Viewing Results + +The UI has three main areas. The tree on the left-hand side, the result viewers in the upper right, and the content viewers in the lower right. Data passes between these areas by encapsulating them in Netbeans Node objects (see org.openide.nodes.Node). These allow Autopsy to generically handle all types of data. The org.sleuthkit.autopsy.datamodel package wraps the generic org.sleuthkit.datamodel Sleuth Kit objects as Netbeans Nodes. + +Nodes are modeled in a parent-child hierarchy with other nodes. All data within a Case is represented in a hierarchy with the disk images being one level below the case and volumes and such below the image. + +The tree on the left hand-side shows the analysis results. +Its contents are populated from the central database. +This is where you can browse the file system contents and see the results from the blackboard. + +The tree is implemented in the org.sleuthkit.autopsy.directorytree package. + +The area in the upper right is the result viewer area. When a node is selected from the tree, the node and its children are sent to this area. This area is used to view a set of nodes. The viewer is itself a framework with modules that display the data in different layouts. For example, the standard version comes with a table viewer and a thumbnail viewer. Refer to \ref mod_result_page for details on building a data result module. + +When an item is selected from the result viewer area, it is passed to the bottom right content viewers. It too is a framework with many modules that know how to show information about a specific file in different ways. For example, there are viewers that show the data in a hex dump format, extract the strings, and display pictures and movies. +See \ref mod_content_page for details on building new content viewers. + +\section design_report Report generation + +When ingest is complete, the user can generate reports. +There is a reporting framework to enable many different formats. Autopsy currently comes with generic html, xml and Excel reports. See the org.sleuthkit.autopsy.report package for details on the framework and +\ref mod_report_page for details on building a new report module. + + + + + +*/ From 5622b9d5cddee39ddbb40ee7c1727cde37de1e71 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Tue, 26 Nov 2013 18:02:08 -0500 Subject: [PATCH 04/12] Reworked hash database create and import dialogs --- .../autopsy/hashdatabase/Bundle.properties | 30 +-- .../hashdatabase/HashDbConfigPanel.form | 22 +- .../hashdatabase/HashDbConfigPanel.java | 27 +-- .../HashDbCreateDatabaseDialog.form | 114 ++++------ .../HashDbCreateDatabaseDialog.java | 213 ++++++++---------- .../HashDbImportDatabaseDialog.form | 134 ++++++----- .../HashDbImportDatabaseDialog.java | 188 +++++++++------- 7 files changed, 363 insertions(+), 365 deletions(-) diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/Bundle.properties b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/Bundle.properties index 28c94254f8..23c89f4231 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/Bundle.properties +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/Bundle.properties @@ -22,26 +22,16 @@ HashDbSearchPanel.errorField.text=Error: Not all files have been hashed. HashDbSearchPanel.saveBox.text=Remember Hashes HashDbSearchPanel.cancelButton.text=Cancel OpenIDE-Module-Short-Description=Hash Database Ingest Module and hash db tools -HashDbImportDatabaseDialog.sendInboxMessagesCheckbox.text=Enable sending messages to inbox during ingest -HashDbImportDatabaseDialog.useForIngestCheckbox.text=Enable for ingest HashDbImportDatabaseDialog.jLabel1.text=Hash Set Name: -HashDbImportDatabaseDialog.databaseNameTextField.text= HashDbImportDatabaseDialog.databasePathTextField.text= -HashDbImportDatabaseDialog.browseButton.text=Browse +HashDbImportDatabaseDialog.browseButton.text=Browse... HashDbImportDatabaseDialog.knownBadRadioButton.text=Known Bad HashDbImportDatabaseDialog.jLabel2.text=Type of database: HashDbImportDatabaseDialog.okButton.text=OK HashDbImportDatabaseDialog.cancelButton.text=Cancel -HashDbCreateDatabaseDialog.jLabel2.text=Type of database: +HashDbCreateDatabaseDialog.jLabel2.text=Type: HashDbCreateDatabaseDialog.knownBadRadioButton.text=Known Bad -HashDbCreateDatabaseDialog.browseButton.text=Browse -HashDbCreateDatabaseDialog.databasePathTextField.text= HashDbCreateDatabaseDialog.cancelButton.text=Cancel -HashDbCreateDatabaseDialog.sendInboxMessagesCheckbox.text=Enable sending messages to inbox during ingest -HashDbCreateDatabaseDialog.okButton.text=OK -HashDbCreateDatabaseDialog.useForIngestCheckbox.text=Enable for ingest -HashDbCreateDatabaseDialog.jLabel1.text=Hash Set Name: -HashDbCreateDatabaseDialog.databaseNameTextField.text= HashDbConfigPanel.nameLabel.text=Hash Set Name: HashDbConfigPanel.hashDbNameLabel.text=No database selected HashDbConfigPanel.hashDatabasesLabel.text=Hash Databases: @@ -58,8 +48,8 @@ HashDbConfigPanel.hashDbIndexStatusLabel.text=No database selected HashDbConfigPanel.hashDbTypeLabel.text=No database selected HashDbConfigPanel.indexButton.text=Index HashDbConfigPanel.indexLabel.text=Index Status: -HashDbConfigPanel.showInboxMessagesCheckBox.text=Enable sending messages to inbox during ingest -HashDbConfigPanel.useForIngestCheckbox.text=Enable for ingest +HashDbConfigPanel.showInboxMessagesCheckBox.text=Send ingest messages +HashDbConfigPanel.useForIngestCheckbox.text=Search during ingest HashDbConfigPanel.informationLabel.text=Information HashDbSimpleConfigPanel.calcHashesButton.text=Calculate hashes even if no hash database is selected HashDbConfigPanel.newDatabaseButton.text=Create Database @@ -75,4 +65,14 @@ ModalNoButtons.CANCEL_BUTTON.text=Cancel HashDbSimpleConfigPanel.knownBadHashDbsLabel.text=Enable known bad databases for ingest: HashDbSimpleConfigPanel.knownHashDbsLabel.text=Enable known hash databases for ingest: HashDbImportDatabaseDialog.knownRadioButton.text=Known (NSRL or other) -HashDbCreateDatabaseDialog.knownRadioButton.text=Known (NSRL or other) +HashDbCreateDatabaseDialog.knownRadioButton.text=Known +HashDbCreateDatabaseDialog.jLabel1.text=Hash Set Name: +HashDbCreateDatabaseDialog.saveAsButton.text=Save As... +HashDbCreateDatabaseDialog.hashSetNameTextField.text= +HashDbImportDatabaseDialog.jLabel3.text=Database Path: +HashDbCreateDatabaseDialog.searchDuringIngestCheckbox.text=Search during ingest +HashDbCreateDatabaseDialog.searchDuringIngestCheckbox.toolTipText= +HashDbCreateDatabaseDialog.sendIngestMessagesCheckbox.text=Send ingest messages +HashDbImportDatabaseDialog.searchDuringIngestCheckbox.text=Search during ingest +HashDbImportDatabaseDialog.sendIngestMessagesCheckbox.text=Send ingest messages +HashDbImportDatabaseDialog.hashSetNameTextField.text= diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbConfigPanel.form b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbConfigPanel.form index cb38db0b8b..8bf5a3a450 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbConfigPanel.form +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbConfigPanel.form @@ -73,13 +73,6 @@ - - - - - - - @@ -96,6 +89,21 @@ + + + + + + + + + + + + + + + diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbConfigPanel.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbConfigPanel.java index aa4a09c3dd..32d87e0532 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbConfigPanel.java +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbConfigPanel.java @@ -168,7 +168,7 @@ public final class HashDbConfigPanel extends javax.swing.JPanel implements Optio indexButton.setEnabled(false); } - // Diable the indexing button if ingest is in progress. + // Disable the indexing button if ingest is in progress. if (ingestIsRunning) { indexButton.setEnabled(false); } @@ -230,10 +230,6 @@ public final class HashDbConfigPanel extends javax.swing.JPanel implements Optio hashSetManager.save(); } - public void discard() { - HashDbManager.getInstance().loadLastSavedConfiguration(); - } - /** * Removes a list of HashDbs from the dialog panel that do not have a companion -md5.idx file. * Occurs when user clicks "No" to the dialog popup box. @@ -570,12 +566,6 @@ public final class HashDbConfigPanel extends javax.swing.JPanel implements Optio .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() .addGap(10, 10, 10) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(nameLabel) - .addGap(53, 53, 53) - .addComponent(hashDbNameLabel)) - .addComponent(useForIngestCheckbox) - .addComponent(showInboxMessagesCheckBox) .addGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(locationLabel) @@ -588,7 +578,18 @@ public final class HashDbConfigPanel extends javax.swing.JPanel implements Optio .addComponent(hashDbTypeLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 241, Short.MAX_VALUE) .addComponent(hashDbLocationLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(indexPathLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(hashDbIndexStatusLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))))) + .addComponent(hashDbIndexStatusLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(nameLabel) + .addGap(53, 53, 53) + .addComponent(hashDbNameLabel)) + .addComponent(useForIngestCheckbox) + .addGroup(layout.createSequentialGroup() + .addGap(21, 21, 21) + .addComponent(showInboxMessagesCheckBox))) + .addGap(0, 0, Short.MAX_VALUE)))) .addGroup(layout.createSequentialGroup() .addComponent(optionsLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) @@ -693,7 +694,7 @@ public final class HashDbConfigPanel extends javax.swing.JPanel implements Optio }//GEN-LAST:event_indexButtonActionPerformed private void deleteDatabaseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteDatabaseButtonActionPerformed - if (JOptionPane.showConfirmDialog(null, "This will remove the hash database entry globally (for all Cases). Do you want to proceed? ", "Deleting a Hash Database Entry", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.YES_OPTION) { + if (JOptionPane.showConfirmDialog(null, "This will remove the hash database for all cases. Do you want to proceed? ", "Delete Hash Database from Configuration", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.YES_OPTION) { HashDb hashDb = ((HashSetTable)hashSetTable).getSelection(); if (hashDb != null) { hashSetManager.removeHashSet(hashDb); diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbCreateDatabaseDialog.form b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbCreateDatabaseDialog.form index 56cbcf2e03..98c1676e0a 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbCreateDatabaseDialog.form +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbCreateDatabaseDialog.form @@ -27,65 +27,55 @@ - + - - - - - - - - - - - - - - - - - - + + + - - - - - - - - - + - - + + + + + + + + + + + + + - + + + + + + + + - - - - - - + @@ -94,28 +84,28 @@ - + - - + + - + - + - + - + - + @@ -128,23 +118,6 @@ - - - - - - - - - - - - - - - - - @@ -179,10 +152,10 @@ - + - + @@ -193,22 +166,25 @@ - + - + + + + - + - + - + diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbCreateDatabaseDialog.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbCreateDatabaseDialog.java index cadbced05d..88d1ff271e 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbCreateDatabaseDialog.java +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbCreateDatabaseDialog.java @@ -27,49 +27,42 @@ import java.util.logging.Level; import org.sleuthkit.autopsy.coreutils.Logger; import javax.swing.JFileChooser; import javax.swing.JOptionPane; -import javax.swing.filechooser.FileNameExtensionFilter; import org.apache.commons.io.FilenameUtils; import org.sleuthkit.autopsy.hashdatabase.HashDb.KnownFilesType; import org.sleuthkit.datamodel.TskCoreException; -final class HashDbCreateDatabaseDialog extends javax.swing.JDialog { - private JFileChooser fileChooser; +/** + * Instances of this class allow a user to create a new hash database. + */ +final class HashDbCreateDatabaseDialog extends javax.swing.JDialog { + + private static String HASH_DATABASE_FILE_EXTENSON = ".kdb"; + private JFileChooser fileChooser = null; private HashDb newHashDb = null; HashDbCreateDatabaseDialog() { super(new javax.swing.JFrame(), "Create Hash Database", true); - setResizable(false); fileChooser = new JFileChooser() { @Override public void approveSelection() { + // The hash database file the user chooses must be a new file with a hash database file extension. File selectedFile = getSelectedFile(); - if (!FilenameUtils.getExtension(selectedFile.getName()).equalsIgnoreCase("kdb")) { - if (JOptionPane.showConfirmDialog(this, "The file must have a .kdb extension.", "File Name Error", JOptionPane.OK_CANCEL_OPTION) == JOptionPane.CANCEL_OPTION) { - cancelSelection(); - } + if (!FilenameUtils.getExtension(selectedFile.getName()).equalsIgnoreCase("kdb") && JOptionPane.showConfirmDialog(this, "The hash database file must have a .kdb extension.", "File Name Error", JOptionPane.OK_CANCEL_OPTION) == JOptionPane.CANCEL_OPTION) { + cancelSelection(); return; } - if (selectedFile.exists()) { - int r = JOptionPane.showConfirmDialog(this, "A file with this name already exists. Please enter a new filename.", "Existing File", JOptionPane.OK_CANCEL_OPTION); - if (r == JOptionPane.CANCEL_OPTION) { - cancelSelection(); - } + if (selectedFile.exists() && JOptionPane.showConfirmDialog(this, "A file with this name already exists. Please enter a new filename.", "Existing File", JOptionPane.OK_CANCEL_OPTION) == JOptionPane.CANCEL_OPTION) { + cancelSelection(); return; } super.approveSelection(); } }; - initComponents(); - customizeComponents(); - } - - void customizeComponents() { - fileChooser.setDragEnabled(false); fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); - String[] EXTENSION = new String[] { "txt", "kdb", "idx", "hash", "Hash", "hsh"}; - FileNameExtensionFilter filter = new FileNameExtensionFilter("Hash Database File", EXTENSION); - fileChooser.setFileFilter(filter); + fileChooser.setDragEnabled(false); fileChooser.setMultiSelectionEnabled(false); + + initComponents(); } HashDb doDialog() { @@ -93,24 +86,22 @@ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog { private void initComponents() { buttonGroup1 = new javax.swing.ButtonGroup(); - okButton = new javax.swing.JButton(); + saveAsButton = new javax.swing.JButton(); cancelButton = new javax.swing.JButton(); - databasePathTextField = new javax.swing.JTextField(); - browseButton = new javax.swing.JButton(); knownRadioButton = new javax.swing.JRadioButton(); knownBadRadioButton = new javax.swing.JRadioButton(); jLabel1 = new javax.swing.JLabel(); - databaseNameTextField = new javax.swing.JTextField(); + hashSetNameTextField = new javax.swing.JTextField(); jLabel2 = new javax.swing.JLabel(); - useForIngestCheckbox = new javax.swing.JCheckBox(); - sendInboxMessagesCheckbox = new javax.swing.JCheckBox(); + searchDuringIngestCheckbox = new javax.swing.JCheckBox(); + sendIngestMessagesCheckbox = new javax.swing.JCheckBox(); setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); - org.openide.awt.Mnemonics.setLocalizedText(okButton, org.openide.util.NbBundle.getMessage(HashDbCreateDatabaseDialog.class, "HashDbCreateDatabaseDialog.okButton.text")); // NOI18N - okButton.addActionListener(new java.awt.event.ActionListener() { + org.openide.awt.Mnemonics.setLocalizedText(saveAsButton, org.openide.util.NbBundle.getMessage(HashDbCreateDatabaseDialog.class, "HashDbCreateDatabaseDialog.saveAsButton.text")); // NOI18N + saveAsButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { - okButtonActionPerformed(evt); + saveAsButtonActionPerformed(evt); } }); @@ -121,15 +112,6 @@ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog { } }); - databasePathTextField.setText(org.openide.util.NbBundle.getMessage(HashDbCreateDatabaseDialog.class, "HashDbCreateDatabaseDialog.databasePathTextField.text")); // NOI18N - - org.openide.awt.Mnemonics.setLocalizedText(browseButton, org.openide.util.NbBundle.getMessage(HashDbCreateDatabaseDialog.class, "HashDbCreateDatabaseDialog.browseButton.text")); // NOI18N - browseButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - browseButtonActionPerformed(evt); - } - }); - buttonGroup1.add(knownRadioButton); org.openide.awt.Mnemonics.setLocalizedText(knownRadioButton, org.openide.util.NbBundle.getMessage(HashDbCreateDatabaseDialog.class, "HashDbCreateDatabaseDialog.knownRadioButton.text")); // NOI18N knownRadioButton.addActionListener(new java.awt.event.ActionListener() { @@ -149,20 +131,21 @@ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog { org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(HashDbCreateDatabaseDialog.class, "HashDbCreateDatabaseDialog.jLabel1.text")); // NOI18N - databaseNameTextField.setText(org.openide.util.NbBundle.getMessage(HashDbCreateDatabaseDialog.class, "HashDbCreateDatabaseDialog.databaseNameTextField.text")); // NOI18N + hashSetNameTextField.setText(org.openide.util.NbBundle.getMessage(HashDbCreateDatabaseDialog.class, "HashDbCreateDatabaseDialog.hashSetNameTextField.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(jLabel2, org.openide.util.NbBundle.getMessage(HashDbCreateDatabaseDialog.class, "HashDbCreateDatabaseDialog.jLabel2.text")); // NOI18N - useForIngestCheckbox.setSelected(true); - org.openide.awt.Mnemonics.setLocalizedText(useForIngestCheckbox, org.openide.util.NbBundle.getMessage(HashDbCreateDatabaseDialog.class, "HashDbCreateDatabaseDialog.useForIngestCheckbox.text")); // NOI18N - useForIngestCheckbox.addActionListener(new java.awt.event.ActionListener() { + searchDuringIngestCheckbox.setSelected(true); + org.openide.awt.Mnemonics.setLocalizedText(searchDuringIngestCheckbox, org.openide.util.NbBundle.getMessage(HashDbCreateDatabaseDialog.class, "HashDbCreateDatabaseDialog.searchDuringIngestCheckbox.text")); // NOI18N + searchDuringIngestCheckbox.setToolTipText(org.openide.util.NbBundle.getMessage(HashDbCreateDatabaseDialog.class, "HashDbCreateDatabaseDialog.searchDuringIngestCheckbox.toolTipText")); // NOI18N + searchDuringIngestCheckbox.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { - useForIngestCheckboxActionPerformed(evt); + searchDuringIngestCheckboxActionPerformed(evt); } }); - sendInboxMessagesCheckbox.setSelected(true); - org.openide.awt.Mnemonics.setLocalizedText(sendInboxMessagesCheckbox, org.openide.util.NbBundle.getMessage(HashDbCreateDatabaseDialog.class, "HashDbCreateDatabaseDialog.sendInboxMessagesCheckbox.text")); // NOI18N + sendIngestMessagesCheckbox.setSelected(true); + org.openide.awt.Mnemonics.setLocalizedText(sendIngestMessagesCheckbox, org.openide.util.NbBundle.getMessage(HashDbCreateDatabaseDialog.class, "HashDbCreateDatabaseDialog.sendIngestMessagesCheckbox.text")); // NOI18N javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); getContentPane().setLayout(layout); @@ -174,45 +157,40 @@ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog { .addGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() - .addGap(0, 0, Short.MAX_VALUE) - .addComponent(okButton) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(cancelButton)) - .addGroup(layout.createSequentialGroup() - .addComponent(databasePathTextField) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(browseButton)) + .addGap(21, 21, 21) + .addComponent(sendIngestMessagesCheckbox)) + .addComponent(searchDuringIngestCheckbox)) + .addGap(0, 0, Short.MAX_VALUE)) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jLabel2) .addGroup(layout.createSequentialGroup() .addComponent(jLabel1) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(databaseNameTextField)) + .addComponent(hashSetNameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 176, javax.swing.GroupLayout.PREFERRED_SIZE)) .addGroup(layout.createSequentialGroup() - .addComponent(jLabel2) - .addGap(0, 0, Short.MAX_VALUE))) - .addContainerGap()) - .addGroup(layout.createSequentialGroup() - .addGap(10, 10, 10) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(knownBadRadioButton) - .addComponent(knownRadioButton)) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(useForIngestCheckbox) - .addComponent(sendInboxMessagesCheckbox)) - .addGap(0, 135, Short.MAX_VALUE)))) + .addGap(20, 20, 20) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(knownRadioButton) + .addComponent(knownBadRadioButton)))) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(saveAsButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(cancelButton) + .addContainerGap()) ); + + layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {cancelButton, saveAsButton}); + layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() .addContainerGap() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(databasePathTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(browseButton)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(jLabel1) - .addComponent(databaseNameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(hashSetNameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jLabel2) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) @@ -220,96 +198,87 @@ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog { .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(knownBadRadioButton) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(useForIngestCheckbox) + .addComponent(searchDuringIngestCheckbox) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(sendInboxMessagesCheckbox) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(sendIngestMessagesCheckbox) + .addGap(18, 18, 18) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(okButton) + .addComponent(saveAsButton) .addComponent(cancelButton)) - .addContainerGap()) + .addContainerGap(12, Short.MAX_VALUE)) ); pack(); }// //GEN-END:initComponents - private void browseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_browseButtonActionPerformed - try { - fileChooser.setSelectedFile(new File("hash.kdb")); - if (fileChooser.showSaveDialog(this) == JFileChooser.APPROVE_OPTION) { - File databaseFile = fileChooser.getSelectedFile(); - databasePathTextField.setText(databaseFile.getCanonicalPath()); - databaseNameTextField.setText(FilenameUtils.removeExtension(databaseFile.getName())); - if (databaseNameTextField.getText().toLowerCase().contains("nsrl")) { - knownRadioButton.setSelected(true); - knownRadioButtonActionPerformed(null); - } - } - } - catch (IOException ex) { - Logger.getLogger(HashDbCreateDatabaseDialog.class.getName()).log(Level.WARNING, "Couldn't get selected file path.", ex); - } - }//GEN-LAST:event_browseButtonActionPerformed - private void knownRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_knownRadioButtonActionPerformed - sendInboxMessagesCheckbox.setSelected(false); - sendInboxMessagesCheckbox.setEnabled(false); + sendIngestMessagesCheckbox.setSelected(false); + sendIngestMessagesCheckbox.setEnabled(false); }//GEN-LAST:event_knownRadioButtonActionPerformed private void knownBadRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_knownBadRadioButtonActionPerformed - sendInboxMessagesCheckbox.setSelected(true); - sendInboxMessagesCheckbox.setEnabled(true); + sendIngestMessagesCheckbox.setSelected(true); + sendIngestMessagesCheckbox.setEnabled(true); }//GEN-LAST:event_knownBadRadioButtonActionPerformed private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed this.dispose(); }//GEN-LAST:event_cancelButtonActionPerformed - private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed - if(databasePathTextField.getText().isEmpty()) { - JOptionPane.showMessageDialog(this, "Database path cannot be empty"); + private void saveAsButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_saveAsButtonActionPerformed + if (hashSetNameTextField.getText().isEmpty()) { + JOptionPane.showMessageDialog(this, "A hash set name must be entered."); return; } - if(databaseNameTextField.getText().isEmpty()) { - JOptionPane.showMessageDialog(this, "Database name cannot be empty"); + + fileChooser.setSelectedFile(new File(hashSetNameTextField.getText() + HASH_DATABASE_FILE_EXTENSON)); + if (fileChooser.showSaveDialog(this) != JFileChooser.APPROVE_OPTION) { return; } KnownFilesType type; - if(knownRadioButton.isSelected()) { + if (knownRadioButton.isSelected()) { type = KnownFilesType.KNOWN; - } else { + } + else { type = KnownFilesType.KNOWN_BAD; } - + try { - newHashDb = HashDb.createHashDatabase(databaseNameTextField.getText(), databasePathTextField.getText(), useForIngestCheckbox.isSelected(), sendInboxMessagesCheckbox.isSelected(), type); + newHashDb = HashDb.createHashDatabase(hashSetNameTextField.getText(), fileChooser.getSelectedFile().getCanonicalPath(), searchDuringIngestCheckbox.isSelected(), sendIngestMessagesCheckbox.isSelected(), type); } + catch (IOException ex) { + Logger.getLogger(HashDbCreateDatabaseDialog.class.getName()).log(Level.WARNING, "Hash database creation error", ex); + JOptionPane.showMessageDialog(this, "Cannot create hash database file at the selected location."); + return; + } catch (TskCoreException ex) { Logger.getLogger(HashDbCreateDatabaseDialog.class.getName()).log(Level.SEVERE, "Hash database creation error", ex); JOptionPane.showMessageDialog(this, "Failed to create hash database."); return; } - - this.dispose(); - }//GEN-LAST:event_okButtonActionPerformed - private void useForIngestCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_useForIngestCheckboxActionPerformed - }//GEN-LAST:event_useForIngestCheckboxActionPerformed + dispose(); + }//GEN-LAST:event_saveAsButtonActionPerformed + + private void searchDuringIngestCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_searchDuringIngestCheckboxActionPerformed + sendIngestMessagesCheckbox.setEnabled(searchDuringIngestCheckbox.isSelected()); + if (!searchDuringIngestCheckbox.isSelected()) { + sendIngestMessagesCheckbox.setSelected(false); + } + }//GEN-LAST:event_searchDuringIngestCheckboxActionPerformed // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JButton browseButton; private javax.swing.ButtonGroup buttonGroup1; private javax.swing.JButton cancelButton; - private javax.swing.JTextField databaseNameTextField; - private javax.swing.JTextField databasePathTextField; + private javax.swing.JTextField hashSetNameTextField; private javax.swing.JLabel jLabel1; private javax.swing.JLabel jLabel2; private javax.swing.JRadioButton knownBadRadioButton; private javax.swing.JRadioButton knownRadioButton; - private javax.swing.JButton okButton; - private javax.swing.JCheckBox sendInboxMessagesCheckbox; - private javax.swing.JCheckBox useForIngestCheckbox; + private javax.swing.JButton saveAsButton; + private javax.swing.JCheckBox searchDuringIngestCheckbox; + private javax.swing.JCheckBox sendIngestMessagesCheckbox; // End of variables declaration//GEN-END:variables } diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbImportDatabaseDialog.form b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbImportDatabaseDialog.form index 5cb8ef9c34..69cd3b34c7 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbImportDatabaseDialog.form +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbImportDatabaseDialog.form @@ -30,47 +30,53 @@ - + - - - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - + + @@ -79,30 +85,38 @@ - + + - + - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - @@ -130,6 +144,7 @@ + @@ -179,10 +194,10 @@ - + - + @@ -193,22 +208,29 @@ - + - + - + - + - + + + + + + + + diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbImportDatabaseDialog.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbImportDatabaseDialog.java index d4cea65dbb..0b3635c9b3 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbImportDatabaseDialog.java +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbImportDatabaseDialog.java @@ -36,6 +36,7 @@ import org.apache.commons.io.FilenameUtils; */ final class HashDbImportDatabaseDialog extends javax.swing.JDialog { private JFileChooser fileChooser = new JFileChooser(); + private String selectedFilePath = ""; private HashDb selectedHashDb; HashDbImportDatabaseDialog() { @@ -65,6 +66,14 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog { return selectedHashDb; } + private static String shortenPath(String path) { + String shortenedPath = path; + if (shortenedPath.length() > 50){ + shortenedPath = shortenedPath.substring(0, 10 + shortenedPath.substring(10).indexOf(File.separator) + 1) + "..." + shortenedPath.substring((shortenedPath.length() - 20) + shortenedPath.substring(shortenedPath.length() - 20).indexOf(File.separator)); + } + return shortenedPath; + } + /** * This method is called from within the constructor to initialize the form. * WARNING: Do NOT modify this code. The content of this method is always @@ -82,10 +91,11 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog { knownRadioButton = new javax.swing.JRadioButton(); knownBadRadioButton = new javax.swing.JRadioButton(); jLabel1 = new javax.swing.JLabel(); - databaseNameTextField = new javax.swing.JTextField(); + hashSetNameTextField = new javax.swing.JTextField(); jLabel2 = new javax.swing.JLabel(); - useForIngestCheckbox = new javax.swing.JCheckBox(); - sendInboxMessagesCheckbox = new javax.swing.JCheckBox(); + searchDuringIngestCheckbox = new javax.swing.JCheckBox(); + sendIngestMessagesCheckbox = new javax.swing.JCheckBox(); + jLabel3 = new javax.swing.JLabel(); setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); @@ -103,6 +113,7 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog { } }); + databasePathTextField.setEditable(false); databasePathTextField.setText(org.openide.util.NbBundle.getMessage(HashDbImportDatabaseDialog.class, "HashDbImportDatabaseDialog.databasePathTextField.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(browseButton, org.openide.util.NbBundle.getMessage(HashDbImportDatabaseDialog.class, "HashDbImportDatabaseDialog.browseButton.text")); // NOI18N @@ -131,20 +142,22 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog { org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(HashDbImportDatabaseDialog.class, "HashDbImportDatabaseDialog.jLabel1.text")); // NOI18N - databaseNameTextField.setText(org.openide.util.NbBundle.getMessage(HashDbImportDatabaseDialog.class, "HashDbImportDatabaseDialog.databaseNameTextField.text")); // NOI18N + hashSetNameTextField.setText(org.openide.util.NbBundle.getMessage(HashDbImportDatabaseDialog.class, "HashDbImportDatabaseDialog.hashSetNameTextField.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(jLabel2, org.openide.util.NbBundle.getMessage(HashDbImportDatabaseDialog.class, "HashDbImportDatabaseDialog.jLabel2.text")); // NOI18N - useForIngestCheckbox.setSelected(true); - org.openide.awt.Mnemonics.setLocalizedText(useForIngestCheckbox, org.openide.util.NbBundle.getMessage(HashDbImportDatabaseDialog.class, "HashDbImportDatabaseDialog.useForIngestCheckbox.text")); // NOI18N - useForIngestCheckbox.addActionListener(new java.awt.event.ActionListener() { + searchDuringIngestCheckbox.setSelected(true); + org.openide.awt.Mnemonics.setLocalizedText(searchDuringIngestCheckbox, org.openide.util.NbBundle.getMessage(HashDbImportDatabaseDialog.class, "HashDbImportDatabaseDialog.searchDuringIngestCheckbox.text")); // NOI18N + searchDuringIngestCheckbox.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { - useForIngestCheckboxActionPerformed(evt); + searchDuringIngestCheckboxActionPerformed(evt); } }); - sendInboxMessagesCheckbox.setSelected(true); - org.openide.awt.Mnemonics.setLocalizedText(sendInboxMessagesCheckbox, org.openide.util.NbBundle.getMessage(HashDbImportDatabaseDialog.class, "HashDbImportDatabaseDialog.sendInboxMessagesCheckbox.text")); // NOI18N + sendIngestMessagesCheckbox.setSelected(true); + org.openide.awt.Mnemonics.setLocalizedText(sendIngestMessagesCheckbox, org.openide.util.NbBundle.getMessage(HashDbImportDatabaseDialog.class, "HashDbImportDatabaseDialog.sendIngestMessagesCheckbox.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(jLabel3, org.openide.util.NbBundle.getMessage(HashDbImportDatabaseDialog.class, "HashDbImportDatabaseDialog.jLabel3.text")); // NOI18N javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); getContentPane().setLayout(layout); @@ -156,60 +169,72 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog { .addGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() - .addGap(0, 0, Short.MAX_VALUE) - .addComponent(okButton) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(cancelButton)) + .addGap(21, 21, 21) + .addComponent(sendIngestMessagesCheckbox)) .addGroup(layout.createSequentialGroup() - .addComponent(databasePathTextField) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(browseButton)) - .addGroup(layout.createSequentialGroup() - .addComponent(jLabel1) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(databaseNameTextField)) - .addGroup(layout.createSequentialGroup() - .addComponent(jLabel2) - .addGap(0, 0, Short.MAX_VALUE))) - .addContainerGap()) - .addGroup(layout.createSequentialGroup() - .addGap(10, 10, 10) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(knownBadRadioButton) - .addComponent(knownRadioButton)) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addComponent(searchDuringIngestCheckbox) + .addGap(29, 29, 29))) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(okButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(cancelButton)) .addGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(useForIngestCheckbox) - .addComponent(sendInboxMessagesCheckbox)) - .addGap(0, 135, Short.MAX_VALUE)))) + .addComponent(jLabel2) + .addGroup(layout.createSequentialGroup() + .addGap(19, 19, 19) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(knownRadioButton) + .addComponent(knownBadRadioButton))) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) + .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup() + .addComponent(jLabel1) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(hashSetNameTextField)) + .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup() + .addComponent(jLabel3) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(databasePathTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 297, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(browseButton))) + .addGap(0, 0, Short.MAX_VALUE))) + .addContainerGap()) ); + + layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {cancelButton, okButton}); + layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(browseButton) .addComponent(databasePathTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(browseButton)) + .addComponent(jLabel3)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(jLabel1) - .addComponent(databaseNameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jLabel2) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(knownRadioButton) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(knownBadRadioButton) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(useForIngestCheckbox) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(sendInboxMessagesCheckbox) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(okButton) - .addComponent(cancelButton)) - .addContainerGap()) + .addComponent(hashSetNameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 119, Short.MAX_VALUE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(okButton) + .addComponent(cancelButton)) + .addContainerGap()) + .addGroup(layout.createSequentialGroup() + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(jLabel2) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(knownRadioButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(knownBadRadioButton) + .addGap(13, 13, 13) + .addComponent(searchDuringIngestCheckbox) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(sendIngestMessagesCheckbox) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))) ); pack(); @@ -219,27 +244,29 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog { if (fileChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { File databaseFile = fileChooser.getSelectedFile(); try { - databasePathTextField.setText(databaseFile.getCanonicalPath()); - databaseNameTextField.setText(FilenameUtils.removeExtension(databaseFile.getName())); - if (databaseNameTextField.getText().toLowerCase().contains("nsrl")) { + selectedFilePath = databaseFile.getCanonicalPath(); + databasePathTextField.setText(shortenPath(selectedFilePath)); + hashSetNameTextField.setText(FilenameUtils.removeExtension(databaseFile.getName())); + if (hashSetNameTextField.getText().toLowerCase().contains("nsrl")) { knownRadioButton.setSelected(true); knownRadioButtonActionPerformed(null); } } catch (IOException ex) { Logger.getLogger(HashDbImportDatabaseDialog.class.getName()).log(Level.SEVERE, "Failed to get path of selected database", ex); + JOptionPane.showMessageDialog(this, "Failed to get the path of the selected database."); } } }//GEN-LAST:event_browseButtonActionPerformed private void knownRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_knownRadioButtonActionPerformed - sendInboxMessagesCheckbox.setSelected(false); - sendInboxMessagesCheckbox.setEnabled(false); + sendIngestMessagesCheckbox.setSelected(false); + sendIngestMessagesCheckbox.setEnabled(false); }//GEN-LAST:event_knownRadioButtonActionPerformed private void knownBadRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_knownBadRadioButtonActionPerformed - sendInboxMessagesCheckbox.setSelected(true); - sendInboxMessagesCheckbox.setEnabled(true); + sendIngestMessagesCheckbox.setSelected(true); + sendIngestMessagesCheckbox.setEnabled(true); }//GEN-LAST:event_knownBadRadioButtonActionPerformed private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed @@ -247,32 +274,22 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog { }//GEN-LAST:event_cancelButtonActionPerformed private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed - if(databasePathTextField.getText().isEmpty()) { - JOptionPane.showMessageDialog(this, "Hash database file path cannot be empty."); + if(selectedFilePath.isEmpty()) { + JOptionPane.showMessageDialog(this, "A hash database file path must be selected."); return; } - if(databaseNameTextField.getText().isEmpty()) { - JOptionPane.showMessageDialog(this, "Hash set name cannot be empty."); + if(hashSetNameTextField.getText().isEmpty()) { + JOptionPane.showMessageDialog(this, "A hash set name must be entered.."); return; } - File file = new File(databasePathTextField.getText()); + File file = new File(selectedFilePath); if (!file.exists()) { - JOptionPane.showMessageDialog(this, "Selected hash database does not exist."); + JOptionPane.showMessageDialog(this, "The selected hash database does not exist."); return; } - - String filePath; - try { - filePath = file.getCanonicalPath(); - } - catch (IOException ex) { - Logger.getLogger(HashDbImportDatabaseDialog.class.getName()).log(Level.SEVERE, "Failed to get path of selected database", ex); - JOptionPane.showMessageDialog(this, "Failed to get path of selected database."); - return; - } - + KnownFilesType type; if (knownRadioButton.isSelected()) { type = KnownFilesType.KNOWN; @@ -282,32 +299,37 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog { } try { - selectedHashDb = HashDb.openHashDatabase(databaseNameTextField.getText(), filePath, useForIngestCheckbox.isSelected(), sendInboxMessagesCheckbox.isSelected(), type); + selectedHashDb = HashDb.openHashDatabase(hashSetNameTextField.getText(), selectedFilePath, searchDuringIngestCheckbox.isSelected(), sendIngestMessagesCheckbox.isSelected(), type); } catch (TskCoreException ex) { - Logger.getLogger(HashDbImportDatabaseDialog.class.getName()).log(Level.WARNING, "Failed to open hash database at " + filePath, ex); - JOptionPane.showMessageDialog(this, "Failed to import selected database.\nPlease verify that the selected file is a hash database."); + Logger.getLogger(HashDbImportDatabaseDialog.class.getName()).log(Level.WARNING, "Failed to open hash database at " + selectedFilePath, ex); + JOptionPane.showMessageDialog(this, "Failed to import the selected database.\nPlease verify that the selected file is a hash database."); return; } this.dispose(); }//GEN-LAST:event_okButtonActionPerformed - private void useForIngestCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_useForIngestCheckboxActionPerformed - }//GEN-LAST:event_useForIngestCheckboxActionPerformed + private void searchDuringIngestCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_searchDuringIngestCheckboxActionPerformed + sendIngestMessagesCheckbox.setEnabled(searchDuringIngestCheckbox.isSelected()); + if (!searchDuringIngestCheckbox.isSelected()) { + sendIngestMessagesCheckbox.setSelected(false); + } + }//GEN-LAST:event_searchDuringIngestCheckboxActionPerformed // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton browseButton; private javax.swing.ButtonGroup buttonGroup1; private javax.swing.JButton cancelButton; - private javax.swing.JTextField databaseNameTextField; private javax.swing.JTextField databasePathTextField; + private javax.swing.JTextField hashSetNameTextField; private javax.swing.JLabel jLabel1; private javax.swing.JLabel jLabel2; + private javax.swing.JLabel jLabel3; private javax.swing.JRadioButton knownBadRadioButton; private javax.swing.JRadioButton knownRadioButton; private javax.swing.JButton okButton; - private javax.swing.JCheckBox sendInboxMessagesCheckbox; - private javax.swing.JCheckBox useForIngestCheckbox; + private javax.swing.JCheckBox searchDuringIngestCheckbox; + private javax.swing.JCheckBox sendIngestMessagesCheckbox; // End of variables declaration//GEN-END:variables } From ebf7a293caeeb5e08b074a1786178ece72382165 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Wed, 27 Nov 2013 11:27:51 -0500 Subject: [PATCH 05/12] Mande enabled state of hash db config components in sync with file ingest --- HashDatabase/nbproject/project.xml | 6 +- .../AddContentToHashDbAction.java | 112 +++++++++--------- .../autopsy/hashdatabase/Bundle.properties | 2 +- .../hashdatabase/HashDbConfigPanel.form | 10 +- .../hashdatabase/HashDbConfigPanel.java | 53 ++++++--- 5 files changed, 101 insertions(+), 82 deletions(-) diff --git a/HashDatabase/nbproject/project.xml b/HashDatabase/nbproject/project.xml index 9cfb151e4c..40499aa189 100644 --- a/HashDatabase/nbproject/project.xml +++ b/HashDatabase/nbproject/project.xml @@ -95,5 +95,9 @@ org.sleuthkit.autopsy.hashdatabase + + openide + org + - \ No newline at end of file + diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/AddContentToHashDbAction.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/AddContentToHashDbAction.java index fca2ac8ab0..1daa4501a1 100755 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/AddContentToHashDbAction.java +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/AddContentToHashDbAction.java @@ -24,7 +24,6 @@ import java.util.Collection; import java.util.List; import java.util.logging.Level; import javax.swing.AbstractAction; -import javax.swing.Action; import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.JOptionPane; @@ -37,92 +36,94 @@ import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.TskCoreException; /** - * Instances of this Action allow users to content to a hash database. + * Instances of this Action allow users to content to a hash database. */ -public class AddContentToHashDbAction extends AbstractAction implements Presenter.Popup { - // This class is a singleton to support multi-selection of nodes, since - // org.openide.nodes.NodeOp.findActions(Node[] nodes) will only pick up an Action if every - // node in the array returns a reference to the same action object from Node.getActions(boolean). +final class AddContentToHashDbAction extends AbstractAction implements Presenter.Popup { private static AddContentToHashDbAction instance; - private static String SINGLE_SELECTION_NAME = "Add file to hash database"; - private static String MULTIPLE_SELECTION_NAME = "Add files to hash database"; - private String menuText; - + + /** + * AddContentToHashDbAction is a singleton to support multi-selection of nodes, since + * org.openide.nodes.NodeOp.findActions(Node[] nodes) will only pick up an Action from a node + * if every node in the nodes array returns a reference to the same action object from + * Node.getActions(boolean). + */ public static synchronized AddContentToHashDbAction getInstance() { if (null == instance) { instance = new AddContentToHashDbAction(); - } - - instance.setEnabled(true); - instance.putValue(Action.NAME, SINGLE_SELECTION_NAME); - instance.menuText = SINGLE_SELECTION_NAME; - - // Disable the action if file ingest is in progress. - IngestConfigurator ingestConfigurator = Lookup.getDefault().lookup(IngestConfigurator.class); - if (null != ingestConfigurator && ingestConfigurator.isIngestRunning()) { - instance.setEnabled(false); - } - - // Set the name of the action based on the selected content and disable the action if there is - // selected content without an MD5 hash. - Collection selectedFiles = Utilities.actionsGlobalContext().lookupAll(AbstractFile.class); - if (selectedFiles.size() > 1) { - instance.putValue(Action.NAME, MULTIPLE_SELECTION_NAME); - instance.menuText = MULTIPLE_SELECTION_NAME; - } - if (selectedFiles.isEmpty()) { - instance.setEnabled(false); - } - else { - for (AbstractFile file : selectedFiles) { - if (null == file.getMd5Hash()) { - instance.setEnabled(false); - break; - } - } - } - + } return instance; } private AddContentToHashDbAction() { - super(SINGLE_SELECTION_NAME); } @Override public JMenuItem getPopupPresenter() { - return new AddContentToHashDbMenu(menuText); + return new AddContentToHashDbMenu(); } @Override public void actionPerformed(ActionEvent event) { } - private class AddContentToHashDbMenu extends JMenu { - AddContentToHashDbMenu(String menuText) { - super(menuText); + // Instances of this class are used to implement the a pop up menu for this + // action. + private final class AddContentToHashDbMenu extends JMenu { + private final static String SINGLE_SELECTION_NAME = "Add file to hash database"; + private final static String MULTIPLE_SELECTION_NAME = "Add files to hash database"; + + AddContentToHashDbMenu() { + super(SINGLE_SELECTION_NAME); + + // Disable the menu if file ingest is in progress. + IngestConfigurator ingestConfigurator = Lookup.getDefault().lookup(IngestConfigurator.class); + if (null != ingestConfigurator && ingestConfigurator.isIngestRunning()) { + setEnabled(false); + return; + } + // Get any AbstractFile objects from the lookup of the currently focused top component. + final Collection selectedFiles = Utilities.actionsGlobalContext().lookupAll(AbstractFile.class); + if (selectedFiles.isEmpty()) { + setEnabled(false); + return; + } + else if (selectedFiles.size() > 1) { + setText(MULTIPLE_SELECTION_NAME); + } + + // Disable the menu if hashes have not been calculated. + for (AbstractFile file : selectedFiles) { + if (null == file.getMd5Hash()) { + setEnabled(false); + return; + } + } + // Get the current set of updateable hash databases and add each - // one as a menu item. - List hashDatabases = HashDbManager.getInstance().getUpdateableHashSets(); + // one to the menu as a separate menu item. Selecting a hash database + // adds the selected files to the selected database. + final List hashDatabases = HashDbManager.getInstance().getUpdateableHashSets(); if (!hashDatabases.isEmpty()) { for (final HashDb database : hashDatabases) { JMenuItem databaseItem = add(database.getHashSetName()); databaseItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - addContentToHashSet(database); + addFilesToHashSet(selectedFiles, database); } }); } } else { - JMenuItem empty = new JMenuItem("No hash databases"); + JMenuItem empty = new JMenuItem("No hash databases configured"); empty.setEnabled(false); add(empty); } - // Add a "New Hash Set..." menu item. + // Add a "New Hash Set..." menu item. Selecting this item invokes a + // a hash database creation dialog and adds the selected files to the + // the new database. addSeparator(); JMenuItem newHashSetItem = new JMenuItem("Create database..."); newHashSetItem.addActionListener(new ActionListener() { @@ -133,16 +134,15 @@ public class AddContentToHashDbAction extends AbstractAction implements Presente HashDbManager hashSetManager = HashDbManager.getInstance(); hashSetManager.addHashSet(hashDb); hashSetManager.save(); - addContentToHashSet(hashDb); + addFilesToHashSet(selectedFiles, hashDb); } } }); add(newHashSetItem); } - private void addContentToHashSet(HashDb hashSet) { - Collection selectedFiles = Utilities.actionsGlobalContext().lookupAll(AbstractFile.class); - for (AbstractFile file : selectedFiles) { + private void addFilesToHashSet(final Collection files, HashDb hashSet) { + for (AbstractFile file : files) { String md5Hash = file.getMd5Hash(); if (null != md5Hash) { try { @@ -154,7 +154,7 @@ public class AddContentToHashDbAction extends AbstractAction implements Presente } } else { - JOptionPane.showMessageDialog(null, "Unable to add the " + (selectedFiles.size() > 1 ? "files" : "file") + " to the hash database. Hashes have not been calculated. Please configure and run an appropriate ingest module.", "Add to Hash Database Error", JOptionPane.ERROR_MESSAGE); + JOptionPane.showMessageDialog(null, "Unable to add the " + (files.size() > 1 ? "files" : "file") + " to the hash database. Hashes have not been calculated. Please configure and run an appropriate ingest module.", "Add to Hash Database Error", JOptionPane.ERROR_MESSAGE); break; } } diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/Bundle.properties b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/Bundle.properties index 23c89f4231..75a109a7d1 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/Bundle.properties +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/Bundle.properties @@ -52,7 +52,6 @@ HashDbConfigPanel.showInboxMessagesCheckBox.text=Send ingest messages HashDbConfigPanel.useForIngestCheckbox.text=Search during ingest HashDbConfigPanel.informationLabel.text=Information HashDbSimpleConfigPanel.calcHashesButton.text=Calculate hashes even if no hash database is selected -HashDbConfigPanel.newDatabaseButton.text=Create Database HashDbConfigPanel.importDatabaseButton.text=Import Database HashDbConfigPanel.deleteDatabaseButton.text=Delete Database HashDbConfigPanel.indexPathLabelLabel.text=Index Path: @@ -76,3 +75,4 @@ HashDbCreateDatabaseDialog.sendIngestMessagesCheckbox.text=Send ingest messages HashDbImportDatabaseDialog.searchDuringIngestCheckbox.text=Search during ingest HashDbImportDatabaseDialog.sendIngestMessagesCheckbox.text=Send ingest messages HashDbImportDatabaseDialog.hashSetNameTextField.text= +HashDbConfigPanel.createDatabaseButton.text=Create Database diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbConfigPanel.form b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbConfigPanel.form index 8bf5a3a450..a63885b203 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbConfigPanel.form +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbConfigPanel.form @@ -119,7 +119,7 @@ - + @@ -192,7 +192,7 @@ - + @@ -398,13 +398,13 @@ - + - + @@ -417,7 +417,7 @@ - + diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbConfigPanel.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbConfigPanel.java index 32d87e0532..97afdc855c 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbConfigPanel.java +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbConfigPanel.java @@ -55,8 +55,19 @@ public final class HashDbConfigPanel extends javax.swing.JPanel implements Optio initComponents(); customizeComponents(); updateComponentsForNoSelection(); + + // Listen to the ingest modules to refresh the enabled/disabled state of + // the components in sync with file ingest. + IngestManager.addPropertyChangeListener(new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + if (isFileIngestStatusChangeEvent(evt)) { + updateComponents(); + } + } + }); } - + private void customizeComponents() { setName("Hash Set Configuration"); this.ingestWarningLabel.setVisible(false); @@ -108,8 +119,8 @@ public final class HashDbConfigPanel extends javax.swing.JPanel implements Optio optionsSeparator.setEnabled(false); // Update database action buttons. - newDatabaseButton.setEnabled(!ingestIsRunning); - importDatabaseButton.setEnabled(!ingestIsRunning); + createDatabaseButton.setEnabled(true); + importDatabaseButton.setEnabled(true); deleteDatabaseButton.setEnabled(false); // Update ingest in progress warning label. @@ -182,9 +193,9 @@ public final class HashDbConfigPanel extends javax.swing.JPanel implements Optio optionsSeparator.setEnabled(!ingestIsRunning && db.getUseForIngest() && db.getKnownFilesType().equals(HashDb.KnownFilesType.KNOWN_BAD)); // Update database action buttons. + createDatabaseButton.setEnabled(true); + importDatabaseButton.setEnabled(true); deleteDatabaseButton.setEnabled(!ingestIsRunning); - importDatabaseButton.setEnabled(!ingestIsRunning); - importDatabaseButton.setEnabled(!ingestIsRunning); // Update ingest in progress warning label. ingestWarningLabel.setVisible(ingestIsRunning); @@ -198,6 +209,10 @@ public final class HashDbConfigPanel extends javax.swing.JPanel implements Optio return shortenedPath; } + private boolean isFileIngestStatusChangeEvent(PropertyChangeEvent evt) { + return evt.getPropertyName().equals(IngestManager.IngestModuleEvent.STARTED.toString()) || evt.getPropertyName().equals(IngestManager.IngestModuleEvent.COMPLETED.toString()) || evt.getPropertyName().equals(IngestManager.IngestModuleEvent.STOPPED.toString()); + } + @Override public void load() { hashSetTable.clearSelection(); @@ -232,7 +247,7 @@ public final class HashDbConfigPanel extends javax.swing.JPanel implements Optio /** * Removes a list of HashDbs from the dialog panel that do not have a companion -md5.idx file. - * Occurs when user clicks "No" to the dialog popup box. + * Occurs when user clicks "No" to the dialog pop up box. * @param toRemove a list of HashDbs that are unindexed */ void removeThese(List toRemove) { @@ -430,7 +445,7 @@ public final class HashDbConfigPanel extends javax.swing.JPanel implements Optio optionsLabel = new javax.swing.JLabel(); informationSeparator = new javax.swing.JSeparator(); optionsSeparator = new javax.swing.JSeparator(); - newDatabaseButton = new javax.swing.JButton(); + createDatabaseButton = new javax.swing.JButton(); indexPathLabelLabel = new javax.swing.JLabel(); indexPathLabel = new javax.swing.JLabel(); @@ -532,14 +547,14 @@ public final class HashDbConfigPanel extends javax.swing.JPanel implements Optio org.openide.awt.Mnemonics.setLocalizedText(optionsLabel, org.openide.util.NbBundle.getMessage(HashDbConfigPanel.class, "HashDbConfigPanel.optionsLabel.text")); // NOI18N - newDatabaseButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/hashdatabase/new16.png"))); // NOI18N - org.openide.awt.Mnemonics.setLocalizedText(newDatabaseButton, org.openide.util.NbBundle.getMessage(HashDbConfigPanel.class, "HashDbConfigPanel.newDatabaseButton.text")); // NOI18N - newDatabaseButton.setMaximumSize(new java.awt.Dimension(140, 25)); - newDatabaseButton.setMinimumSize(new java.awt.Dimension(140, 25)); - newDatabaseButton.setPreferredSize(new java.awt.Dimension(140, 25)); - newDatabaseButton.addActionListener(new java.awt.event.ActionListener() { + createDatabaseButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/hashdatabase/new16.png"))); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(createDatabaseButton, org.openide.util.NbBundle.getMessage(HashDbConfigPanel.class, "HashDbConfigPanel.createDatabaseButton.text")); // NOI18N + createDatabaseButton.setMaximumSize(new java.awt.Dimension(140, 25)); + createDatabaseButton.setMinimumSize(new java.awt.Dimension(140, 25)); + createDatabaseButton.setPreferredSize(new java.awt.Dimension(140, 25)); + createDatabaseButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { - newDatabaseButtonActionPerformed(evt); + createDatabaseButtonActionPerformed(evt); } }); @@ -600,7 +615,7 @@ public final class HashDbConfigPanel extends javax.swing.JPanel implements Optio .addGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) .addComponent(deleteDatabaseButton, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE) - .addComponent(newDatabaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 137, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(createDatabaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 137, javax.swing.GroupLayout.PREFERRED_SIZE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(importDatabaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 133, javax.swing.GroupLayout.PREFERRED_SIZE))) .addGap(0, 0, Short.MAX_VALUE))) @@ -656,7 +671,7 @@ public final class HashDbConfigPanel extends javax.swing.JPanel implements Optio .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(importDatabaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(newDatabaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(createDatabaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(deleteDatabaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addContainerGap()) @@ -737,16 +752,17 @@ public final class HashDbConfigPanel extends javax.swing.JPanel implements Optio } }//GEN-LAST:event_importDatabaseButtonActionPerformed - private void newDatabaseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_newDatabaseButtonActionPerformed + private void createDatabaseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_createDatabaseButtonActionPerformed HashDb hashDb = new HashDbCreateDatabaseDialog().doDialog(); if (null != hashDb) { hashSetManager.addHashSet(hashDb); hashSetTableModel.refreshModel(); ((HashSetTable)hashSetTable).selectRowByName(hashDb.getHashSetName()); } - }//GEN-LAST:event_newDatabaseButtonActionPerformed + }//GEN-LAST:event_createDatabaseButtonActionPerformed // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton createDatabaseButton; private javax.swing.JButton deleteDatabaseButton; private javax.swing.JLabel hashDatabasesLabel; private javax.swing.JLabel hashDbIndexStatusLabel; @@ -769,7 +785,6 @@ public final class HashDbConfigPanel extends javax.swing.JPanel implements Optio private javax.swing.JScrollPane jScrollPane1; private javax.swing.JLabel locationLabel; private javax.swing.JLabel nameLabel; - private javax.swing.JButton newDatabaseButton; private javax.swing.JLabel optionsLabel; private javax.swing.JSeparator optionsSeparator; private javax.swing.JCheckBox showInboxMessagesCheckBox; From 39bc90c4e80a7c73a2f203c23688897ab24e3bff Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Wed, 27 Nov 2013 14:53:46 -0500 Subject: [PATCH 06/12] Made button text consistent with file chooser in hash db import dialog --- .../autopsy/hashdatabase/Bundle.properties | 2 +- .../HashDbImportDatabaseDialog.form | 41 ++++++++--------- .../HashDbImportDatabaseDialog.java | 46 +++++++++---------- 3 files changed, 42 insertions(+), 47 deletions(-) diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/Bundle.properties b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/Bundle.properties index 75a109a7d1..cf0d525e9c 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/Bundle.properties +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/Bundle.properties @@ -24,7 +24,6 @@ HashDbSearchPanel.cancelButton.text=Cancel OpenIDE-Module-Short-Description=Hash Database Ingest Module and hash db tools HashDbImportDatabaseDialog.jLabel1.text=Hash Set Name: HashDbImportDatabaseDialog.databasePathTextField.text= -HashDbImportDatabaseDialog.browseButton.text=Browse... HashDbImportDatabaseDialog.knownBadRadioButton.text=Known Bad HashDbImportDatabaseDialog.jLabel2.text=Type of database: HashDbImportDatabaseDialog.okButton.text=OK @@ -76,3 +75,4 @@ HashDbImportDatabaseDialog.searchDuringIngestCheckbox.text=Search during ingest HashDbImportDatabaseDialog.sendIngestMessagesCheckbox.text=Send ingest messages HashDbImportDatabaseDialog.hashSetNameTextField.text= HashDbConfigPanel.createDatabaseButton.text=Create Database +HashDbImportDatabaseDialog.openButton.text=Open... diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbImportDatabaseDialog.form b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbImportDatabaseDialog.form index 69cd3b34c7..627f4090ba 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbImportDatabaseDialog.form +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbImportDatabaseDialog.form @@ -36,10 +36,7 @@ - - - - + @@ -56,24 +53,24 @@ + + + + + - - - - - - - - - - - - + - + + + + + + - + + @@ -85,7 +82,7 @@ - + @@ -150,14 +147,14 @@ - + - + - + diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbImportDatabaseDialog.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbImportDatabaseDialog.java index 0b3635c9b3..2916b7a48b 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbImportDatabaseDialog.java +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbImportDatabaseDialog.java @@ -87,7 +87,7 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog { okButton = new javax.swing.JButton(); cancelButton = new javax.swing.JButton(); databasePathTextField = new javax.swing.JTextField(); - browseButton = new javax.swing.JButton(); + openButton = new javax.swing.JButton(); knownRadioButton = new javax.swing.JRadioButton(); knownBadRadioButton = new javax.swing.JRadioButton(); jLabel1 = new javax.swing.JLabel(); @@ -116,10 +116,10 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog { databasePathTextField.setEditable(false); databasePathTextField.setText(org.openide.util.NbBundle.getMessage(HashDbImportDatabaseDialog.class, "HashDbImportDatabaseDialog.databasePathTextField.text")); // NOI18N - org.openide.awt.Mnemonics.setLocalizedText(browseButton, org.openide.util.NbBundle.getMessage(HashDbImportDatabaseDialog.class, "HashDbImportDatabaseDialog.browseButton.text")); // NOI18N - browseButton.addActionListener(new java.awt.event.ActionListener() { + org.openide.awt.Mnemonics.setLocalizedText(openButton, org.openide.util.NbBundle.getMessage(HashDbImportDatabaseDialog.class, "HashDbImportDatabaseDialog.openButton.text")); // NOI18N + openButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { - browseButtonActionPerformed(evt); + openButtonActionPerformed(evt); } }); @@ -171,9 +171,7 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog { .addGroup(layout.createSequentialGroup() .addGap(21, 21, 21) .addComponent(sendIngestMessagesCheckbox)) - .addGroup(layout.createSequentialGroup() - .addComponent(searchDuringIngestCheckbox) - .addGap(29, 29, 29))) + .addComponent(searchDuringIngestCheckbox)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(okButton) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) @@ -185,20 +183,20 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog { .addGap(19, 19, 19) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(knownRadioButton) - .addComponent(knownBadRadioButton))) - .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) - .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup() - .addComponent(jLabel1) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(hashSetNameTextField)) - .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup() - .addComponent(jLabel3) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(databasePathTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 297, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addComponent(knownBadRadioButton)))) + .addGap(0, 307, Short.MAX_VALUE)) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup() + .addComponent(jLabel1) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(browseButton))) - .addGap(0, 0, Short.MAX_VALUE))) + .addComponent(hashSetNameTextField)) + .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup() + .addComponent(jLabel3) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(databasePathTextField))) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(openButton))) .addContainerGap()) ); @@ -209,7 +207,7 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog { .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(browseButton) + .addComponent(openButton) .addComponent(databasePathTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(jLabel3)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) @@ -240,7 +238,7 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog { pack(); }// //GEN-END:initComponents - private void browseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_browseButtonActionPerformed + private void openButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_openButtonActionPerformed if (fileChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { File databaseFile = fileChooser.getSelectedFile(); try { @@ -257,7 +255,7 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog { JOptionPane.showMessageDialog(this, "Failed to get the path of the selected database."); } } - }//GEN-LAST:event_browseButtonActionPerformed + }//GEN-LAST:event_openButtonActionPerformed private void knownRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_knownRadioButtonActionPerformed sendIngestMessagesCheckbox.setSelected(false); @@ -318,7 +316,6 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog { }//GEN-LAST:event_searchDuringIngestCheckboxActionPerformed // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JButton browseButton; private javax.swing.ButtonGroup buttonGroup1; private javax.swing.JButton cancelButton; private javax.swing.JTextField databasePathTextField; @@ -329,6 +326,7 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog { private javax.swing.JRadioButton knownBadRadioButton; private javax.swing.JRadioButton knownRadioButton; private javax.swing.JButton okButton; + private javax.swing.JButton openButton; private javax.swing.JCheckBox searchDuringIngestCheckbox; private javax.swing.JCheckBox sendIngestMessagesCheckbox; // End of variables declaration//GEN-END:variables From 4b3d51c32502d15bfe6614381ac79cf833216011 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Fri, 29 Nov 2013 13:16:42 -0500 Subject: [PATCH 07/12] Partial implementaton of hash database uniqueness constraints --- .../AddContentToHashDbAction.java | 5 +- .../autopsy/hashdatabase/HashDb.java | 286 ------------ .../HashDbCreateDatabaseDialog.java | 56 ++- .../HashDbImportDatabaseDialog.java | 4 +- .../autopsy/hashdatabase/HashDbManager.java | 442 ++++++++++++++++-- 5 files changed, 457 insertions(+), 336 deletions(-) delete mode 100644 HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDb.java mode change 100644 => 100755 HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbManager.java diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/AddContentToHashDbAction.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/AddContentToHashDbAction.java index 1daa4501a1..7a868c9d4a 100755 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/AddContentToHashDbAction.java +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/AddContentToHashDbAction.java @@ -34,6 +34,7 @@ import org.sleuthkit.autopsy.ingest.IngestConfigurator; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.TskCoreException; +import static org.sleuthkit.autopsy.hashdatabase.HashDbManager.HashDb; /** * Instances of this Action allow users to content to a hash database. @@ -131,9 +132,7 @@ final class AddContentToHashDbAction extends AbstractAction implements Presenter public void actionPerformed(ActionEvent e) { HashDb hashDb = new HashDbCreateDatabaseDialog().doDialog(); if (null != hashDb) { - HashDbManager hashSetManager = HashDbManager.getInstance(); - hashSetManager.addHashSet(hashDb); - hashSetManager.save(); + HashDbManager.getInstance().save(); addFilesToHashSet(selectedFiles, hashDb); } } diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDb.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDb.java deleted file mode 100644 index a7b5ffcdd4..0000000000 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDb.java +++ /dev/null @@ -1,286 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2011 - 2013 Basis Technology Corp. - * Contact: carrier sleuthkit org - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sleuthkit.autopsy.hashdatabase; - -import java.beans.PropertyChangeListener; -import java.beans.PropertyChangeSupport; -import java.util.logging.Level; -import javax.swing.JOptionPane; -import javax.swing.SwingWorker; -import org.netbeans.api.progress.ProgressHandle; -import org.netbeans.api.progress.ProgressHandleFactory; -import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.datamodel.AbstractFile; -import org.sleuthkit.datamodel.Content; -import org.sleuthkit.datamodel.HashInfo; -import org.sleuthkit.datamodel.SleuthkitJNI; -import org.sleuthkit.datamodel.TskCoreException; - -/** - * Instances of this class represent hash databases used to classify files as - * known or know bad. - */ -public class HashDb { - /** - * Property change events published by hash database objects. - */ - public enum Event { - INDEXING_DONE - } - - /** - * The classification to apply to files whose hashes are stored in the - * hash database. - */ - public enum KnownFilesType{ - KNOWN("Known"), - KNOWN_BAD("Known Bad"); - - private String displayName; - - private KnownFilesType(String displayName) { - this.displayName = displayName; - } - - public String getDisplayName() { - return this.displayName; - } - } - - private int handle; - private KnownFilesType knownFilesType; - private String databasePath; - private String indexPath; - private String hashSetName; - private boolean useForIngest; - private boolean sendHitMessages; - private boolean indexing; - private final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this); - - /** - * Opens an existing hash database. - * @param hashSetName Name used to represent the hash database in user interface components. - * @param selectedFilePath Full path to either a hash database file or a hash database index file. - * @param useForIngest A flag indicating whether or not the hash database should be used during ingest. - * @param sendHitMessages A flag indicating whether hash set hit messages should be sent to the application in box. - * @param knownFilesType The classification to apply to files whose hashes are stored in the hash database. - * @return A HashDb object representation of the new hash database. - * @throws TskCoreException - */ - public static HashDb openHashDatabase(String hashSetName, String selectedFilePath, boolean useForIngest, boolean sendHitMessages, KnownFilesType knownFilesType) throws TskCoreException { - int handle = SleuthkitJNI.openHashDatabase(selectedFilePath); - return new HashDb(handle, SleuthkitJNI.getHashDatabasePath(handle), SleuthkitJNI.getHashDatabaseIndexPath(handle), hashSetName, useForIngest, sendHitMessages, knownFilesType); - } - - /** - * Creates a new hash database. - * @param hashSetName Hash set name used to represent the hash database in user interface components. - * @param databasePath Full path to the database file to be created. The file name component of the path must have a ".kdb" extension. - * @param useForIngest A flag indicating whether or not the data base should be used during the file ingest process. - * @param showInboxMessages A flag indicating whether messages indicating lookup hits should be sent to the application in box. - * @param hashSetType The type of hash set to associate with the database. - * @return A HashDb object representation of the opened hash database. - * @throws TskCoreException - */ - public static HashDb createHashDatabase(String hashSetName, String databasePath, boolean useForIngest, boolean showInboxMessages, KnownFilesType knownFilesType) throws TskCoreException { - int handle = SleuthkitJNI.createHashDatabase(databasePath); - return new HashDb(handle, SleuthkitJNI.getHashDatabasePath(handle), SleuthkitJNI.getHashDatabaseIndexPath(handle), hashSetName, useForIngest, showInboxMessages, knownFilesType); - } - - private HashDb(int handle, String databasePath, String indexPath, String name, boolean useForIngest, boolean sendHitMessages, KnownFilesType knownFilesType) { - this.databasePath = databasePath; - this.indexPath = indexPath; - this.hashSetName = name; - this.useForIngest = useForIngest; - this.sendHitMessages = sendHitMessages; - this.knownFilesType = knownFilesType; - this.handle = handle; - this.indexing = false; - } - - /** - * Adds a listener for the events defined in HashDb.Event. - */ - public void addPropertyChangeListener(PropertyChangeListener pcl) { - propertyChangeSupport.addPropertyChangeListener(pcl); - } - - /** - * Removes a listener for the events defined in HashDb.Event. - */ - public void removePropertyChangeListener(PropertyChangeListener pcl) { - propertyChangeSupport.removePropertyChangeListener(pcl); - } - - public String getHashSetName() { - return hashSetName; - } - - public String getDatabasePath() { - return databasePath; - } - - public String getIndexPath() { - return indexPath; - } - - public KnownFilesType getKnownFilesType() { - return knownFilesType; - } - - public boolean getUseForIngest() { - return useForIngest; - } - - void setUseForIngest(boolean useForIngest) { - this.useForIngest = useForIngest; - } - - public boolean getShowInboxMessages() { - return sendHitMessages; - } - - void setShowInboxMessages(boolean showInboxMessages) { - this.sendHitMessages = showInboxMessages; - } - - public boolean hasLookupIndex() throws TskCoreException { - return SleuthkitJNI.hashDatabaseHasLookupIndex(handle); - } - - public boolean canBeReindexed() throws TskCoreException { - return SleuthkitJNI.hashDatabaseCanBeReindexed(handle); - } - - public boolean hasIndexOnly() throws TskCoreException { - return SleuthkitJNI.hashDatabaseHasLegacyLookupIndexOnly(handle); - } - - /** - * Indicates whether the hash database accepts updates. - * @return True if the database accepts updates, false otherwise. - */ - public boolean isUpdateable() throws TskCoreException { - return SleuthkitJNI.isUpdateableHashDatabase(this.handle); - } - - /** - * Adds hashes of content (if calculated) to the hash database. - * @param content The content for which the calculated hashes, if any, are to be added to the hash database. - * @throws TskCoreException - */ - public void add(Content content) throws TskCoreException { - add(content, null); - } - - /** - * Adds hashes of content (if calculated) to the hash database. - * @param content The content for which the calculated hashes, if any, are to be added to the hash database. - * @param comment A comment to associate with the hashes, e.g., the name of the case in which the content was encountered. - * @throws TskCoreException - */ - public void add(Content content, String comment) throws TskCoreException { - // TODO: This only works for AbstractFiles at present. Change when Content - // can be queried for hashes. - assert content instanceof AbstractFile; - if (content instanceof AbstractFile) { - AbstractFile file = (AbstractFile)content; - // TODO: Add support for SHA-1 and SHA-256 hashes. - if (null != file.getMd5Hash()) { - SleuthkitJNI.addToHashDatabase(file.getName(), file.getMd5Hash(), null, null, comment, handle); - } - } - } - - public boolean hasHashOfContent(Content content) throws TskCoreException { - boolean result = false; - // TODO: This only works for AbstractFiles at present. Change when Content can be queried for hashes. - assert content instanceof AbstractFile; - if (content instanceof AbstractFile) { - AbstractFile file = (AbstractFile)content; - // TODO: Add support for SHA-1 and SHA-256 hashes. - if (null != file.getMd5Hash()) { - result = SleuthkitJNI.lookupInHashDatabase(file.getMd5Hash(), handle); - } - } - return result; - } - - public HashInfo lookUp(Content content) throws TskCoreException { - HashInfo result = null; - // TODO: This only works for AbstractFiles at present. Change when Content can be queried for hashes. - assert content instanceof AbstractFile; - if (content instanceof AbstractFile) { - AbstractFile file = (AbstractFile)content; - // TODO: Add support for SHA-1 and SHA-256 hashes. - if (null != file.getMd5Hash()) { - result = SleuthkitJNI.lookupInHashDatabaseVerbose(file.getMd5Hash(), handle); - } - } - return result; - } - - boolean isIndexing() { - return indexing; - } - - // Tries to index the database (overwrites any existing index) using a - // SwingWorker. - void createIndex(boolean deleteIndexFile) { - CreateIndex creator = new CreateIndex(deleteIndexFile); - creator.execute(); - } - - private class CreateIndex extends SwingWorker { - private ProgressHandle progress; - private boolean deleteIndexFile; - - CreateIndex(boolean deleteIndexFile) { - this.deleteIndexFile = deleteIndexFile; - }; - - @Override - protected Object doInBackground() { - indexing = true; - progress = ProgressHandleFactory.createHandle("Indexing " + hashSetName); - progress.start(); - progress.switchToIndeterminate(); - try { - SleuthkitJNI.createLookupIndexForHashDatabase(handle, deleteIndexFile); - indexPath = SleuthkitJNI.getHashDatabaseIndexPath(handle); - } - catch (TskCoreException ex) { - Logger.getLogger(HashDb.class.getName()).log(Level.SEVERE, "Error indexing hash database", ex); - JOptionPane.showMessageDialog(null, "Error indexing hash database for " + getHashSetName() + ".", "Hash Database Index Error", JOptionPane.ERROR_MESSAGE); - } - return null; - } - - @Override - protected void done() { - indexing = false; - progress.finish(); - propertyChangeSupport.firePropertyChange(Event.INDEXING_DONE.toString(), null, hashSetName); - } - } - - public void close() throws TskCoreException { - SleuthkitJNI.closeHashDatabase(handle); - } -} \ No newline at end of file diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbCreateDatabaseDialog.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbCreateDatabaseDialog.java index 88d1ff271e..fb22de8d5d 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbCreateDatabaseDialog.java +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbCreateDatabaseDialog.java @@ -28,15 +28,15 @@ import org.sleuthkit.autopsy.coreutils.Logger; import javax.swing.JFileChooser; import javax.swing.JOptionPane; import org.apache.commons.io.FilenameUtils; -import org.sleuthkit.autopsy.hashdatabase.HashDb.KnownFilesType; import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.autopsy.hashdatabase.HashDbManager.KnownFilesType; +import org.sleuthkit.autopsy.hashdatabase.HashDbManager.HashDb; /** * Instances of this class allow a user to create a new hash database. */ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog { - private static String HASH_DATABASE_FILE_EXTENSON = ".kdb"; private JFileChooser fileChooser = null; private HashDb newHashDb = null; @@ -45,15 +45,18 @@ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog { fileChooser = new JFileChooser() { @Override public void approveSelection() { - // The hash database file the user chooses must be a new file with a hash database file extension. File selectedFile = getSelectedFile(); - if (!FilenameUtils.getExtension(selectedFile.getName()).equalsIgnoreCase("kdb") && JOptionPane.showConfirmDialog(this, "The hash database file must have a .kdb extension.", "File Name Error", JOptionPane.OK_CANCEL_OPTION) == JOptionPane.CANCEL_OPTION) { - cancelSelection(); - return; + if (!FilenameUtils.getExtension(selectedFile.getName()).equalsIgnoreCase(HashDbManager.getHashDatabaseFileExtension())) { + if (JOptionPane.showConfirmDialog(this, "The hash database file must have a ." + HashDbManager.getHashDatabaseFileExtension() + " extension.", "File Name Error", JOptionPane.OK_CANCEL_OPTION) == JOptionPane.CANCEL_OPTION) { + cancelSelection(); + return; + } } - if (selectedFile.exists() && JOptionPane.showConfirmDialog(this, "A file with this name already exists. Please enter a new filename.", "Existing File", JOptionPane.OK_CANCEL_OPTION) == JOptionPane.CANCEL_OPTION) { - cancelSelection(); - return; + if (selectedFile.exists()) { + if (JOptionPane.showConfirmDialog(this, "A file with this name already exists. Please choose a new file name.", "File Already Exists Error", JOptionPane.OK_CANCEL_OPTION) == JOptionPane.CANCEL_OPTION) { + cancelSelection(); + return; + } } super.approveSelection(); } @@ -222,7 +225,7 @@ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog { }//GEN-LAST:event_knownBadRadioButtonActionPerformed private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed - this.dispose(); + dispose(); }//GEN-LAST:event_cancelButtonActionPerformed private void saveAsButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_saveAsButtonActionPerformed @@ -231,7 +234,7 @@ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog { return; } - fileChooser.setSelectedFile(new File(hashSetNameTextField.getText() + HASH_DATABASE_FILE_EXTENSON)); + fileChooser.setSelectedFile(new File(hashSetNameTextField.getText() + "." + HashDbManager.getHashDatabaseFileExtension())); if (fileChooser.showSaveDialog(this) != JFileChooser.APPROVE_OPTION) { return; } @@ -244,18 +247,39 @@ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog { type = KnownFilesType.KNOWN_BAD; } + String errorMessage = "Hash database creation error"; try { - newHashDb = HashDb.createHashDatabase(hashSetNameTextField.getText(), fileChooser.getSelectedFile().getCanonicalPath(), searchDuringIngestCheckbox.isSelected(), sendIngestMessagesCheckbox.isSelected(), type); + newHashDb = HashDbManager.getInstance().addNewHashDatabase(hashSetNameTextField.getText(), fileChooser.getSelectedFile().getCanonicalPath(), searchDuringIngestCheckbox.isSelected(), sendIngestMessagesCheckbox.isSelected(), type); } catch (IOException ex) { - Logger.getLogger(HashDbCreateDatabaseDialog.class.getName()).log(Level.WARNING, "Hash database creation error", ex); - JOptionPane.showMessageDialog(this, "Cannot create hash database file at the selected location."); + Logger.getLogger(HashDbCreateDatabaseDialog.class.getName()).log(Level.WARNING, errorMessage, ex); + JOptionPane.showMessageDialog(this, "Cannot create a hash database file at the selected location."); return; } + catch (HashDbManager.FileAlreadyExistsException ex) { + Logger.getLogger(HashDbCreateDatabaseDialog.class.getName()).log(Level.WARNING, errorMessage, ex); + JOptionPane.showMessageDialog(this, ex.getMessage()); + return; + } + catch (HashDbManager.DuplicateHashSetNameException ex) { + Logger.getLogger(HashDbCreateDatabaseDialog.class.getName()).log(Level.WARNING, errorMessage, ex); + JOptionPane.showMessageDialog(this, ex.getMessage()); + return; + } + catch (HashDbManager.HashDatabaseAlreadyAddedException ex) { + Logger.getLogger(HashDbCreateDatabaseDialog.class.getName()).log(Level.WARNING, errorMessage, ex); + JOptionPane.showMessageDialog(this, ex.getMessage()); + return; + } + catch (HashDbManager.IllegalHashDatabaseFileNameExtensionException ex) { + Logger.getLogger(HashDbCreateDatabaseDialog.class.getName()).log(Level.WARNING, errorMessage, ex); + JOptionPane.showMessageDialog(this, ex.getMessage()); + return; + } catch (TskCoreException ex) { - Logger.getLogger(HashDbCreateDatabaseDialog.class.getName()).log(Level.SEVERE, "Hash database creation error", ex); - JOptionPane.showMessageDialog(this, "Failed to create hash database."); + Logger.getLogger(HashDbCreateDatabaseDialog.class.getName()).log(Level.SEVERE, errorMessage, ex); + JOptionPane.showMessageDialog(this, "Failed to create the hash database."); return; } diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbImportDatabaseDialog.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbImportDatabaseDialog.java index 2916b7a48b..7035bb2f56 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbImportDatabaseDialog.java +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbImportDatabaseDialog.java @@ -27,14 +27,16 @@ import org.sleuthkit.autopsy.coreutils.Logger; import javax.swing.JFileChooser; import javax.swing.JOptionPane; import javax.swing.filechooser.FileNameExtensionFilter; -import org.sleuthkit.autopsy.hashdatabase.HashDb.KnownFilesType; import org.sleuthkit.datamodel.TskCoreException; import org.apache.commons.io.FilenameUtils; +import org.sleuthkit.autopsy.hashdatabase.HashDbManager.KnownFilesType; +import org.sleuthkit.autopsy.hashdatabase.HashDbManager.HashDb; /** * Instances of this class allow a user to select a hash database for import. */ final class HashDbImportDatabaseDialog extends javax.swing.JDialog { + private JFileChooser fileChooser = new JFileChooser(); private String selectedFilePath = ""; private HashDb selectedHashDb; diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbManager.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbManager.java old mode 100644 new mode 100755 index d7cf3293f1..1c5a9cb715 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbManager.java +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbManager.java @@ -22,7 +22,9 @@ import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.logging.Level; import javax.swing.JFileChooser; import javax.swing.JOptionPane; @@ -33,17 +35,50 @@ import javax.xml.parsers.ParserConfigurationException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.PlatformUtil; import org.sleuthkit.autopsy.coreutils.XMLUtil; -import org.sleuthkit.autopsy.hashdatabase.HashDb.KnownFilesType; import org.sleuthkit.datamodel.TskCoreException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.util.logging.Level; +import javax.swing.JOptionPane; +import javax.swing.SwingWorker; +import org.apache.commons.io.FilenameUtils; +import org.netbeans.api.progress.ProgressHandle; +import org.netbeans.api.progress.ProgressHandleFactory; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.HashInfo; +import org.sleuthkit.datamodel.SleuthkitJNI; +import org.sleuthkit.datamodel.TskCoreException; + /** * This class is a singleton that manages the set of hash databases - * used to classify files as known or known bad. + * used to identify files as known files or known bad files. */ public class HashDbManager { + /** + * Characterizes the files whose hashes are stored in a hash database. + */ + public enum KnownFilesType{ + KNOWN("Known"), + KNOWN_BAD("Known Bad"); + + private String displayName; + + private KnownFilesType(String displayName) { + this.displayName = displayName; + } + + public String getDisplayName() { + return this.displayName; + } + } + + // RJCTODO: Consider making these local private static final String ROOT_EL = "hash_sets"; private static final String SET_EL = "hash_set"; private static final String SET_NAME_ATTR = "name"; @@ -56,14 +91,17 @@ public class HashDbManager { private static final String ENCODING = "UTF-8"; private static final String SET_CALC = "hash_calculate"; private static final String SET_VALUE = "value"; + private static final String HASH_DATABASE_FILE_EXTENSON = "kdb"; private static final String LEGACY_INDEX_FILE_EXTENSION = "-md5.idx"; private static final Logger logger = Logger.getLogger(HashDbManager.class.getName()); - private static HashDbManager instance; - private String xmlFilePath = PlatformUtil.getUserConfigDirectory() + File.separator + CUR_HASHSETS_FILE_NAME; + private static HashDbManager instance; + private final String configFilePath = PlatformUtil.getUserConfigDirectory() + File.separator + CUR_HASHSETS_FILE_NAME; private List knownHashSets = new ArrayList<>(); private List knownBadHashSets = new ArrayList<>(); - private boolean alwaysCalculateHashes; - + private Set hashSetNames = new HashSet<>(); + private Set hashSetPaths = new HashSet<>(); + private boolean alwaysCalculateHashes; + /** * Gets the singleton instance of this class. */ @@ -79,27 +117,126 @@ public class HashDbManager { readHashSetsConfigurationFromDisk(); } } - + + private boolean hashSetsConfigurationFileExists() { + File f = new File(configFilePath); + return f.exists() && f.canRead() && f.canWrite(); + } + + public static String getHashDatabaseFileExtension() { + return HASH_DATABASE_FILE_EXTENSON; + } + + public class DuplicateHashSetNameException extends Exception { + private DuplicateHashSetNameException() { + super("The hash set name has already been used for another hash database."); + } + } + + public class HashDatabaseDoesNotExistException extends Exception { + private HashDatabaseDoesNotExistException() { + super("Attempt to add a hash database that does not exist to the configuration"); + } + } + + public class FileAlreadyExistsException extends Exception { + private FileAlreadyExistsException() { + super("A hash database file already exists at the selected location."); + } + } + + public class HashDatabaseAlreadyAddedException extends Exception { + private HashDatabaseAlreadyAddedException() { + super("The hash database has already been created."); + } + } + + public class IllegalHashDatabaseFileNameExtensionException extends Exception { + private IllegalHashDatabaseFileNameExtensionException() { + super("The hash database file must have a ." + getHashDatabaseFileExtension() + " extension."); + } + } + /** - * Adds a hash database to the configuration. Does not check for duplication - * of hash set names and does not save the configuration - the configuration - * is only saved on demand to support cancellation of configuration panels. + * Adds an existing hash database to the set of hash databases used to classify files as known or known bad. + * @param hashSetName Name used to represent the hash database in user interface components. + * @param filePath Full path to either a hash database file or a hash database index file. + * @param searchDuringIngest A flag indicating whether or not the hash database should be searched during ingest. + * @param sendIngestMessages A flag indicating whether hash set hit messages should be sent as ingest messages. + * @param knownFilesType The classification to apply to files whose hashes are found in the hash database. + * @return A HashDb object representation of the hash database. + * @throws HashDatabaseDoesNotExistException, DuplicateHashSetNameException, HashDatabaseAlreadyAddedException, TskCoreException */ - public void addHashSet(HashDb hashDb) { + public synchronized HashDb addExistingHashDatabase(String hashSetName, String filePath, boolean searchDuringIngest, boolean sendIngestMessages, KnownFilesType knownFilesType) throws HashDatabaseDoesNotExistException, DuplicateHashSetNameException, HashDatabaseAlreadyAddedException, TskCoreException { + if (new File(filePath).exists()) { + throw new HashDatabaseDoesNotExistException(); + } + + if (hashSetNames.contains(hashSetName)) { + throw new DuplicateHashSetNameException(); + } + + if (hashSetPaths.contains(filePath)) { + throw new HashDatabaseAlreadyAddedException(); + } + + int handle = SleuthkitJNI.openHashDatabase(filePath); + HashDb hashDb = new HashDb(handle, SleuthkitJNI.getHashDatabasePath(handle), SleuthkitJNI.getHashDatabaseIndexPath(handle), hashSetName, searchDuringIngest, sendIngestMessages, knownFilesType); + addToConfiguration(hashDb); + return hashDb; + } + + /** + * Adds a new hash database to the set of hash databases used to classify files as known or known bad. + * @param hashSetName Hash set name used to represent the hash database in user interface components. + * @param filePath Full path to the database file to be created. The file name component of the path must have a ".kdb" extension. + * @param useForIngest A flag indicating whether or not the data base should be used during the file ingest process. + * @param showInboxMessages A flag indicating whether messages indicating lookup hits should be sent to the application in box. + * @param hashSetType The type of hash set to associate with the database. + * @return A HashDb object representation of the opened hash database. + * @throws TskCoreException + */ + public synchronized HashDb addNewHashDatabase(String hashSetName, String filePath, boolean useForIngest, boolean showInboxMessages, KnownFilesType knownFilesType) throws FileAlreadyExistsException, DuplicateHashSetNameException, HashDatabaseAlreadyAddedException, IllegalHashDatabaseFileNameExtensionException, TskCoreException { + File file = new File(filePath); + if (file.exists()) { + throw new FileAlreadyExistsException(); + } + if (!FilenameUtils.getExtension(file.getName()).equalsIgnoreCase(HASH_DATABASE_FILE_EXTENSON)) { + throw new IllegalHashDatabaseFileNameExtensionException(); + } + + if (hashSetNames.contains(hashSetName)) { + throw new DuplicateHashSetNameException(); + } + + if (hashSetPaths.contains(filePath)) { + throw new HashDatabaseAlreadyAddedException(); + } + + int handle = SleuthkitJNI.createHashDatabase(filePath); + HashDb hashDb = new HashDb(handle, SleuthkitJNI.getHashDatabasePath(handle), SleuthkitJNI.getHashDatabaseIndexPath(handle), hashSetName, useForIngest, showInboxMessages, knownFilesType); + addToConfiguration(hashDb); + return hashDb; + } + + private void addToConfiguration(HashDb hashDb) { + hashSetNames.add(hashDb.getHashSetName()); + hashSetPaths.add(hashDb.getDatabasePath()); + hashSetPaths.add(hashDb.getIndexPath()); if (hashDb.getKnownFilesType() == HashDb.KnownFilesType.KNOWN) { knownHashSets.add(hashDb); } else { knownBadHashSets.add(hashDb); - } + } } - + /** * Removes a hash database from the configuration. Does not save the * configuration - the configuration is only saved on demand to support * cancellation of configuration panels. */ - public void removeHashSet(HashDb hashDb) { + public synchronized void removeHashDatabase(HashDb hashDb) { try { hashDb.close(); } @@ -115,7 +252,7 @@ public class HashDbManager { * @return A list, possibly empty, of HashDb objects representing the hash * sets. */ - public List getAllHashSets() { + public synchronized List getAllHashSets() { List hashDbs = new ArrayList<>(); hashDbs.addAll(knownHashSets); hashDbs.addAll(knownBadHashSets); @@ -126,7 +263,7 @@ public class HashDbManager { * Gets the configured known files hash sets. * @return A list, possibly empty, of HashDb objects. */ - public List getKnownHashSets() { + public synchronized List getKnownHashSets() { return Collections.unmodifiableList(knownHashSets); } @@ -134,7 +271,7 @@ public class HashDbManager { * Gets the configured known bad files hash sets. * @return A list, possibly empty, of HashDb objects. */ - public List getKnownBadHashSets() { + public synchronized List getKnownBadHashSets() { return Collections.unmodifiableList(knownBadHashSets); } @@ -142,7 +279,7 @@ public class HashDbManager { * Gets all of the configured hash sets that accept updates. * @return A list, possibly empty, of HashDb objects. */ - public List getUpdateableHashSets() { + public synchronized List getUpdateableHashSets() { List updateableDbs = getUpdateableHashSets(knownHashSets); updateableDbs.addAll(getUpdateableHashSets(knownBadHashSets)); return Collections.unmodifiableList(updateableDbs); @@ -167,7 +304,7 @@ public class HashDbManager { * Sets the value for the flag that indicates whether hashes should be calculated * for content even if no hash databases are configured. */ - public void alwaysCalculateHashes(boolean alwaysCalculateHashes) { + public synchronized void alwaysCalculateHashes(boolean alwaysCalculateHashes) { this.alwaysCalculateHashes = alwaysCalculateHashes; } @@ -175,7 +312,7 @@ public class HashDbManager { * Accesses the flag that indicates whether hashes should be calculated * for content even if no hash databases are configured. */ - public boolean shouldAlwaysCalculateHashes() { + public synchronized boolean shouldAlwaysCalculateHashes() { return alwaysCalculateHashes; } @@ -184,7 +321,7 @@ public class HashDbManager { * saved on demand to support cancellation of configuration panels. * @return True on success, false otherwise. */ - public boolean save() { + public synchronized boolean save() { return writeHashSetConfigurationToDisk(); } @@ -192,9 +329,10 @@ public class HashDbManager { * Restores the last saved hash sets configuration. This supports * cancellation of configuration panels. */ - public void loadLastSavedConfiguration() { + public synchronized void loadLastSavedConfiguration() { closeHashDatabases(knownHashSets); closeHashDatabases(knownBadHashSets); + hashSetNames.clear(); if (hashSetsConfigurationFileExists()) { readHashSetsConfigurationFromDisk(); @@ -215,11 +353,6 @@ public class HashDbManager { hashDbs.clear(); } - private boolean hashSetsConfigurationFileExists() { - File f = new File(xmlFilePath); - return f.exists() && f.canRead() && f.canWrite(); - } - private boolean writeHashSetConfigurationToDisk() { boolean success = false; DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance(); @@ -237,7 +370,7 @@ public class HashDbManager { setCalc.setAttribute(SET_VALUE, calcValue); rootEl.appendChild(setCalc); - success = XMLUtil.saveDoc(HashDbManager.class, xmlFilePath, ENCODING, doc); + success = XMLUtil.saveDoc(HashDbManager.class, configFilePath, ENCODING, doc); } catch (ParserConfigurationException e) { logger.log(Level.SEVERE, "Error saving hash databases", e); @@ -274,7 +407,7 @@ public class HashDbManager { // TODO: The return value from this function is never checked. Failure is not indicated to the user. Is this desired? private boolean readHashSetsConfigurationFromDisk() { // Open the XML document that implements the configuration file. - final Document doc = XMLUtil.loadDoc(HashDbManager.class, xmlFilePath, XSDFILE); + final Document doc = XMLUtil.loadDoc(HashDbManager.class, configFilePath, XSDFILE); if (doc == null) { return false; } @@ -300,17 +433,32 @@ public class HashDbManager { for (int i = 0; i < numSets; ++i) { Element setEl = (Element) setsNList.item(i); - final String hashSetName = setEl.getAttribute(SET_NAME_ATTR); + String hashSetName = setEl.getAttribute(SET_NAME_ATTR); if (hashSetName.isEmpty()) { logger.log(Level.SEVERE, SET_NAME_ATTR + attributeErrorMessage, i); continue; } + // Handle configurations saved before duplicate hash set names were not permitted. + if (hashSetNames.contains(hashSetName)) { + int suffix = 0; + String newHashSetName; + do { + ++suffix; + newHashSetName = hashSetName + suffix; + } + while (hashSetNames.contains(newHashSetName)); + JOptionPane.showMessageDialog(null, "Duplicate hash set name " + hashSetName + " found.\nReplacing with " + newHashSetName + ".", "Open Hash Database Error", JOptionPane.ERROR_MESSAGE); + hashSetName = newHashSetName; + } + String knownFilesType = setEl.getAttribute(SET_TYPE_ATTR); if(knownFilesType.isEmpty()) { logger.log(Level.SEVERE, SET_TYPE_ATTR + attributeErrorMessage, i); continue; } + + // Handle legacy known files types. if (knownFilesType.equals("NSRL")) { knownFilesType = KnownFilesType.KNOWN.toString(); } @@ -348,6 +496,7 @@ public class HashDbManager { if (null != dbPath) { try { addHashSet(HashDb.openHashDatabase(hashSetName, dbPath, useForIngestFlag, showInboxMessagesFlag, KnownFilesType.valueOf(knownFilesType))); + hashSetNames.add(hashSetName); } catch (TskCoreException ex) { Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error opening hash database", ex); @@ -422,4 +571,237 @@ public class HashDbManager { } return filePath; } + + /** + * Instances of this class represent hash databases used to classify files as known or know bad. + */ + public static class HashDb { + /** + * Property change events published by hash database objects. + */ + public enum Event { + INDEXING_DONE + } + + private int handle; + private KnownFilesType knownFilesType; + private String databasePath; + private String indexPath; + private String hashSetName; + private boolean useForIngest; + private boolean sendHitMessages; + private boolean indexing; + private final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this); + + /** + * Opens an existing hash database. + * @param hashSetName Name used to represent the hash database in user interface components. + * @param selectedFilePath Full path to either a hash database file or a hash database index file. + * @param useForIngest A flag indicating whether or not the hash database should be used during ingest. + * @param sendHitMessages A flag indicating whether hash set hit messages should be sent to the application in box. + * @param knownFilesType The classification to apply to files whose hashes are stored in the hash database. + * @return A HashDb object representation of the new hash database. + * @throws TskCoreException + */ + public static HashDb openHashDatabase(String hashSetName, String selectedFilePath, boolean useForIngest, boolean sendHitMessages, KnownFilesType knownFilesType) throws TskCoreException { + int handle = SleuthkitJNI.openHashDatabase(selectedFilePath); + return new HashDb(handle, SleuthkitJNI.getHashDatabasePath(handle), SleuthkitJNI.getHashDatabaseIndexPath(handle), hashSetName, useForIngest, sendHitMessages, knownFilesType); + } + + /** + * Creates a new hash database. + * @param hashSetName Hash set name used to represent the hash database in user interface components. + * @param databasePath Full path to the database file to be created. The file name component of the path must have a ".kdb" extension. + * @param useForIngest A flag indicating whether or not the data base should be used during the file ingest process. + * @param showInboxMessages A flag indicating whether messages indicating lookup hits should be sent to the application in box. + * @param hashSetType The type of hash set to associate with the database. + * @return A HashDb object representation of the opened hash database. + * @throws TskCoreException + */ + public static HashDb createHashDatabase(String hashSetName, String databasePath, boolean useForIngest, boolean showInboxMessages, KnownFilesType knownFilesType) throws TskCoreException { + int handle = SleuthkitJNI.createHashDatabase(databasePath); + return new HashDb(handle, SleuthkitJNI.getHashDatabasePath(handle), SleuthkitJNI.getHashDatabaseIndexPath(handle), hashSetName, useForIngest, showInboxMessages, knownFilesType); + } + + private HashDb(int handle, String databasePath, String indexPath, String name, boolean useForIngest, boolean sendHitMessages, KnownFilesType knownFilesType) { + this.databasePath = databasePath; + this.indexPath = indexPath; + this.hashSetName = name; + this.useForIngest = useForIngest; + this.sendHitMessages = sendHitMessages; + this.knownFilesType = knownFilesType; + this.handle = handle; + this.indexing = false; + } + + /** + * Adds a listener for the events defined in HashDb.Event. + */ + public void addPropertyChangeListener(PropertyChangeListener pcl) { + propertyChangeSupport.addPropertyChangeListener(pcl); + } + + /** + * Removes a listener for the events defined in HashDb.Event. + */ + public void removePropertyChangeListener(PropertyChangeListener pcl) { + propertyChangeSupport.removePropertyChangeListener(pcl); + } + + public String getHashSetName() { + return hashSetName; + } + + public String getDatabasePath() { + return databasePath; + } + + public String getIndexPath() { + return indexPath; + } + + public KnownFilesType getKnownFilesType() { + return knownFilesType; + } + + public boolean getUseForIngest() { + return useForIngest; + } + + void setUseForIngest(boolean useForIngest) { + this.useForIngest = useForIngest; + } + + public boolean getShowInboxMessages() { + return sendHitMessages; + } + + void setShowInboxMessages(boolean showInboxMessages) { + this.sendHitMessages = showInboxMessages; + } + + public boolean hasLookupIndex() throws TskCoreException { + return SleuthkitJNI.hashDatabaseHasLookupIndex(handle); + } + + public boolean canBeReindexed() throws TskCoreException { + return SleuthkitJNI.hashDatabaseCanBeReindexed(handle); + } + + public boolean hasIndexOnly() throws TskCoreException { + return SleuthkitJNI.hashDatabaseHasLegacyLookupIndexOnly(handle); + } + + /** + * Indicates whether the hash database accepts updates. + * @return True if the database accepts updates, false otherwise. + */ + public boolean isUpdateable() throws TskCoreException { + return SleuthkitJNI.isUpdateableHashDatabase(this.handle); + } + + /** + * Adds hashes of content (if calculated) to the hash database. + * @param content The content for which the calculated hashes, if any, are to be added to the hash database. + * @throws TskCoreException + */ + public void add(Content content) throws TskCoreException { + add(content, null); + } + + /** + * Adds hashes of content (if calculated) to the hash database. + * @param content The content for which the calculated hashes, if any, are to be added to the hash database. + * @param comment A comment to associate with the hashes, e.g., the name of the case in which the content was encountered. + * @throws TskCoreException + */ + public void add(Content content, String comment) throws TskCoreException { + // TODO: This only works for AbstractFiles at present. Change when Content + // can be queried for hashes. + assert content instanceof AbstractFile; + if (content instanceof AbstractFile) { + AbstractFile file = (AbstractFile)content; + // TODO: Add support for SHA-1 and SHA-256 hashes. + if (null != file.getMd5Hash()) { + SleuthkitJNI.addToHashDatabase(file.getName(), file.getMd5Hash(), null, null, comment, handle); + } + } + } + + public boolean hasHashOfContent(Content content) throws TskCoreException { + boolean result = false; + // TODO: This only works for AbstractFiles at present. Change when Content can be queried for hashes. + assert content instanceof AbstractFile; + if (content instanceof AbstractFile) { + AbstractFile file = (AbstractFile)content; + // TODO: Add support for SHA-1 and SHA-256 hashes. + if (null != file.getMd5Hash()) { + result = SleuthkitJNI.lookupInHashDatabase(file.getMd5Hash(), handle); + } + } + return result; + } + + public HashInfo lookUp(Content content) throws TskCoreException { + HashInfo result = null; + // TODO: This only works for AbstractFiles at present. Change when Content can be queried for hashes. + assert content instanceof AbstractFile; + if (content instanceof AbstractFile) { + AbstractFile file = (AbstractFile)content; + // TODO: Add support for SHA-1 and SHA-256 hashes. + if (null != file.getMd5Hash()) { + result = SleuthkitJNI.lookupInHashDatabaseVerbose(file.getMd5Hash(), handle); + } + } + return result; + } + + boolean isIndexing() { + return indexing; + } + + // Tries to index the database (overwrites any existing index) using a + // SwingWorker. + void createIndex(boolean deleteIndexFile) { + CreateIndex creator = new CreateIndex(deleteIndexFile); + creator.execute(); + } + + private class CreateIndex extends SwingWorker { + private ProgressHandle progress; + private boolean deleteIndexFile; + + CreateIndex(boolean deleteIndexFile) { + this.deleteIndexFile = deleteIndexFile; + }; + + @Override + protected Object doInBackground() { + indexing = true; + progress = ProgressHandleFactory.createHandle("Indexing " + hashSetName); + progress.start(); + progress.switchToIndeterminate(); + try { + SleuthkitJNI.createLookupIndexForHashDatabase(handle, deleteIndexFile); + indexPath = SleuthkitJNI.getHashDatabaseIndexPath(handle); + } + catch (TskCoreException ex) { + Logger.getLogger(HashDb.class.getName()).log(Level.SEVERE, "Error indexing hash database", ex); + JOptionPane.showMessageDialog(null, "Error indexing hash database for " + getHashSetName() + ".", "Hash Database Index Error", JOptionPane.ERROR_MESSAGE); + } + return null; + } + + @Override + protected void done() { + indexing = false; + progress.finish(); + propertyChangeSupport.firePropertyChange(Event.INDEXING_DONE.toString(), null, hashSetName); + } + } + + public void close() throws TskCoreException { + SleuthkitJNI.closeHashDatabase(handle); + } + } } \ No newline at end of file From f74c65c4172d1c7d11bc1154fc5bc56df20aeae2 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Mon, 2 Dec 2013 10:34:09 -0500 Subject: [PATCH 08/12] Implementation of hash database name and path filters --- .../AddContentToHashDbAction.java | 4 +- .../autopsy/hashdatabase/Bundle.properties | 11 +- .../hashdatabase/HashDbConfigPanel.form | 20 +- .../hashdatabase/HashDbConfigPanel.java | 112 +++-- .../HashDbCreateDatabaseDialog.form | 77 ++- .../HashDbCreateDatabaseDialog.java | 201 +++++--- .../HashDbImportDatabaseDialog.java | 94 ++-- .../hashdatabase/HashDbIngestModule.java | 17 +- .../autopsy/hashdatabase/HashDbManager.java | 467 +++++++++--------- .../hashdatabase/HashDbSimpleConfigPanel.form | 8 +- .../hashdatabase/HashDbSimpleConfigPanel.java | 45 +- .../autopsy/hashdatabase/ModalNoButtons.java | 1 + 12 files changed, 603 insertions(+), 454 deletions(-) diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/AddContentToHashDbAction.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/AddContentToHashDbAction.java index 7a868c9d4a..9326fd61cd 100755 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/AddContentToHashDbAction.java +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/AddContentToHashDbAction.java @@ -130,7 +130,7 @@ final class AddContentToHashDbAction extends AbstractAction implements Presenter newHashSetItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - HashDb hashDb = new HashDbCreateDatabaseDialog().doDialog(); + HashDb hashDb = new HashDbCreateDatabaseDialog().getHashDatabase(); if (null != hashDb) { HashDbManager.getInstance().save(); addFilesToHashSet(selectedFiles, hashDb); @@ -145,7 +145,7 @@ final class AddContentToHashDbAction extends AbstractAction implements Presenter String md5Hash = file.getMd5Hash(); if (null != md5Hash) { try { - hashSet.add(file); + hashSet.addHashes(file); } catch (TskCoreException ex) { Logger.getLogger(AddContentToHashDbAction.class.getName()).log(Level.SEVERE, "Error adding to hash database", ex); diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/Bundle.properties b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/Bundle.properties index cf0d525e9c..7e2fa92971 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/Bundle.properties +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/Bundle.properties @@ -47,10 +47,7 @@ HashDbConfigPanel.hashDbIndexStatusLabel.text=No database selected HashDbConfigPanel.hashDbTypeLabel.text=No database selected HashDbConfigPanel.indexButton.text=Index HashDbConfigPanel.indexLabel.text=Index Status: -HashDbConfigPanel.showInboxMessagesCheckBox.text=Send ingest messages -HashDbConfigPanel.useForIngestCheckbox.text=Search during ingest HashDbConfigPanel.informationLabel.text=Information -HashDbSimpleConfigPanel.calcHashesButton.text=Calculate hashes even if no hash database is selected HashDbConfigPanel.importDatabaseButton.text=Import Database HashDbConfigPanel.deleteDatabaseButton.text=Delete Database HashDbConfigPanel.indexPathLabelLabel.text=Index Path: @@ -64,7 +61,7 @@ HashDbSimpleConfigPanel.knownBadHashDbsLabel.text=Enable known bad databases for HashDbSimpleConfigPanel.knownHashDbsLabel.text=Enable known hash databases for ingest: HashDbImportDatabaseDialog.knownRadioButton.text=Known (NSRL or other) HashDbCreateDatabaseDialog.knownRadioButton.text=Known -HashDbCreateDatabaseDialog.jLabel1.text=Hash Set Name: +HashDbCreateDatabaseDialog.jLabel1.text=Database Path: HashDbCreateDatabaseDialog.saveAsButton.text=Save As... HashDbCreateDatabaseDialog.hashSetNameTextField.text= HashDbImportDatabaseDialog.jLabel3.text=Database Path: @@ -76,3 +73,9 @@ HashDbImportDatabaseDialog.sendIngestMessagesCheckbox.text=Send ingest messages HashDbImportDatabaseDialog.hashSetNameTextField.text= HashDbConfigPanel.createDatabaseButton.text=Create Database HashDbImportDatabaseDialog.openButton.text=Open... +HashDbSimpleConfigPanel.alwaysCalcHashesCheckbox.text=Calculate hashes even if no hash database is selected +HashDbCreateDatabaseDialog.jLabel3.text=Hash Set Name: +HashDbCreateDatabaseDialog.okButton.text=OK +HashDbCreateDatabaseDialog.databasePathTextField.text= +HashDbConfigPanel.searchDuringIngestCheckbox.text=Search during ingest +HashDbConfigPanel.sendIngestMessagesCheckBox.text=Send ingest messages diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbConfigPanel.form b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbConfigPanel.form index a63885b203..a56c3f2b57 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbConfigPanel.form +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbConfigPanel.form @@ -96,10 +96,10 @@ - + - + @@ -180,9 +180,9 @@ - + - + @@ -360,24 +360,24 @@ - + - + - + - + - + - + diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbConfigPanel.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbConfigPanel.java index 97afdc855c..5f365f61d9 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbConfigPanel.java +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbConfigPanel.java @@ -40,13 +40,16 @@ import org.sleuthkit.autopsy.corecomponents.OptionsPanel; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.autopsy.hashdatabase.HashDbManager.HashDb; +import org.sleuthkit.autopsy.hashdatabase.HashDbManager.HashDb.KnownFilesType; /** * Instances of this class provide a comprehensive UI for managing the hash sets configuration. */ public final class HashDbConfigPanel extends javax.swing.JPanel implements OptionsPanel { private static final String NO_SELECTION_TEXT = "No database selected"; - private static final String ERROR_GETTING_INDEX_STATUS = "Error occurred getting status"; + private static final String ERROR_GETTING_PATH_TEXT = "Error occurred getting path"; + private static final String ERROR_GETTING_INDEX_STATUS_TEXT = "Error occurred getting status"; private static final String LEGACY_INDEX_FILE_EXTENSION = "-md5.idx"; private HashDbManager hashSetManager = HashDbManager.getInstance(); private HashSetTableModel hashSetTableModel = new HashSetTableModel(); @@ -111,10 +114,10 @@ public final class HashDbConfigPanel extends javax.swing.JPanel implements Optio indexButton.setEnabled(false); // Update ingest options. - useForIngestCheckbox.setSelected(false); - useForIngestCheckbox.setEnabled(false); - showInboxMessagesCheckBox.setSelected(false); - showInboxMessagesCheckBox.setEnabled(false); + searchDuringIngestCheckbox.setSelected(false); + searchDuringIngestCheckbox.setEnabled(false); + sendIngestMessagesCheckBox.setSelected(false); + sendIngestMessagesCheckBox.setEnabled(false); optionsLabel.setEnabled(false); optionsSeparator.setEnabled(false); @@ -133,8 +136,22 @@ public final class HashDbConfigPanel extends javax.swing.JPanel implements Optio // Update descriptive labels. hashDbNameLabel.setText(db.getHashSetName()); hashDbTypeLabel.setText(db.getKnownFilesType().getDisplayName()); - hashDbLocationLabel.setText(shortenPath(db.getDatabasePath())); - indexPathLabel.setText(shortenPath(db.getIndexPath())); + + try { + hashDbLocationLabel.setText(shortenPath(db.getDatabasePath())); + } + catch (TskCoreException ex) { + Logger.getLogger(HashDbConfigPanel.class.getName()).log(Level.SEVERE, "Error getting database path of " + db.getHashSetName() + " hash database", ex); + hashDbLocationLabel.setText(ERROR_GETTING_PATH_TEXT); + } + + try { + indexPathLabel.setText(shortenPath(db.getIndexPath())); + } + catch (TskCoreException ex) { + Logger.getLogger(HashDbConfigPanel.class.getName()).log(Level.SEVERE, "Error getting index path of " + db.getHashSetName() + " hash database", ex); + indexPathLabel.setText(ERROR_GETTING_PATH_TEXT); + } // Update indexing components. try { @@ -155,7 +172,7 @@ public final class HashDbConfigPanel extends javax.swing.JPanel implements Optio hashDbIndexStatusLabel.setText("Indexed"); } hashDbIndexStatusLabel.setForeground(Color.black); - if (db.canBeReindexed()) { + if (db.canBeReIndexed()) { indexButton.setText("Re-Index"); indexButton.setEnabled(true); } @@ -173,7 +190,7 @@ public final class HashDbConfigPanel extends javax.swing.JPanel implements Optio } catch (TskCoreException ex) { Logger.getLogger(HashDbConfigPanel.class.getName()).log(Level.SEVERE, "Error getting index state of hash database", ex); - hashDbIndexStatusLabel.setText(ERROR_GETTING_INDEX_STATUS); + hashDbIndexStatusLabel.setText(ERROR_GETTING_INDEX_STATUS_TEXT); hashDbIndexStatusLabel.setForeground(Color.red); indexButton.setText("Index"); indexButton.setEnabled(false); @@ -185,12 +202,12 @@ public final class HashDbConfigPanel extends javax.swing.JPanel implements Optio } // Update ingest option components. - useForIngestCheckbox.setSelected(db.getUseForIngest()); - useForIngestCheckbox.setEnabled(!ingestIsRunning); - showInboxMessagesCheckBox.setSelected(db.getShowInboxMessages()); - showInboxMessagesCheckBox.setEnabled(!ingestIsRunning && db.getUseForIngest() && db.getKnownFilesType().equals(HashDb.KnownFilesType.KNOWN_BAD)); - optionsLabel.setEnabled(!ingestIsRunning && db.getUseForIngest() && db.getKnownFilesType().equals(HashDb.KnownFilesType.KNOWN_BAD)); - optionsSeparator.setEnabled(!ingestIsRunning && db.getUseForIngest() && db.getKnownFilesType().equals(HashDb.KnownFilesType.KNOWN_BAD)); + searchDuringIngestCheckbox.setSelected(db.getSearchDuringIngest()); + searchDuringIngestCheckbox.setEnabled(!ingestIsRunning); + sendIngestMessagesCheckBox.setSelected(db.getSendIngestMessages()); + sendIngestMessagesCheckBox.setEnabled(!ingestIsRunning && db.getSearchDuringIngest() && db.getKnownFilesType().equals(KnownFilesType.KNOWN_BAD)); + optionsLabel.setEnabled(!ingestIsRunning); + optionsSeparator.setEnabled(!ingestIsRunning); // Update database action buttons. createDatabaseButton.setEnabled(true); @@ -245,14 +262,9 @@ public final class HashDbConfigPanel extends javax.swing.JPanel implements Optio hashSetManager.save(); } - /** - * Removes a list of HashDbs from the dialog panel that do not have a companion -md5.idx file. - * Occurs when user clicks "No" to the dialog pop up box. - * @param toRemove a list of HashDbs that are unindexed - */ void removeThese(List toRemove) { for (HashDb hashDb : toRemove) { - hashSetManager.removeHashSet(hashDb); + hashSetManager.removeHashDatabase(hashDb); } hashSetTableModel.refreshModel(); } @@ -290,7 +302,6 @@ public final class HashDbConfigPanel extends javax.swing.JPanel implements Optio } boolean valid() { - // TODO check whether form is consistent and complete return true; } @@ -439,8 +450,8 @@ public final class HashDbConfigPanel extends javax.swing.JPanel implements Optio hashDbIndexStatusLabel = new javax.swing.JLabel(); indexLabel = new javax.swing.JLabel(); indexButton = new javax.swing.JButton(); - useForIngestCheckbox = new javax.swing.JCheckBox(); - showInboxMessagesCheckBox = new javax.swing.JCheckBox(); + searchDuringIngestCheckbox = new javax.swing.JCheckBox(); + sendIngestMessagesCheckBox = new javax.swing.JCheckBox(); informationLabel = new javax.swing.JLabel(); optionsLabel = new javax.swing.JLabel(); informationSeparator = new javax.swing.JSeparator(); @@ -529,17 +540,17 @@ public final class HashDbConfigPanel extends javax.swing.JPanel implements Optio } }); - org.openide.awt.Mnemonics.setLocalizedText(useForIngestCheckbox, org.openide.util.NbBundle.getMessage(HashDbConfigPanel.class, "HashDbConfigPanel.useForIngestCheckbox.text")); // NOI18N - useForIngestCheckbox.addActionListener(new java.awt.event.ActionListener() { + org.openide.awt.Mnemonics.setLocalizedText(searchDuringIngestCheckbox, org.openide.util.NbBundle.getMessage(HashDbConfigPanel.class, "HashDbConfigPanel.searchDuringIngestCheckbox.text")); // NOI18N + searchDuringIngestCheckbox.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { - useForIngestCheckboxActionPerformed(evt); + searchDuringIngestCheckboxActionPerformed(evt); } }); - org.openide.awt.Mnemonics.setLocalizedText(showInboxMessagesCheckBox, org.openide.util.NbBundle.getMessage(HashDbConfigPanel.class, "HashDbConfigPanel.showInboxMessagesCheckBox.text")); // NOI18N - showInboxMessagesCheckBox.addActionListener(new java.awt.event.ActionListener() { + org.openide.awt.Mnemonics.setLocalizedText(sendIngestMessagesCheckBox, org.openide.util.NbBundle.getMessage(HashDbConfigPanel.class, "HashDbConfigPanel.sendIngestMessagesCheckBox.text")); // NOI18N + sendIngestMessagesCheckBox.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { - showInboxMessagesCheckBoxActionPerformed(evt); + sendIngestMessagesCheckBoxActionPerformed(evt); } }); @@ -600,10 +611,10 @@ public final class HashDbConfigPanel extends javax.swing.JPanel implements Optio .addComponent(nameLabel) .addGap(53, 53, 53) .addComponent(hashDbNameLabel)) - .addComponent(useForIngestCheckbox) + .addComponent(searchDuringIngestCheckbox) .addGroup(layout.createSequentialGroup() .addGap(21, 21, 21) - .addComponent(showInboxMessagesCheckBox))) + .addComponent(sendIngestMessagesCheckBox))) .addGap(0, 0, Short.MAX_VALUE)))) .addGroup(layout.createSequentialGroup() .addComponent(optionsLabel) @@ -661,9 +672,9 @@ public final class HashDbConfigPanel extends javax.swing.JPanel implements Optio .addComponent(optionsLabel) .addComponent(optionsSeparator, javax.swing.GroupLayout.PREFERRED_SIZE, 6, javax.swing.GroupLayout.PREFERRED_SIZE)) .addGap(18, 18, 18) - .addComponent(useForIngestCheckbox) + .addComponent(searchDuringIngestCheckbox) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(showInboxMessagesCheckBox) + .addComponent(sendIngestMessagesCheckBox) .addGap(18, 18, 18) .addComponent(ingestWarningLabel) .addGap(0, 0, Short.MAX_VALUE)) @@ -712,7 +723,7 @@ public final class HashDbConfigPanel extends javax.swing.JPanel implements Optio if (JOptionPane.showConfirmDialog(null, "This will remove the hash database for all cases. Do you want to proceed? ", "Delete Hash Database from Configuration", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.YES_OPTION) { HashDb hashDb = ((HashSetTable)hashSetTable).getSelection(); if (hashDb != null) { - hashSetManager.removeHashSet(hashDb); + hashSetManager.removeHashDatabase(hashDb); hashSetTableModel.refreshModel(); } } @@ -722,40 +733,41 @@ public final class HashDbConfigPanel extends javax.swing.JPanel implements Optio if (evt.getKeyCode() == KeyEvent.VK_DELETE) { HashDb hashDb = ((HashSetTable)hashSetTable).getSelection(); if (hashDb != null) { - hashSetManager.removeHashSet(hashDb); + hashSetManager.removeHashDatabase(hashDb); hashSetTableModel.refreshModel(); } } }//GEN-LAST:event_hashSetTableKeyPressed - private void useForIngestCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_useForIngestCheckboxActionPerformed + private void searchDuringIngestCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_searchDuringIngestCheckboxActionPerformed HashDb hashDb = ((HashSetTable)hashSetTable).getSelection(); if (hashDb != null) { - hashDb.setUseForIngest(useForIngestCheckbox.isSelected()); - showInboxMessagesCheckBox.setEnabled(useForIngestCheckbox.isSelected()); + hashDb.setSearchDuringIngest(searchDuringIngestCheckbox.isSelected()); + if (!searchDuringIngestCheckbox.isSelected()) { + sendIngestMessagesCheckBox.setSelected(false); + } + hashDb.setSendIngestMessages(sendIngestMessagesCheckBox.isSelected()); } - }//GEN-LAST:event_useForIngestCheckboxActionPerformed + }//GEN-LAST:event_searchDuringIngestCheckboxActionPerformed - private void showInboxMessagesCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_showInboxMessagesCheckBoxActionPerformed + private void sendIngestMessagesCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_sendIngestMessagesCheckBoxActionPerformed HashDb hashDb = ((HashSetTable)hashSetTable).getSelection(); if (hashDb != null) { - hashDb.setShowInboxMessages(showInboxMessagesCheckBox.isSelected()); + hashDb.setSendIngestMessages(sendIngestMessagesCheckBox.isSelected()); } - }//GEN-LAST:event_showInboxMessagesCheckBoxActionPerformed + }//GEN-LAST:event_sendIngestMessagesCheckBoxActionPerformed private void importDatabaseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_importDatabaseButtonActionPerformed - HashDb hashDb = new HashDbImportDatabaseDialog().doDialog(); - if (hashDb != null) { - hashSetManager.addHashSet(hashDb); + HashDb hashDb = new HashDbImportDatabaseDialog().getHashDatabase(); + if (null != hashDb) { hashSetTableModel.refreshModel(); ((HashSetTable)hashSetTable).selectRowByName(hashDb.getHashSetName()); } }//GEN-LAST:event_importDatabaseButtonActionPerformed private void createDatabaseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_createDatabaseButtonActionPerformed - HashDb hashDb = new HashDbCreateDatabaseDialog().doDialog(); + HashDb hashDb = new HashDbCreateDatabaseDialog().getHashDatabase(); if (null != hashDb) { - hashSetManager.addHashSet(hashDb); hashSetTableModel.refreshModel(); ((HashSetTable)hashSetTable).selectRowByName(hashDb.getHashSetName()); } @@ -787,8 +799,8 @@ public final class HashDbConfigPanel extends javax.swing.JPanel implements Optio private javax.swing.JLabel nameLabel; private javax.swing.JLabel optionsLabel; private javax.swing.JSeparator optionsSeparator; - private javax.swing.JCheckBox showInboxMessagesCheckBox; + private javax.swing.JCheckBox searchDuringIngestCheckbox; + private javax.swing.JCheckBox sendIngestMessagesCheckBox; private javax.swing.JLabel typeLabel; - private javax.swing.JCheckBox useForIngestCheckbox; // End of variables declaration//GEN-END:variables } \ No newline at end of file diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbCreateDatabaseDialog.form b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbCreateDatabaseDialog.form index 98c1676e0a..047c483d48 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbCreateDatabaseDialog.form +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbCreateDatabaseDialog.form @@ -27,7 +27,7 @@ - + @@ -42,12 +42,13 @@ - - - + + + - + + @@ -55,29 +56,44 @@ + + + + + + + + + + + + + + + + - + - - - - - - - - + - + + + + + + + @@ -87,12 +103,12 @@ - + - + - + @@ -188,5 +204,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbCreateDatabaseDialog.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbCreateDatabaseDialog.java index fb22de8d5d..842f1d5e06 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbCreateDatabaseDialog.java +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbCreateDatabaseDialog.java @@ -27,21 +27,48 @@ import java.util.logging.Level; import org.sleuthkit.autopsy.coreutils.Logger; import javax.swing.JFileChooser; import javax.swing.JOptionPane; +import javax.swing.JFrame; import org.apache.commons.io.FilenameUtils; import org.sleuthkit.datamodel.TskCoreException; -import org.sleuthkit.autopsy.hashdatabase.HashDbManager.KnownFilesType; import org.sleuthkit.autopsy.hashdatabase.HashDbManager.HashDb; +import org.sleuthkit.autopsy.hashdatabase.HashDbManager.HashDb.KnownFilesType; +import org.sleuthkit.autopsy.hashdatabase.HashDbManager.HashDatabaseFileAlreadyExistsException; +import org.sleuthkit.autopsy.hashdatabase.HashDbManager.DuplicateHashSetNameException; +import org.sleuthkit.autopsy.hashdatabase.HashDbManager.HashDatabaseAlreadyAddedException; +import org.sleuthkit.autopsy.hashdatabase.HashDbManager.IllegalHashDatabaseFileNameExtensionException; /** - * Instances of this class allow a user to create a new hash database. + * Instances of this class allow a user to create a new hash database and + * add it to the set of hash databases used to classify files as unknown, known + * or known bad. */ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog { + private static final String DEFAULT_FILE_NAME = "hashset"; private JFileChooser fileChooser = null; private HashDb newHashDb = null; + /** + * Displays a dialog that allows a user to create a new hash database and + * add it to the set of hash databases used to classify files as unknown, known + * or known bad. + */ HashDbCreateDatabaseDialog() { - super(new javax.swing.JFrame(), "Create Hash Database", true); + super(new JFrame(), "Create Hash Database", true); + initFileChooser(); + initComponents(); + display(); + } + + /** + * Get the hash database created by the user, if any. + * @return A HashDb object or null. + */ + HashDb getHashDatabase() { + return newHashDb; + } + + private void initFileChooser() { fileChooser = new JFileChooser() { @Override public void approveSelection() { @@ -49,36 +76,29 @@ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog { if (!FilenameUtils.getExtension(selectedFile.getName()).equalsIgnoreCase(HashDbManager.getHashDatabaseFileExtension())) { if (JOptionPane.showConfirmDialog(this, "The hash database file must have a ." + HashDbManager.getHashDatabaseFileExtension() + " extension.", "File Name Error", JOptionPane.OK_CANCEL_OPTION) == JOptionPane.CANCEL_OPTION) { cancelSelection(); - return; } + return; } if (selectedFile.exists()) { if (JOptionPane.showConfirmDialog(this, "A file with this name already exists. Please choose a new file name.", "File Already Exists Error", JOptionPane.OK_CANCEL_OPTION) == JOptionPane.CANCEL_OPTION) { cancelSelection(); - return; } + return; } super.approveSelection(); } }; fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); fileChooser.setDragEnabled(false); - fileChooser.setMultiSelectionEnabled(false); - - initComponents(); + fileChooser.setMultiSelectionEnabled(false); } - HashDb doDialog() { - newHashDb = null; - - // Center and display the dialog. + private void display() { Dimension screenDimension = Toolkit.getDefaultToolkit().getScreenSize(); setLocation((screenDimension.width - getSize().width) / 2, (screenDimension.height - getSize().height) / 2); - this.setVisible(true); - - return newHashDb; + setVisible(true); } - + /** * This method is called from within the constructor to initialize the form. * WARNING: Do NOT modify this code. The content of this method is always @@ -98,6 +118,9 @@ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog { jLabel2 = new javax.swing.JLabel(); searchDuringIngestCheckbox = new javax.swing.JCheckBox(); sendIngestMessagesCheckbox = new javax.swing.JCheckBox(); + jLabel3 = new javax.swing.JLabel(); + databasePathTextField = new javax.swing.JTextField(); + okButton = new javax.swing.JButton(); setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); @@ -150,6 +173,18 @@ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog { sendIngestMessagesCheckbox.setSelected(true); org.openide.awt.Mnemonics.setLocalizedText(sendIngestMessagesCheckbox, org.openide.util.NbBundle.getMessage(HashDbCreateDatabaseDialog.class, "HashDbCreateDatabaseDialog.sendIngestMessagesCheckbox.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(jLabel3, org.openide.util.NbBundle.getMessage(HashDbCreateDatabaseDialog.class, "HashDbCreateDatabaseDialog.jLabel3.text")); // NOI18N + + databasePathTextField.setEditable(false); + databasePathTextField.setText(org.openide.util.NbBundle.getMessage(HashDbCreateDatabaseDialog.class, "HashDbCreateDatabaseDialog.databasePathTextField.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(okButton, org.openide.util.NbBundle.getMessage(HashDbCreateDatabaseDialog.class, "HashDbCreateDatabaseDialog.okButton.text")); // NOI18N + okButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + okButtonActionPerformed(evt); + } + }); + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); getContentPane().setLayout(layout); layout.setHorizontalGroup( @@ -166,35 +201,47 @@ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog { .addGap(0, 0, Short.MAX_VALUE)) .addGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jLabel2) - .addGroup(layout.createSequentialGroup() - .addComponent(jLabel1) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addGap(0, 0, Short.MAX_VALUE) + .addComponent(okButton) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(hashSetNameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 176, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(cancelButton)) + .addComponent(jLabel2) .addGroup(layout.createSequentialGroup() .addGap(20, 20, 20) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(knownRadioButton) - .addComponent(knownBadRadioButton)))) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(saveAsButton) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(cancelButton) - .addContainerGap()) + .addComponent(knownBadRadioButton))) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) + .addGroup(layout.createSequentialGroup() + .addComponent(jLabel1) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(databasePathTextField)) + .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup() + .addComponent(jLabel3) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(hashSetNameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 272, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(saveAsButton))) + .addContainerGap()))) ); - layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {cancelButton, saveAsButton}); + layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {cancelButton, okButton}); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addContainerGap() + .addGap(2, 2, 2) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(jLabel1) + .addComponent(jLabel3) .addComponent(hashSetNameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(databasePathTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(saveAsButton) + .addComponent(jLabel1)) + .addGap(7, 7, 7) .addComponent(jLabel2) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(knownRadioButton) @@ -204,22 +251,24 @@ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog { .addComponent(searchDuringIngestCheckbox) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(sendIngestMessagesCheckbox) - .addGap(18, 18, 18) + .addGap(3, 3, 3) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(saveAsButton) - .addComponent(cancelButton)) - .addContainerGap(12, Short.MAX_VALUE)) + .addComponent(cancelButton) + .addComponent(okButton)) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); pack(); }// //GEN-END:initComponents private void knownRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_knownRadioButtonActionPerformed + searchDuringIngestCheckbox.setSelected(true); sendIngestMessagesCheckbox.setSelected(false); sendIngestMessagesCheckbox.setEnabled(false); }//GEN-LAST:event_knownRadioButtonActionPerformed private void knownBadRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_knownBadRadioButtonActionPerformed + searchDuringIngestCheckbox.setSelected(true); sendIngestMessagesCheckbox.setSelected(true); sendIngestMessagesCheckbox.setEnabled(true); }//GEN-LAST:event_knownBadRadioButtonActionPerformed @@ -229,16 +278,47 @@ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog { }//GEN-LAST:event_cancelButtonActionPerformed private void saveAsButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_saveAsButtonActionPerformed - if (hashSetNameTextField.getText().isEmpty()) { - JOptionPane.showMessageDialog(this, "A hash set name must be entered."); - return; - } - - fileChooser.setSelectedFile(new File(hashSetNameTextField.getText() + "." + HashDbManager.getHashDatabaseFileExtension())); - if (fileChooser.showSaveDialog(this) != JFileChooser.APPROVE_OPTION) { - return; + try { + StringBuilder path = new StringBuilder(); + if (!hashSetNameTextField.getText().isEmpty()) { + path.append(hashSetNameTextField.getText()); + } + else { + path.append(DEFAULT_FILE_NAME); + } + path.append(".").append(HashDbManager.getHashDatabaseFileExtension()); + fileChooser.setSelectedFile(new File(path.toString())); + if (fileChooser.showSaveDialog(this) == JFileChooser.APPROVE_OPTION) { + File databaseFile = fileChooser.getSelectedFile(); + databasePathTextField.setText(databaseFile.getCanonicalPath()); + } } + catch (IOException ex) { + Logger.getLogger(HashDbCreateDatabaseDialog.class.getName()).log(Level.WARNING, "Couldn't get selected file path.", ex); + } + }//GEN-LAST:event_saveAsButtonActionPerformed + private void searchDuringIngestCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_searchDuringIngestCheckboxActionPerformed + sendIngestMessagesCheckbox.setEnabled(searchDuringIngestCheckbox.isSelected()); + if (!searchDuringIngestCheckbox.isSelected()) { + sendIngestMessagesCheckbox.setSelected(false); + } + }//GEN-LAST:event_searchDuringIngestCheckboxActionPerformed + + private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed + // Note that the error handlers in this method call return without disposing of the + // dialog to allow the user to try again, if desired. + + if (hashSetNameTextField.getText().isEmpty()) { + JOptionPane.showMessageDialog(this, "A hash set name must be entered.", "Create Hash Database Error", JOptionPane.ERROR_MESSAGE); + return; + } + + if (databasePathTextField.getText().isEmpty()) { + JOptionPane.showMessageDialog(this, "A database path must be entered.", "Create Hash Database Error", JOptionPane.ERROR_MESSAGE); + return; + } + KnownFilesType type; if (knownRadioButton.isSelected()) { type = KnownFilesType.KNOWN; @@ -254,53 +334,34 @@ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog { } catch (IOException ex) { Logger.getLogger(HashDbCreateDatabaseDialog.class.getName()).log(Level.WARNING, errorMessage, ex); - JOptionPane.showMessageDialog(this, "Cannot create a hash database file at the selected location."); + JOptionPane.showMessageDialog(this, "Cannot create a hash database file at the selected location.", "Create Hash Database Error", JOptionPane.ERROR_MESSAGE); return; } - catch (HashDbManager.FileAlreadyExistsException ex) { + catch (HashDatabaseFileAlreadyExistsException | DuplicateHashSetNameException | HashDatabaseAlreadyAddedException | IllegalHashDatabaseFileNameExtensionException ex) { Logger.getLogger(HashDbCreateDatabaseDialog.class.getName()).log(Level.WARNING, errorMessage, ex); - JOptionPane.showMessageDialog(this, ex.getMessage()); + JOptionPane.showMessageDialog(this, ex.getMessage(), "Create Hash Database Error", JOptionPane.ERROR_MESSAGE); return; } - catch (HashDbManager.DuplicateHashSetNameException ex) { - Logger.getLogger(HashDbCreateDatabaseDialog.class.getName()).log(Level.WARNING, errorMessage, ex); - JOptionPane.showMessageDialog(this, ex.getMessage()); - return; - } - catch (HashDbManager.HashDatabaseAlreadyAddedException ex) { - Logger.getLogger(HashDbCreateDatabaseDialog.class.getName()).log(Level.WARNING, errorMessage, ex); - JOptionPane.showMessageDialog(this, ex.getMessage()); - return; - } - catch (HashDbManager.IllegalHashDatabaseFileNameExtensionException ex) { - Logger.getLogger(HashDbCreateDatabaseDialog.class.getName()).log(Level.WARNING, errorMessage, ex); - JOptionPane.showMessageDialog(this, ex.getMessage()); - return; - } catch (TskCoreException ex) { Logger.getLogger(HashDbCreateDatabaseDialog.class.getName()).log(Level.SEVERE, errorMessage, ex); - JOptionPane.showMessageDialog(this, "Failed to create the hash database."); + JOptionPane.showMessageDialog(this, "Failed to create the hash database.", "Create Hash Database Error", JOptionPane.ERROR_MESSAGE); return; } dispose(); - }//GEN-LAST:event_saveAsButtonActionPerformed - - private void searchDuringIngestCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_searchDuringIngestCheckboxActionPerformed - sendIngestMessagesCheckbox.setEnabled(searchDuringIngestCheckbox.isSelected()); - if (!searchDuringIngestCheckbox.isSelected()) { - sendIngestMessagesCheckbox.setSelected(false); - } - }//GEN-LAST:event_searchDuringIngestCheckboxActionPerformed + }//GEN-LAST:event_okButtonActionPerformed // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.ButtonGroup buttonGroup1; private javax.swing.JButton cancelButton; + private javax.swing.JTextField databasePathTextField; private javax.swing.JTextField hashSetNameTextField; private javax.swing.JLabel jLabel1; private javax.swing.JLabel jLabel2; + private javax.swing.JLabel jLabel3; private javax.swing.JRadioButton knownBadRadioButton; private javax.swing.JRadioButton knownRadioButton; + private javax.swing.JButton okButton; private javax.swing.JButton saveAsButton; private javax.swing.JCheckBox searchDuringIngestCheckbox; private javax.swing.JCheckBox sendIngestMessagesCheckbox; diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbImportDatabaseDialog.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbImportDatabaseDialog.java index 7035bb2f56..33516bad99 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbImportDatabaseDialog.java +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbImportDatabaseDialog.java @@ -27,47 +27,61 @@ import org.sleuthkit.autopsy.coreutils.Logger; import javax.swing.JFileChooser; import javax.swing.JOptionPane; import javax.swing.filechooser.FileNameExtensionFilter; +import javax.swing.JFrame; import org.sleuthkit.datamodel.TskCoreException; import org.apache.commons.io.FilenameUtils; -import org.sleuthkit.autopsy.hashdatabase.HashDbManager.KnownFilesType; +import org.sleuthkit.autopsy.hashdatabase.HashDbManager.HashDb.KnownFilesType; import org.sleuthkit.autopsy.hashdatabase.HashDbManager.HashDb; +import org.sleuthkit.autopsy.hashdatabase.HashDbManager.HashDatabaseDoesNotExistException; +import org.sleuthkit.autopsy.hashdatabase.HashDbManager.DuplicateHashSetNameException; +import org.sleuthkit.autopsy.hashdatabase.HashDbManager.HashDatabaseAlreadyAddedException; /** - * Instances of this class allow a user to select a hash database for import. + * Instances of this class allow a user to select an existing hash database and + * add it to the set of hash databases used to classify files as unknown, known, + * or known bad. */ final class HashDbImportDatabaseDialog extends javax.swing.JDialog { private JFileChooser fileChooser = new JFileChooser(); private String selectedFilePath = ""; - private HashDb selectedHashDb; - - HashDbImportDatabaseDialog() { - super(new javax.swing.JFrame(), "Import Hash Database", true); - setResizable(false); - initComponents(); - customizeComponents(); - } + private HashDb selectedHashDb = null; - void customizeComponents() { + /** + * Displays a dialog that allows a user to select an existing hash database + * and add it to the set of hash databases used to classify files as unknown, + * known, or known bad. + */ + HashDbImportDatabaseDialog() { + super(new JFrame(), "Import Hash Database", true); + initFileChooser(); + initComponents(); + display(); + } + + /** + * Get the hash database imported by the user, if any. + * @return A HashDb object or null. + */ + HashDb getHashDatabase() { + return selectedHashDb; + } + + private void initFileChooser() { fileChooser.setDragEnabled(false); fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); String[] EXTENSION = new String[] { "txt", "kdb", "idx", "hash", "Hash", "hsh"}; FileNameExtensionFilter filter = new FileNameExtensionFilter("Hash Database File", EXTENSION); fileChooser.setFileFilter(filter); - fileChooser.setMultiSelectionEnabled(false); - } - - HashDb doDialog() { - selectedHashDb = null; - - // Center and display the dialog. - Dimension screenDimension = Toolkit.getDefaultToolkit().getScreenSize(); - setLocation((screenDimension.width - getSize().width) / 2, (screenDimension.height - getSize().height) / 2); - this.setVisible(true); - - return selectedHashDb; + fileChooser.setMultiSelectionEnabled(false); } + private void display() { + Dimension screenDimension = Toolkit.getDefaultToolkit().getScreenSize(); + setLocation((screenDimension.width - getSize().width) / 2, (screenDimension.height - getSize().height) / 2); + setVisible(true); + } + private static String shortenPath(String path) { String shortenedPath = path; if (shortenedPath.length() > 50){ @@ -260,11 +274,13 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog { }//GEN-LAST:event_openButtonActionPerformed private void knownRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_knownRadioButtonActionPerformed + searchDuringIngestCheckbox.setSelected(true); sendIngestMessagesCheckbox.setSelected(false); sendIngestMessagesCheckbox.setEnabled(false); }//GEN-LAST:event_knownRadioButtonActionPerformed private void knownBadRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_knownBadRadioButtonActionPerformed + searchDuringIngestCheckbox.setSelected(true); sendIngestMessagesCheckbox.setSelected(true); sendIngestMessagesCheckbox.setEnabled(true); }//GEN-LAST:event_knownBadRadioButtonActionPerformed @@ -274,19 +290,21 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog { }//GEN-LAST:event_cancelButtonActionPerformed private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed - if(selectedFilePath.isEmpty()) { - JOptionPane.showMessageDialog(this, "A hash database file path must be selected."); - return; - } + // Note that the error handlers in this method call return without disposing of the + // dialog to allow the user to try again, if desired. if(hashSetNameTextField.getText().isEmpty()) { - JOptionPane.showMessageDialog(this, "A hash set name must be entered.."); + JOptionPane.showMessageDialog(this, "A hash set name must be entered.", "Import Hash Database Error", JOptionPane.ERROR_MESSAGE); return; } + if(selectedFilePath.isEmpty()) { + JOptionPane.showMessageDialog(this, "A hash database file path must be selected.", "Import Hash Database Error", JOptionPane.ERROR_MESSAGE); + return; + } File file = new File(selectedFilePath); if (!file.exists()) { - JOptionPane.showMessageDialog(this, "The selected hash database does not exist."); + JOptionPane.showMessageDialog(this, "The selected hash database does not exist.", "Import Hash Database Error", JOptionPane.ERROR_MESSAGE); return; } @@ -298,16 +316,22 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog { type = KnownFilesType.KNOWN_BAD; } + String errorMessage = "Failed to open hash database at " + selectedFilePath + "."; try { - selectedHashDb = HashDb.openHashDatabase(hashSetNameTextField.getText(), selectedFilePath, searchDuringIngestCheckbox.isSelected(), sendIngestMessagesCheckbox.isSelected(), type); + selectedHashDb = HashDbManager.getInstance().addExistingHashDatabase(hashSetNameTextField.getText(), selectedFilePath, searchDuringIngestCheckbox.isSelected(), sendIngestMessagesCheckbox.isSelected(), type); } - catch (TskCoreException ex) { - Logger.getLogger(HashDbImportDatabaseDialog.class.getName()).log(Level.WARNING, "Failed to open hash database at " + selectedFilePath, ex); - JOptionPane.showMessageDialog(this, "Failed to import the selected database.\nPlease verify that the selected file is a hash database."); + catch (HashDatabaseDoesNotExistException | DuplicateHashSetNameException | HashDatabaseAlreadyAddedException ex) { + Logger.getLogger(HashDbImportDatabaseDialog.class.getName()).log(Level.WARNING, errorMessage, ex); + JOptionPane.showMessageDialog(this, ex.getMessage(), "Import Hash Database Error", JOptionPane.ERROR_MESSAGE); return; - } + } + catch (TskCoreException ex) { + Logger.getLogger(HashDbCreateDatabaseDialog.class.getName()).log(Level.SEVERE, errorMessage, ex); + JOptionPane.showMessageDialog(this, errorMessage, "Import Hash Database Error", JOptionPane.ERROR_MESSAGE); + return; + } - this.dispose(); + dispose(); }//GEN-LAST:event_okButtonActionPerformed private void searchDuringIngestCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_searchDuringIngestCheckboxActionPerformed diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbIngestModule.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbIngestModule.java index 403790a86c..8e0967daf9 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbIngestModule.java +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbIngestModule.java @@ -42,6 +42,7 @@ import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.TskException; +import org.sleuthkit.autopsy.hashdatabase.HashDbManager.HashDb; public class HashDbIngestModule extends IngestModuleAbstractFile { private static HashDbIngestModule instance = null; @@ -138,9 +139,9 @@ public class HashDbIngestModule extends IngestModuleAbstractFile { skCase = Case.getCurrentCase().getSleuthkitCase(); HashDbManager hashDbManager = HashDbManager.getInstance(); - getHashSetsUsableForIngest(hashDbManager.getKnownBadHashSets(), knownBadHashSets); - getHashSetsUsableForIngest(hashDbManager.getKnownHashSets(), knownHashSets); - calcHashesIsSet = hashDbManager.shouldAlwaysCalculateHashes(); + getHashSetsUsableForIngest(hashDbManager.getKnownBadFileHashSets(), knownBadHashSets); + getHashSetsUsableForIngest(hashDbManager.getKnownFileHashSets(), knownHashSets); + calcHashesIsSet = hashDbManager.getAlwaysCalculateHashes(); if (knownHashSets.isEmpty()) { services.postMessage(IngestMessage.createWarningMessage(++messageId, this, "No known hash database set", "Known file search will not be executed.")); @@ -155,14 +156,14 @@ public class HashDbIngestModule extends IngestModuleAbstractFile { assert hashDbsForIngest != null; hashDbsForIngest.clear(); for (HashDb db : hashDbs) { - if (db.getUseForIngest()) { + if (db.getSearchDuringIngest()) { try { if (db.hasLookupIndex()) { hashDbsForIngest.add(db); } } catch (TskCoreException ex) { - logger.log(Level.WARNING, "Error get index status for hash database at " +db.getDatabasePath(), ex); + logger.log(Level.WARNING, "Error getting index status for " + db.getHashSetName() +" hash database", ex); } } } @@ -211,7 +212,7 @@ public class HashDbIngestModule extends IngestModuleAbstractFile { for (HashDb db : knownBadHashSets) { try { long lookupstart = System.currentTimeMillis(); - if (db.hasHashOfContent(file)) { + if (db.hasMd5HashOf(file)) { foundBad = true; knownBadCount += 1; try { @@ -223,7 +224,7 @@ public class HashDbIngestModule extends IngestModuleAbstractFile { ret = ProcessResult.ERROR; } String hashSetName = db.getHashSetName(); - postHashSetHitToBlackboard(file, md5Hash, hashSetName, db.getShowInboxMessages()); + postHashSetHitToBlackboard(file, md5Hash, hashSetName, db.getSendIngestMessages()); } lookuptime += (System.currentTimeMillis() - lookupstart); } catch (TskException ex) { @@ -241,7 +242,7 @@ public class HashDbIngestModule extends IngestModuleAbstractFile { for (HashDb db : knownHashSets) { try { long lookupstart = System.currentTimeMillis(); - if (db.hasHashOfContent(file)) { + if (db.hasMd5HashOf(file)) { try { skCase.setKnown(file, TskData.FileKnown.KNOWN); break; diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbManager.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbManager.java index 1c5a9cb715..75f7139f98 100755 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbManager.java +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbManager.java @@ -16,26 +16,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.sleuthkit.autopsy.hashdatabase; import java.io.File; import java.io.IOException; import java.util.ArrayList; -import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; -import java.util.logging.Level; import javax.swing.JFileChooser; -import javax.swing.JOptionPane; import javax.swing.filechooser.FileNameExtensionFilter; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; -import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.PlatformUtil; import org.sleuthkit.autopsy.coreutils.XMLUtil; -import org.sleuthkit.datamodel.TskCoreException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; @@ -54,48 +50,28 @@ import org.sleuthkit.datamodel.HashInfo; import org.sleuthkit.datamodel.SleuthkitJNI; import org.sleuthkit.datamodel.TskCoreException; - /** - * This class is a singleton that manages the set of hash databases - * used to identify files as known files or known bad files. + * This class implements a singleton that manages the set of hash databases + * used to classify files as unknown, known or known bad. */ public class HashDbManager { - /** - * Characterizes the files whose hashes are stored in a hash database. - */ - public enum KnownFilesType{ - KNOWN("Known"), - KNOWN_BAD("Known Bad"); - - private String displayName; - - private KnownFilesType(String displayName) { - this.displayName = displayName; - } - - public String getDisplayName() { - return this.displayName; - } - } - // RJCTODO: Consider making these local - private static final String ROOT_EL = "hash_sets"; - private static final String SET_EL = "hash_set"; - private static final String SET_NAME_ATTR = "name"; - private static final String SET_TYPE_ATTR = "type"; - private static final String SET_USE_FOR_INGEST_ATTR = "use_for_ingest"; - private static final String SET_SHOW_INBOX_MESSAGES = "show_inbox_messages"; - private static final String PATH_EL = "hash_set_path"; - private static final String CUR_HASHSETS_FILE_NAME = "hashsets.xml"; - private static final String XSDFILE = "HashsetsSchema.xsd"; + private static final String ROOT_ELEMENT = "hash_sets"; + private static final String SET_ELEMENT = "hash_set"; + private static final String SET_NAME_ATTRIBUTE = "name"; + private static final String SET_TYPE_ATTRIBUTE = "type"; + private static final String SEARCH_DURING_INGEST_ATTRIBUTE = "use_for_ingest"; + private static final String SEND_INGEST_MESSAGES_ATTRIBUTE = "show_inbox_messages"; + private static final String PATH_ELEMENT = "hash_set_path"; + private static final String CONFIG_FILE_NAME = "hashsets.xml"; + private static final String XSD_FILE_NAME = "HashsetsSchema.xsd"; private static final String ENCODING = "UTF-8"; - private static final String SET_CALC = "hash_calculate"; - private static final String SET_VALUE = "value"; + private static final String ALWAYS_CALCULATE_HASHES_ELEMENT = "hash_calculate"; + private static final String VALUE_ATTRIBUTE = "value"; private static final String HASH_DATABASE_FILE_EXTENSON = "kdb"; private static final String LEGACY_INDEX_FILE_EXTENSION = "-md5.idx"; - private static final Logger logger = Logger.getLogger(HashDbManager.class.getName()); private static HashDbManager instance; - private final String configFilePath = PlatformUtil.getUserConfigDirectory() + File.separator + CUR_HASHSETS_FILE_NAME; + private final String configFilePath = PlatformUtil.getUserConfigDirectory() + File.separator + CONFIG_FILE_NAME; private List knownHashSets = new ArrayList<>(); private List knownBadHashSets = new ArrayList<>(); private Set hashSetNames = new HashSet<>(); @@ -118,171 +94,209 @@ public class HashDbManager { } } - private boolean hashSetsConfigurationFileExists() { - File f = new File(configFilePath); - return f.exists() && f.canRead() && f.canWrite(); - } - + /** + * Gets the extension, without the dot separator, that the SleuthKit requires + * for hash database files that combine the database and the index. + */ public static String getHashDatabaseFileExtension() { return HASH_DATABASE_FILE_EXTENSON; } public class DuplicateHashSetNameException extends Exception { - private DuplicateHashSetNameException() { - super("The hash set name has already been used for another hash database."); + private DuplicateHashSetNameException(String hashSetName) { + super("The hash set name '"+ hashSetName +"' has already been used for another hash database."); } } public class HashDatabaseDoesNotExistException extends Exception { - private HashDatabaseDoesNotExistException() { - super("Attempt to add a hash database that does not exist to the configuration"); + private HashDatabaseDoesNotExistException(String path) { + super("No hash database found at\n" + path); } } - public class FileAlreadyExistsException extends Exception { - private FileAlreadyExistsException() { - super("A hash database file already exists at the selected location."); + public class HashDatabaseFileAlreadyExistsException extends Exception { + private HashDatabaseFileAlreadyExistsException(String path) { + super("A file already exists at\n" + path); } } public class HashDatabaseAlreadyAddedException extends Exception { - private HashDatabaseAlreadyAddedException() { - super("The hash database has already been created."); + private HashDatabaseAlreadyAddedException(String path) { + super("The hash database at\n" + path + "\nhas already been created or imported."); } } public class IllegalHashDatabaseFileNameExtensionException extends Exception { private IllegalHashDatabaseFileNameExtensionException() { - super("The hash database file must have a ." + getHashDatabaseFileExtension() + " extension."); + super("The hash database file name must have a ." + getHashDatabaseFileExtension() + " extension."); } } /** * Adds an existing hash database to the set of hash databases used to classify files as known or known bad. + * Does not save the configuration - the configuration is only saved on demand to support cancellation of + * configuration panels. * @param hashSetName Name used to represent the hash database in user interface components. - * @param filePath Full path to either a hash database file or a hash database index file. + * @param path Full path to either a hash database file or a hash database index file. * @param searchDuringIngest A flag indicating whether or not the hash database should be searched during ingest. * @param sendIngestMessages A flag indicating whether hash set hit messages should be sent as ingest messages. * @param knownFilesType The classification to apply to files whose hashes are found in the hash database. - * @return A HashDb object representation of the hash database. + * @return A HashDb representing the hash database. * @throws HashDatabaseDoesNotExistException, DuplicateHashSetNameException, HashDatabaseAlreadyAddedException, TskCoreException */ - public synchronized HashDb addExistingHashDatabase(String hashSetName, String filePath, boolean searchDuringIngest, boolean sendIngestMessages, KnownFilesType knownFilesType) throws HashDatabaseDoesNotExistException, DuplicateHashSetNameException, HashDatabaseAlreadyAddedException, TskCoreException { - if (new File(filePath).exists()) { - throw new HashDatabaseDoesNotExistException(); + public synchronized HashDb addExistingHashDatabase(String hashSetName, String path, boolean searchDuringIngest, boolean sendIngestMessages, HashDb.KnownFilesType knownFilesType) throws HashDatabaseDoesNotExistException, DuplicateHashSetNameException, HashDatabaseAlreadyAddedException, TskCoreException { + if (!new File(path).exists()) { + throw new HashDatabaseDoesNotExistException(path); + } + + if (hashSetPaths.contains(path)) { + throw new HashDatabaseAlreadyAddedException(path); } if (hashSetNames.contains(hashSetName)) { - throw new DuplicateHashSetNameException(); + throw new DuplicateHashSetNameException(hashSetName); } - if (hashSetPaths.contains(filePath)) { - throw new HashDatabaseAlreadyAddedException(); - } - - int handle = SleuthkitJNI.openHashDatabase(filePath); - HashDb hashDb = new HashDb(handle, SleuthkitJNI.getHashDatabasePath(handle), SleuthkitJNI.getHashDatabaseIndexPath(handle), hashSetName, searchDuringIngest, sendIngestMessages, knownFilesType); - addToConfiguration(hashDb); - return hashDb; + return addHashDatabase(SleuthkitJNI.openHashDatabase(path), hashSetName, searchDuringIngest, sendIngestMessages, knownFilesType); } /** * Adds a new hash database to the set of hash databases used to classify files as known or known bad. + * Does not save the configuration - the configuration is only saved on demand to support cancellation of + * configuration panels. * @param hashSetName Hash set name used to represent the hash database in user interface components. - * @param filePath Full path to the database file to be created. The file name component of the path must have a ".kdb" extension. - * @param useForIngest A flag indicating whether or not the data base should be used during the file ingest process. - * @param showInboxMessages A flag indicating whether messages indicating lookup hits should be sent to the application in box. - * @param hashSetType The type of hash set to associate with the database. - * @return A HashDb object representation of the opened hash database. + * @param path Full path to the database file to be created. + * @param searchDuringIngest A flag indicating whether or not the hash database should be searched during ingest. + * @param sendIngestMessages A flag indicating whether hash set hit messages should be sent as ingest messages. + * @param knownFilesType The classification to apply to files whose hashes are found in the hash database. + * @return A HashDb representing the hash database. * @throws TskCoreException */ - public synchronized HashDb addNewHashDatabase(String hashSetName, String filePath, boolean useForIngest, boolean showInboxMessages, KnownFilesType knownFilesType) throws FileAlreadyExistsException, DuplicateHashSetNameException, HashDatabaseAlreadyAddedException, IllegalHashDatabaseFileNameExtensionException, TskCoreException { - File file = new File(filePath); + public synchronized HashDb addNewHashDatabase(String hashSetName, String path, boolean searchDuringIngest, boolean sendIngestMessages, HashDb.KnownFilesType knownFilesType) throws HashDatabaseFileAlreadyExistsException, IllegalHashDatabaseFileNameExtensionException, DuplicateHashSetNameException, HashDatabaseAlreadyAddedException, TskCoreException { + File file = new File(path); if (file.exists()) { - throw new FileAlreadyExistsException(); + throw new HashDatabaseFileAlreadyExistsException(path); } if (!FilenameUtils.getExtension(file.getName()).equalsIgnoreCase(HASH_DATABASE_FILE_EXTENSON)) { throw new IllegalHashDatabaseFileNameExtensionException(); } + if (hashSetPaths.contains(path)) { + throw new HashDatabaseAlreadyAddedException(path); + } + if (hashSetNames.contains(hashSetName)) { - throw new DuplicateHashSetNameException(); + throw new DuplicateHashSetNameException(hashSetName); } - if (hashSetPaths.contains(filePath)) { - throw new HashDatabaseAlreadyAddedException(); - } - - int handle = SleuthkitJNI.createHashDatabase(filePath); - HashDb hashDb = new HashDb(handle, SleuthkitJNI.getHashDatabasePath(handle), SleuthkitJNI.getHashDatabaseIndexPath(handle), hashSetName, useForIngest, showInboxMessages, knownFilesType); - addToConfiguration(hashDb); - return hashDb; + return addHashDatabase(SleuthkitJNI.createHashDatabase(path), hashSetName, searchDuringIngest, sendIngestMessages, knownFilesType); } - private void addToConfiguration(HashDb hashDb) { + private HashDb addHashDatabase(int handle, String hashSetName, boolean searchDuringIngest, boolean sendIngestMessages, HashDb.KnownFilesType knownFilesType) throws TskCoreException { + HashDb hashDb = new HashDb(handle, hashSetName, searchDuringIngest, sendIngestMessages, knownFilesType); + + // Get the paths before updating the collections since the path + // getting methods may throw. + String databasePath = hashDb.getDatabasePath(); + String indexPath = hashDb.getIndexPath(); + + // Update the collections used to ensure that hash set names are unique + // and the same database is not added to the configuration more than once. hashSetNames.add(hashDb.getHashSetName()); - hashSetPaths.add(hashDb.getDatabasePath()); - hashSetPaths.add(hashDb.getIndexPath()); + hashSetPaths.add(indexPath); + if (!hashDb.hasIndexOnly()) { + hashSetPaths.add(databasePath); + } + + // Add the hash database to the appropriate collection for its type. if (hashDb.getKnownFilesType() == HashDb.KnownFilesType.KNOWN) { knownHashSets.add(hashDb); } else { knownBadHashSets.add(hashDb); - } + } + + return hashDb; } /** - * Removes a hash database from the configuration. Does not save the - * configuration - the configuration is only saved on demand to support - * cancellation of configuration panels. + * Removes a hash database from the set of hash databases used to classify + * files as known or known bad. Does not save the configuration - the + * configuration is only saved on demand to support cancellation of + * configuration panels. + * @throws TskCoreException */ public synchronized void removeHashDatabase(HashDb hashDb) { - try { - hashDb.close(); - } - catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error closing hash database at " + hashDb.getDatabasePath(), ex); - } + // First remove the database from whichever hash set list it occupies, + // and remove its hash set name from the hash set used to ensure unique + // hash set names are used. These operations will succeed and constitute + // a mostly effective removal, even if the subsequent operations fail. knownHashSets.remove(hashDb); knownBadHashSets.remove(hashDb); + hashSetNames.remove(hashDb.getHashSetName()); + + // Now undertake the operations that could fail. + try { + hashSetPaths.remove(hashDb.getIndexPath()); + } + catch (TskCoreException ex) { + Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error getting index path of " + hashDb.getHashSetName() + " hash database when removing the database", ex); + } + try { + if (!hashDb.hasIndexOnly()) { + hashSetPaths.remove(hashDb.getDatabasePath()); + } + } + catch (TskCoreException ex) { + Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error getting database path of " + hashDb.getHashSetName() + " hash database when removing the database", ex); + } + try { + hashDb.close(); + } + catch (TskCoreException ex) { + Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error closing " + hashDb.getHashSetName() + " hash database when removing the database", ex); + } } /** - * Gets all of the configured hash sets. - * @return A list, possibly empty, of HashDb objects representing the hash - * sets. + * Gets all of the hash databases used to classify files as known or known bad. + * @return A list, possibly empty, of hash databases. */ public synchronized List getAllHashSets() { List hashDbs = new ArrayList<>(); hashDbs.addAll(knownHashSets); hashDbs.addAll(knownBadHashSets); - return Collections.unmodifiableList(hashDbs); + return hashDbs; } /** - * Gets the configured known files hash sets. - * @return A list, possibly empty, of HashDb objects. + * Gets all of the hash databases used to classify files as known. + * @return A list, possibly empty, of hash databases. */ - public synchronized List getKnownHashSets() { - return Collections.unmodifiableList(knownHashSets); + public synchronized List getKnownFileHashSets() { + List hashDbs = new ArrayList<>(); + hashDbs.addAll(knownHashSets); + return hashDbs; } /** - * Gets the configured known bad files hash sets. - * @return A list, possibly empty, of HashDb objects. + * Gets all of the hash databases used to classify files as known bad. + * @return A list, possibly empty, of hash databases. */ - public synchronized List getKnownBadHashSets() { - return Collections.unmodifiableList(knownBadHashSets); + public synchronized List getKnownBadFileHashSets() { + List hashDbs = new ArrayList<>(); + hashDbs.addAll(knownBadHashSets); + return hashDbs; } - - /** - * Gets all of the configured hash sets that accept updates. - * @return A list, possibly empty, of HashDb objects. + + /** + * Gets all of the hash databases that accept updates. + * @return A list, possibly empty, of hash databases. */ public synchronized List getUpdateableHashSets() { List updateableDbs = getUpdateableHashSets(knownHashSets); updateableDbs.addAll(getUpdateableHashSets(knownBadHashSets)); - return Collections.unmodifiableList(updateableDbs); + return updateableDbs; } private List getUpdateableHashSets(List hashDbs) { @@ -294,25 +308,25 @@ public class HashDbManager { } } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error checking updateable status of hash database at " + db.getDatabasePath(), ex); + Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error checking updateable status of " + db.getHashSetName() + " hash database", ex); } } return updateableDbs; - } + } /** * Sets the value for the flag that indicates whether hashes should be calculated * for content even if no hash databases are configured. */ - public synchronized void alwaysCalculateHashes(boolean alwaysCalculateHashes) { + public synchronized void setAlwaysCalculateHashes(boolean alwaysCalculateHashes) { this.alwaysCalculateHashes = alwaysCalculateHashes; } /** - * Accesses the flag that indicates whether hashes should be calculated + * Gets the flag that indicates whether hashes should be calculated * for content even if no hash databases are configured. */ - public synchronized boolean shouldAlwaysCalculateHashes() { + public synchronized boolean getAlwaysCalculateHashes() { return alwaysCalculateHashes; } @@ -333,6 +347,7 @@ public class HashDbManager { closeHashDatabases(knownHashSets); closeHashDatabases(knownBadHashSets); hashSetNames.clear(); + hashSetPaths.clear(); if (hashSetsConfigurationFileExists()) { readHashSetsConfigurationFromDisk(); @@ -348,7 +363,7 @@ public class HashDbManager { } } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error closing hash database at " + dbPath, ex); + Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error closing hash database at " + dbPath, ex); } hashDbs.clear(); } @@ -359,33 +374,30 @@ public class HashDbManager { try { DocumentBuilder docBuilder = dbfac.newDocumentBuilder(); Document doc = docBuilder.newDocument(); - Element rootEl = doc.createElement(ROOT_EL); + Element rootEl = doc.createElement(ROOT_ELEMENT); doc.appendChild(rootEl); writeHashDbsToDisk(doc, rootEl, knownHashSets); writeHashDbsToDisk(doc, rootEl, knownBadHashSets); String calcValue = Boolean.toString(alwaysCalculateHashes); - Element setCalc = doc.createElement(SET_CALC); - setCalc.setAttribute(SET_VALUE, calcValue); + Element setCalc = doc.createElement(ALWAYS_CALCULATE_HASHES_ELEMENT); + setCalc.setAttribute(VALUE_ATTRIBUTE, calcValue); rootEl.appendChild(setCalc); success = XMLUtil.saveDoc(HashDbManager.class, configFilePath, ENCODING, doc); } catch (ParserConfigurationException e) { - logger.log(Level.SEVERE, "Error saving hash databases", e); + Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error saving hash databases", e); } return success; } private static void writeHashDbsToDisk(Document doc, Element rootEl, List hashDbs) { for (HashDb db : hashDbs) { - Element setEl = doc.createElement(SET_EL); - setEl.setAttribute(SET_NAME_ATTR, db.getHashSetName()); - setEl.setAttribute(SET_TYPE_ATTR, db.getKnownFilesType().toString()); - setEl.setAttribute(SET_USE_FOR_INGEST_ATTR, Boolean.toString(db.getUseForIngest())); - setEl.setAttribute(SET_SHOW_INBOX_MESSAGES, Boolean.toString(db.getShowInboxMessages())); - String path = null; + // Get the path for the hash database before writing anything, in + // case an exception is thrown. + String path; try { if (db.hasIndexOnly()) { path = db.getIndexPath(); @@ -393,21 +405,32 @@ public class HashDbManager { else { path = db.getDatabasePath(); } - Element pathEl = doc.createElement(PATH_EL); - pathEl.setTextContent(path); - setEl.appendChild(pathEl); - rootEl.appendChild(setEl); } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error getting path of hash database " + db.getHashSetName() + ", unable to save configuration", ex); + Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error getting path of hash database " + db.getHashSetName() + ", discarding from hash database configuration", ex); + continue; } + + Element setElement = doc.createElement(SET_ELEMENT); + setElement.setAttribute(SET_NAME_ATTRIBUTE, db.getHashSetName()); + setElement.setAttribute(SET_TYPE_ATTRIBUTE, db.getKnownFilesType().toString()); + setElement.setAttribute(SEARCH_DURING_INGEST_ATTRIBUTE, Boolean.toString(db.getSearchDuringIngest())); + setElement.setAttribute(SEND_INGEST_MESSAGES_ATTRIBUTE, Boolean.toString(db.getSendIngestMessages())); + Element pathElement = doc.createElement(PATH_ELEMENT); + pathElement.setTextContent(path); + setElement.appendChild(pathElement); + rootEl.appendChild(setElement); } } - // TODO: The return value from this function is never checked. Failure is not indicated to the user. Is this desired? + private boolean hashSetsConfigurationFileExists() { + File f = new File(configFilePath); + return f.exists() && f.canRead() && f.canWrite(); + } + private boolean readHashSetsConfigurationFromDisk() { // Open the XML document that implements the configuration file. - final Document doc = XMLUtil.loadDoc(HashDbManager.class, configFilePath, XSDFILE); + final Document doc = XMLUtil.loadDoc(HashDbManager.class, configFilePath, XSD_FILE_NAME); if (doc == null) { return false; } @@ -415,15 +438,15 @@ public class HashDbManager { // Get the root element. Element root = doc.getDocumentElement(); if (root == null) { - logger.log(Level.SEVERE, "Error loading hash sets: invalid file format."); + Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error loading hash sets: invalid file format."); return false; } // Get the hash set elements. - NodeList setsNList = root.getElementsByTagName(SET_EL); + NodeList setsNList = root.getElementsByTagName(SET_ELEMENT); int numSets = setsNList.getLength(); if(numSets == 0) { - logger.log(Level.WARNING, "No element hash_set exists."); + Logger.getLogger(HashDbManager.class.getName()).log(Level.WARNING, "No element hash_set exists."); } // Create HashDb objects for each hash set element. @@ -433,9 +456,9 @@ public class HashDbManager { for (int i = 0; i < numSets; ++i) { Element setEl = (Element) setsNList.item(i); - String hashSetName = setEl.getAttribute(SET_NAME_ATTR); + String hashSetName = setEl.getAttribute(SET_NAME_ATTRIBUTE); if (hashSetName.isEmpty()) { - logger.log(Level.SEVERE, SET_NAME_ATTR + attributeErrorMessage, i); + Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, SET_NAME_ATTRIBUTE + attributeErrorMessage, i); continue; } @@ -452,71 +475,70 @@ public class HashDbManager { hashSetName = newHashSetName; } - String knownFilesType = setEl.getAttribute(SET_TYPE_ATTR); + String knownFilesType = setEl.getAttribute(SET_TYPE_ATTRIBUTE); if(knownFilesType.isEmpty()) { - logger.log(Level.SEVERE, SET_TYPE_ATTR + attributeErrorMessage, i); + Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, SET_TYPE_ATTRIBUTE + attributeErrorMessage, i); continue; } // Handle legacy known files types. if (knownFilesType.equals("NSRL")) { - knownFilesType = KnownFilesType.KNOWN.toString(); + knownFilesType = HashDb.KnownFilesType.KNOWN.toString(); } - final String useForIngest = setEl.getAttribute(SET_USE_FOR_INGEST_ATTR); - if (useForIngest.isEmpty()) { - logger.log(Level.SEVERE, SET_USE_FOR_INGEST_ATTR + attributeErrorMessage, i); + final String searchDuringIngest = setEl.getAttribute(SEARCH_DURING_INGEST_ATTRIBUTE); + if (searchDuringIngest.isEmpty()) { + Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, SEARCH_DURING_INGEST_ATTRIBUTE + attributeErrorMessage, i); continue; } - Boolean useForIngestFlag = Boolean.parseBoolean(useForIngest); + Boolean seearchDuringIngestFlag = Boolean.parseBoolean(searchDuringIngest); - final String showInboxMessages = setEl.getAttribute(SET_SHOW_INBOX_MESSAGES); - if (useForIngest.isEmpty()) { - logger.log(Level.SEVERE, SET_SHOW_INBOX_MESSAGES + attributeErrorMessage, i); + final String sendIngestMessages = setEl.getAttribute(SEND_INGEST_MESSAGES_ATTRIBUTE); + if (searchDuringIngest.isEmpty()) { + Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, SEND_INGEST_MESSAGES_ATTRIBUTE + attributeErrorMessage, i); continue; } - Boolean showInboxMessagesFlag = Boolean.parseBoolean(showInboxMessages); + Boolean sendIngestMessagesFlag = Boolean.parseBoolean(sendIngestMessages); String dbPath; - NodeList pathsNList = setEl.getElementsByTagName(PATH_EL); + NodeList pathsNList = setEl.getElementsByTagName(PATH_ELEMENT); if (pathsNList.getLength() > 0) { Element pathEl = (Element) pathsNList.item(0); // Shouldn't be more than one. dbPath = pathEl.getTextContent(); if (dbPath.isEmpty()) { - logger.log(Level.SEVERE, PATH_EL + elementErrorMessage, i); + Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, PATH_ELEMENT + elementErrorMessage, i); continue; } } else { - logger.log(Level.SEVERE, PATH_EL + elementErrorMessage, i); + Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, PATH_ELEMENT + elementErrorMessage, i); continue; } dbPath = getValidFilePath(hashSetName, dbPath); if (null != dbPath) { try { - addHashSet(HashDb.openHashDatabase(hashSetName, dbPath, useForIngestFlag, showInboxMessagesFlag, KnownFilesType.valueOf(knownFilesType))); - hashSetNames.add(hashSetName); + addExistingHashDatabase(hashSetName, dbPath, seearchDuringIngestFlag, sendIngestMessagesFlag, HashDb.KnownFilesType.valueOf(knownFilesType)); } - catch (TskCoreException ex) { + catch (HashDatabaseDoesNotExistException | DuplicateHashSetNameException | HashDatabaseAlreadyAddedException | TskCoreException ex) { Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error opening hash database", ex); JOptionPane.showMessageDialog(null, "Unable to open " + dbPath + " hash database.", "Open Hash Database Error", JOptionPane.ERROR_MESSAGE); } } else { - logger.log(Level.WARNING, "No valid path for hash_set at index {0}, cannot make instance of HashDb class", i); + Logger.getLogger(HashDbManager.class.getName()).log(Level.WARNING, "No valid path for hash_set at index {0}, cannot make instance of HashDb class", i); } } // Get the element that stores the always calculate hashes flag. - NodeList calcList = root.getElementsByTagName(SET_CALC); + NodeList calcList = root.getElementsByTagName(ALWAYS_CALCULATE_HASHES_ELEMENT); if (calcList.getLength() > 0) { Element calcEl = (Element) calcList.item(0); // Shouldn't be more than one. - final String value = calcEl.getAttribute(SET_VALUE); + final String value = calcEl.getAttribute(VALUE_ATTRIBUTE); alwaysCalculateHashes = Boolean.parseBoolean(value); } else { - logger.log(Level.WARNING, " element "); + Logger.getLogger(HashDbManager.class.getName()).log(Level.WARNING, " element "); alwaysCalculateHashes = false; } @@ -566,7 +588,7 @@ public class HashDbManager { filePath = f.getCanonicalPath(); } catch (IOException ex) { - logger.log(Level.WARNING, "Couldn't get selected file path", ex); + Logger.getLogger(HashDbManager.class.getName()).log(Level.WARNING, "Couldn't get selected file path", ex); } } return filePath; @@ -576,6 +598,26 @@ public class HashDbManager { * Instances of this class represent hash databases used to classify files as known or know bad. */ public static class HashDb { + + /** + * Indicates how files with hashes stored in a particular hash database + * object should be classified. + */ + public enum KnownFilesType{ + KNOWN("Known"), + KNOWN_BAD("Known Bad"); + + private String displayName; + + private KnownFilesType(String displayName) { + this.displayName = displayName; + } + + public String getDisplayName() { + return this.displayName; + } + } + /** * Property change events published by hash database objects. */ @@ -584,53 +626,19 @@ public class HashDbManager { } private int handle; - private KnownFilesType knownFilesType; - private String databasePath; - private String indexPath; private String hashSetName; - private boolean useForIngest; - private boolean sendHitMessages; + private boolean searchDuringIngest; + private boolean sendIngestMessages; + private KnownFilesType knownFilesType; private boolean indexing; private final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this); - /** - * Opens an existing hash database. - * @param hashSetName Name used to represent the hash database in user interface components. - * @param selectedFilePath Full path to either a hash database file or a hash database index file. - * @param useForIngest A flag indicating whether or not the hash database should be used during ingest. - * @param sendHitMessages A flag indicating whether hash set hit messages should be sent to the application in box. - * @param knownFilesType The classification to apply to files whose hashes are stored in the hash database. - * @return A HashDb object representation of the new hash database. - * @throws TskCoreException - */ - public static HashDb openHashDatabase(String hashSetName, String selectedFilePath, boolean useForIngest, boolean sendHitMessages, KnownFilesType knownFilesType) throws TskCoreException { - int handle = SleuthkitJNI.openHashDatabase(selectedFilePath); - return new HashDb(handle, SleuthkitJNI.getHashDatabasePath(handle), SleuthkitJNI.getHashDatabaseIndexPath(handle), hashSetName, useForIngest, sendHitMessages, knownFilesType); - } - - /** - * Creates a new hash database. - * @param hashSetName Hash set name used to represent the hash database in user interface components. - * @param databasePath Full path to the database file to be created. The file name component of the path must have a ".kdb" extension. - * @param useForIngest A flag indicating whether or not the data base should be used during the file ingest process. - * @param showInboxMessages A flag indicating whether messages indicating lookup hits should be sent to the application in box. - * @param hashSetType The type of hash set to associate with the database. - * @return A HashDb object representation of the opened hash database. - * @throws TskCoreException - */ - public static HashDb createHashDatabase(String hashSetName, String databasePath, boolean useForIngest, boolean showInboxMessages, KnownFilesType knownFilesType) throws TskCoreException { - int handle = SleuthkitJNI.createHashDatabase(databasePath); - return new HashDb(handle, SleuthkitJNI.getHashDatabasePath(handle), SleuthkitJNI.getHashDatabaseIndexPath(handle), hashSetName, useForIngest, showInboxMessages, knownFilesType); - } - - private HashDb(int handle, String databasePath, String indexPath, String name, boolean useForIngest, boolean sendHitMessages, KnownFilesType knownFilesType) { - this.databasePath = databasePath; - this.indexPath = indexPath; - this.hashSetName = name; - this.useForIngest = useForIngest; - this.sendHitMessages = sendHitMessages; - this.knownFilesType = knownFilesType; + private HashDb(int handle, String hashSetName, boolean useForIngest, boolean sendHitMessages, KnownFilesType knownFilesType) { this.handle = handle; + this.hashSetName = hashSetName; + this.searchDuringIngest = useForIngest; + this.sendIngestMessages = sendHitMessages; + this.knownFilesType = knownFilesType; this.indexing = false; } @@ -652,45 +660,45 @@ public class HashDbManager { return hashSetName; } - public String getDatabasePath() { - return databasePath; + public String getDatabasePath() throws TskCoreException { + return SleuthkitJNI.getHashDatabasePath(handle); } - public String getIndexPath() { - return indexPath; + public String getIndexPath() throws TskCoreException { + return SleuthkitJNI.getHashDatabaseIndexPath(handle); } public KnownFilesType getKnownFilesType() { return knownFilesType; } - public boolean getUseForIngest() { - return useForIngest; + public boolean getSearchDuringIngest() { + return searchDuringIngest; } - void setUseForIngest(boolean useForIngest) { - this.useForIngest = useForIngest; + void setSearchDuringIngest(boolean useForIngest) { + this.searchDuringIngest = useForIngest; } - public boolean getShowInboxMessages() { - return sendHitMessages; + public boolean getSendIngestMessages() { + return sendIngestMessages; } - void setShowInboxMessages(boolean showInboxMessages) { - this.sendHitMessages = showInboxMessages; + void setSendIngestMessages(boolean showInboxMessages) { + this.sendIngestMessages = showInboxMessages; } public boolean hasLookupIndex() throws TskCoreException { return SleuthkitJNI.hashDatabaseHasLookupIndex(handle); } - public boolean canBeReindexed() throws TskCoreException { - return SleuthkitJNI.hashDatabaseCanBeReindexed(handle); - } - public boolean hasIndexOnly() throws TskCoreException { return SleuthkitJNI.hashDatabaseHasLegacyLookupIndexOnly(handle); } + + public boolean canBeReIndexed() throws TskCoreException { + return SleuthkitJNI.hashDatabaseCanBeReindexed(handle); + } /** * Indicates whether the hash database accepts updates. @@ -705,8 +713,8 @@ public class HashDbManager { * @param content The content for which the calculated hashes, if any, are to be added to the hash database. * @throws TskCoreException */ - public void add(Content content) throws TskCoreException { - add(content, null); + public void addHashes(Content content) throws TskCoreException { + addHashes(content, null); } /** @@ -715,7 +723,7 @@ public class HashDbManager { * @param comment A comment to associate with the hashes, e.g., the name of the case in which the content was encountered. * @throws TskCoreException */ - public void add(Content content, String comment) throws TskCoreException { + public void addHashes(Content content, String comment) throws TskCoreException { // TODO: This only works for AbstractFiles at present. Change when Content // can be queried for hashes. assert content instanceof AbstractFile; @@ -728,13 +736,11 @@ public class HashDbManager { } } - public boolean hasHashOfContent(Content content) throws TskCoreException { + public boolean hasMd5HashOf(Content content) throws TskCoreException { boolean result = false; - // TODO: This only works for AbstractFiles at present. Change when Content can be queried for hashes. assert content instanceof AbstractFile; if (content instanceof AbstractFile) { AbstractFile file = (AbstractFile)content; - // TODO: Add support for SHA-1 and SHA-256 hashes. if (null != file.getMd5Hash()) { result = SleuthkitJNI.lookupInHashDatabase(file.getMd5Hash(), handle); } @@ -783,7 +789,6 @@ public class HashDbManager { progress.switchToIndeterminate(); try { SleuthkitJNI.createLookupIndexForHashDatabase(handle, deleteIndexFile); - indexPath = SleuthkitJNI.getHashDatabaseIndexPath(handle); } catch (TskCoreException ex) { Logger.getLogger(HashDb.class.getName()).log(Level.SEVERE, "Error indexing hash database", ex); @@ -800,7 +805,7 @@ public class HashDbManager { } } - public void close() throws TskCoreException { + private void close() throws TskCoreException { SleuthkitJNI.closeHashDatabase(handle); } } diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbSimpleConfigPanel.form b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbSimpleConfigPanel.form index f25f5bdc32..75a4788a57 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbSimpleConfigPanel.form +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbSimpleConfigPanel.form @@ -28,7 +28,7 @@ - + @@ -46,7 +46,7 @@ - + @@ -92,10 +92,10 @@ - + - + diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbSimpleConfigPanel.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbSimpleConfigPanel.java index bd8388fc62..02cce28f49 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbSimpleConfigPanel.java +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbSimpleConfigPanel.java @@ -30,6 +30,7 @@ import javax.swing.table.TableColumn; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.autopsy.hashdatabase.HashDbManager.HashDb; /** * Instances of this class provide a simplified UI for managing the hash sets configuration. @@ -39,8 +40,8 @@ public class HashDbSimpleConfigPanel extends javax.swing.JPanel { private HashDbsTableModel knownBadTableModel; public HashDbSimpleConfigPanel() { - knownTableModel = new HashDbsTableModel(HashDbManager.getInstance().getKnownHashSets()); - knownBadTableModel = new HashDbsTableModel(HashDbManager.getInstance().getKnownBadHashSets()); + knownTableModel = new HashDbsTableModel(HashDbManager.getInstance().getKnownFileHashSets()); + knownBadTableModel = new HashDbsTableModel(HashDbManager.getInstance().getKnownBadFileHashSets()); initComponents(); customizeComponents(); } @@ -51,10 +52,10 @@ public class HashDbSimpleConfigPanel extends javax.swing.JPanel { // Add a listener to the always calculate hashes checkbox component. // The listener passes the user's selection on to the hash database manager. - calcHashesButton.addActionListener( new ActionListener() { + alwaysCalcHashesCheckbox.addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - HashDbManager.getInstance().alwaysCalculateHashes(calcHashesButton.isSelected()); + HashDbManager.getInstance().setAlwaysCalculateHashes(alwaysCalcHashesCheckbox.isSelected()); } }); @@ -82,33 +83,33 @@ public class HashDbSimpleConfigPanel extends javax.swing.JPanel { public void refreshComponents() { knownTableModel.refresh(); knownBadTableModel.refresh(); - refreshAlwaysCalcHashesComponents(); + refreshAlwaysCalcHashesCheckbox(); } - private void refreshAlwaysCalcHashesComponents() { + private void refreshAlwaysCalcHashesCheckbox() { boolean noHashDbsConfiguredForIngest = true; for (HashDb hashDb : HashDbManager.getInstance().getAllHashSets()) { try { - if (hashDb.getUseForIngest()== true && hashDb.hasLookupIndex()) { + if (hashDb.getSearchDuringIngest()== true && hashDb.hasLookupIndex()) { noHashDbsConfiguredForIngest = false; break; } } catch (TskCoreException ex) { - Logger.getLogger(HashDbSimpleConfigPanel.class.getName()).log(Level.SEVERE, "Error getting info for hash database at " + hashDb.getDatabasePath(), ex); + Logger.getLogger(HashDbSimpleConfigPanel.class.getName()).log(Level.SEVERE, "Error getting info for " + hashDb.getHashSetName() + " hash database", ex); } } // If there are no hash databases configured for use during file ingest, // default to always calculating hashes of the files. if (noHashDbsConfiguredForIngest) { - calcHashesButton.setEnabled(true); - calcHashesButton.setSelected(true); - HashDbManager.getInstance().alwaysCalculateHashes(true); + alwaysCalcHashesCheckbox.setEnabled(true); + alwaysCalcHashesCheckbox.setSelected(true); + HashDbManager.getInstance().setAlwaysCalculateHashes(true); } else { - calcHashesButton.setEnabled(false); - calcHashesButton.setSelected(false); - HashDbManager.getInstance().alwaysCalculateHashes(false); + alwaysCalcHashesCheckbox.setEnabled(false); + alwaysCalcHashesCheckbox.setSelected(false); + HashDbManager.getInstance().setAlwaysCalculateHashes(false); } } @@ -137,7 +138,7 @@ public class HashDbSimpleConfigPanel extends javax.swing.JPanel { public Object getValueAt(int rowIndex, int columnIndex) { HashDb db = hashDbs.get(rowIndex); if (columnIndex == 0) { - return db.getUseForIngest(); + return db.getSearchDuringIngest(); } else { return db.getHashSetName(); } @@ -157,10 +158,10 @@ public class HashDbSimpleConfigPanel extends javax.swing.JPanel { dbHasIndex = db.hasLookupIndex(); } catch (TskCoreException ex) { - Logger.getLogger(HashDbSimpleConfigPanel.class.getName()).log(Level.SEVERE, "Error getting info for hash database at " + db.getDatabasePath(), ex); + Logger.getLogger(HashDbSimpleConfigPanel.class.getName()).log(Level.SEVERE, "Error getting info for " + db.getHashSetName() + " hash database", ex); } if(((Boolean) getValueAt(rowIndex, columnIndex)) || dbHasIndex) { - db.setUseForIngest((Boolean) aValue); + db.setSearchDuringIngest((Boolean) aValue); } else { JOptionPane.showMessageDialog(HashDbSimpleConfigPanel.this, "Hash databases must be indexed before they can be used for ingest"); @@ -187,7 +188,7 @@ public class HashDbSimpleConfigPanel extends javax.swing.JPanel { knownHashTable = new javax.swing.JTable(); knownBadHashDbsLabel = new javax.swing.JLabel(); knownHashDbsLabel = new javax.swing.JLabel(); - calcHashesButton = new javax.swing.JCheckBox(); + alwaysCalcHashesCheckbox = new javax.swing.JCheckBox(); jScrollPane2 = new javax.swing.JScrollPane(); knownBadHashTable = new javax.swing.JTable(); @@ -202,7 +203,7 @@ public class HashDbSimpleConfigPanel extends javax.swing.JPanel { knownHashDbsLabel.setText(org.openide.util.NbBundle.getMessage(HashDbSimpleConfigPanel.class, "HashDbSimpleConfigPanel.knownHashDbsLabel.text")); // NOI18N - calcHashesButton.setText(org.openide.util.NbBundle.getMessage(HashDbSimpleConfigPanel.class, "HashDbSimpleConfigPanel.calcHashesButton.text")); // NOI18N + alwaysCalcHashesCheckbox.setText(org.openide.util.NbBundle.getMessage(HashDbSimpleConfigPanel.class, "HashDbSimpleConfigPanel.alwaysCalcHashesCheckbox.text")); // NOI18N jScrollPane2.setBorder(javax.swing.BorderFactory.createEtchedBorder()); @@ -233,7 +234,7 @@ public class HashDbSimpleConfigPanel extends javax.swing.JPanel { .addComponent(knownBadHashDbsLabel)) .addGap(0, 0, Short.MAX_VALUE)) .addComponent(jScrollPane2, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE) - .addComponent(calcHashesButton, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addComponent(alwaysCalcHashesCheckbox, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addContainerGap()) ); layout.setVerticalGroup( @@ -248,13 +249,13 @@ public class HashDbSimpleConfigPanel extends javax.swing.JPanel { .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jScrollPane2, javax.swing.GroupLayout.PREFERRED_SIZE, 55, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(calcHashesButton) + .addComponent(alwaysCalcHashesCheckbox) .addContainerGap()) ); }// //GEN-END:initComponents // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JCheckBox calcHashesButton; + private javax.swing.JCheckBox alwaysCalcHashesCheckbox; private javax.swing.JScrollPane jScrollPane1; private javax.swing.JScrollPane jScrollPane2; private javax.swing.JLabel knownBadHashDbsLabel; diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/ModalNoButtons.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/ModalNoButtons.java index 432a4011f2..1218a34bc7 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/ModalNoButtons.java +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/ModalNoButtons.java @@ -28,6 +28,7 @@ import java.util.logging.Level; import javax.swing.JOptionPane; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.autopsy.hashdatabase.HashDbManager.HashDb; /** * This class exists as a stop-gap measure to force users to have an indexed database. From 1c216923529cf9a0d6d5937274ae87130c3e0a14 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Mon, 2 Dec 2013 17:14:00 -0500 Subject: [PATCH 09/12] Fixed error in implementation of HashDbManager.closeDatabases() --- .../autopsy/hashdatabase/HashDbManager.java | 85 ++++++++++--------- 1 file changed, 47 insertions(+), 38 deletions(-) diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbManager.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbManager.java index 75f7139f98..3880142572 100755 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbManager.java +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbManager.java @@ -70,13 +70,13 @@ public class HashDbManager { private static final String VALUE_ATTRIBUTE = "value"; private static final String HASH_DATABASE_FILE_EXTENSON = "kdb"; private static final String LEGACY_INDEX_FILE_EXTENSION = "-md5.idx"; - private static HashDbManager instance; + private static HashDbManager instance = null; private final String configFilePath = PlatformUtil.getUserConfigDirectory() + File.separator + CONFIG_FILE_NAME; private List knownHashSets = new ArrayList<>(); private List knownBadHashSets = new ArrayList<>(); private Set hashSetNames = new HashSet<>(); private Set hashSetPaths = new HashSet<>(); - private boolean alwaysCalculateHashes; + private boolean alwaysCalculateHashes = false; /** * Gets the singleton instance of this class. @@ -96,37 +96,38 @@ public class HashDbManager { /** * Gets the extension, without the dot separator, that the SleuthKit requires - * for hash database files that combine the database and the index. + * for the hash database files that combine a database and an index and can + * therefore be updated. */ - public static String getHashDatabaseFileExtension() { + static String getHashDatabaseFileExtension() { return HASH_DATABASE_FILE_EXTENSON; } - public class DuplicateHashSetNameException extends Exception { + class DuplicateHashSetNameException extends Exception { private DuplicateHashSetNameException(String hashSetName) { super("The hash set name '"+ hashSetName +"' has already been used for another hash database."); } } - public class HashDatabaseDoesNotExistException extends Exception { + class HashDatabaseDoesNotExistException extends Exception { private HashDatabaseDoesNotExistException(String path) { super("No hash database found at\n" + path); } } - public class HashDatabaseFileAlreadyExistsException extends Exception { + class HashDatabaseFileAlreadyExistsException extends Exception { private HashDatabaseFileAlreadyExistsException(String path) { super("A file already exists at\n" + path); } } - public class HashDatabaseAlreadyAddedException extends Exception { + class HashDatabaseAlreadyAddedException extends Exception { private HashDatabaseAlreadyAddedException(String path) { super("The hash database at\n" + path + "\nhas already been created or imported."); } } - public class IllegalHashDatabaseFileNameExtensionException extends Exception { + class IllegalHashDatabaseFileNameExtensionException extends Exception { private IllegalHashDatabaseFileNameExtensionException() { super("The hash database file name must have a ." + getHashDatabaseFileExtension() + " extension."); } @@ -144,7 +145,7 @@ public class HashDbManager { * @return A HashDb representing the hash database. * @throws HashDatabaseDoesNotExistException, DuplicateHashSetNameException, HashDatabaseAlreadyAddedException, TskCoreException */ - public synchronized HashDb addExistingHashDatabase(String hashSetName, String path, boolean searchDuringIngest, boolean sendIngestMessages, HashDb.KnownFilesType knownFilesType) throws HashDatabaseDoesNotExistException, DuplicateHashSetNameException, HashDatabaseAlreadyAddedException, TskCoreException { + synchronized HashDb addExistingHashDatabase(String hashSetName, String path, boolean searchDuringIngest, boolean sendIngestMessages, HashDb.KnownFilesType knownFilesType) throws HashDatabaseDoesNotExistException, DuplicateHashSetNameException, HashDatabaseAlreadyAddedException, TskCoreException { if (!new File(path).exists()) { throw new HashDatabaseDoesNotExistException(path); } @@ -172,7 +173,7 @@ public class HashDbManager { * @return A HashDb representing the hash database. * @throws TskCoreException */ - public synchronized HashDb addNewHashDatabase(String hashSetName, String path, boolean searchDuringIngest, boolean sendIngestMessages, HashDb.KnownFilesType knownFilesType) throws HashDatabaseFileAlreadyExistsException, IllegalHashDatabaseFileNameExtensionException, DuplicateHashSetNameException, HashDatabaseAlreadyAddedException, TskCoreException { + synchronized HashDb addNewHashDatabase(String hashSetName, String path, boolean searchDuringIngest, boolean sendIngestMessages, HashDb.KnownFilesType knownFilesType) throws HashDatabaseFileAlreadyExistsException, IllegalHashDatabaseFileNameExtensionException, DuplicateHashSetNameException, HashDatabaseAlreadyAddedException, TskCoreException { File file = new File(path); if (file.exists()) { throw new HashDatabaseFileAlreadyExistsException(path); @@ -195,16 +196,17 @@ public class HashDbManager { private HashDb addHashDatabase(int handle, String hashSetName, boolean searchDuringIngest, boolean sendIngestMessages, HashDb.KnownFilesType knownFilesType) throws TskCoreException { HashDb hashDb = new HashDb(handle, hashSetName, searchDuringIngest, sendIngestMessages, knownFilesType); - // Get the paths before updating the collections since the path - // getting methods may throw. + // Get the indentity data before updating the collections since the + // accessor methods may throw. String databasePath = hashDb.getDatabasePath(); String indexPath = hashDb.getIndexPath(); + boolean hasIndexOnly = hashDb.hasIndexOnly(); // Update the collections used to ensure that hash set names are unique // and the same database is not added to the configuration more than once. hashSetNames.add(hashDb.getHashSetName()); hashSetPaths.add(indexPath); - if (!hashDb.hasIndexOnly()) { + if (hasIndexOnly) { hashSetPaths.add(databasePath); } @@ -226,16 +228,16 @@ public class HashDbManager { * configuration panels. * @throws TskCoreException */ - public synchronized void removeHashDatabase(HashDb hashDb) { - // First remove the database from whichever hash set list it occupies, + synchronized void removeHashDatabase(HashDb hashDb) { + // Remove the database from whichever hash set list it occupies, // and remove its hash set name from the hash set used to ensure unique - // hash set names are used. These operations will succeed and constitute + // hash set names are used, before undertaking These operations will succeed and constitute // a mostly effective removal, even if the subsequent operations fail. knownHashSets.remove(hashDb); knownBadHashSets.remove(hashDb); hashSetNames.remove(hashDb.getHashSetName()); - // Now undertake the operations that could fail. + // Now undertake the operations that could throw. try { hashSetPaths.remove(hashDb.getIndexPath()); } @@ -318,7 +320,7 @@ public class HashDbManager { * Sets the value for the flag that indicates whether hashes should be calculated * for content even if no hash databases are configured. */ - public synchronized void setAlwaysCalculateHashes(boolean alwaysCalculateHashes) { + synchronized void setAlwaysCalculateHashes(boolean alwaysCalculateHashes) { this.alwaysCalculateHashes = alwaysCalculateHashes; } @@ -326,7 +328,7 @@ public class HashDbManager { * Gets the flag that indicates whether hashes should be calculated * for content even if no hash databases are configured. */ - public synchronized boolean getAlwaysCalculateHashes() { + synchronized boolean getAlwaysCalculateHashes() { return alwaysCalculateHashes; } @@ -354,18 +356,16 @@ public class HashDbManager { } } - private void closeHashDatabases(List hashDbs) { - String dbPath = ""; - try { - for (HashDb db : hashDbs) { - dbPath = db.getDatabasePath(); - db.close(); + private void closeHashDatabases(List hashDatabases) { + for (HashDb database : hashDatabases) { + try { + database.close(); + } + catch (TskCoreException ex) { + Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error closing " + database.getHashSetName() + " hash database", ex); } } - catch (TskCoreException ex) { - Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error closing hash database at " + dbPath, ex); - } - hashDbs.clear(); + hashDatabases.clear(); } private boolean writeHashSetConfigurationToDisk() { @@ -387,8 +387,8 @@ public class HashDbManager { success = XMLUtil.saveDoc(HashDbManager.class, configFilePath, ENCODING, doc); } - catch (ParserConfigurationException e) { - Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error saving hash databases", e); + catch (ParserConfigurationException ex) { + Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error saving hash databases", ex); } return success; } @@ -449,8 +449,8 @@ public class HashDbManager { Logger.getLogger(HashDbManager.class.getName()).log(Level.WARNING, "No element hash_set exists."); } - // Create HashDb objects for each hash set element. - // TODO: Does this code implement the correct policy for handling a malformed config file? + // Create HashDb objects for each hash set element. Skip to the next hash database if the definition of + // a particular hash database is not well-formed. String attributeErrorMessage = " attribute was not set for hash_set at index {0}, cannot make instance of HashDb class"; String elementErrorMessage = " element was not set for hash_set at index {0}, cannot make instance of HashDb class"; for (int i = 0; i < numSets; ++i) { @@ -594,6 +594,17 @@ public class HashDbManager { return filePath; } + @Override + protected void finalize() throws Throwable { + try { + closeHashDatabases(knownHashSets); + closeHashDatabases(knownBadHashSets); + } + finally { + super.finalize(); + } + } + /** * Instances of this class represent hash databases used to classify files as known or know bad. */ @@ -724,14 +735,12 @@ public class HashDbManager { * @throws TskCoreException */ public void addHashes(Content content, String comment) throws TskCoreException { - // TODO: This only works for AbstractFiles at present. Change when Content - // can be queried for hashes. + // TODO: This only works for AbstractFiles and MD5 hashes at present. assert content instanceof AbstractFile; if (content instanceof AbstractFile) { AbstractFile file = (AbstractFile)content; - // TODO: Add support for SHA-1 and SHA-256 hashes. if (null != file.getMd5Hash()) { - SleuthkitJNI.addToHashDatabase(file.getName(), file.getMd5Hash(), null, null, comment, handle); + SleuthkitJNI.addToHashDatabase(null, file.getMd5Hash(), null, null, comment, handle); } } } From 7ba22bae162dc44e501f5988903a8854f9946479 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Mon, 2 Dec 2013 18:03:22 -0500 Subject: [PATCH 10/12] Fixed bug introduced in recently improved version of HashDbManager.addHashDatabase() --- .../src/org/sleuthkit/autopsy/hashdatabase/HashDbManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbManager.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbManager.java index 3880142572..af22876bf7 100755 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbManager.java +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbManager.java @@ -206,7 +206,7 @@ public class HashDbManager { // and the same database is not added to the configuration more than once. hashSetNames.add(hashDb.getHashSetName()); hashSetPaths.add(indexPath); - if (hasIndexOnly) { + if (!hasIndexOnly) { hashSetPaths.add(databasePath); } From b80d43a0d0203cb483c988690ade54a00a9b3dda Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Mon, 2 Dec 2013 19:07:05 -0500 Subject: [PATCH 11/12] Routed hash db indexing through mgr to get index path --- .../autopsy/hashdatabase/HashDbManager.java | 137 ++++++++++-------- .../autopsy/hashdatabase/ModalNoButtons.java | 7 +- 2 files changed, 82 insertions(+), 62 deletions(-) diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbManager.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbManager.java index af22876bf7..adc953b254 100755 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbManager.java +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbManager.java @@ -19,6 +19,7 @@ package org.sleuthkit.autopsy.hashdatabase; +import java.beans.PropertyChangeEvent; import java.io.File; import java.io.IOException; import java.util.ArrayList; @@ -54,7 +55,7 @@ import org.sleuthkit.datamodel.TskCoreException; * This class implements a singleton that manages the set of hash databases * used to classify files as unknown, known or known bad. */ -public class HashDbManager { +public class HashDbManager implements PropertyChangeListener { private static final String ROOT_ELEMENT = "hash_sets"; private static final String SET_ELEMENT = "hash_set"; @@ -194,20 +195,22 @@ public class HashDbManager { } private HashDb addHashDatabase(int handle, String hashSetName, boolean searchDuringIngest, boolean sendIngestMessages, HashDb.KnownFilesType knownFilesType) throws TskCoreException { + // Wrap an object around the handle. HashDb hashDb = new HashDb(handle, hashSetName, searchDuringIngest, sendIngestMessages, knownFilesType); // Get the indentity data before updating the collections since the // accessor methods may throw. String databasePath = hashDb.getDatabasePath(); String indexPath = hashDb.getIndexPath(); - boolean hasIndexOnly = hashDb.hasIndexOnly(); // Update the collections used to ensure that hash set names are unique // and the same database is not added to the configuration more than once. hashSetNames.add(hashDb.getHashSetName()); - hashSetPaths.add(indexPath); - if (!hasIndexOnly) { - hashSetPaths.add(databasePath); + if (!databasePath.equals("None")) { + hashSetPaths.add(databasePath); + } + if (!indexPath.equals("None")) { + hashSetPaths.add(indexPath); } // Add the hash database to the appropriate collection for its type. @@ -221,6 +224,30 @@ public class HashDbManager { return hashDb; } + synchronized void indexHashDatabase(HashDb hashDb, boolean deleteIndexFile) { + hashDb.addPropertyChangeListener(this); + HashDbIndexer creator = new HashDbIndexer(hashDb, deleteIndexFile); + creator.execute(); + } + + @Override + public void propertyChange(PropertyChangeEvent event) { + if (event.getPropertyName().equals(HashDb.Event.INDEXING_DONE.name())) { + HashDb hashDb = (HashDb)event.getNewValue(); + if (null != hashDb) { + try { + String indexPath = hashDb.getIndexPath(); + if (!indexPath.equals("None")) { + hashSetPaths.add(indexPath); + } + } + catch (TskCoreException ex) { + Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error getting index path of " + hashDb.getHashSetName() + " hash database after indexing", ex); + } + } + } + } + /** * Removes a hash database from the set of hash databases used to classify * files as known or known bad. Does not save the configuration - the @@ -699,18 +726,6 @@ public class HashDbManager { this.sendIngestMessages = showInboxMessages; } - public boolean hasLookupIndex() throws TskCoreException { - return SleuthkitJNI.hashDatabaseHasLookupIndex(handle); - } - - public boolean hasIndexOnly() throws TskCoreException { - return SleuthkitJNI.hashDatabaseHasLegacyLookupIndexOnly(handle); - } - - public boolean canBeReIndexed() throws TskCoreException { - return SleuthkitJNI.hashDatabaseCanBeReindexed(handle); - } - /** * Indicates whether the hash database accepts updates. * @return True if the database accepts updates, false otherwise. @@ -759,11 +774,10 @@ public class HashDbManager { public HashInfo lookUp(Content content) throws TskCoreException { HashInfo result = null; - // TODO: This only works for AbstractFiles at present. Change when Content can be queried for hashes. + // TODO: This only works for AbstractFiles and MD5 hashes at present. assert content instanceof AbstractFile; if (content instanceof AbstractFile) { AbstractFile file = (AbstractFile)content; - // TODO: Add support for SHA-1 and SHA-256 hashes. if (null != file.getMd5Hash()) { result = SleuthkitJNI.lookupInHashDatabaseVerbose(file.getMd5Hash(), handle); } @@ -771,51 +785,58 @@ public class HashDbManager { return result; } + boolean hasLookupIndex() throws TskCoreException { + return SleuthkitJNI.hashDatabaseHasLookupIndex(handle); + } + + boolean hasIndexOnly() throws TskCoreException { + return SleuthkitJNI.hashDatabaseHasLegacyLookupIndexOnly(handle); + } + + boolean canBeReIndexed() throws TskCoreException { + return SleuthkitJNI.hashDatabaseCanBeReindexed(handle); + } + boolean isIndexing() { return indexing; } - // Tries to index the database (overwrites any existing index) using a - // SwingWorker. - void createIndex(boolean deleteIndexFile) { - CreateIndex creator = new CreateIndex(deleteIndexFile); - creator.execute(); - } - - private class CreateIndex extends SwingWorker { - private ProgressHandle progress; - private boolean deleteIndexFile; - - CreateIndex(boolean deleteIndexFile) { - this.deleteIndexFile = deleteIndexFile; - }; - - @Override - protected Object doInBackground() { - indexing = true; - progress = ProgressHandleFactory.createHandle("Indexing " + hashSetName); - progress.start(); - progress.switchToIndeterminate(); - try { - SleuthkitJNI.createLookupIndexForHashDatabase(handle, deleteIndexFile); - } - catch (TskCoreException ex) { - Logger.getLogger(HashDb.class.getName()).log(Level.SEVERE, "Error indexing hash database", ex); - JOptionPane.showMessageDialog(null, "Error indexing hash database for " + getHashSetName() + ".", "Hash Database Index Error", JOptionPane.ERROR_MESSAGE); - } - return null; - } - - @Override - protected void done() { - indexing = false; - progress.finish(); - propertyChangeSupport.firePropertyChange(Event.INDEXING_DONE.toString(), null, hashSetName); - } - } - private void close() throws TskCoreException { SleuthkitJNI.closeHashDatabase(handle); } } + + private class HashDbIndexer extends SwingWorker { + private ProgressHandle progress = null; + private HashDb hashDb = null; + private boolean deleteIndexFile = false; + + HashDbIndexer(HashDb hashDb, boolean deleteIndexFile) { + this.hashDb = hashDb; + this.deleteIndexFile = deleteIndexFile; + }; + + @Override + protected Object doInBackground() { + hashDb.indexing = true; + progress = ProgressHandleFactory.createHandle("Indexing " + hashDb.hashSetName); + progress.start(); + progress.switchToIndeterminate(); + try { + SleuthkitJNI.createLookupIndexForHashDatabase(hashDb.handle, deleteIndexFile); + } + catch (TskCoreException ex) { + Logger.getLogger(HashDb.class.getName()).log(Level.SEVERE, "Error indexing hash database", ex); + JOptionPane.showMessageDialog(null, "Error indexing " + hashDb.getHashSetName() + " hash database.", "Hash Database Indexing Error", JOptionPane.ERROR_MESSAGE); + } + return null; + } + + @Override + protected void done() { + hashDb.indexing = false; + progress.finish(); + hashDb.propertyChangeSupport.firePropertyChange(HashDb.Event.INDEXING_DONE.toString(), null, hashDb); + } + } } \ No newline at end of file diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/ModalNoButtons.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/ModalNoButtons.java index 1218a34bc7..9f1e10c7ba 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/ModalNoButtons.java +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/ModalNoButtons.java @@ -211,7 +211,7 @@ class ModalNoButtons extends javax.swing.JDialog implements PropertyChangeListen this.CURRENTLYON_LABEL.setText("Currently indexing 1 database"); if (!this.toIndex.isIndexing()) { this.toIndex.addPropertyChangeListener(this); - this.toIndex.createIndex(okToDeleteOldIndexFile(toIndex)); + HashDbManager.getInstance().indexHashDatabase(toIndex, okToDeleteOldIndexFile(toIndex)); } } @@ -227,7 +227,7 @@ class ModalNoButtons extends javax.swing.JDialog implements PropertyChangeListen this.CURRENTLYON_LABEL.setText("Currently indexing 1 of " + length); if (!db.isIndexing()) { db.addPropertyChangeListener(this); - db.createIndex(okToDeleteOldIndexFile(db)); + HashDbManager.getInstance().indexHashDatabase(db, okToDeleteOldIndexFile(db)); } } } @@ -252,8 +252,7 @@ class ModalNoButtons extends javax.swing.JDialog implements PropertyChangeListen this.dispose(); } else { currentcount++; - this.CURRENTLYON_LABEL.setText("Currently indexing " + currentcount + " of " + length); - + this.CURRENTLYON_LABEL.setText("Currently indexing " + currentcount + " of " + length); } } } From 0a0aa564dc335b23191e147b302ea8e3a0f76935 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Mon, 2 Dec 2013 19:47:57 -0500 Subject: [PATCH 12/12] Removed finalize() from HashDbManager --- .../autopsy/hashdatabase/HashDbManager.java | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbManager.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbManager.java index adc953b254..f5abf778e4 100755 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbManager.java +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbManager.java @@ -620,18 +620,7 @@ public class HashDbManager implements PropertyChangeListener { } return filePath; } - - @Override - protected void finalize() throws Throwable { - try { - closeHashDatabases(knownHashSets); - closeHashDatabases(knownBadHashSets); - } - finally { - super.finalize(); - } - } - + /** * Instances of this class represent hash databases used to classify files as known or know bad. */