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 bef2c3e889..9326fd61cd 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; @@ -35,124 +34,128 @@ 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. + * 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("New Hash Set..."); + JMenuItem newHashSetItem = new JMenuItem("Create database..."); newHashSetItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - HashDb hashDb = new HashDbCreateDatabaseDialog().doDialog(); + HashDb hashDb = new HashDbCreateDatabaseDialog().getHashDatabase(); if (null != hashDb) { - HashDbManager hashSetManager = HashDbManager.getInstance(); - hashSetManager.addHashSet(hashDb); - hashSetManager.save(); - addContentToHashSet(hashDb); + HashDbManager.getInstance().save(); + 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 { - hashSet.add(file); + hashSet.addHashes(file); } 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 " + (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 891a9e4358..7e2fa92971 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/Bundle.properties +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/Bundle.properties @@ -22,26 +22,15 @@ 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.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,11 +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=Enable sending messages to inbox during ingest -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.importDatabaseButton.text=Import Database HashDbConfigPanel.deleteDatabaseButton.text=Delete Database HashDbConfigPanel.indexPathLabelLabel.text=Index Path: @@ -75,4 +60,22 @@ 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=Database Path: +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= +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/HashDb.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDb.java deleted file mode 100644 index ec6e84435f..0000000000 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDb.java +++ /dev/null @@ -1,287 +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; -import org.sleuthkit.datamodel.TskData; - -/** - * 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 lookUp(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 lookUpVerbose(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/HashDbConfigPanel.form b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbConfigPanel.form index 2cef194731..a56c3f2b57 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbConfigPanel.form +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbConfigPanel.form @@ -60,7 +60,6 @@ - @@ -74,13 +73,6 @@ - - - - - - - @@ -97,6 +89,21 @@ + + + + + + + + + + + + + + + @@ -106,12 +113,20 @@ - - - - + + + + + + + + + + + + + - @@ -165,9 +180,9 @@ - + - + @@ -177,7 +192,7 @@ - + @@ -345,24 +360,24 @@ - + - + - + - + - + - + @@ -383,13 +398,13 @@ - + - + @@ -402,7 +417,7 @@ - + diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbConfigPanel.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbConfigPanel.java index 34f9bca200..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(); @@ -55,8 +58,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); @@ -100,16 +114,16 @@ 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); // 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. @@ -122,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 { @@ -144,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); } @@ -162,29 +190,29 @@ 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); } - // Diable the indexing button if ingest is in progress. + // Disable the indexing button if ingest is in progress. if (ingestIsRunning) { indexButton.setEnabled(false); } // 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); + importDatabaseButton.setEnabled(true); deleteDatabaseButton.setEnabled(!ingestIsRunning); - importDatabaseButton.setEnabled(!ingestIsRunning); - importDatabaseButton.setEnabled(!ingestIsRunning); // Update ingest in progress warning label. ingestWarningLabel.setVisible(ingestIsRunning); @@ -198,6 +226,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(); @@ -230,18 +262,9 @@ 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. - * @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(); } @@ -279,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; } @@ -428,13 +450,13 @@ 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(); 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(); @@ -518,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); } }); @@ -536,14 +558,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); } }); @@ -558,7 +580,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) @@ -571,12 +592,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) @@ -589,16 +604,32 @@ 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(searchDuringIngestCheckbox) + .addGroup(layout.createSequentialGroup() + .addGap(21, 21, 21) + .addComponent(sendIngestMessagesCheckBox))) + .addGap(0, 0, Short.MAX_VALUE)))) .addGroup(layout.createSequentialGroup() .addComponent(optionsLabel) .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(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))) .addGap(24, 24, 24)) ); layout.setVerticalGroup( @@ -641,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)) @@ -651,7 +682,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()) @@ -689,10 +720,10 @@ 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); + hashSetManager.removeHashDatabase(hashDb); hashSetTableModel.refreshModel(); } } @@ -702,46 +733,48 @@ 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 newDatabaseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_newDatabaseButtonActionPerformed - HashDb hashDb = new HashDbCreateDatabaseDialog().doDialog(); + private void createDatabaseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_createDatabaseButtonActionPerformed + HashDb hashDb = new HashDbCreateDatabaseDialog().getHashDatabase(); 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; @@ -764,11 +797,10 @@ 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; + 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 56cbcf2e03..047c483d48 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbCreateDatabaseDialog.form +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbCreateDatabaseDialog.form @@ -32,43 +32,48 @@ - - - - - - - - - - - - - - - - - - + + + - - - - - - - - - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -77,45 +82,46 @@ + + + + + - - - - + - - + - + - - + + - + - + - + - + - + @@ -128,23 +134,6 @@ - - - - - - - - - - - - - - - - - @@ -179,10 +168,10 @@ - + - + @@ -193,24 +182,52 @@ - + - + + + + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbCreateDatabaseDialog.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbCreateDatabaseDialog.java index cadbced05d..842f1d5e06 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbCreateDatabaseDialog.java +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbCreateDatabaseDialog.java @@ -27,31 +27,60 @@ 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 javax.swing.JFrame; import org.apache.commons.io.FilenameUtils; -import org.sleuthkit.autopsy.hashdatabase.HashDb.KnownFilesType; import org.sleuthkit.datamodel.TskCoreException; +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; -final class HashDbCreateDatabaseDialog extends javax.swing.JDialog { - private JFileChooser fileChooser; +/** + * 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); - setResizable(false); + 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() { 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) { + 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()) { - 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) { + 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; @@ -59,30 +88,17 @@ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog { 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.setMultiSelectionEnabled(false); + fileChooser.setDragEnabled(false); + 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 @@ -93,24 +109,25 @@ 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(); + jLabel3 = new javax.swing.JLabel(); + databasePathTextField = new javax.swing.JTextField(); + okButton = new javax.swing.JButton(); 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 +138,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 +157,33 @@ 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 + + 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); @@ -174,142 +195,175 @@ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog { .addGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() + .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) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() .addGap(0, 0, Short.MAX_VALUE) .addComponent(okButton) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(cancelButton)) + .addComponent(jLabel2) .addGroup(layout.createSequentialGroup() - .addComponent(databasePathTextField) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(browseButton)) + .addGap(20, 20, 20) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(knownRadioButton) + .addComponent(knownBadRadioButton))) .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)) - .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(useForIngestCheckbox) - .addComponent(sendInboxMessagesCheckbox)) - .addGap(0, 135, Short.MAX_VALUE)))) + .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, 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(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(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)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(saveAsButton) + .addComponent(jLabel1)) + .addGap(7, 7, 7) .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) + .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(3, 3, 3) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(okButton) - .addComponent(cancelButton)) - .addContainerGap()) + .addComponent(cancelButton) + .addComponent(okButton)) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, 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); + 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 - sendInboxMessagesCheckbox.setSelected(true); - sendInboxMessagesCheckbox.setEnabled(true); + searchDuringIngestCheckbox.setSelected(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(); + 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"); - return; - } - if(databaseNameTextField.getText().isEmpty()) { - JOptionPane.showMessageDialog(this, "Database name cannot be empty"); - return; + private void saveAsButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_saveAsButtonActionPerformed + 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()) { + if (knownRadioButton.isSelected()) { type = KnownFilesType.KNOWN; - } else { + } + else { type = KnownFilesType.KNOWN_BAD; } - + + String errorMessage = "Hash database creation error"; try { - newHashDb = HashDb.createHashDatabase(databaseNameTextField.getText(), databasePathTextField.getText(), useForIngestCheckbox.isSelected(), sendInboxMessagesCheckbox.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, errorMessage, ex); + JOptionPane.showMessageDialog(this, "Cannot create a hash database file at the selected location.", "Create Hash Database Error", JOptionPane.ERROR_MESSAGE); + return; + } + catch (HashDatabaseFileAlreadyExistsException | DuplicateHashSetNameException | HashDatabaseAlreadyAddedException | IllegalHashDatabaseFileNameExtensionException ex) { + Logger.getLogger(HashDbCreateDatabaseDialog.class.getName()).log(Level.WARNING, errorMessage, ex); + JOptionPane.showMessageDialog(this, ex.getMessage(), "Create Hash Database Error", JOptionPane.ERROR_MESSAGE); + 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.", "Create Hash Database Error", JOptionPane.ERROR_MESSAGE); return; } - - this.dispose(); + + dispose(); }//GEN-LAST:event_okButtonActionPerformed - private void useForIngestCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_useForIngestCheckboxActionPerformed - }//GEN-LAST:event_useForIngestCheckboxActionPerformed - // 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.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..627f4090ba 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbImportDatabaseDialog.form +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbImportDatabaseDialog.form @@ -30,47 +30,50 @@ - + - - - - + + - - - - + + + + + + + + + + + + + + + + + + + + + - + - - + + + - - - - - - - - - - - - - - - + + @@ -79,30 +82,38 @@ + - + - + - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - @@ -130,19 +141,20 @@ + - + - + - + @@ -179,10 +191,10 @@ - + - + @@ -193,22 +205,29 @@ - + - + - + - + - + + + + + + + + diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbImportDatabaseDialog.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbImportDatabaseDialog.java index d4cea65dbb..33516bad99 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbImportDatabaseDialog.java +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbImportDatabaseDialog.java @@ -27,44 +27,69 @@ 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 javax.swing.JFrame; import org.sleuthkit.datamodel.TskCoreException; import org.apache.commons.io.FilenameUtils; +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 HashDb selectedHashDb; - - HashDbImportDatabaseDialog() { - super(new javax.swing.JFrame(), "Import Hash Database", true); - setResizable(false); - initComponents(); - customizeComponents(); - } - void customizeComponents() { + private JFileChooser fileChooser = new JFileChooser(); + private String selectedFilePath = ""; + private HashDb selectedHashDb = null; + + /** + * 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){ + 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 @@ -78,14 +103,15 @@ 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(); - 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,12 +129,13 @@ 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); } }); @@ -131,20 +158,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,90 +185,104 @@ 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)) - .addGroup(layout.createSequentialGroup() - .addComponent(databasePathTextField) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(browseButton)) + .addGap(21, 21, 21) + .addComponent(sendIngestMessagesCheckbox)) + .addComponent(searchDuringIngestCheckbox)) + .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(jLabel2) .addGroup(layout.createSequentialGroup() + .addGap(19, 19, 19) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(knownRadioButton) + .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(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)) - .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(useForIngestCheckbox) - .addComponent(sendInboxMessagesCheckbox)) - .addGap(0, 135, 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()) ); + + 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(openButton) .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(); }// //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 { - 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 + }//GEN-LAST:event_openButtonActionPerformed private void knownRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_knownRadioButtonActionPerformed - sendInboxMessagesCheckbox.setSelected(false); - sendInboxMessagesCheckbox.setEnabled(false); + 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 - sendInboxMessagesCheckbox.setSelected(true); - sendInboxMessagesCheckbox.setEnabled(true); + searchDuringIngestCheckbox.setSelected(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 +290,24 @@ 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."); + // 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.", "Import Hash Database Error", JOptionPane.ERROR_MESSAGE); return; } - if(databaseNameTextField.getText().isEmpty()) { - JOptionPane.showMessageDialog(this, "Hash set name cannot be empty."); + 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(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.", "Import Hash Database Error", JOptionPane.ERROR_MESSAGE); 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; @@ -281,33 +316,44 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog { type = KnownFilesType.KNOWN_BAD; } + String errorMessage = "Failed to open hash database at " + selectedFilePath + "."; try { - selectedHashDb = HashDb.openHashDatabase(databaseNameTextField.getText(), filePath, useForIngestCheckbox.isSelected(), sendInboxMessagesCheckbox.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 " + filePath, ex); - JOptionPane.showMessageDialog(this, "Failed to import 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 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.JButton openButton; + 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/HashDbIngestModule.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbIngestModule.java index c3e2340a51..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); } } } @@ -206,14 +207,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.hasMd5HashOf(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.getSendIngestMessages()); } lookuptime += (System.currentTimeMillis() - lookupstart); } catch (TskException ex) { @@ -222,21 +233,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 +242,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.hasMd5HashOf(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 +260,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 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 687903e96b..f5abf778e4 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbManager.java +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbManager.java @@ -16,53 +16,69 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.sleuthkit.autopsy.hashdatabase; +import java.beans.PropertyChangeEvent; 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.logging.Level; +import java.util.Set; 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.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. + * 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 { - 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"; +public class HashDbManager implements PropertyChangeListener { + + 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 Logger logger = Logger.getLogger(HashDbManager.class.getName()); - private static HashDbManager instance; - private String xmlFilePath = PlatformUtil.getUserConfigDirectory() + File.separator + CUR_HASHSETS_FILE_NAME; + 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 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 boolean alwaysCalculateHashes; - + private Set hashSetNames = new HashSet<>(); + private Set hashSetPaths = new HashSet<>(); + private boolean alwaysCalculateHashes = false; + /** * Gets the singleton instance of this class. */ @@ -78,73 +94,238 @@ public class HashDbManager { readHashSetsConfigurationFromDisk(); } } - + /** - * 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. + * Gets the extension, without the dot separator, that the SleuthKit requires + * for the hash database files that combine a database and an index and can + * therefore be updated. */ - public void addHashSet(HashDb hashDb) { + static String getHashDatabaseFileExtension() { + return HASH_DATABASE_FILE_EXTENSON; + } + + class DuplicateHashSetNameException extends Exception { + private DuplicateHashSetNameException(String hashSetName) { + super("The hash set name '"+ hashSetName +"' has already been used for another hash database."); + } + } + + class HashDatabaseDoesNotExistException extends Exception { + private HashDatabaseDoesNotExistException(String path) { + super("No hash database found at\n" + path); + } + } + + class HashDatabaseFileAlreadyExistsException extends Exception { + private HashDatabaseFileAlreadyExistsException(String path) { + super("A file already exists at\n" + path); + } + } + + class HashDatabaseAlreadyAddedException extends Exception { + private HashDatabaseAlreadyAddedException(String path) { + super("The hash database at\n" + path + "\nhas already been created or imported."); + } + } + + class IllegalHashDatabaseFileNameExtensionException extends Exception { + private IllegalHashDatabaseFileNameExtensionException() { + 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 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 representing the hash database. + * @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); + } + + if (hashSetPaths.contains(path)) { + throw new HashDatabaseAlreadyAddedException(path); + } + + if (hashSetNames.contains(hashSetName)) { + throw new DuplicateHashSetNameException(hashSetName); + } + + 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 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 + */ + 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); + } + 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(hashSetName); + } + + return addHashDatabase(SleuthkitJNI.createHashDatabase(path), hashSetName, searchDuringIngest, sendIngestMessages, knownFilesType); + } + + 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(); + + // 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()); + 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. if (hashDb.getKnownFilesType() == HashDb.KnownFilesType.KNOWN) { knownHashSets.add(hashDb); } else { knownBadHashSets.add(hashDb); - } + } + + 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 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 void removeHashSet(HashDb hashDb) { - try { - hashDb.close(); - } - catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error closing hash database at " + hashDb.getDatabasePath(), ex); - } + 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, 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 throw. + 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 List getAllHashSets() { + 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 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 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 List getUpdateableHashSets() { + public synchronized List getUpdateableHashSets() { List updateableDbs = getUpdateableHashSets(knownHashSets); updateableDbs.addAll(getUpdateableHashSets(knownBadHashSets)); - return Collections.unmodifiableList(updateableDbs); + return updateableDbs; } private List getUpdateableHashSets(List hashDbs) { @@ -156,25 +337,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 void alwaysCalculateHashes(boolean alwaysCalculateHashes) { + 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 boolean shouldAlwaysCalculateHashes() { + synchronized boolean getAlwaysCalculateHashes() { return alwaysCalculateHashes; } @@ -183,7 +364,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(); } @@ -191,67 +372,59 @@ 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(); + hashSetPaths.clear(); if (hashSetsConfigurationFileExists()) { readHashSetsConfigurationFromDisk(); } } - 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.log(Level.SEVERE, "Error closing hash database at " + dbPath, ex); - } - hashDbs.clear(); + hashDatabases.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(); 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, xmlFilePath, ENCODING, doc); + success = XMLUtil.saveDoc(HashDbManager.class, configFilePath, ENCODING, doc); } - catch (ParserConfigurationException e) { - logger.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; } 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(); @@ -259,102 +432,176 @@ 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() { - final Document doc = XMLUtil.loadDoc(HashDbManager.class, xmlFilePath, XSDFILE); + // Open the XML document that implements the configuration file. + final Document doc = XMLUtil.loadDoc(HashDbManager.class, configFilePath, XSD_FILE_NAME); 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."); + Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error loading hash sets: invalid file format."); return false; } - NodeList setsNList = root.getElementsByTagName(SET_EL); + // Get the hash set elements. + 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. 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) { Element setEl = (Element) setsNList.item(i); - final String name = setEl.getAttribute(SET_NAME_ATTR); - final String type = setEl.getAttribute(SET_TYPE_ATTR); - 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); - - String path = null; - 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(); + + String hashSetName = setEl.getAttribute(SET_NAME_ATTRIBUTE); + if (hashSetName.isEmpty()) { + Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, SET_NAME_ATTRIBUTE + 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_ATTRIBUTE); + if(knownFilesType.isEmpty()) { + Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, SET_TYPE_ATTRIBUTE + attributeErrorMessage, i); + continue; } - if(name.isEmpty()) { - logger.log(Level.WARNING, "Name was not set for hash_set at index {0}.", i); - } + // Handle legacy known files types. + if (knownFilesType.equals("NSRL")) { + knownFilesType = HashDb.KnownFilesType.KNOWN.toString(); + } - 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; + 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; } - - if(useForIngest.isEmpty()) { - logger.log(Level.WARNING, "UseForIngest was not set for hash_set at index {0}.", i); + Boolean seearchDuringIngestFlag = Boolean.parseBoolean(searchDuringIngest); + + 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; } - - if(showInboxMessages.isEmpty()) { - logger.log(Level.WARNING, "ShowInboxMessages was not set for hash_set at index {0}.", i); + Boolean sendIngestMessagesFlag = Boolean.parseBoolean(sendIngestMessages); + + String dbPath; + 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.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, PATH_ELEMENT + elementErrorMessage, i); + continue; + } } - - if(path == null) { - logger.log(Level.WARNING, "No path for hash_set at index {0}, cannot make instance of HashDb class.", i); + else { + Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, PATH_ELEMENT + elementErrorMessage, i); + continue; + } + dbPath = getValidFilePath(hashSetName, dbPath); + + if (null != dbPath) { + try { + addExistingHashDatabase(hashSetName, dbPath, seearchDuringIngestFlag, sendIngestMessagesFlag, HashDb.KnownFilesType.valueOf(knownFilesType)); + } + 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 { - try { - addHashSet(HashDb.openHashDatabase(name, path, useForIngestBool, showInboxMessagesBool, KnownFilesType.valueOf(type))); - } - 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); - } + Logger.getLogger(HashDbManager.class.getName()).log(Level.WARNING, "No valid path for hash_set at index {0}, cannot make instance of HashDb class", i); } } - NodeList calcList = root.getElementsByTagName(SET_CALC); + // Get the element that stores the always calculate hashes flag. + NodeList calcList = root.getElementsByTagName(ALWAYS_CALCULATE_HASHES_ELEMENT); if (calcList.getLength() > 0) { - Element calcEl = (Element) calcList.item(0); // Shouldn't be more than 1 - final String value = calcEl.getAttribute(SET_VALUE); + Element calcEl = (Element) calcList.item(0); // Shouldn't be more than one. + final String value = calcEl.getAttribute(VALUE_ATTRIBUTE); alwaysCalculateHashes = Boolean.parseBoolean(value); } else { - logger.log(Level.WARNING, "No element hash_calculate exists."); + Logger.getLogger(HashDbManager.class.getName()).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,18 +609,223 @@ 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 { 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; } + + /** + * 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. + */ + public enum Event { + INDEXING_DONE + } + + private int handle; + private String hashSetName; + private boolean searchDuringIngest; + private boolean sendIngestMessages; + private KnownFilesType knownFilesType; + private boolean indexing; + private final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this); + + 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; + } + + /** + * 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() throws TskCoreException { + return SleuthkitJNI.getHashDatabasePath(handle); + } + + public String getIndexPath() throws TskCoreException { + return SleuthkitJNI.getHashDatabaseIndexPath(handle); + } + + public KnownFilesType getKnownFilesType() { + return knownFilesType; + } + + public boolean getSearchDuringIngest() { + return searchDuringIngest; + } + + void setSearchDuringIngest(boolean useForIngest) { + this.searchDuringIngest = useForIngest; + } + + public boolean getSendIngestMessages() { + return sendIngestMessages; + } + + void setSendIngestMessages(boolean showInboxMessages) { + this.sendIngestMessages = showInboxMessages; + } + + /** + * 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 addHashes(Content content) throws TskCoreException { + addHashes(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 addHashes(Content content, String comment) throws TskCoreException { + // TODO: This only works for AbstractFiles and MD5 hashes at present. + assert content instanceof AbstractFile; + if (content instanceof AbstractFile) { + AbstractFile file = (AbstractFile)content; + if (null != file.getMd5Hash()) { + SleuthkitJNI.addToHashDatabase(null, file.getMd5Hash(), null, null, comment, handle); + } + } + } + + public boolean hasMd5HashOf(Content content) throws TskCoreException { + boolean result = false; + assert content instanceof AbstractFile; + if (content instanceof AbstractFile) { + AbstractFile file = (AbstractFile)content; + 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 and MD5 hashes at present. + assert content instanceof AbstractFile; + if (content instanceof AbstractFile) { + AbstractFile file = (AbstractFile)content; + if (null != file.getMd5Hash()) { + result = SleuthkitJNI.lookupInHashDatabaseVerbose(file.getMd5Hash(), handle); + } + } + 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; + } + + 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/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..9f1e10c7ba 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. @@ -210,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)); } } @@ -226,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)); } } } @@ -251,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); } } }