diff --git a/Core/src/org/sleuthkit/autopsy/corecomponentinterfaces/ContextMenuActionsProvider.java b/Core/src/org/sleuthkit/autopsy/corecomponentinterfaces/ContextMenuActionsProvider.java new file mode 100755 index 0000000000..b6a96eb1aa --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/corecomponentinterfaces/ContextMenuActionsProvider.java @@ -0,0 +1,38 @@ +/* + * 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.corecomponentinterfaces; + +import java.util.List; +import javax.swing.Action; + +/** + * Implementers of this interface provide Actions that will be added to context + * menus in Autopsy. + */ +public interface ContextMenuActionsProvider { + /** + * Gets context menu Actions for the currently selected data model objects + * exposed by the NetBeans Lookup of the active TopComponent. Implementers + * should discover the selected objects by calling + * org.openide.util.Utilities.actionsGlobalContext().lookupAll() for the + * org.sleuthkit.datamodel classes of interest to the provider. + * @return A list, possibly empty, of Action objects. + */ + public List getActions(); +} diff --git a/Core/src/org/sleuthkit/autopsy/coreutils/ContextMenuExtensionPoint.java b/Core/src/org/sleuthkit/autopsy/coreutils/ContextMenuExtensionPoint.java new file mode 100755 index 0000000000..868a2bafd2 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/coreutils/ContextMenuExtensionPoint.java @@ -0,0 +1,49 @@ +/* + * 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.coreutils; + +import org.openide.util.Lookup; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import javax.swing.Action; +import org.sleuthkit.autopsy.corecomponentinterfaces.ContextMenuActionsProvider; + +/** + * This class implements the ContextMenuActionsProvider extension point. + */ +public class ContextMenuExtensionPoint { + /** + * Gets all of the Actions provided by registered implementers of the + * ContextMenuActionsProvider interface. + * @return A list, possibly empty, of Action objects. + */ + static public List getActions() { + ArrayList actions = new ArrayList<>(); + Collection actionProviders = Lookup.getDefault().lookupAll(ContextMenuActionsProvider.class); + for (ContextMenuActionsProvider provider : actionProviders) { + List providerActions = provider.getActions(); + if (!providerActions.isEmpty()) { + actions.add(null); // Separator to set off this provider's actions. + actions.addAll(provider.getActions()); + } + } + return actions; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DirectoryNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/DirectoryNode.java index 278c147917..624facfebc 100755 --- a/Core/src/org/sleuthkit/autopsy/datamodel/DirectoryNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/DirectoryNode.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011 Basis Technology Corp. + * Copyright 2011 - 2013 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.datamodel; import java.util.ArrayList; import java.util.List; import javax.swing.Action; +import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint; import org.sleuthkit.autopsy.actions.AddContentTagAction; import org.sleuthkit.autopsy.directorytree.ExtractAction; import org.sleuthkit.autopsy.directorytree.NewWindowViewAction; @@ -77,6 +78,7 @@ public class DirectoryNode extends AbstractFsContentNode { actions.add(ExtractAction.getInstance()); actions.add(null); // creates a menu separator actions.add(AddContentTagAction.getInstance()); + actions.addAll(ContextMenuExtensionPoint.getActions()); return actions.toArray(new Action[0]); } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileNode.java index b3312292cf..f16325609a 100755 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileNode.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011 Basis Technology Corp. + * Copyright 2011 - 2013 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.datamodel; import java.util.ArrayList; import java.util.List; import javax.swing.Action; +import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint; import org.sleuthkit.autopsy.actions.AddContentTagAction; import org.sleuthkit.autopsy.directorytree.ExternalViewerAction; import org.sleuthkit.autopsy.directorytree.ExtractAction; @@ -85,6 +86,7 @@ public class FileNode extends AbstractFsContentNode { actionsList.add(new HashSearchAction("Search for files with the same MD5 hash", this)); actionsList.add(null); // creates a menu separator actionsList.add(AddContentTagAction.getInstance()); + actionsList.addAll(ContextMenuExtensionPoint.getActions()); return actionsList.toArray(new Action[0]); } @@ -172,4 +174,4 @@ public class FileNode extends AbstractFsContentNode { public boolean isLeafTypeNode() { return true; } -} +} \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/LayoutFileNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/LayoutFileNode.java index 3becca50d1..a896442f04 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/LayoutFileNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/LayoutFileNode.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011 Basis Technology Corp. + * Copyright 2011 - 2013 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -24,6 +24,7 @@ import java.util.List; import java.util.Map; import javax.swing.Action; import org.openide.nodes.Sheet; +import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint; import org.sleuthkit.autopsy.directorytree.ExternalViewerAction; import org.sleuthkit.autopsy.directorytree.ExtractAction; import org.sleuthkit.autopsy.directorytree.NewWindowViewAction; @@ -102,13 +103,14 @@ public class LayoutFileNode extends AbstractAbstractFileNode { @Override public Action[] getActions(boolean context) { - List actionsList = new ArrayList(); + List actionsList = new ArrayList<>(); actionsList.add(new NewWindowViewAction("View in New Window", this)); actionsList.add(new ExternalViewerAction("Open in External Viewer", this)); actionsList.add(null); // creates a menu separator actionsList.add(ExtractAction.getInstance()); actionsList.add(null); // creates a menu separator actionsList.add(AddContentTagAction.getInstance()); + actionsList.addAll(ContextMenuExtensionPoint.getActions()); return actionsList.toArray(new Action[0]); } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/LocalFileNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/LocalFileNode.java index 5bd762f85b..02121410da 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/LocalFileNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/LocalFileNode.java @@ -25,6 +25,7 @@ import java.util.List; import java.util.Map; import javax.swing.Action; import org.openide.nodes.Sheet; +import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint; import org.sleuthkit.autopsy.directorytree.ExternalViewerAction; import org.sleuthkit.autopsy.directorytree.ExtractAction; import org.sleuthkit.autopsy.directorytree.HashSearchAction; @@ -87,6 +88,7 @@ public class LocalFileNode extends AbstractAbstractFileNode { actionsList.add(new HashSearchAction("Search for files with the same MD5 hash", this)); actionsList.add(null); // creates a menu separator actionsList.add(AddContentTagAction.getInstance()); + actionsList.addAll(ContextMenuExtensionPoint.getActions()); return actionsList.toArray(new Action[0]); } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/VirtualDirectoryNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/VirtualDirectoryNode.java index 9a234df3c3..6a9f5c7645 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/VirtualDirectoryNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/VirtualDirectoryNode.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011 Basis Technology Corp. + * Copyright 2011 - 2013 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -24,6 +24,7 @@ import java.util.List; import java.util.Map; import javax.swing.Action; import org.openide.nodes.Sheet; +import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.directorytree.ExtractAction; import org.sleuthkit.autopsy.directorytree.NewWindowViewAction; @@ -82,6 +83,7 @@ public class VirtualDirectoryNode extends AbstractAbstractFileNode sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -36,6 +36,7 @@ import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JTable; import javax.swing.table.DefaultTableModel; +import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.ContentVisitor; @@ -101,40 +102,45 @@ public class ExplorerNodeActionVisitor extends ContentVisitor.Default visit(final Directory d) { - List actions = new ArrayList(); + List actions = new ArrayList<>(); actions.add(AddContentTagAction.getInstance()); + actions.addAll(ContextMenuExtensionPoint.getActions()); return actions; } @Override public List visit(final VirtualDirectory d) { - List actions = new ArrayList(); + List actions = new ArrayList<>(); actions.add(ExtractAction.getInstance()); actions.add(AddContentTagAction.getInstance()); + actions.addAll(ContextMenuExtensionPoint.getActions()); return actions; } @Override public List visit(final DerivedFile d) { - List actions = new ArrayList(); + List actions = new ArrayList<>(); actions.add(ExtractAction.getInstance()); actions.add(AddContentTagAction.getInstance()); + actions.addAll(ContextMenuExtensionPoint.getActions()); return actions; } @Override public List visit(final LocalFile d) { - List actions = new ArrayList(); + List actions = new ArrayList<>(); actions.add(ExtractAction.getInstance()); actions.add(AddContentTagAction.getInstance()); + actions.addAll(ContextMenuExtensionPoint.getActions()); return actions; } @Override public List visit(final org.sleuthkit.datamodel.File d) { - List actions = new ArrayList(); + List actions = new ArrayList<>(); actions.add(ExtractAction.getInstance()); actions.add(AddContentTagAction.getInstance()); + actions.addAll(ContextMenuExtensionPoint.getActions()); return actions; } diff --git a/HashDatabase/nbproject/project.xml b/HashDatabase/nbproject/project.xml index b650bf0ecc..9cfb151e4c 100644 --- a/HashDatabase/nbproject/project.xml +++ b/HashDatabase/nbproject/project.xml @@ -81,10 +81,19 @@ 7.0 + + org.sleuthkit.autopsy.corelibs + + + + 3 + 1.1 + + org.sleuthkit.autopsy.hashdatabase - + \ 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 new file mode 100755 index 0000000000..4b58c3c4b2 --- /dev/null +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/AddContentToHashDbAction.java @@ -0,0 +1,138 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 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.awt.event.ActionEvent; +import java.awt.event.ActionListener; +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; +import org.openide.util.Utilities; +import org.openide.util.Lookup; +import org.openide.util.actions.Presenter; +import org.sleuthkit.autopsy.ingest.IngestConfigurator; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * 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). + 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; + + 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); + } + + @Override + public void actionPerformed(ActionEvent event) { + } + + private class AddContentToHashDbMenu extends JMenu { + AddContentToHashDbMenu(String menuText) { + super(menuText); + + // Get the current set of updateable hash databases and add each + // one as a menu item. + List hashDatabases = HashDbManager.getInstance().getKnownBadHashSets(); + if (!hashDatabases.isEmpty()) { + for (final HashDb database : HashDbManager.getInstance().getUpdateableHashSets()) { + JMenuItem databaseItem = add(database.getDisplayName()); + databaseItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + Collection selectedFiles = Utilities.actionsGlobalContext().lookupAll(AbstractFile.class); + for (AbstractFile file : selectedFiles) { + String md5Hash = file.getMd5Hash(); + if (null != md5Hash) { + try { + database.add(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); + } + } + } + } + }); + } + } + else { + JMenuItem empty = new JMenuItem("No hash databases"); + empty.setEnabled(false); + add(empty); + } + } + } +} diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/Bundle.properties b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/Bundle.properties index bf9c210f1a..60bee53079 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/Bundle.properties +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/Bundle.properties @@ -8,18 +8,6 @@ HashDbSimplePanel.knownLabel.text=NSRL Database: HashDbSimplePanel.notableLabel.text=Known Bad Database(s): HashDbSimplePanel.knownValLabel.text=- HashDbSimplePanel.notableValLabel.text=- -HashDbSimplePanel.jLabel1.text=Enable known bad databases for ingest: -HashDbAddDatabaseDialog.cancelButton.text=Cancel -HashDbAddDatabaseDialog.okButton.text=OK -HashDbAddDatabaseDialog.nsrlRadioButton.text=NSRL -HashDbAddDatabaseDialog.knownBadRadioButton.text=Known Bad -HashDbAddDatabaseDialog.databasePathTextField.text= -HashDbAddDatabaseDialog.browseButton.text=Browse -HashDbAddDatabaseDialog.jLabel1.text=Enter the name of the database: -HashDbAddDatabaseDialog.databaseNameTextField.text= -HashDbAddDatabaseDialog.jLabel2.text=Select the type of database: -HashDbAddDatabaseDialog.useForIngestCheckbox.text=Enable for ingest -HashDbAddDatabaseDialog.sendInboxMessagesCheckbox.text=Enable sending messages to inbox during ingest HashDbSearchPanel.hashTable.columnModel.title0=MD5 Hashes HashDbSearchPanel.hashTable.columnModel.title3=Title 4 HashDbSearchPanel.hashTable.columnModel.title2=Title 3 @@ -33,32 +21,56 @@ HashDbSearchPanel.titleLabel.text=Search for files with the following MD5 hash(e HashDbSearchPanel.errorField.text=Error: Not all files have been hashed. HashDbSearchPanel.saveBox.text=Remember Hashes HashDbSearchPanel.cancelButton.text=Cancel -HashDbSimplePanel.calcHashesButton.text=Calculate hashes even if no hash database is selected -HashDbSimplePanel.nsrlDbLabel.text=NSRL Database: -HashDbSimplePanel.nsrlDbLabelVal.text=- -HashDbManagementPanel.hashDbIndexStatusLabel.text=No database selected -HashDbManagementPanel.jLabel2.text=Name: -HashDbManagementPanel.showInboxMessagesCheckBox.text=Enable sending messages to inbox during ingest -HashDbManagementPanel.useForIngestCheckbox.text=Enable for ingest -HashDbManagementPanel.indexButton.text=Index -HashDbManagementPanel.indexLabel.text=Index Status: -HashDbManagementPanel.optionsLabel.text=Options -HashDbManagementPanel.jLabel4.text=Location: -HashDbManagementPanel.jLabel6.text=Type: -HashDbManagementPanel.ingestWarningLabel.text=Ingest is ongoing, some settings will be unavailable until it finishes. -HashDbManagementPanel.hashDbTypeLabel.text=No database selected -HashDbManagementPanel.typeLabel.text=Type: -HashDbManagementPanel.deleteButton.text=Delete Database -HashDbManagementPanel.importButton.text=Import Database -HashDbManagementPanel.hashDbNameLabel.text=No database selected -HashDbManagementPanel.nameLabel.text=Name: -HashDbManagementPanel.jButton3.text=Import Database -HashDbManagementPanel.locationLabel.text=Location: -HashDbManagementPanel.hashDbLocationLabel.text=No database selected -HashDbManagementPanel.informationLabel.text=Information -HashDbManagementPanel.hashDatabasesLabel.text=Hash Databases: OpenIDE-Module-Short-Description=Hash Database Ingest Module and hash db tools ModalNoButtons.CURRENTLYON_LABEL.text=Currently Indexing x of y ModalNoButtons.GO_GET_COFFEE_LABEL.text=Hash databases are currently being indexed, this may take some time. ModalNoButtons.CURRENTDB_LABEL.text=(CurrentDb) ModalNoButtons.CANCEL_BUTTON.text=Cancel +HashDbImportDatabaseDialog.sendInboxMessagesCheckbox.text=Enable sending messages to inbox during ingest +HashDbImportDatabaseDialog.useForIngestCheckbox.text=Enable for ingest +HashDbImportDatabaseDialog.jLabel1.text=Display name of database: +HashDbImportDatabaseDialog.databaseNameTextField.text= +HashDbImportDatabaseDialog.databasePathTextField.text= +HashDbImportDatabaseDialog.browseButton.text=Browse +HashDbImportDatabaseDialog.nsrlRadioButton.text=NSRL +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.knownBadRadioButton.text=Known Bad +HashDbCreateDatabaseDialog.nsrlRadioButton.text=NSRL +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=Display name of database: +HashDbCreateDatabaseDialog.databaseNameTextField.text= +HashDbConfigPanel.nameLabel.text=Name: +HashDbConfigPanel.hashDbNameLabel.text=No database selected +HashDbConfigPanel.hashDatabasesLabel.text=Hash Databases: +HashDbConfigPanel.hashDbLocationLabel.text=No database selected +HashDbConfigPanel.ingestWarningLabel.text=Ingest is ongoing, some settings will be unavailable until it finishes. +HashDbConfigPanel.jButton3.text=Import Database +HashDbConfigPanel.jLabel6.text=Type: +HashDbConfigPanel.jLabel4.text=Location: +HashDbConfigPanel.jLabel2.text=Name: +HashDbConfigPanel.optionsLabel.text=Options +HashDbConfigPanel.typeLabel.text=Type: +HashDbConfigPanel.locationLabel.text=Location: +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.nsrlDbLabelVal.text=- +HashDbSimpleConfigPanel.calcHashesButton.text=Calculate hashes even if no hash database is selected +HashDbSimpleConfigPanel.jLabel1.text=Enable known bad databases for ingest: +HashDbSimpleConfigPanel.nsrlDbLabel.text=NSRL Database: +HashDbConfigPanel.newDatabaseButton.text=New Database +HashDbConfigPanel.importDatabaseButton.text=Import Database +HashDbConfigPanel.deleteDatabaseButton.text=Delete Database \ No newline at end of file diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDatabaseOptionsPanelController.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDatabaseOptionsPanelController.java index 2492db7d5f..3e8fa443aa 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDatabaseOptionsPanelController.java +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDatabaseOptionsPanelController.java @@ -36,7 +36,7 @@ id = "HashDatabase") @org.openide.util.NbBundle.Messages({"OptionsCategory_Name_HashDatabase=Hash Database", "OptionsCategory_Keywords_HashDatabase=Hash Database"}) public final class HashDatabaseOptionsPanelController extends OptionsPanelController { - private HashDbManagementPanel panel; + private HashDbConfigPanel panel; private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); private boolean changed; @@ -55,7 +55,7 @@ public final class HashDatabaseOptionsPanelController extends OptionsPanelContro @Override public void cancel() { // Reset the XML on cancel - HashDbXML.getCurrent().reload(); + HashDbManager.getInstance().loadLastSavedConfiguration(); } @Override @@ -88,9 +88,9 @@ public final class HashDatabaseOptionsPanelController extends OptionsPanelContro pcs.removePropertyChangeListener(l); } - private HashDbManagementPanel getPanel() { + private HashDbConfigPanel getPanel() { if (panel == null) { - panel = new HashDbManagementPanel(); + panel = new HashDbConfigPanel(); } return panel; } diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDb.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDb.java index 47d3e58c52..eaea16b7d6 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDb.java +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDb.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011 Basis Technology Corp. + * Copyright 2011 - 2013 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,285 +20,248 @@ package org.sleuthkit.autopsy.hashdatabase; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; -import java.io.File; -import java.util.List; -import java.util.logging.Level; import javax.swing.SwingWorker; import org.netbeans.api.progress.ProgressHandle; import org.netbeans.api.progress.ProgressHandleFactory; -import org.openide.util.Cancellable; -import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.SleuthkitJNI; -import org.sleuthkit.datamodel.TskException; +import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.TskData; /** - * Hash database representation of NSRL and Known Bad hash databases - * with indexing capability - * + * Instances of this class represent the hash databases underlying known files + * hash sets. */ public class HashDb implements Comparable { - - enum EVENT {INDEXING_DONE }; - private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); + public enum Event { + INDEXING_DONE + } - - public enum DBType{ - NSRL("NSRL"), KNOWN_BAD("Known Bad"); + public enum KnownFilesType{ + NSRL("NSRL"), + KNOWN_BAD("Known Bad"); private String displayName; - private DBType(String displayName) { + private KnownFilesType(String displayName) { this.displayName = displayName; } - public String getDisplayName() { + String getDisplayName() { return this.displayName; } } - // Suffix added to the end of a database name to get its index file - private static final String INDEX_SUFFIX = "-md5.idx"; + /** + * Opens an existing 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 hash database should be used during ingest. + * @param showInboxMessages A flag indicating whether hash set hit messages should be sent to the application inbox. + * @param knownType The known files type of the database. + * @return A HashDb object representation of the new hash database. + * @throws TskCoreException + */ + public static HashDb openHashDatabase(String hashSetName, String databasePath, boolean useForIngest, boolean showInboxMessages, KnownFilesType knownType) throws TskCoreException { + return new HashDb(SleuthkitJNI.openHashDatabase(databasePath), hashSetName, databasePath, useForIngest, showInboxMessages, knownType); + } - private String name; - private List databasePaths; // TODO: Length limited to one for now... + /** + * Creates a new hash database. + * @param hashSetName Name used to represent the 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 knownType The known files type of 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 type) throws TskCoreException { + return new HashDb(SleuthkitJNI.createHashDatabase(databasePath), hashSetName, databasePath, useForIngest, showInboxMessages, type); + } + + private static final String INDEX_FILE_EXTENSION = ".kdb"; + private static final String LEGACY_INDEX_FILE_EXTENSION = "-md5.idx"; + + private final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this); + private String displayName; + private String databasePath; private boolean useForIngest; private boolean showInboxMessages; + private KnownFilesType type; + private int handle; private boolean indexing; - private DBType type; - public HashDb(String name, List databasePaths, boolean useForIngest, boolean showInboxMessages, DBType type) { - this.name = name; - this.databasePaths = databasePaths; + HashDb(int handle, String name, String databasePath, boolean useForIngest, boolean showInboxMessages, KnownFilesType type) { + this.displayName = name; + this.databasePath = databasePath; this.useForIngest = useForIngest; this.showInboxMessages = showInboxMessages; this.type = type; + this.handle = handle; this.indexing = false; } - + + @Override + public int compareTo(HashDb o) { + return this.displayName.compareTo(o.displayName); + } + void addPropertyChangeListener(PropertyChangeListener pcl) { - pcs.addPropertyChangeListener(pcl); + propertyChangeSupport.addPropertyChangeListener(pcl); } void removePropertyChangeListener(PropertyChangeListener pcl) { - pcs.removePropertyChangeListener(pcl); + propertyChangeSupport.removePropertyChangeListener(pcl); + } + + String getDisplayName() { + return displayName; } + String getDatabasePath() { + return databasePath; + } + + KnownFilesType getKnownFilesType() { + return type; + } + boolean getUseForIngest() { return useForIngest; } - - boolean getShowInboxMessages() { - return showInboxMessages; - } - - DBType getDbType() { - return type; - } - - String getName() { - return name; - } - - List getDatabasePaths() { - return databasePaths; - } - + void setUseForIngest(boolean useForIngest) { this.useForIngest = useForIngest; } + + boolean getShowInboxMessages() { + return showInboxMessages; + } void setShowInboxMessages(boolean showInboxMessages) { this.showInboxMessages = showInboxMessages; } - - void setName(String name) { - this.name = name; - } - - void setDatabasePaths(List databasePaths) { - this.databasePaths = databasePaths; - } - - void setDbType(DBType type) { - this.type = type; - } - - /** - * Checks if the database exists. - * @return true if a file exists at the database path, else false - */ - boolean databaseExists() { - return databaseFile().exists(); - } - - /** - * Checks if Sleuth Kit can open the index for the database path. - * @return true if the index was found and opened successfully, else false - */ - boolean indexExists() { + + boolean hasLookupIndex() { try { - return hasIndex(databasePaths.get(0)); // TODO: support multiple paths - } catch (TskException ex) { - Logger.getLogger(this.getClass().getName()).log(Level.WARNING, "Error checking if index exists.", ex); + return SleuthkitJNI.hashDatabaseHasLookupIndex(handle); + } + catch (TskCoreException ex) { + // RJCTODO return false; } } - - /** - * Gets the database file. - * @return a File initialized with the database path - */ - File databaseFile() { - return new File(databasePaths.get(0)); // TODO: support multiple paths + + boolean hasTextLookupIndexOnly() throws TskCoreException { + return SleuthkitJNI.hashDatabaseHasLegacyLookupIndexOnly(handle); } /** - * Gets the index file - * @return a File initialized with an index path derived from the database - * path + * Indicates whether the hash database accepts updates. + * @return True if the database accepts updates, false otherwise. */ - File indexFile() { - return new File(toIndexPath(databasePaths.get(0))); // TODO: support multiple paths - } - - /** - * Checks if the index file is older than the database file - * @return true if there is are files at the index path and the database - * path, and the index file has an older modified-time than the database - * file, else false - */ - boolean isOutdated() { - File i = indexFile(); - File db = databaseFile(); - - return i.exists() && db.exists() && isOlderThan(i, db); + public boolean isUpdateable() throws TskCoreException { + return SleuthkitJNI.isUpdateableHashDatabase(this.handle); } /** - * Checks if the database is being indexed + * 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 */ - boolean isIndexing() { - return indexing; - } - - /** - * Returns the status of the HashDb as determined from indexExists(), - * databaseExists(), and isOutdated() - * @return IndexStatus enum according to their definitions - */ - IndexStatus status() { - boolean i = this.indexExists(); - boolean db = this.databaseExists(); - - if(indexing) - return IndexStatus.INDEXING; - if (i) { - if (db) { - return this.isOutdated() ? IndexStatus.INDEX_OUTDATED : IndexStatus.INDEX_CURRENT; - } else { - return IndexStatus.NO_DB; + public void add(Content content) 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(), "", "", handle); } - } else { - return db ? IndexStatus.NO_INDEX : IndexStatus.NONE; } } - + + public TskData.FileKnown lookUp(Content content) throws TskCoreException { + TskData.FileKnown result = TskData.FileKnown.UKNOWN; + // 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()) { + if (type == KnownFilesType.NSRL) { + result = SleuthkitJNI.lookupInNSRLDatabase(file.getMd5Hash()); + } + else { + result = SleuthkitJNI.lookupInHashDatabase(file.getMd5Hash(), handle); + } + } + } + return result; + } + /** - * Tries to index the database (overwrites any existing index) - * @throws TskException if an error occurs in the SleuthKit bindings - */ - void createIndex() throws TskException { - indexing = true; - CreateIndex creator = new CreateIndex(); - creator.execute(); - } - - /** - * Checks if one file is older than an other - * @param a first file - * @param b second file - * @return true if the first file's last modified data is before the second - * file's last modified date - */ - private static boolean isOlderThan(File a, File b) { - return a.lastModified() < b.lastModified(); - } - - /** - * Determines if a path points to an index by checking the suffix - * @param path - * @return true if index - */ - static boolean isIndexPath(String path) { - return path.endsWith(INDEX_SUFFIX); - } - - /** - * Derives database path from an image path by removing the suffix. - * @param indexPath - * @return - */ - static String toDatabasePath(String indexPath) { - return indexPath.substring(0, indexPath.lastIndexOf(INDEX_SUFFIX)); - } - - /** - * Derives image path from an database path by appending the suffix. + * Derives index path from an database path by appending the suffix. * @param databasePath * @return */ static String toIndexPath(String databasePath) { - return databasePath.concat(INDEX_SUFFIX); + return databasePath.concat(INDEX_FILE_EXTENSION); + } + + boolean isIndexing() { + return indexing; } - /** - * Calls Sleuth Kit method via JNI to determine whether there is an - * index for the given path - * @param databasePath path Path for the database the index is of - * (database doesn't have to actually exist)' - * @return true if index exists - * @throws TskException if there is an error in the JNI call - */ - static boolean hasIndex(String databasePath) throws TskException { - return SleuthkitJNI.lookupIndexExists(databasePath); + IndexStatus getStatus() throws TskCoreException { + IndexStatus status = IndexStatus.NO_INDEX; + + if (indexing) { + status = IndexStatus.INDEXING; + } + else if (hasLookupIndex()) { + if (hasTextLookupIndexOnly()) { + status = IndexStatus.INDEX_ONLY; + } + else { + status = IndexStatus.INDEXED; + } + } + + return status; } - - @Override - public int compareTo(HashDb o) { - return this.name.compareTo(o.name); + + // Tries to index the database (overwrites any existing index) using a + // SwingWorker. + void createIndex() throws TskCoreException { + CreateIndex creator = new CreateIndex(); + creator.execute(); } - - /* Thread that creates a database's index */ + private class CreateIndex extends SwingWorker { - private ProgressHandle progress; - CreateIndex(){}; + CreateIndex() { + }; @Override protected Object doInBackground() throws Exception { - progress = ProgressHandleFactory.createHandle("Indexing " + name); - - /** We need proper cancel support in TSK to make the task cancellable - new Cancellable() { - Override - public boolean cancel() { - return CreateIndex.this.cancel(true); - } - }); - */ + indexing = true; + progress = ProgressHandleFactory.createHandle("Indexing " + displayName); progress.start(); progress.switchToIndeterminate(); - SleuthkitJNI.createLookupIndex(databasePaths.get(0)); + SleuthkitJNI.createLookupIndexForHashDatabase(handle); // RJCTODO: There is nobody to catch, fix this. return null; } - /* clean up or start the worker threads */ @Override protected void done() { indexing = false; progress.finish(); - pcs.firePropertyChange(EVENT.INDEXING_DONE.toString(), null, name); + propertyChangeSupport.firePropertyChange(Event.INDEXING_DONE.toString(), null, displayName); } } } \ No newline at end of file diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbManagementPanel.form b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbConfigPanel.form similarity index 80% rename from HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbManagementPanel.form rename to HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbConfigPanel.form index ab3578fe41..1d841ad64a 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbManagementPanel.form +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbConfigPanel.form @@ -5,21 +5,21 @@ - + - + - + @@ -29,7 +29,7 @@ - + @@ -62,14 +62,7 @@ - - - - - - - - + @@ -115,6 +108,12 @@ + + + + + + @@ -170,14 +169,16 @@ - + - - + + + + @@ -189,7 +190,7 @@ - + @@ -223,13 +224,13 @@ - + - + @@ -242,16 +243,16 @@ - + - + - + @@ -264,76 +265,76 @@ - + - + - + - + - + - + - + - + - + - + - + @@ -344,7 +345,7 @@ - + @@ -354,7 +355,7 @@ - + @@ -364,14 +365,14 @@ - + - + @@ -379,5 +380,27 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbManagementPanel.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbConfigPanel.java similarity index 50% rename from HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbManagementPanel.java rename to HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbConfigPanel.java index d64b93ec92..e24ac3a7a1 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbManagementPanel.java +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbConfigPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011 Basis Technology Corp. + * Copyright 2011 - 2013 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,7 +16,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.sleuthkit.autopsy.hashdatabase; import java.awt.Color; @@ -27,12 +26,8 @@ import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.File; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; -import javax.swing.JButton; import javax.swing.JComponent; -import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JTable; import javax.swing.ListSelectionModel; @@ -42,110 +37,341 @@ import javax.swing.table.AbstractTableModel; import javax.swing.table.TableCellRenderer; import org.sleuthkit.autopsy.corecomponents.OptionsPanel; import org.sleuthkit.autopsy.coreutils.Logger; +import static org.sleuthkit.autopsy.hashdatabase.IndexStatus.NO_INDEX; import org.sleuthkit.autopsy.ingest.IngestManager; +import org.sleuthkit.datamodel.TskCoreException; -final class HashDbManagementPanel extends javax.swing.JPanel implements OptionsPanel { - - private HashSetTableModel hashSetTableModel; - private static final Logger logger = Logger.getLogger(HashDbManagementPanel.class.getName()); - private boolean ingestRunning = false; - - - //keep track of dbs being indexed, since HashDb objects are reloaded, - //we cannot rely on their status - private final MapindexingState = new HashMap(); - - HashDbManagementPanel() { - this.hashSetTableModel = new HashSetTableModel(); +/** + * Instances of this class provide a UI for managing the hash sets configuration. + */ +final class HashDbConfigPanel extends javax.swing.JPanel implements OptionsPanel { + private HashDbManager hashSetManager = HashDbManager.getInstance(); + private HashSetTableModel hashSetTableModel = new HashSetTableModel(); + + HashDbConfigPanel() { initComponents(); customizeComponents(); - + updateComponentsForNoSelection(); } private void customizeComponents() { - setName("Hash Database Configuration"); + setName("Hash Set Configuration"); this.ingestWarningLabel.setVisible(false); this.hashSetTable.setModel(hashSetTableModel); this.hashSetTable.setTableHeader(null); hashSetTable.getParent().setBackground(hashSetTable.getBackground()); hashSetTable.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION); hashSetTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() { - @Override public void valueChanged(ListSelectionEvent e) { - ListSelectionModel listSelectionModel = (ListSelectionModel) e.getSource(); - if (!listSelectionModel.isSelectionEmpty()) { - int index = listSelectionModel.getMinSelectionIndex(); - listSelectionModel.setSelectionInterval(index, index); - HashDbXML loader = HashDbXML.getCurrent(); - HashDb current = loader.getAllSets().get(index); - initUI(current); - } else { - initUI(null); + if (!e.getValueIsAdjusting()) { + updateComponents(); } } - }); + }); } - private void initUI(HashDb db) { - boolean useForIngestEnabled = db != null && !ingestRunning; - boolean useForIngestSelected = db != null && db.getUseForIngest(); - boolean showInboxMessagesEnabled = db != null && !ingestRunning && useForIngestSelected && db.getDbType().equals(HashDb.DBType.KNOWN_BAD); - boolean showInboxMessagesSelected = db != null && db.getShowInboxMessages(); - boolean deleteButtonEnabled = db != null && !ingestRunning; - boolean importButtonEnabled = !ingestRunning; - if (db == null) { - setButtonFromIndexStatus(this.indexButton, this.hashDbIndexStatusLabel, IndexStatus.NONE); - this.hashDbLocationLabel.setText("No database selected"); - this.hashDbNameLabel.setText("No database selected"); - this.hashDbIndexStatusLabel.setText("No database selected"); - this.hashDbTypeLabel.setText("No database selected"); - } else { - //check if dn in indexing state - String dbName = db.getName(); - IndexStatus status = db.status(); - Boolean state = indexingState.get(dbName); - if (state != null && state.equals(Boolean.TRUE) ) { - status = IndexStatus.INDEXING; - } - - setButtonFromIndexStatus(this.indexButton, this.hashDbIndexStatusLabel, status); - String shortenPath = db.getDatabasePaths().get(0); - this.hashDbLocationLabel.setToolTipText(shortenPath); - if(shortenPath.length() > 50){ - shortenPath = shortenPath.substring(0, 10 + shortenPath.substring(10).indexOf(File.separator) + 1) + "..." + - shortenPath.substring((shortenPath.length() - 20) + shortenPath.substring(shortenPath.length() - 20).indexOf(File.separator)); - } - this.hashDbLocationLabel.setText(shortenPath); - this.hashDbNameLabel.setText(db.getName()); - this.hashDbTypeLabel.setText(db.getDbType().getDisplayName()); + private void updateComponents() { + HashDb db = ((HashSetTable)hashSetTable).getSelection(); + if (db != null) { + updateComponentsForSelection(db); + } + else { + updateComponentsForNoSelection(); + } + } + + private void updateComponentsForNoSelection() { + boolean ingestIsRunning = IngestManager.getDefault().isIngestRunning(); + + // Update labels. + hashDbLocationLabel.setText("No database selected"); + hashDbNameLabel.setText("No database selected"); + hashDbIndexStatusLabel.setText("No database selected"); + hashDbTypeLabel.setText("No database selected"); + + // Update indexing components. + indexButton.setText("Index"); + hashDbIndexStatusLabel.setForeground(Color.black); + indexButton.setEnabled(false); + + // Update ingest options. + useForIngestCheckbox.setSelected(false); + useForIngestCheckbox.setEnabled(false); + showInboxMessagesCheckBox.setSelected(false); + showInboxMessagesCheckBox.setEnabled(false); + optionsLabel.setEnabled(false); + optionsSeparator.setEnabled(false); + + // Update database action buttons. + newDatabaseButton.setEnabled(!ingestIsRunning); + importDatabaseButton.setEnabled(!ingestIsRunning); + deleteDatabaseButton.setEnabled(false); + + // Update ingest in progress warning label. + ingestWarningLabel.setVisible(ingestIsRunning); + } + + private void updateComponentsForSelection(HashDb db) { + boolean ingestIsRunning = IngestManager.getDefault().isIngestRunning(); + + // Update labels. + hashDbLocationLabel.setToolTipText(db.getDatabasePath()); + if (db.getDatabasePath().length() > 50){ + String shortenedPath = db.getDatabasePath(); + 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)); + hashDbLocationLabel.setText(shortenedPath); } - this.useForIngestCheckbox.setSelected(useForIngestSelected); - this.useForIngestCheckbox.setEnabled(useForIngestEnabled); - this.showInboxMessagesCheckBox.setSelected(showInboxMessagesSelected); - this.showInboxMessagesCheckBox.setEnabled(showInboxMessagesEnabled); - this.deleteButton.setEnabled(deleteButtonEnabled); - this.importButton.setEnabled(importButtonEnabled); - this.optionsLabel.setEnabled(useForIngestEnabled || showInboxMessagesEnabled); - this.optionsSeparator.setEnabled(useForIngestEnabled || showInboxMessagesEnabled); + else { + hashDbLocationLabel.setText(db.getDatabasePath()); + } + hashDbNameLabel.setText(db.getDisplayName()); + hashDbTypeLabel.setText(db.getKnownFilesType().getDisplayName()); + + // Update indexing components. + IndexStatus status = IndexStatus.UNKNOWN; + try { + status = db.getStatus(); + } + catch (TskCoreException ex) { + // RJCTODO + // Logger.getLogger(HashDbIngestModule.class.getName()) + } + hashDbIndexStatusLabel.setText(status.message()); + switch (status) { + case NO_INDEX: + indexButton.setText("Index"); + hashDbIndexStatusLabel.setForeground(Color.red); + indexButton.setEnabled(true); + break; + case INDEXING: + indexButton.setText("Indexing"); + hashDbIndexStatusLabel.setForeground(Color.black); + indexButton.setEnabled(false); + break; + case UNKNOWN: + indexButton.setText("Index"); + hashDbIndexStatusLabel.setForeground(Color.red); + indexButton.setEnabled(false); + break; + case INDEXED: + // TODO: Restore ability to re-index an indexed database. + case INDEX_ONLY: + default: + indexButton.setText("Index"); + hashDbIndexStatusLabel.setForeground(Color.black); + indexButton.setEnabled(false); + break; + } + 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)); + + // Update database action buttons. + deleteDatabaseButton.setEnabled(!ingestIsRunning); + importDatabaseButton.setEnabled(!ingestIsRunning); + importDatabaseButton.setEnabled(!ingestIsRunning); + + // Update ingest in progress warning label. + ingestWarningLabel.setVisible(ingestIsRunning); + } + + @Override + public void load() { + hashSetTable.clearSelection(); + hashSetTableModel.refreshModel(); + } + + @Override + public void store() { + //Checking for for any unindexed databases + List unindexed = new ArrayList<>(); + for (HashDb hashSet : hashSetManager.getAllHashSets()) { + if (!hashSet.hasLookupIndex()) { + unindexed.add(hashSet); + } + } + + //If unindexed ones are found, show a popup box that will either index them, or remove them. + if (unindexed.size() == 1){ + showInvalidIndex(false, unindexed); + } + else if (unindexed.size() > 1){ + showInvalidIndex(true, unindexed); + } + + hashSetManager.save(); } /** - * Sets the current state of ingest. - * Don't allow any changes if ingest is running. - * @param running Whether ingest is running or not. + * 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); + } + hashSetTableModel.refreshModel(); + } + + /** + * Displays the popup box that tells user that some of his databases are unindexed, along with solutions. + * This method is related to ModalNoButtons, to be removed at a later date. + * @param plural Whether or not there are multiple unindexed databases + * @param unindexed The list of unindexed databases. Can be of size 1. */ - private void setIngestStatus(boolean running) { - ingestRunning = running; - ingestWarningLabel.setVisible(running); - importButton.setEnabled(!running); - - int selection = getSelection(); - if(selection != -1) { - initUI(HashDbXML.getCurrent().getAllSets().get(selection)); + private void showInvalidIndex(boolean plural, List unindexed){ + String total = ""; + String message; + for(HashDb hdb : unindexed){ + total+= "\n" + hdb.getDisplayName(); + } + if(plural){ + message = "The following databases are not indexed, would you like to index them now? \n " + total; + } + else{ + message = "The following database is not indexed, would you like to index it now? \n" + total; + } + int res = JOptionPane.showConfirmDialog(this, message, "Unindexed databases", JOptionPane.YES_NO_OPTION); + if(res == JOptionPane.YES_OPTION){ + ModalNoButtons indexingDialog = new ModalNoButtons(this, new Frame(),unindexed); + indexingDialog.setLocationRelativeTo(null); + indexingDialog.setVisible(true); + indexingDialog.setModal(true); + hashSetTableModel.refreshModel(); + } + if(res == JOptionPane.NO_OPTION){ + JOptionPane.showMessageDialog(this, "All unindexed databases will be removed the list"); + removeThese(unindexed); } } + boolean valid() { + // TODO check whether form is consistent and complete + return true; + } + + /** + * This class implements a table for displaying configured hash sets. + */ + private class HashSetTable extends JTable { + @Override + public Component prepareRenderer(TableCellRenderer renderer, int row, int column) { + // Use the hash set name as the cell text. + JComponent cellRenderer = (JComponent)super.prepareRenderer(renderer, row, column); + cellRenderer.setToolTipText((String)getValueAt(row, column)); + + // Give the user a visual indication of any hash sets with a hash + // database that needs to be indexed by displaying the hash set name + // in red. + if (hashSetTableModel.indexExists(row)){ + cellRenderer.setForeground(Color.black); + } + else{ + cellRenderer.setForeground(Color.red); + } + + return cellRenderer; + } + + public HashDb getSelection() { + return hashSetTableModel.getHashSetAt(getSelectionModel().getMinSelectionIndex()); + } + + public void setSelection(int index) { + if (index >= 0 && index < hashSetTable.getRowCount()) { + getSelectionModel().setSelectionInterval(index, index); + } + } + + public void selectRowByName(String name) { + setSelection(hashSetTableModel.getIndexByName(name)); + } + } + + /** + * This class implements the table model for the table used to display + * configured hash sets. + */ + private class HashSetTableModel extends AbstractTableModel { + List hashSets = HashDbManager.getInstance().getAllHashSets(); + + @Override + public int getColumnCount() { + return 1; + } + + @Override + public int getRowCount() { + return hashSets.size(); + } + + @Override + public String getColumnName(int column) { + return "Name"; + } + + @Override + public Object getValueAt(int rowIndex, int columnIndex) { + return hashSets.get(rowIndex).getDisplayName(); + } + + private boolean indexExists(int rowIndex){ + return hashSets.get(rowIndex).hasLookupIndex(); + } + + @Override + public boolean isCellEditable(int rowIndex, int columnIndex) { + return false; + } + + @Override + public void setValueAt(Object aValue, int rowIndex, int columnIndex) { + throw new UnsupportedOperationException("Editing of cells is not supported"); + } + + @Override + public Class getColumnClass(int c) { + return getValueAt(0, c).getClass(); + } + + HashDb getHashSetAt(int index) { + if (!hashSets.isEmpty() && index >= 0 && index < hashSets.size()) { + return hashSets.get(index); + } + else { + return null; + } + } + + int getIndexByName(String name) { + for (int i = 0; i < hashSets.size(); ++i) { + if (hashSets.get(i).getDisplayName().equals(name)) { + return i; + } + } + return -1; + } + + void refreshModel() { + hashSets = HashDbManager.getInstance().getAllHashSets(); + refreshDisplay(); + } + + void refreshDisplay() { + fireTableDataChanged(); + } + } + /** * 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 @@ -161,8 +387,8 @@ final class HashDbManagementPanel extends javax.swing.JPanel implements OptionsP ingestWarningLabel = new javax.swing.JLabel(); jScrollPane1 = new javax.swing.JScrollPane(); hashSetTable = new HashSetTable(); - deleteButton = new javax.swing.JButton(); - importButton = new javax.swing.JButton(); + deleteDatabaseButton = new javax.swing.JButton(); + importDatabaseButton = new javax.swing.JButton(); hashDatabasesLabel = new javax.swing.JLabel(); nameLabel = new javax.swing.JLabel(); hashDbNameLabel = new javax.swing.JLabel(); @@ -179,21 +405,22 @@ final class HashDbManagementPanel extends javax.swing.JPanel implements OptionsP optionsLabel = new javax.swing.JLabel(); informationSeparator = new javax.swing.JSeparator(); optionsSeparator = new javax.swing.JSeparator(); + newDatabaseButton = new javax.swing.JButton(); - org.openide.awt.Mnemonics.setLocalizedText(jLabel2, org.openide.util.NbBundle.getMessage(HashDbManagementPanel.class, "HashDbManagementPanel.jLabel2.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(jLabel2, org.openide.util.NbBundle.getMessage(HashDbConfigPanel.class, "HashDbConfigPanel.jLabel2.text")); // NOI18N - org.openide.awt.Mnemonics.setLocalizedText(jLabel4, org.openide.util.NbBundle.getMessage(HashDbManagementPanel.class, "HashDbManagementPanel.jLabel4.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(jLabel4, org.openide.util.NbBundle.getMessage(HashDbConfigPanel.class, "HashDbConfigPanel.jLabel4.text")); // NOI18N - org.openide.awt.Mnemonics.setLocalizedText(jLabel6, org.openide.util.NbBundle.getMessage(HashDbManagementPanel.class, "HashDbManagementPanel.jLabel6.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(jLabel6, org.openide.util.NbBundle.getMessage(HashDbConfigPanel.class, "HashDbConfigPanel.jLabel6.text")); // NOI18N jButton3.setFont(new java.awt.Font("Tahoma", 0, 14)); // NOI18N - org.openide.awt.Mnemonics.setLocalizedText(jButton3, org.openide.util.NbBundle.getMessage(HashDbManagementPanel.class, "HashDbManagementPanel.jButton3.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(jButton3, org.openide.util.NbBundle.getMessage(HashDbConfigPanel.class, "HashDbConfigPanel.jButton3.text")); // NOI18N setMinimumSize(new java.awt.Dimension(700, 500)); setPreferredSize(new java.awt.Dimension(700, 500)); ingestWarningLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/hashdatabase/warning16.png"))); // NOI18N - org.openide.awt.Mnemonics.setLocalizedText(ingestWarningLabel, org.openide.util.NbBundle.getMessage(HashDbManagementPanel.class, "HashDbManagementPanel.ingestWarningLabel.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(ingestWarningLabel, org.openide.util.NbBundle.getMessage(HashDbConfigPanel.class, "HashDbConfigPanel.ingestWarningLabel.text")); // NOI18N hashSetTable.setModel(new javax.swing.table.DefaultTableModel( new Object [][] { @@ -212,47 +439,47 @@ final class HashDbManagementPanel extends javax.swing.JPanel implements OptionsP }); jScrollPane1.setViewportView(hashSetTable); - deleteButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/hashdatabase/delete16.png"))); // NOI18N - org.openide.awt.Mnemonics.setLocalizedText(deleteButton, org.openide.util.NbBundle.getMessage(HashDbManagementPanel.class, "HashDbManagementPanel.deleteButton.text")); // NOI18N - deleteButton.setMaximumSize(new java.awt.Dimension(140, 25)); - deleteButton.setMinimumSize(new java.awt.Dimension(140, 25)); - deleteButton.setPreferredSize(new java.awt.Dimension(140, 25)); - deleteButton.addActionListener(new java.awt.event.ActionListener() { + deleteDatabaseButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/hashdatabase/delete16.png"))); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(deleteDatabaseButton, org.openide.util.NbBundle.getMessage(HashDbConfigPanel.class, "HashDbConfigPanel.deleteDatabaseButton.text")); // NOI18N + deleteDatabaseButton.setMaximumSize(new java.awt.Dimension(140, 25)); + deleteDatabaseButton.setMinimumSize(new java.awt.Dimension(140, 25)); + deleteDatabaseButton.setPreferredSize(new java.awt.Dimension(140, 25)); + deleteDatabaseButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { - deleteButtonActionPerformed(evt); + deleteDatabaseButtonActionPerformed(evt); } }); - importButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/hashdatabase/import16.png"))); // NOI18N - org.openide.awt.Mnemonics.setLocalizedText(importButton, org.openide.util.NbBundle.getMessage(HashDbManagementPanel.class, "HashDbManagementPanel.importButton.text")); // NOI18N - importButton.setMaximumSize(new java.awt.Dimension(140, 25)); - importButton.setMinimumSize(new java.awt.Dimension(140, 25)); - importButton.setPreferredSize(new java.awt.Dimension(140, 25)); - importButton.addActionListener(new java.awt.event.ActionListener() { + importDatabaseButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/hashdatabase/import16.png"))); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(importDatabaseButton, org.openide.util.NbBundle.getMessage(HashDbConfigPanel.class, "HashDbConfigPanel.importDatabaseButton.text")); // NOI18N + importDatabaseButton.setMaximumSize(new java.awt.Dimension(140, 25)); + importDatabaseButton.setMinimumSize(new java.awt.Dimension(140, 25)); + importDatabaseButton.setPreferredSize(new java.awt.Dimension(140, 25)); + importDatabaseButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { - importButtonActionPerformed(evt); + importDatabaseButtonActionPerformed(evt); } }); - org.openide.awt.Mnemonics.setLocalizedText(hashDatabasesLabel, org.openide.util.NbBundle.getMessage(HashDbManagementPanel.class, "HashDbManagementPanel.hashDatabasesLabel.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(hashDatabasesLabel, org.openide.util.NbBundle.getMessage(HashDbConfigPanel.class, "HashDbConfigPanel.hashDatabasesLabel.text")); // NOI18N - org.openide.awt.Mnemonics.setLocalizedText(nameLabel, org.openide.util.NbBundle.getMessage(HashDbManagementPanel.class, "HashDbManagementPanel.nameLabel.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(nameLabel, org.openide.util.NbBundle.getMessage(HashDbConfigPanel.class, "HashDbConfigPanel.nameLabel.text")); // NOI18N - org.openide.awt.Mnemonics.setLocalizedText(hashDbNameLabel, org.openide.util.NbBundle.getMessage(HashDbManagementPanel.class, "HashDbManagementPanel.hashDbNameLabel.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(hashDbNameLabel, org.openide.util.NbBundle.getMessage(HashDbConfigPanel.class, "HashDbConfigPanel.hashDbNameLabel.text")); // NOI18N - org.openide.awt.Mnemonics.setLocalizedText(hashDbLocationLabel, org.openide.util.NbBundle.getMessage(HashDbManagementPanel.class, "HashDbManagementPanel.hashDbLocationLabel.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(hashDbLocationLabel, org.openide.util.NbBundle.getMessage(HashDbConfigPanel.class, "HashDbConfigPanel.hashDbLocationLabel.text")); // NOI18N - org.openide.awt.Mnemonics.setLocalizedText(locationLabel, org.openide.util.NbBundle.getMessage(HashDbManagementPanel.class, "HashDbManagementPanel.locationLabel.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(locationLabel, org.openide.util.NbBundle.getMessage(HashDbConfigPanel.class, "HashDbConfigPanel.locationLabel.text")); // NOI18N - org.openide.awt.Mnemonics.setLocalizedText(typeLabel, org.openide.util.NbBundle.getMessage(HashDbManagementPanel.class, "HashDbManagementPanel.typeLabel.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(typeLabel, org.openide.util.NbBundle.getMessage(HashDbConfigPanel.class, "HashDbConfigPanel.typeLabel.text")); // NOI18N - org.openide.awt.Mnemonics.setLocalizedText(hashDbTypeLabel, org.openide.util.NbBundle.getMessage(HashDbManagementPanel.class, "HashDbManagementPanel.hashDbTypeLabel.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(hashDbTypeLabel, org.openide.util.NbBundle.getMessage(HashDbConfigPanel.class, "HashDbConfigPanel.hashDbTypeLabel.text")); // NOI18N - org.openide.awt.Mnemonics.setLocalizedText(hashDbIndexStatusLabel, org.openide.util.NbBundle.getMessage(HashDbManagementPanel.class, "HashDbManagementPanel.hashDbIndexStatusLabel.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(hashDbIndexStatusLabel, org.openide.util.NbBundle.getMessage(HashDbConfigPanel.class, "HashDbConfigPanel.hashDbIndexStatusLabel.text")); // NOI18N - org.openide.awt.Mnemonics.setLocalizedText(indexLabel, org.openide.util.NbBundle.getMessage(HashDbManagementPanel.class, "HashDbManagementPanel.indexLabel.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(indexLabel, org.openide.util.NbBundle.getMessage(HashDbConfigPanel.class, "HashDbConfigPanel.indexLabel.text")); // NOI18N - org.openide.awt.Mnemonics.setLocalizedText(indexButton, org.openide.util.NbBundle.getMessage(HashDbManagementPanel.class, "HashDbManagementPanel.indexButton.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(indexButton, org.openide.util.NbBundle.getMessage(HashDbConfigPanel.class, "HashDbConfigPanel.indexButton.text")); // NOI18N indexButton.setEnabled(false); indexButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { @@ -260,23 +487,34 @@ final class HashDbManagementPanel extends javax.swing.JPanel implements OptionsP } }); - org.openide.awt.Mnemonics.setLocalizedText(useForIngestCheckbox, org.openide.util.NbBundle.getMessage(HashDbManagementPanel.class, "HashDbManagementPanel.useForIngestCheckbox.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(useForIngestCheckbox, org.openide.util.NbBundle.getMessage(HashDbConfigPanel.class, "HashDbConfigPanel.useForIngestCheckbox.text")); // NOI18N useForIngestCheckbox.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { useForIngestCheckboxActionPerformed(evt); } }); - org.openide.awt.Mnemonics.setLocalizedText(showInboxMessagesCheckBox, org.openide.util.NbBundle.getMessage(HashDbManagementPanel.class, "HashDbManagementPanel.showInboxMessagesCheckBox.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(showInboxMessagesCheckBox, org.openide.util.NbBundle.getMessage(HashDbConfigPanel.class, "HashDbConfigPanel.showInboxMessagesCheckBox.text")); // NOI18N showInboxMessagesCheckBox.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { showInboxMessagesCheckBoxActionPerformed(evt); } }); - org.openide.awt.Mnemonics.setLocalizedText(informationLabel, org.openide.util.NbBundle.getMessage(HashDbManagementPanel.class, "HashDbManagementPanel.informationLabel.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(informationLabel, org.openide.util.NbBundle.getMessage(HashDbConfigPanel.class, "HashDbConfigPanel.informationLabel.text")); // NOI18N - org.openide.awt.Mnemonics.setLocalizedText(optionsLabel, org.openide.util.NbBundle.getMessage(HashDbManagementPanel.class, "HashDbManagementPanel.optionsLabel.text")); // NOI18N + 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() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + newDatabaseButtonActionPerformed(evt); + } + }); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); @@ -287,12 +525,7 @@ final class HashDbManagementPanel extends javax.swing.JPanel implements OptionsP .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(hashDatabasesLabel) .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) - .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE) - .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup() - .addComponent(importButton, javax.swing.GroupLayout.PREFERRED_SIZE, 133, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(deleteButton, javax.swing.GroupLayout.PREFERRED_SIZE, 132, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 275, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) .addGroup(layout.createSequentialGroup() @@ -325,7 +558,12 @@ final class HashDbManagementPanel extends javax.swing.JPanel implements OptionsP .addComponent(hashDbNameLabel))) .addComponent(useForIngestCheckbox) .addComponent(showInboxMessagesCheckBox) - .addComponent(indexButton, javax.swing.GroupLayout.PREFERRED_SIZE, 75, javax.swing.GroupLayout.PREFERRED_SIZE)))))) + .addComponent(indexButton, javax.swing.GroupLayout.PREFERRED_SIZE, 75, 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)) .addContainerGap(40, Short.MAX_VALUE)) ); layout.setVerticalGroup( @@ -370,230 +608,110 @@ final class HashDbManagementPanel extends javax.swing.JPanel implements OptionsP .addGap(18, 18, 18) .addComponent(ingestWarningLabel) .addGap(0, 0, Short.MAX_VALUE)) - .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 422, Short.MAX_VALUE)) + .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 391, Short.MAX_VALUE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(importButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(deleteButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .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)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(deleteDatabaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addContainerGap()) ); }// //GEN-END:initComponents private void indexButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_indexButtonActionPerformed - int selected = getSelection(); - final HashDb current = HashDbXML.getCurrent().getAllSets().get(selected); - current.addPropertyChangeListener(new PropertyChangeListener() { - - @Override - public void propertyChange(PropertyChangeEvent evt) { - if (evt.getPropertyName().equals(HashDb.EVENT.INDEXING_DONE.toString())) { - //update tracking of indexing status - indexingState.put((String)evt.getNewValue(), Boolean.FALSE); - - setButtonFromIndexStatus(indexButton, hashDbIndexStatusLabel, current.status()); - resync(); - } - } + final HashDb hashDbToBeIndexed = ((HashSetTable)hashSetTable).getSelection(); + if (hashDbToBeIndexed != null) { + // Add a listener for the INDEXING_DONE event. The listener will update + // the UI. + hashDbToBeIndexed.addPropertyChangeListener(new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + if (evt.getPropertyName().equals(HashDb.Event.INDEXING_DONE.toString())) { + HashDb selectedHashDb = ((HashSetTable)hashSetTable).getSelection(); + if (selectedHashDb != null && hashDbToBeIndexed != null && hashDbToBeIndexed.equals(selectedHashDb)) { + updateComponents(); + } + hashSetTableModel.refreshDisplay(); + } + } + }); - }); - indexingState.put(current.getName(), Boolean.TRUE); - ModalNoButtons singleMNB = new ModalNoButtons(this, new Frame(), current); //Modal reference, to be removed later - singleMNB.setLocationRelativeTo(null); - singleMNB.setVisible(true); - singleMNB.setModal(true); //End Modal reference - indexingState.put(current.getName(), Boolean.FALSE); - setButtonFromIndexStatus(indexButton, this.hashDbIndexStatusLabel, current.status()); + // Display a modal dialog box to kick off the indexing on a worker thread + // and try to persuade the user to wait for the indexing task to finish. + // TODO: This defeats the purpose of doing the indexing on a worker thread. + // The user may also cancel the dialog and change the hash sets configuration. + // That should be fine, as long as the indexing DB is not deleted, which + // shu;d be able to be controlled. + ModalNoButtons indexDialog = new ModalNoButtons(this, new Frame(), hashDbToBeIndexed); + indexDialog.setLocationRelativeTo(null); + indexDialog.setVisible(true); + indexDialog.setModal(true); + } }//GEN-LAST:event_indexButtonActionPerformed - private void deleteButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteButtonActionPerformed - 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) { - - int selected = getSelection(); - HashDbXML xmlHandle = HashDbXML.getCurrent(); - if (xmlHandle.getNSRLSet() != null) { - if (selected == 0) { - HashDbXML.getCurrent().removeNSRLSet(); - } else { - HashDbXML.getCurrent().removeKnownBadSetAt(selected - 1); - } - } else { - HashDbXML.getCurrent().removeKnownBadSetAt(selected); + 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) { + HashDb hashDb = ((HashSetTable)hashSetTable).getSelection(); + if (hashDb != null) { + hashSetManager.removeHashSet(hashDb); + hashSetTableModel.refreshModel(); } - hashSetTableModel.resync(); } - }//GEN-LAST:event_deleteButtonActionPerformed + }//GEN-LAST:event_deleteDatabaseButtonActionPerformed private void hashSetTableKeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_hashSetTableKeyPressed if (evt.getKeyCode() == KeyEvent.VK_DELETE) { - int selected = getSelection(); - HashDbXML xmlHandle = HashDbXML.getCurrent(); - if (xmlHandle.getNSRLSet() != null) { - if (selected == 0) { - HashDbXML.getCurrent().removeNSRLSet(); - } else { - HashDbXML.getCurrent().removeKnownBadSetAt(selected - 1); - } - } else { - HashDbXML.getCurrent().removeKnownBadSetAt(selected); + HashDb hashDb = ((HashSetTable)hashSetTable).getSelection(); + if (hashDb != null) { + hashSetManager.removeHashSet(hashDb); + hashSetTableModel.refreshModel(); } - } - hashSetTableModel.resync(); + } }//GEN-LAST:event_hashSetTableKeyPressed private void useForIngestCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_useForIngestCheckboxActionPerformed - int selected = getSelection(); - HashDbXML xmlHandle = HashDbXML.getCurrent(); - if (xmlHandle.getNSRLSet() != null) { - if (selected == 0) { - HashDb current = HashDbXML.getCurrent().getNSRLSet(); - current.setUseForIngest(useForIngestCheckbox.isSelected()); - HashDbXML.getCurrent().setNSRLSet(current); - } else { - HashDb current = HashDbXML.getCurrent().getKnownBadSets().remove(selected - 1); - current.setUseForIngest(useForIngestCheckbox.isSelected()); - HashDbXML.getCurrent().addKnownBadSet(selected - 1, current); - this.showInboxMessagesCheckBox.setEnabled(useForIngestCheckbox.isSelected()); - } - } else { - HashDb current = HashDbXML.getCurrent().getKnownBadSets().remove(selected); - current.setUseForIngest(useForIngestCheckbox.isSelected()); - HashDbXML.getCurrent().addKnownBadSet(selected, current); - this.showInboxMessagesCheckBox.setEnabled(useForIngestCheckbox.isSelected()); + HashDb hashDb = ((HashSetTable)hashSetTable).getSelection(); + if (hashDb != null) { + hashDb.setUseForIngest(useForIngestCheckbox.isSelected()); + showInboxMessagesCheckBox.setEnabled(useForIngestCheckbox.isSelected()); } }//GEN-LAST:event_useForIngestCheckboxActionPerformed private void showInboxMessagesCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_showInboxMessagesCheckBoxActionPerformed - int selected = getSelection(); - HashDbXML xmlHandle = HashDbXML.getCurrent(); - if (xmlHandle.getNSRLSet() != null) { - if (selected == 0) { - HashDb current = HashDbXML.getCurrent().getNSRLSet(); - current.setShowInboxMessages(showInboxMessagesCheckBox.isSelected()); - HashDbXML.getCurrent().setNSRLSet(current); - } else { - HashDb current = HashDbXML.getCurrent().getKnownBadSets().remove(selected - 1); - current.setShowInboxMessages(showInboxMessagesCheckBox.isSelected()); - HashDbXML.getCurrent().addKnownBadSet(selected - 1, current); - } - } else { - HashDb current = HashDbXML.getCurrent().getKnownBadSets().remove(selected); - current.setShowInboxMessages(showInboxMessagesCheckBox.isSelected()); - HashDbXML.getCurrent().addKnownBadSet(selected, current); + HashDb hashDb = ((HashSetTable)hashSetTable).getSelection(); + if (hashDb != null) { + hashDb.setShowInboxMessages(showInboxMessagesCheckBox.isSelected()); } }//GEN-LAST:event_showInboxMessagesCheckBoxActionPerformed - private void importButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_importButtonActionPerformed - importHashSet(evt); - }//GEN-LAST:event_importButtonActionPerformed + private void importDatabaseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_importDatabaseButtonActionPerformed + HashDb hashDb = new HashDbImportDatabaseDialog().doDialog(); + if (hashDb != null) { + hashSetManager.addHashSet(hashDb); + hashSetTableModel.refreshModel(); + ((HashSetTable)hashSetTable).selectRowByName(hashDb.getDisplayName()); + } + }//GEN-LAST:event_importDatabaseButtonActionPerformed - @Override - public void load() { - hashSetTable.clearSelection(); // Deselect all rows - HashDbXML.getCurrent().reload(); // Reload XML - initUI(null); // Update the UI - hashSetTableModel.resync(); // resync the table - setIngestStatus(IngestManager.getDefault().isIngestRunning()); // check if ingest is running - } - - @Override - /** - * Saves the HashDb's current state. - * This version of store is modified heavily to make use of the ModalNoButtons class. - * The only call that matters is the final HashDbXML.getCurrent().save() - */ - public void store() { - //Checking for for any unindexed databases - List unindexed = new ArrayList(); - for(int i = 0; i < hashSetTableModel.getRowCount(); i++){ - if(! hashSetTableModel.indexExists(i)){ - unindexed.add(hashSetTableModel.getDBAt(i)); - } + private void newDatabaseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_newDatabaseButtonActionPerformed + HashDb hashDb = new HashDbCreateDatabaseDialog().doDialog(); + if (null != hashDb) { + hashSetManager.addHashSet(hashDb); + hashSetTableModel.refreshModel(); + ((HashSetTable)hashSetTable).selectRowByName(hashDb.getDisplayName()); } - //If unindexed ones are found, show a popup box that will either index them, or remove them. - if (unindexed.size() == 1){ - showInvalidIndex(false, unindexed); - } - else if (unindexed.size() > 1){ - showInvalidIndex(true, unindexed); - } - HashDbXML.getCurrent().save(); - - } - - - /** - * Removes a list of HashDbs from the dialog panel that do not have a companion -md5.idx file. - * Occurs when user clicks "No" to the dialog popup box. - * @param toRemove a list of HashDbs that are unindexed - */ - void removeThese(List toRemove) { - HashDbXML xmlHandle = HashDbXML.getCurrent(); - for (HashDb hdb : toRemove) { - for (int i = 0; i < hashSetTableModel.getRowCount(); i++) { - if (hashSetTableModel.getDBAt(i).equals(hdb)) { - if (xmlHandle.getNSRLSet() != null) { - if (i == 0) { - HashDbXML.getCurrent().removeNSRLSet(); - } else { - HashDbXML.getCurrent().removeKnownBadSetAt(i - 1); - } - } else { - HashDbXML.getCurrent().removeKnownBadSetAt(i); - } - hashSetTableModel.resync(); - } - } - } - } - - /** - * Displays the popup box that tells user that some of his databases are unindexed, along with solutions. - * This method is related to ModalNoButtons, to be removed at a later date. - * @param plural Whether or not there are multiple unindexed databases - * @param unindexed The list of unindexed databases. Can be of size 1. - */ - private void showInvalidIndex(boolean plural, List unindexed){ - String total = ""; - String message; - for(HashDb hdb : unindexed){ - total+= "\n" + hdb.getName(); - } - if(plural){ - message = "The following databases are not indexed, would you like to index them now? \n " + total; - } - else{ - message = "The following database is not indexed, would you like to index it now? \n" + total; - } - int res = JOptionPane.showConfirmDialog(this, message, "Unindexed databases", JOptionPane.YES_NO_OPTION); - if(res == JOptionPane.YES_OPTION){ - ModalNoButtons indexingDialog = new ModalNoButtons(this, new Frame(),unindexed); - indexingDialog.setLocationRelativeTo(null); - indexingDialog.setVisible(true); - indexingDialog.setModal(true); - hashSetTableModel.resync(); - } - if(res == JOptionPane.NO_OPTION){ - JOptionPane.showMessageDialog(this, "All unindexed databases will be removed the list"); - removeThese(unindexed); - } - } - - boolean valid() { - // TODO check whether form is consistent and complete - return true; - } + }//GEN-LAST:event_newDatabaseButtonActionPerformed + // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JButton deleteButton; + private javax.swing.JButton deleteDatabaseButton; private javax.swing.JLabel hashDatabasesLabel; private javax.swing.JLabel hashDbIndexStatusLabel; private javax.swing.JLabel hashDbLocationLabel; private javax.swing.JLabel hashDbNameLabel; private javax.swing.JLabel hashDbTypeLabel; private javax.swing.JTable hashSetTable; - private javax.swing.JButton importButton; + private javax.swing.JButton importDatabaseButton; private javax.swing.JButton indexButton; private javax.swing.JLabel indexLabel; private javax.swing.JLabel informationLabel; @@ -606,179 +724,11 @@ final class HashDbManagementPanel extends javax.swing.JPanel implements OptionsP 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.JLabel typeLabel; private javax.swing.JCheckBox useForIngestCheckbox; // End of variables declaration//GEN-END:variables - private void importHashSet(java.awt.event.ActionEvent evt) { - String name = new HashDbAddDatabaseDialog().display(); - if(name != null) { - hashSetTableModel.selectRowByName(name); - } - resync(); - } - - /** - * The visual display of hash databases loaded. - */ - private class HashSetTable extends JTable { - @Override - public Component prepareRenderer(TableCellRenderer renderer, int row, int column) { - Component c = super.prepareRenderer(renderer, row, column); - JComponent jc = (JComponent) c; - String valueText = (String) getValueAt(row, column); - jc.setToolTipText(valueText); - - //Letting the user know which DBs need to be indexed - if(hashSetTableModel.indexExists(row)){ - c.setForeground(Color.black); - } - else{ - c.setForeground(Color.red); - } - return c; - } - } - - - private class HashSetTableModel extends AbstractTableModel { - - private HashDbXML xmlHandle = HashDbXML.getCurrent(); - - @Override - public int getColumnCount() { - return 1; - } - - @Override - public int getRowCount() { - return xmlHandle.getAllSets().size(); - } - - @Override - public String getColumnName(int column) { - return "Name"; - } - - @Override - public Object getValueAt(int rowIndex, int columnIndex) { - if(xmlHandle.getNSRLSet() == null) { - return getDBAt(rowIndex).getName(); - } else { - return rowIndex == 0 ? getDBAt(rowIndex).getName() + " (NSRL)" : getDBAt(rowIndex).getName(); - } - } - - //Internal function for determining whether a companion -md5.idx file exists - private boolean indexExists(int rowIndex){ - return getDBAt(rowIndex).indexExists(); - } - - - //Internal function for getting the DB at a certain index. Used as-is, as well as by dispatch from getValueAt() and indexExists() - private HashDb getDBAt(int rowIndex){ - if (xmlHandle.getNSRLSet() != null) { - if(rowIndex == 0) { - return xmlHandle.getNSRLSet(); - } else { - return xmlHandle.getKnownBadSets().get(rowIndex-1); - } - } else { - return xmlHandle.getKnownBadSets().get(rowIndex); - } - } - - // Selects the row with the given name - private void selectRowByName(String name) { - HashDb NSRL = xmlHandle.getNSRLSet(); - List bad = xmlHandle.getKnownBadSets(); - if(NSRL != null) { - if(NSRL.getName().equals(name)) { - setSelection(0); - } else { - for(int i=0; i getColumnClass(int c) { - return getValueAt(0, c).getClass(); - } - - void resync() { - fireTableDataChanged(); - } - } - - static void setButtonFromIndexStatus(JButton theButton, JLabel theLabel, IndexStatus status) { - theLabel.setText(status.message()); - switch (status) { - case INDEX_OUTDATED: - theButton.setText("Re-index"); - theLabel.setForeground(Color.black); - theButton.setEnabled(true); - break; - case INDEX_CURRENT: - theButton.setText("Re-index"); - theLabel.setForeground(Color.black); - theButton.setEnabled(true); - break; - case NO_INDEX: - theButton.setText("Index"); - theLabel.setForeground(Color.red); - theButton.setEnabled(true); - break; - case INDEXING: - theButton.setText("Indexing"); - theLabel.setForeground(Color.black); - theButton.setEnabled(false); - break; - default: - theButton.setText("Index"); - theLabel.setForeground(Color.black); - theButton.setEnabled(false); - } - if (IngestManager.getDefault().isIngestRunning()) { - theButton.setEnabled(false); - } - } - - private int getSelection() { - return hashSetTable.getSelectionModel().getMinSelectionIndex(); - } - - private void setSelection(int index) { - if(index >= 0 && index < hashSetTable.getRowCount()) { - hashSetTable.getSelectionModel().setSelectionInterval(index, index); - } - } - void resync() { - int index = getSelection(); - this.hashSetTableModel.resync(); - setSelection(index); - } - } \ No newline at end of file diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbContextMenuActionsProvider.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbContextMenuActionsProvider.java new file mode 100755 index 0000000000..128416ff4b --- /dev/null +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbContextMenuActionsProvider.java @@ -0,0 +1,41 @@ +/* + * 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.util.ArrayList; +import java.util.Collection; +import java.util.List; +import javax.swing.Action; +import org.openide.util.Utilities; +import org.openide.util.lookup.ServiceProvider; +import org.sleuthkit.autopsy.corecomponentinterfaces.ContextMenuActionsProvider; +import org.sleuthkit.datamodel.AbstractFile; + +@ServiceProvider(service = ContextMenuActionsProvider.class) +public class HashDbContextMenuActionsProvider implements ContextMenuActionsProvider { + @Override + public List getActions() { + ArrayList actions = new ArrayList<>(); + Collection selectedFiles = Utilities.actionsGlobalContext().lookupAll(AbstractFile.class); + if (!selectedFiles.isEmpty()) { + actions.add(AddContentToHashDbAction.getInstance()); + } + return actions; + } +} diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbCreateDatabaseDialog.form b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbCreateDatabaseDialog.form new file mode 100644 index 0000000000..1342083256 --- /dev/null +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbCreateDatabaseDialog.form @@ -0,0 +1,217 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbAddDatabaseDialog.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbCreateDatabaseDialog.java similarity index 68% rename from HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbAddDatabaseDialog.java rename to HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbCreateDatabaseDialog.java index cb635520fe..1a69551f0b 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbAddDatabaseDialog.java +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbCreateDatabaseDialog.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011 Basis Technology Corp. + * Copyright 2013 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -23,57 +23,64 @@ import java.awt.Dimension; import java.awt.Toolkit; import java.io.File; import java.io.IOException; -import java.util.Arrays; import java.util.logging.Level; import org.sleuthkit.autopsy.coreutils.Logger; import javax.swing.JFileChooser; import javax.swing.JOptionPane; import javax.swing.filechooser.FileNameExtensionFilter; -import org.sleuthkit.autopsy.hashdatabase.HashDb.DBType; -import org.sleuthkit.datamodel.SleuthkitJNI; -import org.sleuthkit.datamodel.TskException; +import org.apache.commons.io.FilenameUtils; +import org.sleuthkit.autopsy.hashdatabase.HashDb.KnownFilesType; +import org.sleuthkit.datamodel.TskCoreException; -/** - * - * @author dfickling - */ -final class HashDbAddDatabaseDialog extends javax.swing.JDialog { - - private JFileChooser fc = new JFileChooser(); - private String databaseName; - private static final Logger logger = Logger.getLogger(HashDbAddDatabaseDialog.class.getName()); - /** - * Creates new form HashDbAddDatabaseDialog - */ - HashDbAddDatabaseDialog() { - super(new javax.swing.JFrame(), "Add Hash Database", true); - setResizable(false); +final class HashDbCreateDatabaseDialog extends javax.swing.JDialog { + private JFileChooser fileChooser; + private HashDb newHashDb = null; + + HashDbCreateDatabaseDialog() { + super(new javax.swing.JFrame(), "Create Hash Database", true); + setResizable(false); + 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) { + cancelSelection(); + } + return; + } + if (selectedFile.exists()) { + int r = JOptionPane.showConfirmDialog(this, "A file with this name already exists. Please enter a new filename.", "Existing File", JOptionPane.OK_CANCEL_OPTION); + if (r == JOptionPane.CANCEL_OPTION) { + cancelSelection(); + } + return; + } + super.approveSelection(); + } + }; initComponents(); customizeComponents(); } void customizeComponents() { - fc.setDragEnabled(false); - fc.setFileSelectionMode(JFileChooser.FILES_ONLY); - String[] EXTENSION = new String[] { "txt", "idx", "hash", "Hash", "hsh"}; - FileNameExtensionFilter filter = new FileNameExtensionFilter( - "Hash Database File", EXTENSION); - fc.setFileFilter(filter); - fc.setMultiSelectionEnabled(false); + 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); } - String display() { - Dimension screenDimension = Toolkit.getDefaultToolkit().getScreenSize(); - - // set the popUp window / JFrame - int w = this.getSize().width; - int h = this.getSize().height; - - // set the location of the popUp Window on the center of the screen - setLocation((screenDimension.width - w) / 2, (screenDimension.height - h) / 2); + HashDb doDialog() { + newHashDb = null; - this.setVisible(true); - return databaseName; + // 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 newHashDb; } /** @@ -100,23 +107,23 @@ final class HashDbAddDatabaseDialog extends javax.swing.JDialog { setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); - org.openide.awt.Mnemonics.setLocalizedText(okButton, org.openide.util.NbBundle.getMessage(HashDbAddDatabaseDialog.class, "HashDbAddDatabaseDialog.okButton.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); } }); - org.openide.awt.Mnemonics.setLocalizedText(cancelButton, org.openide.util.NbBundle.getMessage(HashDbAddDatabaseDialog.class, "HashDbAddDatabaseDialog.cancelButton.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(cancelButton, org.openide.util.NbBundle.getMessage(HashDbCreateDatabaseDialog.class, "HashDbCreateDatabaseDialog.cancelButton.text")); // NOI18N cancelButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { cancelButtonActionPerformed(evt); } }); - databasePathTextField.setText(org.openide.util.NbBundle.getMessage(HashDbAddDatabaseDialog.class, "HashDbAddDatabaseDialog.databasePathTextField.text")); // NOI18N + databasePathTextField.setText(org.openide.util.NbBundle.getMessage(HashDbCreateDatabaseDialog.class, "HashDbCreateDatabaseDialog.databasePathTextField.text")); // NOI18N - org.openide.awt.Mnemonics.setLocalizedText(browseButton, org.openide.util.NbBundle.getMessage(HashDbAddDatabaseDialog.class, "HashDbAddDatabaseDialog.browseButton.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); @@ -124,7 +131,8 @@ final class HashDbAddDatabaseDialog extends javax.swing.JDialog { }); buttonGroup1.add(nsrlRadioButton); - org.openide.awt.Mnemonics.setLocalizedText(nsrlRadioButton, org.openide.util.NbBundle.getMessage(HashDbAddDatabaseDialog.class, "HashDbAddDatabaseDialog.nsrlRadioButton.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(nsrlRadioButton, org.openide.util.NbBundle.getMessage(HashDbCreateDatabaseDialog.class, "HashDbCreateDatabaseDialog.nsrlRadioButton.text")); // NOI18N + nsrlRadioButton.setEnabled(false); nsrlRadioButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { nsrlRadioButtonActionPerformed(evt); @@ -133,21 +141,21 @@ final class HashDbAddDatabaseDialog extends javax.swing.JDialog { buttonGroup1.add(knownBadRadioButton); knownBadRadioButton.setSelected(true); - org.openide.awt.Mnemonics.setLocalizedText(knownBadRadioButton, org.openide.util.NbBundle.getMessage(HashDbAddDatabaseDialog.class, "HashDbAddDatabaseDialog.knownBadRadioButton.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(knownBadRadioButton, org.openide.util.NbBundle.getMessage(HashDbCreateDatabaseDialog.class, "HashDbCreateDatabaseDialog.knownBadRadioButton.text")); // NOI18N knownBadRadioButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { knownBadRadioButtonActionPerformed(evt); } }); - org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(HashDbAddDatabaseDialog.class, "HashDbAddDatabaseDialog.jLabel1.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(HashDbCreateDatabaseDialog.class, "HashDbCreateDatabaseDialog.jLabel1.text")); // NOI18N - databaseNameTextField.setText(org.openide.util.NbBundle.getMessage(HashDbAddDatabaseDialog.class, "HashDbAddDatabaseDialog.databaseNameTextField.text")); // NOI18N + databaseNameTextField.setText(org.openide.util.NbBundle.getMessage(HashDbCreateDatabaseDialog.class, "HashDbCreateDatabaseDialog.databaseNameTextField.text")); // NOI18N - org.openide.awt.Mnemonics.setLocalizedText(jLabel2, org.openide.util.NbBundle.getMessage(HashDbAddDatabaseDialog.class, "HashDbAddDatabaseDialog.jLabel2.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(HashDbAddDatabaseDialog.class, "HashDbAddDatabaseDialog.useForIngestCheckbox.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(useForIngestCheckbox, org.openide.util.NbBundle.getMessage(HashDbCreateDatabaseDialog.class, "HashDbCreateDatabaseDialog.useForIngestCheckbox.text")); // NOI18N useForIngestCheckbox.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { useForIngestCheckboxActionPerformed(evt); @@ -155,7 +163,7 @@ final class HashDbAddDatabaseDialog extends javax.swing.JDialog { }); sendInboxMessagesCheckbox.setSelected(true); - org.openide.awt.Mnemonics.setLocalizedText(sendInboxMessagesCheckbox, org.openide.util.NbBundle.getMessage(HashDbAddDatabaseDialog.class, "HashDbAddDatabaseDialog.sendInboxMessagesCheckbox.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(sendInboxMessagesCheckbox, org.openide.util.NbBundle.getMessage(HashDbCreateDatabaseDialog.class, "HashDbCreateDatabaseDialog.sendInboxMessagesCheckbox.text")); // NOI18N javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); getContentPane().setLayout(layout); @@ -227,36 +235,20 @@ final class HashDbAddDatabaseDialog extends javax.swing.JDialog { }// //GEN-END:initComponents private void browseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_browseButtonActionPerformed - String oldText = databasePathTextField.getText(); - // set the current directory of the FileChooser if the databasePath Field is valid - File currentDir = new File(oldText); - if (currentDir.exists()) { - fc.setCurrentDirectory(currentDir); - } - int retval = fc.showOpenDialog(this); - if (retval == JFileChooser.APPROVE_OPTION) { - File f = fc.getSelectedFile(); - try { - String filePath = f.getCanonicalPath(); - if (HashDb.isIndexPath(filePath)) { - filePath = HashDb.toDatabasePath(filePath); - } - String derivedName = SleuthkitJNI.getDatabaseName(filePath); - databasePathTextField.setText(filePath); - databaseNameTextField.setText(derivedName); - if (derivedName.toLowerCase().contains("nsrl")) { + 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")) { nsrlRadioButton.setSelected(true); nsrlRadioButtonActionPerformed(null); } - } catch (IOException ex) { - logger.log(Level.WARNING, "Couldn't get selected file path.", ex); - } catch (TskException ex) { - logger.log(Level.WARNING, "Invalid database: ", ex); - int tryAgain = JOptionPane.showConfirmDialog(this, "Database file you chose cannot be opened.\n" + "If it was just an index, please try to recreate it from the database.\n" + "Would you like to choose another database?", "Invalid File", JOptionPane.YES_NO_OPTION); - if (tryAgain == JOptionPane.YES_OPTION) { - browseButtonActionPerformed(evt); - } - } + } + } + catch (IOException ex) { + Logger.getLogger(HashDbCreateDatabaseDialog.class.getName()).log(Level.WARNING, "Couldn't get selected file path.", ex); } }//GEN-LAST:event_browseButtonActionPerformed @@ -283,41 +275,28 @@ final class HashDbAddDatabaseDialog extends javax.swing.JDialog { JOptionPane.showMessageDialog(this, "Database name cannot be empty"); return; } - try { - File db = new File(databasePathTextField.getText()); - File idx = new File(databasePathTextField.getText() + "-md5.idx"); - if (!db.exists() && !idx.exists()) { - JOptionPane.showMessageDialog(this, "Selected file does not exist"); - return; - } - String path = db.getCanonicalPath(); - SleuthkitJNI.getDatabaseName(path); - } catch (Exception ex) { - JOptionPane.showMessageDialog(this, "Database file you chose cannot be opened.\n" + "If it was just an index, please try to recreate it from the database"); - return; - } - DBType type; + + KnownFilesType type; if(nsrlRadioButton.isSelected()) { - type = DBType.NSRL; + type = KnownFilesType.NSRL; } else { - type = DBType.KNOWN_BAD; + type = KnownFilesType.KNOWN_BAD; } - HashDb db = new HashDb(databaseNameTextField.getText(), - Arrays.asList(new String[] {databasePathTextField.getText()}), - useForIngestCheckbox.isSelected(), - sendInboxMessagesCheckbox.isSelected(), - type); - if(type == DBType.KNOWN_BAD) { - HashDbXML.getCurrent().addKnownBadSet(db); - } else if(type == DBType.NSRL) { - HashDbXML.getCurrent().setNSRLSet(db); - } - databaseName = databaseNameTextField.getText(); + + try + { + newHashDb = HashDb.createHashDatabase(databaseNameTextField.getText(), databasePathTextField.getText(), useForIngestCheckbox.isSelected(), sendInboxMessagesCheckbox.isSelected(), type); + } + catch (TskCoreException ex) { + Logger.getLogger(HashDbCreateDatabaseDialog.class.getName()).log(Level.SEVERE, "Hash database creation error", ex); + JOptionPane.showMessageDialog(this, "Failed to create hash database."); + return; + } + this.dispose(); }//GEN-LAST:event_okButtonActionPerformed private void useForIngestCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_useForIngestCheckboxActionPerformed - // TODO add your handling code here: }//GEN-LAST:event_useForIngestCheckboxActionPerformed // Variables declaration - do not modify//GEN-BEGIN:variables diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbAddDatabaseDialog.form b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbImportDatabaseDialog.form similarity index 86% rename from HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbAddDatabaseDialog.form rename to HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbImportDatabaseDialog.form index f336266aed..e6473a9688 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbAddDatabaseDialog.form +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbImportDatabaseDialog.form @@ -10,6 +10,7 @@ + @@ -110,7 +111,7 @@ - + @@ -120,7 +121,7 @@ - + @@ -130,14 +131,14 @@ - + - + @@ -150,7 +151,7 @@ - + @@ -164,7 +165,7 @@ - + @@ -174,21 +175,21 @@ - + - + - + @@ -196,7 +197,7 @@ - + @@ -207,7 +208,7 @@ - + diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbImportDatabaseDialog.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbImportDatabaseDialog.java new file mode 100644 index 0000000000..8dc8e459ed --- /dev/null +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbImportDatabaseDialog.java @@ -0,0 +1,313 @@ +/* + * 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.awt.Dimension; +import java.awt.Toolkit; +import java.io.File; +import java.io.IOException; +import java.util.logging.Level; +import org.sleuthkit.autopsy.coreutils.Logger; +import javax.swing.JFileChooser; +import javax.swing.JOptionPane; +import javax.swing.filechooser.FileNameExtensionFilter; +import org.sleuthkit.autopsy.hashdatabase.HashDb.KnownFilesType; +import org.sleuthkit.datamodel.TskCoreException; +import org.apache.commons.io.FilenameUtils; + +/** + * Instances of this class allow a user to select a hash database for import. + */ +final class HashDbImportDatabaseDialog extends javax.swing.JDialog { + private JFileChooser fileChooser = new JFileChooser(); + private HashDb selectedHashDb; + + HashDbImportDatabaseDialog() { + super(new javax.swing.JFrame(), "Import Hash Database", true); + setResizable(false); + 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); + } + + 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; + } + + /** + * 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 + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + buttonGroup1 = new javax.swing.ButtonGroup(); + okButton = new javax.swing.JButton(); + cancelButton = new javax.swing.JButton(); + databasePathTextField = new javax.swing.JTextField(); + browseButton = new javax.swing.JButton(); + nsrlRadioButton = new javax.swing.JRadioButton(); + knownBadRadioButton = new javax.swing.JRadioButton(); + jLabel1 = new javax.swing.JLabel(); + databaseNameTextField = new javax.swing.JTextField(); + jLabel2 = new javax.swing.JLabel(); + useForIngestCheckbox = new javax.swing.JCheckBox(); + sendInboxMessagesCheckbox = new javax.swing.JCheckBox(); + + setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); + + org.openide.awt.Mnemonics.setLocalizedText(okButton, org.openide.util.NbBundle.getMessage(HashDbImportDatabaseDialog.class, "HashDbImportDatabaseDialog.okButton.text")); // NOI18N + okButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + okButtonActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(cancelButton, org.openide.util.NbBundle.getMessage(HashDbImportDatabaseDialog.class, "HashDbImportDatabaseDialog.cancelButton.text")); // NOI18N + cancelButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + cancelButtonActionPerformed(evt); + } + }); + + 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() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + browseButtonActionPerformed(evt); + } + }); + + buttonGroup1.add(nsrlRadioButton); + org.openide.awt.Mnemonics.setLocalizedText(nsrlRadioButton, org.openide.util.NbBundle.getMessage(HashDbImportDatabaseDialog.class, "HashDbImportDatabaseDialog.nsrlRadioButton.text")); // NOI18N + nsrlRadioButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + nsrlRadioButtonActionPerformed(evt); + } + }); + + buttonGroup1.add(knownBadRadioButton); + knownBadRadioButton.setSelected(true); + org.openide.awt.Mnemonics.setLocalizedText(knownBadRadioButton, org.openide.util.NbBundle.getMessage(HashDbImportDatabaseDialog.class, "HashDbImportDatabaseDialog.knownBadRadioButton.text")); // NOI18N + knownBadRadioButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + knownBadRadioButtonActionPerformed(evt); + } + }); + + 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 + + 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() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + useForIngestCheckboxActionPerformed(evt); + } + }); + + sendInboxMessagesCheckbox.setSelected(true); + org.openide.awt.Mnemonics.setLocalizedText(sendInboxMessagesCheckbox, org.openide.util.NbBundle.getMessage(HashDbImportDatabaseDialog.class, "HashDbImportDatabaseDialog.sendInboxMessagesCheckbox.text")); // NOI18N + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .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)) + .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(nsrlRadioButton)) + .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)))) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(databasePathTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(browseButton)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel1) + .addComponent(databaseNameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jLabel2) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(nsrlRadioButton) + .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()) + ); + + pack(); + }// //GEN-END:initComponents + + private void browseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_browseButtonActionPerformed + if (fileChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { + File databaseFile = fileChooser.getSelectedFile(); + try { + databasePathTextField.setText(databaseFile.getCanonicalPath()); + databaseNameTextField.setText(FilenameUtils.removeExtension(databaseFile.getName())); + } + catch (IOException ex) { + Logger.getLogger(HashDbImportDatabaseDialog.class.getName()).log(Level.SEVERE, "Failed to get path of selected database", ex); + } + } + }//GEN-LAST:event_browseButtonActionPerformed + + private void nsrlRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_nsrlRadioButtonActionPerformed + sendInboxMessagesCheckbox.setSelected(false); + sendInboxMessagesCheckbox.setEnabled(false); + }//GEN-LAST:event_nsrlRadioButtonActionPerformed + + private void knownBadRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_knownBadRadioButtonActionPerformed + sendInboxMessagesCheckbox.setSelected(true); + sendInboxMessagesCheckbox.setEnabled(true); + }//GEN-LAST:event_knownBadRadioButtonActionPerformed + + private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed + this.dispose(); + }//GEN-LAST:event_cancelButtonActionPerformed + + private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed + if(databasePathTextField.getText().isEmpty()) { + JOptionPane.showMessageDialog(this, "Database path cannot be empty."); + return; + } + + if(databaseNameTextField.getText().isEmpty()) { + JOptionPane.showMessageDialog(this, "Database display name cannot be empty."); + return; + } + + File file = new File(databasePathTextField.getText()); + if (!file.exists()) { + JOptionPane.showMessageDialog(this, "Selected database does not exist."); + return; + } + + String filePath; + try { + filePath = file.getCanonicalPath(); + } + catch (IOException ex) { + Logger.getLogger(HashDbImportDatabaseDialog.class.getName()).log(Level.SEVERE, "Failed to get path of selected database", ex); + JOptionPane.showMessageDialog(this, "Failed to get path of selected database."); + return; + } + + KnownFilesType type; + if (nsrlRadioButton.isSelected()) { + type = KnownFilesType.NSRL; + } + else { + type = KnownFilesType.KNOWN_BAD; + } + + try { + selectedHashDb = HashDb.openHashDatabase(databaseNameTextField.getText(), filePath, useForIngestCheckbox.isSelected(), sendInboxMessagesCheckbox.isSelected(), type); + + +// if (!selectedHashDb.hasTextLookupIndexOnly()) + + } + 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."); + return; + } + + this.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.JLabel jLabel1; + private javax.swing.JLabel jLabel2; + private javax.swing.JRadioButton knownBadRadioButton; + private javax.swing.JRadioButton nsrlRadioButton; + private javax.swing.JButton okButton; + private javax.swing.JCheckBox sendInboxMessagesCheckbox; + private javax.swing.JCheckBox useForIngestCheckbox; + // 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 3a3ff6dd0e..17125cb262 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbIngestModule.java +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbIngestModule.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2013 Basis Technology Corp. + * Copyright 2011 - 2013 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,8 +21,6 @@ package org.sleuthkit.autopsy.hashdatabase; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; -import java.util.Map; import java.util.logging.Level; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.coreutils.Logger; @@ -39,33 +37,28 @@ import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; import org.sleuthkit.datamodel.Hash; import org.sleuthkit.datamodel.SleuthkitCase; -import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.TskException; public class HashDbIngestModule extends IngestModuleAbstractFile { - private static HashDbIngestModule instance = null; public final static String MODULE_NAME = "Hash Lookup"; public final static String MODULE_DESCRIPTION = "Identifies known and notables files using supplied hash databases, such as a standard NSRL database."; final public static String MODULE_VERSION = "1.0"; private static final Logger logger = Logger.getLogger(HashDbIngestModule.class.getName()); + private HashDbConfigPanel panel; private IngestServices services; private SleuthkitCase skCase; private static int messageId = 0; - private int knownBadCount; - // Whether or not to do hash lookups (only set to true if there are dbs set) - private boolean nsrlIsSet; - private boolean knownBadIsSet; + private int knownBadCount = 0; private boolean calcHashesIsSet; + private HashDb nsrlHashSet; + private ArrayList knownBadHashSets = new ArrayList<>(); static long calctime = 0; static long lookuptime = 0; - private Map knownBadSets = new HashMap<>(); - private HashDbManagementPanel panel; private final Hash hasher = new Hash(); private HashDbIngestModule() { - knownBadCount = 0; } public static synchronized HashDbIngestModule getDefault() { @@ -75,97 +68,6 @@ public class HashDbIngestModule extends IngestModuleAbstractFile { return instance; } - @Override - public void init(IngestModuleInit initContext) { - services = IngestServices.getDefault(); - this.skCase = Case.getCurrentCase().getSleuthkitCase(); - try { - HashDbXML hdbxml = HashDbXML.getCurrent(); - knownBadSets.clear(); - skCase.clearLookupDatabases(); - nsrlIsSet = false; - knownBadIsSet = false; - calcHashesIsSet = hdbxml.getCalculate(); - - HashDb nsrl = hdbxml.getNSRLSet(); - if (nsrl != null && nsrl.getUseForIngest() && IndexStatus.isIngestible(nsrl.status())) { - nsrlIsSet = true; - // @@@ Unchecked return value - skCase.setNSRLDatabase(nsrl.getDatabasePaths().get(0)); - } - - for (HashDb db : hdbxml.getKnownBadSets()) { - IndexStatus status = db.status(); - if (db.getUseForIngest() && IndexStatus.isIngestible(status)) { - knownBadIsSet = true; - int ret = skCase.addKnownBadDatabase(db.getDatabasePaths().get(0)); // TODO: support multiple paths - knownBadSets.put(ret, db); - } - } - - if (!nsrlIsSet) { - this.services.postMessage(IngestMessage.createWarningMessage(++messageId, this, "No NSRL database set", "Known file search will not be executed.")); - } - if (!knownBadIsSet) { - this.services.postMessage(IngestMessage.createWarningMessage(++messageId, this, "No known bad database set", "Known bad file search will not be executed.")); - } - - } catch (TskException ex) { - logger.log(Level.SEVERE, "Setting NSRL and Known database failed", ex); - this.services.postMessage(IngestMessage.createErrorMessage(++messageId, this, "Error Configuring Hash Databases", "Setting NSRL and Known database failed.")); - } - } - - @Override - public void complete() { - if ((knownBadIsSet) || (nsrlIsSet)) { - StringBuilder detailsSb = new StringBuilder(); - //details - detailsSb.append(""); - - detailsSb.append(""); - detailsSb.append(""); - - detailsSb.append("\n"); - detailsSb.append("\n"); - detailsSb.append("
Known bads found:").append(knownBadCount).append("
Total Calculation Time").append(calctime).append("
Total Lookup Time").append(lookuptime).append("
"); - - detailsSb.append("

Databases Used:

\n
    "); - for (HashDb db : knownBadSets.values()) { - detailsSb.append("
  • ").append(db.getName()).append("
  • \n"); - } - - detailsSb.append("
"); - services.postMessage(IngestMessage.createMessage(++messageId, IngestMessage.MessageType.INFO, this, "Hash Lookup Results", detailsSb.toString())); - clearHashDatabaseHandles(); - } - } - - private void clearHashDatabaseHandles() { - try { - skCase.clearLookupDatabases(); - } catch (TskCoreException ex) { - logger.log(Level.WARNING, "Error clearing hash database handles. ", ex); - } - this.nsrlIsSet = false; - this.knownBadIsSet = false; - } - - /** - * notification from manager to stop processing due to some interruption - * (user, error, exception) - */ - @Override - public void stop() { - clearHashDatabaseHandles(); - } - - /** - * get specific name of the module should be unique across modules, a - * user-friendly name of the module shown in GUI - * - * @return The name of this Ingest Module - */ @Override public String getName() { return MODULE_NAME; @@ -181,6 +83,76 @@ public class HashDbIngestModule extends IngestModuleAbstractFile { return MODULE_VERSION; } + @Override + public boolean hasSimpleConfiguration() { + return true; + } + + @Override + public javax.swing.JPanel getSimpleConfiguration(String context) { + return new HashDbSimpleConfigPanel(); + } + + @Override + public void saveSimpleConfiguration() { + HashDbManager.getInstance().save(); + } + + @Override + public boolean hasAdvancedConfiguration() { + return true; + } + + @Override + public javax.swing.JPanel getAdvancedConfiguration(String context) { + if (panel == null) { + panel = new HashDbConfigPanel(); + } + + panel.load(); + return panel; + } + + @Override + public void saveAdvancedConfiguration() { + if (panel != null) { + panel.store(); + } + } + + @Override + public void init(IngestModuleInit initContext) { + services = IngestServices.getDefault(); + skCase = Case.getCurrentCase().getSleuthkitCase(); + HashDbManager hashDbManager = HashDbManager.getInstance(); + + nsrlHashSet = null; + knownBadHashSets.clear(); + calcHashesIsSet = hashDbManager.shouldAlwaysCalculateHashes(); + + HashDb nsrl = hashDbManager.getNSRLHashSet(); + if (nsrl != null && nsrl.getUseForIngest() && nsrl.hasLookupIndex()) { + nsrlHashSet = nsrl; + } + + for (HashDb db : hashDbManager.getKnownBadHashSets()) { + if (db.getUseForIngest() && db.hasLookupIndex()) { + knownBadHashSets.add(db); + } + } + + if (nsrlHashSet == null) { + services.postMessage(IngestMessage.createWarningMessage(++messageId, this, "No NSRL database set", "Known file search will not be executed.")); + } + if (knownBadHashSets.isEmpty()) { + services.postMessage(IngestMessage.createWarningMessage(++messageId, this, "No known bad database set", "Known bad file search will not be executed.")); + } + } + + @Override + public boolean hasBackgroundJobsRunning() { + return false; + } @Override public ProcessResult process(PipelineContextpipelineContext, AbstractFile file) { @@ -191,51 +163,87 @@ public class HashDbIngestModule extends IngestModuleAbstractFile { return processFile(file); } - - - @Override - public boolean hasBackgroundJobsRunning() { - return false; - } - - @Override - public boolean hasSimpleConfiguration() { - return true; - } - - @Override - public boolean hasAdvancedConfiguration() { - return true; - } - - @Override - public javax.swing.JPanel getSimpleConfiguration(String context) { - HashDbXML.getCurrent().reload(); - return new HashDbSimplePanel(); - } - - @Override - public javax.swing.JPanel getAdvancedConfiguration(String context) { - //return HashDbManagementPanel.getDefault(); - getPanel().load(); - return getPanel(); - } - - @Override - public void saveAdvancedConfiguration() { - getPanel().store(); - } - - private HashDbManagementPanel getPanel() { - if (panel == null) { - panel = new HashDbManagementPanel(); + + private ProcessResult processFile(AbstractFile file) { + // bail out if we have no hashes set + if ((nsrlHashSet == null) && (knownBadHashSets.isEmpty()) && (calcHashesIsSet == false)) { + return ProcessResult.OK; } - return panel; - } - @Override - public void saveSimpleConfiguration() { - HashDbXML.getCurrent().save(); + // calc hash value + String name = file.getName(); + String md5Hash = file.getMd5Hash(); + if (md5Hash == null || md5Hash.isEmpty()) { + try { + long calcstart = System.currentTimeMillis(); + md5Hash = hasher.calculateMd5(file); + calctime += (System.currentTimeMillis() - calcstart); + } catch (IOException ex) { + logger.log(Level.WARNING, "Error calculating hash of file " + name, ex); + services.postMessage(IngestMessage.createErrorMessage(++messageId, HashDbIngestModule.this, "Read Error: " + name, + "Error encountered while calculating the hash value for " + name + ".")); + return ProcessResult.ERROR; + } + } + + // look up in known bad first + TskData.FileKnown status = TskData.FileKnown.UKNOWN; + boolean foundBad = false; + ProcessResult ret = ProcessResult.OK; + for (HashDb db : knownBadHashSets) { + try { + long lookupstart = System.currentTimeMillis(); + status = db.lookUp(file); + lookuptime += (System.currentTimeMillis() - lookupstart); + } catch (TskException ex) { + logger.log(Level.WARNING, "Couldn't lookup known bad hash for file " + name + " - see sleuthkit log for details", ex); + services.postMessage(IngestMessage.createErrorMessage(++messageId, HashDbIngestModule.this, "Hash Lookup Error: " + name, + "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.getDisplayName(); + processBadFile(file, md5Hash, hashSetName, db.getShowInboxMessages()); + } + } + + // only do NSRL if we didn't find a known bad + if (!foundBad && nsrlHashSet != null) { + try { + long lookupstart = System.currentTimeMillis(); + status = nsrlHashSet.lookUp(file); + lookuptime += (System.currentTimeMillis() - lookupstart); + } catch (TskException ex) { + logger.log(Level.WARNING, "Couldn't lookup NSRL hash for file " + name + " - see sleuthkit log for details", ex); + services.postMessage(IngestMessage.createErrorMessage(++messageId, HashDbIngestModule.this, "Hash Lookup Error: " + name, + "Error encountered while looking up NSRL hash value for " + name + ".")); + ret = ProcessResult.ERROR; + } + + if (status.equals(TskData.FileKnown.KNOWN)) { + try { + skCase.setKnown(file, TskData.FileKnown.KNOWN); + } 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 (NSRL) state for " + name + ".")); + ret = ProcessResult.ERROR; + } + } + } + + return ret; } private void processBadFile(AbstractFile abstractFile, String md5Hash, String hashSetName, boolean showInboxMessage) { @@ -281,100 +289,32 @@ public class HashDbIngestModule extends IngestModuleAbstractFile { } } + + @Override + public void complete() { + if ((!knownBadHashSets.isEmpty()) || (nsrlHashSet != null)) { + StringBuilder detailsSb = new StringBuilder(); + //details + detailsSb.append(""); - private ProcessResult processFile(AbstractFile file) { - // bail out if we have no hashes set - if ((nsrlIsSet == false) && (knownBadIsSet == false) && (calcHashesIsSet == false)) { - return ProcessResult.OK; - } + detailsSb.append(""); + detailsSb.append(""); - // calc hash value - String name = file.getName(); - String md5Hash = file.getMd5Hash(); - if (md5Hash == null || md5Hash.isEmpty()) { - try { - long calcstart = System.currentTimeMillis(); - md5Hash = hasher.calculateMd5(file); - calctime += (System.currentTimeMillis() - calcstart); - } catch (IOException ex) { - logger.log(Level.WARNING, "Error calculating hash of file " + name, ex); - services.postMessage(IngestMessage.createErrorMessage(++messageId, HashDbIngestModule.this, "Read Error: " + name, - "Error encountered while calculating the hash value for " + name + ".")); - return ProcessResult.ERROR; - } - } - + detailsSb.append("\n"); + detailsSb.append("\n"); + detailsSb.append("
Known bads found:").append(knownBadCount).append("
Total Calculation Time").append(calctime).append("
Total Lookup Time").append(lookuptime).append("
"); - // look up in known bad first - TskData.FileKnown status = TskData.FileKnown.UKNOWN; - boolean foundBad = false; - ProcessResult ret = ProcessResult.OK; - - if (knownBadIsSet) { - for (Map.Entry entry : knownBadSets.entrySet()) { - try { - long lookupstart = System.currentTimeMillis(); - status = skCase.knownBadLookupMd5(md5Hash, entry.getKey()); - lookuptime += (System.currentTimeMillis() - lookupstart); - } catch (TskException ex) { - logger.log(Level.WARNING, "Couldn't lookup known bad hash for file " + name + " - see sleuthkit log for details", ex); - services.postMessage(IngestMessage.createErrorMessage(++messageId, HashDbIngestModule.this, "Hash Lookup Error: " + name, - "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 = entry.getValue().getName(); - processBadFile(file, md5Hash, hashSetName, entry.getValue().getShowInboxMessages()); - } - } - } - - // only do NSRL if we didn't find a known bad - if (!foundBad && nsrlIsSet) { - try { - long lookupstart = System.currentTimeMillis(); - status = skCase.nsrlLookupMd5(md5Hash); - lookuptime += (System.currentTimeMillis() - lookupstart); - } catch (TskException ex) { - logger.log(Level.WARNING, "Couldn't lookup NSRL hash for file " + name + " - see sleuthkit log for details", ex); - services.postMessage(IngestMessage.createErrorMessage(++messageId, HashDbIngestModule.this, "Hash Lookup Error: " + name, - "Error encountered while looking up NSRL hash value for " + name + ".")); - ret = ProcessResult.ERROR; + detailsSb.append("

Databases Used:

\n
    "); + for (HashDb db : knownBadHashSets) { + detailsSb.append("
  • ").append(db.getDisplayName()).append("
  • \n"); } - if (status.equals(TskData.FileKnown.KNOWN)) { - try { - skCase.setKnown(file, TskData.FileKnown.KNOWN); - } 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 (NSRL) state for " + name + ".")); - ret = ProcessResult.ERROR; - } - } + detailsSb.append("
"); + services.postMessage(IngestMessage.createMessage(++messageId, IngestMessage.MessageType.INFO, this, "Hash Lookup Results", detailsSb.toString())); } - - return ret; } - public ArrayList getKnownBadSetNames() { - ArrayList knownBadSetNames = new ArrayList<>(); - HashDbXML hdbxml = HashDbXML.getCurrent(); - for (HashDb db : hdbxml.getKnownBadSets()) { - knownBadSetNames.add(db.getName()); - } - return knownBadSetNames; + @Override + public void stop() { } - -} +} \ No newline at end of file diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbXML.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbManager.java similarity index 51% rename from HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbXML.java rename to HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbManager.java index b0040ff080..40016af622 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbXML.java +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbManager.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011 Basis Technology Corp. + * Copyright 2011 - 2013 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.hashdatabase; import java.io.File; import java.io.IOException; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.logging.Level; import javax.swing.JFileChooser; @@ -32,14 +33,19 @@ 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.DBType; +import org.sleuthkit.autopsy.hashdatabase.HashDb.KnownFilesType; import org.sleuthkit.datamodel.SleuthkitJNI; import org.sleuthkit.datamodel.TskCoreException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; -public class HashDbXML { +/** + * This class is a singleton that manages the configuration of the hash databases + * that serve as hash sets for the identification of known files, known good files, + * and known bad files. + */ +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"; @@ -51,146 +57,231 @@ public class HashDbXML { private static final String CUR_HASHSETS_FILE_NAME = "hashsets.xml"; private static final String XSDFILE = "HashsetsSchema.xsd"; private static final String ENCODING = "UTF-8"; - private static final String CUR_HASHSET_FILE = PlatformUtil.getUserConfigDirectory() + File.separator + CUR_HASHSETS_FILE_NAME; private static final String SET_CALC = "hash_calculate"; private static final String SET_VALUE = "value"; - private static final Logger logger = Logger.getLogger(HashDbXML.class.getName()); - private static HashDbXML currentInstance; - - private List knownBadSets; - private HashDb nsrlSet; - private String xmlFile; - private boolean calculate; - - private HashDbXML(String xmlFile) { - knownBadSets = new ArrayList(); - this.xmlFile = xmlFile; - } - + private static final Logger logger = Logger.getLogger(HashDbManager.class.getName()); + private static HashDbManager instance; + private String xmlFile = PlatformUtil.getUserConfigDirectory() + File.separator + CUR_HASHSETS_FILE_NAME; + private List knownBadHashSets = new ArrayList<>(); + private HashDb nsrlHashSet; + private boolean alwaysCalculateHashes; + /** - * get instance for managing the current keyword list of the application + * Gets the singleton instance of this class. */ - static synchronized HashDbXML getCurrent() { - if (currentInstance == null) { - currentInstance = new HashDbXML(CUR_HASHSET_FILE); - currentInstance.reload(); + public static synchronized HashDbManager getInstance() { + if (instance == null) { + instance = new HashDbManager(); } - return currentInstance; + return instance; } - - /** - * Get the hash sets - */ - public List getAllSets() { - List ret = new ArrayList(); - if(nsrlSet != null) { - ret.add(nsrlSet); + + private HashDbManager() { + if (hashSetsConfigurationFileExists()) { + readHashSetsConfigurationFromDisk(); } - ret.addAll(knownBadSets); - return ret; } - - /** - * Get the Known Bad sets + + /** + * Adds a hash set to the configuration as the designated National Software + * Reference Library (NSRL) hash set. Assumes that the hash set previously + * designated as NSRL set, if any, is not being indexed. Does not save the + * configuration. */ - public List getKnownBadSets() { - return knownBadSets; + public void setNSRLHashSet(HashDb set) { + if (nsrlHashSet != null) { + // RJCTODO: When the closeHashDatabase() API exists, close the existing database + } + nsrlHashSet = set; } - + /** - * Get the NSRL set + * Gets the hash set from the configuration, if any, that is designated as + * the National Software Reference Library (NSRL) hash set. + * @return A HashDb object representing the hash set or null. */ - public HashDb getNSRLSet() { - return nsrlSet; + public HashDb getNSRLHashSet() { + return nsrlHashSet; + } + + /** + * Removes the hash set designated as the National Software Reference + * Library (NSRL) hash set from the configuration. Does not save the + * configuration. + */ + public void removeNSRLHashSet() { + if (nsrlHashSet != null) { + // RJCTODO: When the closeHashDatabase() API exists, close the existing database + } + nsrlHashSet = null; + } + + /** + * Adds a hash set to the configuration as a known bad files hash set. Does + * not check for duplication of sets and does not save the configuration. + */ + public void addKnownBadHashSet(HashDb set) { + knownBadHashSets.add(set); + } + + /** + * Gets the configured known bad files hash sets. + * @return A list, possibly empty, of HashDb objects. + */ + public List getKnownBadHashSets() { + return Collections.unmodifiableList(knownBadHashSets); } /** - * Add a known bad hash set + * Adds a hash set to the configuration. If the hash set is designated as + * the National Software Reference Library (NSRL) hash set, it is assumed + * the the hash set previously designated as the NSRL set, if any, is not + * being indexed. Does not check for duplication of sets and does not save + * the configuration. */ - public void addKnownBadSet(HashDb set) { - knownBadSets.add(set); - //save(); + public void addHashSet(HashDb hashSet) { + if (hashSet.getKnownFilesType() == HashDb.KnownFilesType.NSRL) { + setNSRLHashSet(hashSet); + } + else { + addKnownBadHashSet(hashSet); + } } /** - * Add a known bad hash set + * Removes a hash set from the hash sets configuration. + */ + public void removeHashSet(HashDb hashSetToRemove) { + if (nsrlHashSet != null && nsrlHashSet.equals(hashSetToRemove)) { + removeNSRLHashSet(); + } + else { + knownBadHashSets.remove(hashSetToRemove); + // RJCTODO: Close HashDb + } + } + + /** + * Gets the configured known files hash sets that accept updates. + * @return A list, possibly empty, of HashDb objects. + */ + public List getUpdateableHashSets() { + ArrayList updateableDbs = new ArrayList<>(); + for (HashDb db : knownBadHashSets) { + try { + if (db.isUpdateable()) { + updateableDbs.add(db); + } + } + catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error checking updateable status of " + db.getDatabasePath(), ex); + } + } + return Collections.unmodifiableList(updateableDbs); + } + + /** + * Gets all of the configured hash sets. + * @return A list, possibly empty, of HashDb objects representing the hash + * sets. + */ + public List getAllHashSets() { + List hashDbs = new ArrayList<>(); + if (nsrlHashSet != null) { + hashDbs.add(nsrlHashSet); + } + hashDbs.addAll(knownBadHashSets); + return Collections.unmodifiableList(hashDbs); + } + + /** Gets the configured hash set, if any, with a given name. + * @return A HashDb object or null. + */ + public HashDb getHashSetByName(String name) { + if (nsrlHashSet != null && nsrlHashSet.getDisplayName().equals(name)) { + return nsrlHashSet; + } + + for (HashDb hashSet : knownBadHashSets) { + if (hashSet.getDisplayName().equals(name)) { + return hashSet; + } + } + + return null; + } + +// public HashDb getHashSetAt(int index) + + // RJCTODO: Get rid of this + /** + * Adds a hash set to the configuration as a known bad files hash set. The + * set is added to the internal known bad sets collection at the index + * specified by the caller. Does not save the configuration. */ public void addKnownBadSet(int index, HashDb set) { - knownBadSets.add(index, set); - //save(); + knownBadHashSets.add(index, set); } - - /** - * Set the NSRL hash set (override old set) - */ - public void setNSRLSet(HashDb set) { - this.nsrlSet = set; - //save(); - } - - /** - * Remove a hash known bad set + + // RJCTODO: Get rid of this + /** + * Removes the known bad files hash set from the internal known bad files + * hash sets collection at the specified index. Does not save the configuration. */ public void removeKnownBadSetAt(int index) { - knownBadSets.remove(index); - //save(); - } - - /** - * Remove the NSRL database - */ - public void removeNSRLSet() { - this.nsrlSet = null; - //save(); + knownBadHashSets.remove(index); } /** - * load the file or create new + * Sets the value for the flag indicates whether hashes should be calculated + * for content even if no hash databases are configured. */ - public void reload() { - boolean created = false; - - //TODO clearing the list causes a bug: we lose track of the state - //whether db is being indexed, we should somehow preserve the state when loading new HashDb objects - - knownBadSets.clear(); - nsrlSet = null; - - if (!this.setsFileExists()) { - //create new if it doesn't exist - save(); - created = true; - } - - //load, if fails to load create new; save regardless - load(); - if (!created) { - //create new if failed to load - save(); - } + public void setShouldAlwaysCalculateHashes(boolean alwaysCalculateHashes) { + this.alwaysCalculateHashes = alwaysCalculateHashes; } /** - * Sets the local variable calculate to the given boolean. - * @param set the state to make calculate + * Accesses the flag that indicates whether hashes should be calculated + * for content even if no hash databases are configured. */ - public void setCalculate(boolean set) { - this.calculate = set; - //save(); + public boolean shouldAlwaysCalculateHashes() { + return alwaysCalculateHashes; } /** - * Returns the value of the local boolean calculate. - * @return true if calculate is true, false otherwise - */ - public boolean getCalculate() { - return this.calculate; - } - - /** - * writes out current sets file replacing the last one + * Saves the hash sets configuration. Note that the configuration is only + * saved on demand to support cancellation of configuration panels. + * @return True on success, false otherwise. */ public boolean save() { + return writeHashSetConfigurationToDisk(); + } + + /** + * Restores the last saved hash sets configuration. This supports + * cancellation of configuration panels. + */ + public void loadLastSavedConfiguration() { + try { + SleuthkitJNI.closeHashDatabases(); + } + catch (TskCoreException ex) { + // RJCTODO: Log + } + + nsrlHashSet = null; + knownBadHashSets.clear(); + if (hashSetsConfigurationFileExists()) { + readHashSetsConfigurationFromDisk(); + } + } + + private boolean hashSetsConfigurationFileExists() { + File f = new File(xmlFile); + return f.exists() && f.canRead() && f.canWrite(); + } + + private boolean writeHashSetConfigurationToDisk() { boolean success = false; DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance(); @@ -202,14 +293,15 @@ public class HashDbXML { Element rootEl = doc.createElement(ROOT_EL); doc.appendChild(rootEl); - for (HashDb set : knownBadSets) { + // TODO: Remove all the multiple database paths stuff, it was a mistake. + for (HashDb set : knownBadHashSets) { String useForIngest = Boolean.toString(set.getUseForIngest()); String showInboxMessages = Boolean.toString(set.getShowInboxMessages()); - List paths = set.getDatabasePaths(); - String type = DBType.KNOWN_BAD.toString(); + List paths = Collections.singletonList(set.getDatabasePath()); + String type = KnownFilesType.KNOWN_BAD.toString(); Element setEl = doc.createElement(SET_EL); - setEl.setAttribute(SET_NAME_ATTR, set.getName()); + setEl.setAttribute(SET_NAME_ATTR, set.getDisplayName()); setEl.setAttribute(SET_TYPE_ATTR, type); setEl.setAttribute(SET_USE_FOR_INGEST_ATTR, useForIngest); setEl.setAttribute(SET_SHOW_INBOX_MESSAGES, showInboxMessages); @@ -224,14 +316,15 @@ public class HashDbXML { rootEl.appendChild(setEl); } - if(nsrlSet != null) { - String useForIngest = Boolean.toString(nsrlSet.getUseForIngest()); - String showInboxMessages = Boolean.toString(nsrlSet.getShowInboxMessages()); - List paths = nsrlSet.getDatabasePaths(); - String type = DBType.NSRL.toString(); + // TODO: Remove all the multiple database paths stuff. + if(nsrlHashSet != null) { + String useForIngest = Boolean.toString(nsrlHashSet.getUseForIngest()); + String showInboxMessages = Boolean.toString(nsrlHashSet.getShowInboxMessages()); + List paths = Collections.singletonList(nsrlHashSet.getDatabasePath()); + String type = KnownFilesType.NSRL.toString(); Element setEl = doc.createElement(SET_EL); - setEl.setAttribute(SET_NAME_ATTR, nsrlSet.getName()); + setEl.setAttribute(SET_NAME_ATTR, nsrlHashSet.getDisplayName()); setEl.setAttribute(SET_TYPE_ATTR, type); setEl.setAttribute(SET_USE_FOR_INGEST_ATTR, useForIngest); setEl.setAttribute(SET_SHOW_INBOX_MESSAGES, showInboxMessages); @@ -245,24 +338,22 @@ public class HashDbXML { } rootEl.appendChild(setEl); } - - String calcValue = Boolean.toString(calculate); + + String calcValue = Boolean.toString(alwaysCalculateHashes); Element setCalc = doc.createElement(SET_CALC); setCalc.setAttribute(SET_VALUE, calcValue); rootEl.appendChild(setCalc); - success = XMLUtil.saveDoc(HashDbXML.class, xmlFile, ENCODING, doc); - } catch (ParserConfigurationException e) { + success = XMLUtil.saveDoc(HashDbManager.class, xmlFile, ENCODING, doc); + } + catch (ParserConfigurationException e) { logger.log(Level.SEVERE, "Error saving hash sets: can't initialize parser.", e); } - return success; + return success; } - - /** - * load and parse XML, then dispose - */ - public boolean load() { - final Document doc = XMLUtil.loadDoc(HashDbXML.class, xmlFile, XSDFILE); + + private boolean readHashSetsConfigurationFromDisk() { + final Document doc = XMLUtil.loadDoc(HashDbManager.class, xmlFile, XSDFILE); if (doc == null) { return false; } @@ -285,9 +376,10 @@ public class HashDbXML { final String showInboxMessages = setEl.getAttribute(SET_SHOW_INBOX_MESSAGES); Boolean useForIngestBool = Boolean.parseBoolean(useForIngest); Boolean showInboxMessagesBool = Boolean.parseBoolean(showInboxMessages); - List paths = new ArrayList(); + List paths = new ArrayList<>(); - // Parse all paths + // TODO: Remove all the multiple database paths stuff. + // RJCTODO: Rework this to do a search a bit differently, or simply indicate the file is missing... NodeList pathsNList = setEl.getElementsByTagName(PATH_EL); final int numPaths = pathsNList.getLength(); for (int j = 0; j < numPaths; ++j) { @@ -330,16 +422,23 @@ public class HashDbXML { } if(paths.isEmpty()) { - logger.log(Level.WARNING, "No paths were set for hash_set at index {0}. Removing the database.", i); - } else { // No paths for this entry, the user most likely declined to search for them - DBType typeDBType = DBType.valueOf(type); - HashDb set = new HashDb(name, paths, useForIngestBool, showInboxMessagesBool, typeDBType); - - if(typeDBType == DBType.KNOWN_BAD) { - knownBadSets.add(set); - } else if(typeDBType == DBType.NSRL) { - this.nsrlSet = set; + logger.log(Level.WARNING, "No paths were set for hash_set at index {0}. Removing the database.", i); + } + else { + KnownFilesType typeDBType = KnownFilesType.valueOf(type); + try { + HashDb db = HashDb.openHashDatabase(name, paths.get(0), useForIngestBool, showInboxMessagesBool, typeDBType); + if (typeDBType == KnownFilesType.NSRL) { + setNSRLHashSet(db); + } + else { + addKnownBadHashSet(db); + } + } + catch (TskCoreException ex) { + Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error opening hash database", ex); + JOptionPane.showMessageDialog(null, "Unable to open " + paths.get(0) + " hash database.", "Open Hash Database Error", JOptionPane.ERROR_MESSAGE); } } } @@ -352,7 +451,7 @@ public class HashDbXML { for(int i=0; i - +
- + - + - + diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbSimplePanel.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbSimpleConfigPanel.java similarity index 79% rename from HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbSimplePanel.java rename to HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbSimpleConfigPanel.java index 9dfee56671..f33040b36b 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbSimplePanel.java +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbSimpleConfigPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011 Basis Technology Corp. + * Copyright 2011 - 2013 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,49 +16,37 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -/* - * HashDbSimplePanel.java - * - * Created on May 7, 2012, 10:38:26 AM - */ package org.sleuthkit.autopsy.hashdatabase; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.List; -import org.sleuthkit.autopsy.coreutils.Logger; import javax.swing.JOptionPane; import javax.swing.JTable; import javax.swing.table.AbstractTableModel; import javax.swing.table.TableColumn; import org.sleuthkit.autopsy.ingest.IngestManager; +import org.sleuthkit.datamodel.TskCoreException; /** - * - * @author dfickling + * Instances of this class are used as a file ingest module configuration panel + * by the known files hash set lookup file ingest module. */ -public class HashDbSimplePanel extends javax.swing.JPanel { - - private static final Logger logger = Logger.getLogger(HashDbSimplePanel.class.getName()); +public class HashDbSimpleConfigPanel extends javax.swing.JPanel { private HashTableModel knownBadTableModel; private HashDb nsrl; - /** Creates new form HashDbSimplePanel */ - public HashDbSimplePanel() { + public HashDbSimpleConfigPanel() { knownBadTableModel = new HashTableModel(); initComponents(); customizeComponents(); } private void reloadCalc() { - final HashDbXML xmlHandle = HashDbXML.getCurrent(); - final HashDb nsrlDb = xmlHandle.getNSRLSet(); - final boolean nsrlUsed = - nsrlDb != null - && nsrlDb.getUseForIngest()== true - && nsrlDb.indexExists(); - final List knowns = xmlHandle.getKnownBadSets(); + final HashDbManager xmlHandle = HashDbManager.getInstance(); + final HashDb nsrlDb = xmlHandle.getNSRLHashSet(); + final boolean nsrlUsed = nsrlDb != null && nsrlDb.getUseForIngest()== true && nsrlDb.hasLookupIndex(); + final List knowns = xmlHandle.getKnownBadHashSets(); final boolean knownExists = !knowns.isEmpty(); boolean knownUsed = false; if (knownExists) { @@ -70,31 +58,29 @@ public class HashDbSimplePanel extends javax.swing.JPanel { } } - if(! nsrlUsed - && ! knownUsed ) { + if (!nsrlUsed && !knownUsed ) { calcHashesButton.setEnabled(true); calcHashesButton.setSelected(true); - xmlHandle.setCalculate(true); + xmlHandle.setShouldAlwaysCalculateHashes(true); } else { calcHashesButton.setEnabled(false); calcHashesButton.setSelected(false); - xmlHandle.setCalculate(false); + xmlHandle.setShouldAlwaysCalculateHashes(false); } } private void customizeComponents() { - final HashDbXML xmlHandle = HashDbXML.getCurrent(); + final HashDbManager xmlHandle = HashDbManager.getInstance(); calcHashesButton.addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if(calcHashesButton.isSelected()) { - xmlHandle.setCalculate(true); + xmlHandle.setShouldAlwaysCalculateHashes(true); } else { - xmlHandle.setCalculate(false); + xmlHandle.setShouldAlwaysCalculateHashes(false); } - } - + } }); notableHashTable.setModel(knownBadTableModel); @@ -104,7 +90,7 @@ public class HashDbSimplePanel extends javax.swing.JPanel { //customize column witdhs final int width1 = jScrollPane1.getPreferredSize().width; notableHashTable.setAutoResizeMode(JTable.AUTO_RESIZE_NEXT_COLUMN); - TableColumn column1 = null; + TableColumn column1; for (int i = 0; i < notableHashTable.getColumnCount(); i++) { column1 = notableHashTable.getColumnModel().getColumn(i); if (i == 0) { @@ -117,6 +103,24 @@ public class HashDbSimplePanel extends javax.swing.JPanel { reloadSets(); } + private void reloadSets() { + nsrl = HashDbManager.getInstance().getNSRLHashSet(); + + if (nsrl == null || nsrl.getUseForIngest() == false) { + nsrlDbLabelVal.setText("Disabled"); + } + else if (nsrl.hasLookupIndex() == false) { + nsrlDbLabelVal.setText("Disabled (No index)"); + } + else { + nsrlDbLabelVal.setText("Enabled"); + } + + reloadCalc(); + + knownBadTableModel.resync(); + } + /** This method is called from within the constructor to * initialize the form. * WARNING: Do NOT modify this code. The content of this method is @@ -140,13 +144,13 @@ public class HashDbSimplePanel extends javax.swing.JPanel { notableHashTable.setShowVerticalLines(false); jScrollPane1.setViewportView(notableHashTable); - jLabel1.setText(org.openide.util.NbBundle.getMessage(HashDbSimplePanel.class, "HashDbSimplePanel.jLabel1.text")); // NOI18N + jLabel1.setText(org.openide.util.NbBundle.getMessage(HashDbSimpleConfigPanel.class, "HashDbSimpleConfigPanel.jLabel1.text")); // NOI18N - nsrlDbLabel.setText(org.openide.util.NbBundle.getMessage(HashDbSimplePanel.class, "HashDbSimplePanel.nsrlDbLabel.text")); // NOI18N + nsrlDbLabel.setText(org.openide.util.NbBundle.getMessage(HashDbSimpleConfigPanel.class, "HashDbSimpleConfigPanel.nsrlDbLabel.text")); // NOI18N - calcHashesButton.setText(org.openide.util.NbBundle.getMessage(HashDbSimplePanel.class, "HashDbSimplePanel.calcHashesButton.text")); // NOI18N + calcHashesButton.setText(org.openide.util.NbBundle.getMessage(HashDbSimpleConfigPanel.class, "HashDbSimpleConfigPanel.calcHashesButton.text")); // NOI18N - nsrlDbLabelVal.setText(org.openide.util.NbBundle.getMessage(HashDbSimplePanel.class, "HashDbSimplePanel.nsrlDbLabelVal.text")); // NOI18N + nsrlDbLabelVal.setText(org.openide.util.NbBundle.getMessage(HashDbSimpleConfigPanel.class, "HashDbSimpleConfigPanel.nsrlDbLabelVal.text")); // NOI18N javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); @@ -193,29 +197,9 @@ public class HashDbSimplePanel extends javax.swing.JPanel { private javax.swing.JLabel nsrlDbLabelVal; // End of variables declaration//GEN-END:variables - private void reloadSets() { - nsrl = HashDbXML.getCurrent().getNSRLSet(); - - if (nsrl == null || nsrl.getUseForIngest() == false) { - nsrlDbLabelVal.setText("Disabled"); - } - else if (nsrl.indexExists() == false) { - nsrlDbLabelVal.setText("Disabled (No index)"); - } - else { - nsrlDbLabelVal.setText("Enabled"); - } - - reloadCalc(); - - knownBadTableModel.resync(); - } - - - private class HashTableModel extends AbstractTableModel { - private HashDbXML xmlHandle = HashDbXML.getCurrent(); + private HashDbManager xmlHandle = HashDbManager.getInstance(); private void resync() { fireTableDataChanged(); @@ -223,7 +207,7 @@ public class HashDbSimplePanel extends javax.swing.JPanel { @Override public int getRowCount() { - int size = xmlHandle.getKnownBadSets().size(); + int size = xmlHandle.getKnownBadHashSets().size(); return size == 0 ? 1 : size; } @@ -234,18 +218,18 @@ public class HashDbSimplePanel extends javax.swing.JPanel { @Override public Object getValueAt(int rowIndex, int columnIndex) { - if (xmlHandle.getKnownBadSets().isEmpty()) { + if (xmlHandle.getKnownBadHashSets().isEmpty()) { if (columnIndex == 0) { return ""; } else { return "Disabled"; } } else { - HashDb db = xmlHandle.getKnownBadSets().get(rowIndex); + HashDb db = xmlHandle.getKnownBadHashSets().get(rowIndex); if (columnIndex == 0) { return db.getUseForIngest(); } else { - return db.getName(); + return db.getDisplayName(); } } } @@ -258,11 +242,18 @@ public class HashDbSimplePanel extends javax.swing.JPanel { @Override public void setValueAt(Object aValue, int rowIndex, int columnIndex) { if(columnIndex == 0){ - HashDb db = xmlHandle.getKnownBadSets().get(rowIndex); - if(((Boolean) getValueAt(rowIndex, columnIndex)) || IndexStatus.isIngestible(db.status())) { + HashDb db = xmlHandle.getKnownBadHashSets().get(rowIndex); + IndexStatus status = IndexStatus.NO_INDEX; + try { + status = db.getStatus(); + } + catch (TskCoreException ex) { + // RJCTODO + } + if(((Boolean) getValueAt(rowIndex, columnIndex)) || IndexStatus.isIngestible(status)) { db.setUseForIngest((Boolean) aValue); } else { - JOptionPane.showMessageDialog(HashDbSimplePanel.this, "Databases must be indexed before they can be used for ingest"); + JOptionPane.showMessageDialog(HashDbSimpleConfigPanel.this, "Databases must be indexed before they can be used for ingest"); } reloadSets(); } @@ -272,6 +263,5 @@ public class HashDbSimplePanel extends javax.swing.JPanel { public Class getColumnClass(int c) { return getValueAt(0, c).getClass(); } - } } diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/IndexStatus.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/IndexStatus.java index cf02a9f326..c48ecdab9b 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/IndexStatus.java +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/IndexStatus.java @@ -26,30 +26,27 @@ package org.sleuthkit.autopsy.hashdatabase; enum IndexStatus { /** - * The index and database both exist, and the index is older. + * The index exists but the database does not. This indicates a text index + * without an accompanying text database. */ - INDEX_OUTDATED("WARNING: Index is older than database"), + INDEX_ONLY("Index only"), /** - * The index and database both exist, and the index is not older. + * The database exists but the index does not. This indicates a text database + * with no index. */ - INDEX_CURRENT("Database and index exist"), + NO_INDEX("No index"), /** - * The index exists but the database does not. + * The index is currently being generated. */ - NO_DB("Index exists (no database)"), + INDEXING("Index is currently being generated"), /** - * The database exists but the index does not. + * The index is generated. */ - NO_INDEX("ERROR: Index does not exist"), + INDEXED("Indexed"), /** - * Neither the index nor the database exists. + * An error occurred while determining status. */ - NONE("ERROR: No index or database"), - /** - * The index is currently being generated - */ - INDEXING("Index is currently being generated"); - + UNKNOWN("Error determining status"); private String message; /** @@ -68,6 +65,6 @@ enum IndexStatus { } public static boolean isIngestible(IndexStatus status) { - return status == NO_DB || status == INDEX_CURRENT || status == INDEX_OUTDATED; + return status == INDEX_ONLY || status == INDEXED; } } diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/ModalNoButtons.form b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/ModalNoButtons.form index df913c59e1..7ed52bedf9 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/ModalNoButtons.form +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/ModalNoButtons.form @@ -17,6 +17,7 @@ + diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/ModalNoButtons.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/ModalNoButtons.java index 4a7862bea1..61e55c07b3 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/ModalNoButtons.java +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/ModalNoButtons.java @@ -42,7 +42,7 @@ class ModalNoButtons extends javax.swing.JDialog implements PropertyChangeListen List unindexed; HashDb toIndex; - HashDbManagementPanel hdbmp; + HashDbConfigPanel hdbmp; int length = 0; int currentcount = 1; String currentDb = ""; @@ -53,7 +53,7 @@ class ModalNoButtons extends javax.swing.JDialog implements PropertyChangeListen * @param parent Swing parent frame. * @param unindexed the list of unindexed databases to index. */ - ModalNoButtons(HashDbManagementPanel hdbmp, java.awt.Frame parent, List unindexed) { + ModalNoButtons(HashDbConfigPanel hdbmp, java.awt.Frame parent, List unindexed) { super(parent, "Indexing databases", true); this.unindexed = unindexed; this.toIndex = null; @@ -68,7 +68,7 @@ class ModalNoButtons extends javax.swing.JDialog implements PropertyChangeListen * @param parent Swing parent frame. * @param unindexed The unindexed database to index. */ - ModalNoButtons(HashDbManagementPanel hdbmp, java.awt.Frame parent, HashDb unindexed){ + ModalNoButtons(HashDbConfigPanel hdbmp, java.awt.Frame parent, HashDb unindexed){ super(parent, "Indexing database", true); this.unindexed = null; this.toIndex = unindexed; @@ -203,7 +203,7 @@ class ModalNoButtons extends javax.swing.JDialog implements PropertyChangeListen */ private void indexThis() { this.INDEXING_PROGBAR.setIndeterminate(true); - currentDb = this.toIndex.getName(); + currentDb = this.toIndex.getDisplayName(); this.CURRENTDB_LABEL.setText("(" + currentDb + ")"); this.length = 1; this.CURRENTLYON_LABEL.setText("Currently indexing 1 database"); @@ -224,7 +224,7 @@ class ModalNoButtons extends javax.swing.JDialog implements PropertyChangeListen length = this.unindexed.size(); this.INDEXING_PROGBAR.setIndeterminate(true); for (HashDb db : this.unindexed) { - currentDb = db.getName(); + currentDb = db.getDisplayName(); this.CURRENTDB_LABEL.setText("(" + currentDb + ")"); this.CURRENTLYON_LABEL.setText("Currently indexing 1 of " + length); if (!db.isIndexing()) { @@ -250,7 +250,7 @@ class ModalNoButtons extends javax.swing.JDialog implements PropertyChangeListen * Displays the current count of indexing when one is completed, or kills this dialog if all indexing is complete. */ public void propertyChange(PropertyChangeEvent evt) { - if (evt.getPropertyName().equals(HashDb.EVENT.INDEXING_DONE.name())) { + if (evt.getPropertyName().equals(HashDb.Event.INDEXING_DONE.name())) { if (currentcount >= length) { this.INDEXING_PROGBAR.setValue(100); this.setModal(false); diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/btn_icon_create_new_16.png b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/btn_icon_create_new_16.png new file mode 100644 index 0000000000..86c1018f39 Binary files /dev/null and b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/btn_icon_create_new_16.png differ diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/new16.png b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/new16.png new file mode 100644 index 0000000000..f286d2b6c0 Binary files /dev/null and b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/new16.png differ diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchFilterNode.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchFilterNode.java index 268bedddcd..03d331fca6 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchFilterNode.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchFilterNode.java @@ -28,6 +28,7 @@ import org.openide.nodes.PropertySupport; import org.openide.nodes.Sheet; import org.openide.util.lookup.Lookups; import org.openide.util.lookup.ProxyLookup; +import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint; import org.sleuthkit.autopsy.directorytree.ExternalViewerAction; import org.sleuthkit.autopsy.directorytree.ExtractAction; import org.sleuthkit.autopsy.actions.AddContentTagAction; @@ -152,6 +153,7 @@ class KeywordSearchFilterNode extends FilterNode { actions.add(new HashSearchAction("Search for files with the same MD5 hash", getOriginal())); actions.add(null); // creates a menu separator actions.add(AddContentTagAction.getInstance()); + actions.addAll(ContextMenuExtensionPoint.getActions()); return actions; } diff --git a/Testing/test/qa-functional/src/org/sleuthkit/autopsy/testing/RegressionTest.java b/Testing/test/qa-functional/src/org/sleuthkit/autopsy/testing/RegressionTest.java index b7c798e4ea..af3ebc35c6 100644 --- a/Testing/test/qa-functional/src/org/sleuthkit/autopsy/testing/RegressionTest.java +++ b/Testing/test/qa-functional/src/org/sleuthkit/autopsy/testing/RegressionTest.java @@ -60,6 +60,9 @@ import org.netbeans.junit.NbModuleSuite; import org.openide.util.Exceptions; import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.keywordsearch.*; +import org.sleuthkit.datamodel.SleuthkitJNI; +import org.sleuthkit.datamodel.TskData; +import org.sleuthkit.datamodel.TskException; /** * This test expects the following system properties to be set: img_path: The @@ -93,7 +96,8 @@ public class RegressionTest extends TestCase { NbModuleSuite.Configuration conf = NbModuleSuite.createConfiguration(RegressionTest.class). clusters(".*"). enableModules(".*"); - conf = conf.addTest("testNewCaseWizardOpen", + conf = conf.addTest("testHashDbJni", + "testNewCaseWizardOpen", "testNewCaseWizard", "testStartAddDataSource", "testConfigureIngest1", @@ -103,7 +107,8 @@ public class RegressionTest extends TestCase { "testAddSourceWizard1", "testIngest", "testGenerateReportToolbar", - "testGenerateReportButton"); + "testGenerateReportButton" + ); return NbModuleSuite.create(conf); @@ -125,6 +130,39 @@ public class RegressionTest extends TestCase { public void tearDown() { } + public void testHashDbJni() { + logger.info("HashDb JNI"); + try + { + String hashfn = "regtestHash.kdb"; + String md5hash = "b8c51089ebcdf9f11154a021438f5bd6"; + String md5hash2 = "cb4aca35f3fd54aacf96da9cd9acadb8"; + String md5hashBad = "35b299c6fcf47ece375b3221bdc16969"; + + logger.info("Creating hash db " + hashfn); + int handle = SleuthkitJNI.createHashDatabase(hashfn); + + logger.info("Adding hash " + md5hash); + SleuthkitJNI.addToHashDatabase("", md5hash, "", "", handle); + + logger.info("Adding hash " + md5hash2); + SleuthkitJNI.addToHashDatabase("", md5hash2, "", "", handle); + + logger.info("Querying for known hash " + md5hash); + TskData.FileKnown k = SleuthkitJNI.lookupInHashDatabase(md5hash, handle); + logger.info("Query result: " + k.toString()); + + logger.info("Querying for unknown hash " + md5hashBad); + TskData.FileKnown k2 = SleuthkitJNI.lookupInHashDatabase(md5hashBad, handle); + logger.info("Query result: " + k2.toString()); + + } catch (TskException ex) { + logger.log(Level.WARNING, "Database creation error: ", ex); + logger.info("A TskException occurred."); + return; + } + } + public void testNewCaseWizardOpen() { logger.info("New Case"); NbDialogOperator nbdo = new NbDialogOperator("Welcome");