diff --git a/Core/build.xml b/Core/build.xml index 173dda76a5..609fb1367b 100644 --- a/Core/build.xml +++ b/Core/build.xml @@ -21,11 +21,6 @@ - - - - - @@ -96,6 +91,10 @@ + + + + diff --git a/Core/nbproject/project.properties b/Core/nbproject/project.properties index 7ae5484bc9..93d0e96015 100644 --- a/Core/nbproject/project.properties +++ b/Core/nbproject/project.properties @@ -3,7 +3,7 @@ file.reference.c3p0-0.9.5.jar=release/modules/ext/c3p0-0.9.5.jar file.reference.commons-compress-1.14.jar=release/modules/ext/commons-compress-1.14.jar file.reference.commons-dbcp2-2.1.1.jar=release\\modules\\ext\\commons-dbcp2-2.1.1.jar file.reference.commons-pool2-2.4.2.jar=release\\modules\\ext\\commons-pool2-2.4.2.jar -file.reference.dd-plist-1.20.jar=release\\modules\\ext\\dd-plist-1.20.jar +file.reference.dd-plist-1.20.jar=release/modules/ext/dd-plist-1.20.jar file.reference.jdom-2.0.5-contrib.jar=release/modules/ext/jdom-2.0.5-contrib.jar file.reference.jdom-2.0.5.jar=release/modules/ext/jdom-2.0.5.jar file.reference.jgraphx-v3.8.0.jar=release/modules/ext/jgraphx-v3.8.0.jar @@ -12,7 +12,6 @@ file.reference.jython-standalone-2.7.0.jar=release/modules/ext/jython-standalone file.reference.mchange-commons-java-0.2.9.jar=release/modules/ext/mchange-commons-java-0.2.9.jar file.reference.metadata-extractor-2.10.1.jar=release/modules/ext/metadata-extractor-2.10.1.jar file.reference.postgresql-9.4.1211.jre7.jar=release/modules/ext/postgresql-9.4.1211.jre7.jar -file.reference.opencv-248.jar=release/modules/ext/opencv-248.jar file.reference.Rejistry-1.0-SNAPSHOT.jar=release/modules/ext/Rejistry-1.0-SNAPSHOT.jar file.reference.sevenzipjbinding-AllPlatforms.jar=release/modules/ext/sevenzipjbinding-AllPlatforms.jar file.reference.sevenzipjbinding.jar=release/modules/ext/sevenzipjbinding.jar diff --git a/Core/nbproject/project.xml b/Core/nbproject/project.xml index ac3ec60d44..f6a841482c 100644 --- a/Core/nbproject/project.xml +++ b/Core/nbproject/project.xml @@ -339,6 +339,10 @@ org.sleuthkit.autopsy.report org.sleuthkit.datamodel + + ext/jackcess-2.1.8.jar + release/modules/ext/jackcess-2.1.8.jar + ext/zookeeper-3.4.6.jar release/modules/ext/zookeeper-3.4.6.jar @@ -348,33 +352,33 @@ release/modules/ext/jdom-2.0.5.jar - ext/sleuthkit-postgresql-4.6.1.jar - release/modules/ext/sleuthkit-postgresql-4.6.1.jar - - - ext/opencv-248.jar - release/modules/ext/opencv-248.jar + ext/cxf-rt-transports-http-3.0.16.jar + release/modules/ext/cxf-rt-transports-http-3.0.16.jar ext/curator-framework-2.8.0.jar release/modules/ext/curator-framework-2.8.0.jar - ext/commons-dbcp2-2.1.1.jar - release/modules/ext/commons-dbcp2-2.1.1.jar - - - ext/jgraphx-v3.8.0.jar - release/modules/ext/jgraphx-v3.8.0.jar + ext/bcprov-jdk15on-1.54.jar + release/modules/ext/bcprov-jdk15on-1.54.jar ext/commons-compress-1.14.jar release/modules/ext/commons-compress-1.14.jar + + ext/fontbox-2.0.8.jar + release/modules/ext/fontbox-2.0.8.jar + ext/commons-dbcp2-2.1.1.jar release\modules\ext\commons-dbcp2-2.1.1.jar + + ext/jgraphx-v3.8.0.jar + release/modules/ext/jgraphx-v3.8.0.jar + ext/jython-standalone-2.7.0.jar release/modules/ext/jython-standalone-2.7.0.jar @@ -387,6 +391,14 @@ ext/mchange-commons-java-0.2.9.jar release/modules/ext/mchange-commons-java-0.2.9.jar + + ext/cxf-core-3.0.16.jar + release/modules/ext/cxf-core-3.0.16.jar + + + ext/javax.ws.rs-api-2.0.1.jar + release/modules/ext/javax.ws.rs-api-2.0.1.jar + ext/postgresql-9.4.1211.jre7.jar release/modules/ext/postgresql-9.4.1211.jre7.jar @@ -399,6 +411,10 @@ ext/metadata-extractor-2.10.1.jar release/modules/ext/metadata-extractor-2.10.1.jar + + ext/sleuthkit-postgresql-4.6.1.jar + release/modules/ext/sleuthkit-postgresql-4.6.1.jar + ext/tika-core-1.17.jar release/modules/ext/tika-core-1.17.jar @@ -411,58 +427,18 @@ ext/curator-client-2.8.0.jar release/modules/ext/curator-client-2.8.0.jar - - ext/bcprov-jdk15on-1.54.jar - release/modules/ext/bcprov-jdk15on-1.54.jar - - - ext/jackcess-2.1.8.jar - release/modules/ext/jackcess-2.1.8.jar - - - ext/jackcess-encrypt-2.1.2.jar - release/modules/ext/jackcess-encrypt-2.1.2.jar - - - ext/jempbox-1.8.13.jar - release/modules/ext/jempbox-1.8.13.jar - - - ext/javax.ws.rs-api-2.0.1.jar - release/modules/ext/javax.ws.rs-api-2.0.1.jar - - - ext/cxf-rt-rs-client-3.0.16.jar - release/modules/ext/cxf-rt-rs-client-3.0.16.jar - - - ext/cxf-rt-transports-http-3.0.16.jar - release/modules/ext/cxf-rt-transports-http-3.0.16.jar - - - ext/cxf-core-3.0.16.jar - release/modules/ext/cxf-core-3.0.16.jar - ext/cxf-rt-frontend-jaxrs-3.0.16.jar release/modules/ext/cxf-rt-frontend-jaxrs-3.0.16.jar - - ext/tika-parsers-1.17.jar - release/modules/ext/tika-parsers-1.17.jar - - - ext/fontbox-2.0.8.jar - release/modules/ext/fontbox-2.0.8.jar - - - ext/pdfbox-2.0.8.jar - release/modules/ext/pdfbox-2.0.8.jar - ext/pdfbox-tools-2.0.8.jar release/modules/ext/pdfbox-tools-2.0.8.jar + + ext/tika-parsers-1.17.jar + release/modules/ext/tika-parsers-1.17.jar + ext/sqlite-jdbc-3.8.11.jar release/modules/ext/sqlite-jdbc-3.8.11.jar @@ -483,6 +459,14 @@ ext/dd-plist-1.20.jar release/modules/ext/dd-plist-1.20.jar + + ext/jempbox-1.8.13.jar + release/modules/ext/jempbox-1.8.13.jar + + + ext/cxf-rt-rs-client-3.0.16.jar + release/modules/ext/cxf-rt-rs-client-3.0.16.jar + ext/sevenzipjbinding-AllPlatforms.jar release/modules/ext/sevenzipjbinding-AllPlatforms.jar @@ -491,6 +475,10 @@ ext/commons-pool2-2.4.2.jar release\modules\ext\commons-pool2-2.4.2.jar + + ext/jackcess-encrypt-2.1.2.jar + release/modules/ext/jackcess-encrypt-2.1.2.jar + ext/jsoup-1.10.3.jar release/modules/ext/jsoup-1.10.3.jar @@ -499,6 +487,10 @@ ext/jdom-2.0.5-contrib.jar release/modules/ext/jdom-2.0.5-contrib.jar + + ext/pdfbox-2.0.8.jar + release/modules/ext/pdfbox-2.0.8.jar + ext/c3p0-0.9.5.jar release/modules/ext/c3p0-0.9.5.jar diff --git a/Core/src/org/sleuthkit/autopsy/actions/AddTagAction.java b/Core/src/org/sleuthkit/autopsy/actions/AddTagAction.java index 7d0212b51c..6efb30e6a2 100644 --- a/Core/src/org/sleuthkit/autopsy/actions/AddTagAction.java +++ b/Core/src/org/sleuthkit/autopsy/actions/AddTagAction.java @@ -83,7 +83,7 @@ abstract class AddTagAction extends AbstractAction implements Presenter.Popup { */ // @@@ This user interface has some significant usability issues and needs // to be reworked. - private class TagMenu extends JMenu { + private final class TagMenu extends JMenu { private static final long serialVersionUID = 1L; @@ -99,13 +99,8 @@ abstract class AddTagAction extends AbstractAction implements Presenter.Popup { Logger.getLogger(TagsManager.class.getName()).log(Level.SEVERE, "Failed to get tag names", ex); //NON-NLS } - // Create a "Quick Tag" sub-menu. - JMenu quickTagMenu = new JMenu(NbBundle.getMessage(this.getClass(), "AddTagAction.quickTag")); - add(quickTagMenu); - - // Each tag name in the current set of tags gets its own menu item in - // the "Quick Tags" sub-menu. Selecting one of these menu items adds - // a tag with the associated tag name. + // Create a menu item for each of the existing and visible tags. + // Selecting one of these menu items adds a tag with the associated tag name. if (null != tagNamesMap && !tagNamesMap.isEmpty()) { for (Map.Entry entry : tagNamesMap.entrySet()) { String tagDisplayName = entry.getKey(); @@ -119,28 +114,15 @@ abstract class AddTagAction extends AbstractAction implements Presenter.Popup { tagNameItem.addActionListener((ActionEvent e) -> { getAndAddTag(entry.getKey(), entry.getValue(), NO_COMMENT); }); - quickTagMenu.add(tagNameItem); + + add(tagNameItem); } - } else { - JMenuItem empty = new JMenuItem(NbBundle.getMessage(this.getClass(), "AddTagAction.noTags")); - empty.setEnabled(false); - quickTagMenu.add(empty); + } + + if (getItemCount() > 0) { + addSeparator(); } - quickTagMenu.addSeparator(); - - // The "Quick Tag" menu also gets an "Choose Tag..." menu item. - // Selecting this item initiates a dialog that can be used to create - // or select a tag name and adds a tag with the resulting name. - JMenuItem newTagMenuItem = new JMenuItem(NbBundle.getMessage(this.getClass(), "AddTagAction.newTag")); - newTagMenuItem.addActionListener((ActionEvent e) -> { - TagName tagName = GetTagNameDialog.doDialog(); - if (null != tagName) { - addTag(tagName, NO_COMMENT); - } - }); - quickTagMenu.add(newTagMenuItem); - // Create a "Choose Tag and Comment..." menu item. Selecting this item initiates // a dialog that can be used to create or select a tag name with an // optional comment and adds a tag with the resulting name. @@ -153,6 +135,19 @@ abstract class AddTagAction extends AbstractAction implements Presenter.Popup { } }); add(tagAndCommentItem); + + // Create a "New Tag..." menu item. + // Selecting this item initiates a dialog that can be used to create + // or select a tag name and adds a tag with the resulting name. + JMenuItem newTagMenuItem = new JMenuItem(NbBundle.getMessage(this.getClass(), "AddTagAction.newTag")); + newTagMenuItem.addActionListener((ActionEvent e) -> { + TagName tagName = GetTagNameDialog.doDialog(); + if (null != tagName) { + addTag(tagName, NO_COMMENT); + } + }); + add(newTagMenuItem); + } /** diff --git a/Core/src/org/sleuthkit/autopsy/actions/GetTagNameAndCommentDialog.java b/Core/src/org/sleuthkit/autopsy/actions/GetTagNameAndCommentDialog.java index 51665012cd..d2c66f410b 100644 --- a/Core/src/org/sleuthkit/autopsy/actions/GetTagNameAndCommentDialog.java +++ b/Core/src/org/sleuthkit/autopsy/actions/GetTagNameAndCommentDialog.java @@ -43,6 +43,10 @@ import org.sleuthkit.datamodel.TagName; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; +/** + * This dialog allows tag assignment with a comment attached. + */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public class GetTagNameAndCommentDialog extends JDialog { private static final long serialVersionUID = 1L; diff --git a/Core/src/org/sleuthkit/autopsy/actions/GetTagNameDialog.java b/Core/src/org/sleuthkit/autopsy/actions/GetTagNameDialog.java index 21785f2003..8403a301e7 100644 --- a/Core/src/org/sleuthkit/autopsy/actions/GetTagNameDialog.java +++ b/Core/src/org/sleuthkit/autopsy/actions/GetTagNameDialog.java @@ -46,8 +46,12 @@ import org.sleuthkit.datamodel.TagName; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; +/** + * Displays existing tag names, and allows the creation of new tags. + */ @Messages({"GetTagNameDialog.descriptionLabel.text=Description:", "GetTagNameDialog.notableCheckbox.text=Tag indicates item is notable."}) +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public class GetTagNameDialog extends JDialog { private static final long serialVersionUID = 1L; diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageErrorsDialog.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageErrorsDialog.java index 742cb1750c..a469a36584 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageErrorsDialog.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageErrorsDialog.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,15 +18,22 @@ */ package org.sleuthkit.autopsy.casemodule; +import java.awt.Frame; +import javax.swing.JDialog; + /** * Dialog to show add image error messages */ -public class AddImageErrorsDialog extends javax.swing.JDialog { +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives +public class AddImageErrorsDialog extends JDialog { /** * Creates new form AddImageErrorsDialog + * + * @param parent The parent frame. + * @param modal Does this dialog act as a modal? */ - public AddImageErrorsDialog(java.awt.Frame parent, boolean modal) { + public AddImageErrorsDialog(Frame parent, boolean modal) { super(parent, modal); initComponents(); } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardAddingProgressPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardAddingProgressPanel.java index 6c6097717e..15eb0c3c67 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardAddingProgressPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardAddingProgressPanel.java @@ -57,6 +57,7 @@ import org.sleuthkit.autopsy.coreutils.Logger; * {@link AddImageWizardIngestConfigPanel} (which is a bit weird if you ask m * -jm) */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class AddImageWizardAddingProgressPanel extends ShortcutWizardDescriptorPanel { private boolean readyToIngest = false; @@ -79,7 +80,7 @@ class AddImageWizardAddingProgressPanel extends ShortcutWizardDescriptorPanel { */ private AddImageWizardAddingProgressVisual component; private final Set listeners = new HashSet<>(1); // or can use ChangeSupport in NB 6.0 - private final List newContents = Collections.synchronizedList(new ArrayList()); + private final List newContents = Collections.synchronizedList(new ArrayList<>()); private final DSPProgressMonitorImpl dspProgressMonitorImpl = new DSPProgressMonitorImpl(); private IngestJobSettings ingestJobSettings; diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardAddingProgressVisual.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardAddingProgressVisual.java index 3b6772637f..63f4cae069 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardAddingProgressVisual.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardAddingProgressVisual.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2012 Basis Technology Corp. + * Copyright 2012-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,16 +19,14 @@ package org.sleuthkit.autopsy.casemodule; import java.awt.Color; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import javax.swing.JProgressBar; -import org.openide.WizardDescriptor; import org.openide.util.NbBundle; /** * visual component to display progress bar and status updates while adding an * image in the wizard */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class AddImageWizardAddingProgressVisual extends javax.swing.JPanel { private static final String ADDING_DATA_SOURCE_COMPLETE = NbBundle diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardDataSourceSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardDataSourceSettingsPanel.java index e4d6d09984..15f1ae86a5 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardDataSourceSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardDataSourceSettingsPanel.java @@ -39,6 +39,7 @@ import org.sleuthkit.autopsy.ingest.runIngestModuleWizard.ShortcutWizardDescript * The "Add Image" wizard panel1 handling the logic of selecting image file(s) * to add to Case, and pick the time zone. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class AddImageWizardDataSourceSettingsPanel extends ShortcutWizardDescriptorPanel implements PropertyChangeListener { /** diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardDataSourceSettingsVisual.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardDataSourceSettingsVisual.java index a5671ce0ee..d44b3ed522 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardDataSourceSettingsVisual.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardDataSourceSettingsVisual.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -37,8 +37,8 @@ import org.sleuthkit.autopsy.coreutils.Logger; /** * visual component for the first panel of add image wizard. Allows the user to * choose the data source type and then select the data source - * */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class AddImageWizardDataSourceSettingsVisual extends JPanel { private static final Logger logger = Logger.getLogger(AddImageWizardDataSourceSettingsVisual.class.getName()); diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIngestConfigPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIngestConfigPanel.java index 4678e136a7..cfcbdd015c 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIngestConfigPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIngestConfigPanel.java @@ -40,10 +40,11 @@ import org.sleuthkit.autopsy.ingest.runIngestModuleWizard.ShortcutWizardDescript * TODO: review this for dead code. think about moving logic of adding image to * 3rd panel( {@link AddImageWizardAddingProgressPanel}) separate class -jm */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class AddImageWizardIngestConfigPanel extends ShortcutWizardDescriptorPanel { @Messages("AddImageWizardIngestConfigPanel.name.text=Configure Ingest Modules") - private IngestJobSettingsPanel ingestJobSettingsPanel; + private final IngestJobSettingsPanel ingestJobSettingsPanel; /** * The visual component that displays this panel. If you need to access the * component from this class, just use getComponent(). diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIngestConfigVisual.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIngestConfigVisual.java index f1a0211425..3d730e6b0e 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIngestConfigVisual.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardIngestConfigVisual.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2014 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -25,8 +25,8 @@ import javax.swing.JPanel; /** * UI panel for the ingest job configuration wizard panel of the add data source * wizard. - * */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class AddImageWizardIngestConfigVisual extends JPanel { private final JPanel ingestPanel; diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardSelectDspPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardSelectDspPanel.java index 86981e9d5d..5db401fd0f 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardSelectDspPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardSelectDspPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -36,6 +36,7 @@ import org.sleuthkit.autopsy.ingest.runIngestModuleWizard.ShortcutWizardDescript * Create a wizard panel which contains a panel allowing the selection of the * DataSourceProcessor */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class AddImageWizardSelectDspPanel extends ShortcutWizardDescriptorPanel implements PropertyChangeListener { @NbBundle.Messages("SelectDataSourceProcessorPanel.name.text=Select Type of Data Source To Add") diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardSelectDspVisual.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardSelectDspVisual.java index 43bfed1caa..ee14581bc7 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardSelectDspVisual.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardSelectDspVisual.java @@ -46,6 +46,7 @@ import org.sleuthkit.autopsy.coreutils.Logger; * Panel which displays the available DataSourceProcessors and allows selection * of one */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class AddImageWizardSelectDspVisual extends JPanel { private static final Logger logger = Logger.getLogger(AddImageWizardSelectDspVisual.class.getName()); diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/CaseBrowser.java b/Core/src/org/sleuthkit/autopsy/casemodule/CaseBrowser.java index 4f25030fd3..402b64d78f 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/CaseBrowser.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/CaseBrowser.java @@ -49,8 +49,8 @@ import org.sleuthkit.autopsy.datamodel.EmptyNode; * * Used to display a list of multi user cases and allow the user to open one of * them. - * */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class CaseBrowser extends javax.swing.JPanel implements ExplorerManager.Provider { private static final long serialVersionUID = 1L; diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/CaseInformationPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/CaseInformationPanel.java index b8542552c4..252a44e577 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/CaseInformationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/CaseInformationPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -32,6 +32,7 @@ import org.openide.windows.WindowManager; * Panel for displaying the case information, including both case details and * ingest job history. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class CaseInformationPanel extends javax.swing.JPanel { private static final long serialVersionUID = 1L; diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/CasePropertiesPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/CasePropertiesPanel.java index cc3c682830..5a16e0ee1f 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/CasePropertiesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/CasePropertiesPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -31,10 +31,11 @@ import org.sleuthkit.autopsy.coreutils.Logger; * A panel that allows the user to view various properties of a case and change * the display name of the case. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class CasePropertiesPanel extends javax.swing.JPanel { private static final long serialVersionUID = 1L; - private static final Logger LOGGER = Logger.getLogger(CasePropertiesPanel.class.getName()); + private static final Logger logger = Logger.getLogger(CasePropertiesPanel.class.getName()); private Case theCase; /** @@ -52,7 +53,7 @@ final class CasePropertiesPanel extends javax.swing.JPanel { try { theCase = Case.getCurrentCaseThrows(); } catch (NoCurrentCaseException ex) { - LOGGER.log(Level.SEVERE, "Exception while getting open case.", ex); + logger.log(Level.SEVERE, "Exception while getting open case.", ex); return; } lbCaseNameText.setText(theCase.getDisplayName()); @@ -90,7 +91,7 @@ final class CasePropertiesPanel extends javax.swing.JPanel { currentOrg = correlationCase.getOrg(); } } catch (EamDbException ex) { - LOGGER.log(Level.SEVERE, "Unable to access Correlation Case when Central Repo is enabled", ex); + logger.log(Level.SEVERE, "Unable to access Correlation Case when Central Repo is enabled", ex); } } if (currentOrg != null) { diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/CueBannerPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/CueBannerPanel.java index 478820353f..3ddb97fcfd 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/CueBannerPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/CueBannerPanel.java @@ -35,6 +35,7 @@ import org.openide.windows.WindowManager; /* * The panel in the default Autopsy startup window. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public class CueBannerPanel extends javax.swing.JPanel { private static final long serialVersionUID = 1L; diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/EditOptionalCasePropertiesPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/EditOptionalCasePropertiesPanel.java index d1c726d3eb..907c2fd0f6 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/EditOptionalCasePropertiesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/EditOptionalCasePropertiesPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,9 +22,9 @@ import java.awt.event.ActionListener; import org.openide.util.NbBundle.Messages; /** - * - * @author wschaefer + * Panel to allow examiner to edit option case properties. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class EditOptionalCasePropertiesPanel extends javax.swing.JPanel { private static final long serialVersionUID = 1L; diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.java index 56d5e67839..1d2b1ce1e2 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.java @@ -31,7 +31,6 @@ import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.filechooser.FileFilter; import org.apache.commons.lang3.StringUtils; -import org.openide.util.Exceptions; import org.openide.util.NbBundle; import static org.sleuthkit.autopsy.casemodule.Bundle.*; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; @@ -46,6 +45,7 @@ import org.sleuthkit.autopsy.coreutils.PathValidator; * to select a file as well as choose the timezone and whether to ignore orphan * files in FAT32. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public class ImageFilePanel extends JPanel implements DocumentListener { private static final Logger logger = Logger.getLogger(ImageFilePanel.class.getName()); diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/IngestJobInfoPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/IngestJobInfoPanel.java index 9bce28753f..940f6a9cd5 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/IngestJobInfoPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/IngestJobInfoPanel.java @@ -39,6 +39,7 @@ import org.sleuthkit.datamodel.TskCoreException; /** * Panel for displaying ingest job history. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public final class IngestJobInfoPanel extends javax.swing.JPanel { private static final Logger logger = Logger.getLogger(IngestJobInfoPanel.class.getName()); @@ -91,7 +92,7 @@ public final class IngestJobInfoPanel extends javax.swing.JPanel { "IngestJobInfoPanel.IngestJobTableModel.IngestStatus.header=Ingest Status"}) private class IngestJobTableModel extends AbstractTableModel { - private List columnHeaders = new ArrayList<>(); + private final List columnHeaders = new ArrayList<>(); IngestJobTableModel() { columnHeaders.add(Bundle.IngestJobInfoPanel_IngestJobTableModel_DataSource_header()); @@ -146,8 +147,8 @@ public final class IngestJobInfoPanel extends javax.swing.JPanel { "IngestJobInfoPanel.IngestModuleTableModel.ModuleVersion.header=Module Version"}) private class IngestModuleTableModel extends AbstractTableModel { - private List columnHeaders = new ArrayList<>(); - private IngestJobInfo currJob; + private final List columnHeaders = new ArrayList<>(); + private final IngestJobInfo currJob; IngestModuleTableModel(IngestJobInfo currJob) { columnHeaders.add(Bundle.IngestJobInfoPanel_IngestModuleTableModel_ModuleName_header()); diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LocalDiskPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/LocalDiskPanel.java index fc702076a0..cb1ec5ddfd 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/LocalDiskPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LocalDiskPanel.java @@ -34,6 +34,10 @@ import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.coreutils.PlatformUtil; import org.sleuthkit.autopsy.imagewriter.ImageWriterSettings; +/** + * ImageTypePanel for adding a local disk or partition such as PhysicalDrive0 or + * C:. + */ @NbBundle.Messages({ "LocalDiskPanel.errorMessage.noOpenCaseTitle=No open case available", "LocalDiskPanel.errorMessage.noOpenCaseBody=LocalDiskPanel listener couldn't get the open case.", @@ -45,10 +49,7 @@ import org.sleuthkit.autopsy.imagewriter.ImageWriterSettings; "LocalDiskPanel.moduleErrorMessage.body=A module caused an error listening to LocalDiskPanel updates. See log to determine which module. Some data could be incomplete.", "LocalDiskPanel.localDiskMessage.unspecified=Unspecified" }) -/** - * ImageTypePanel for adding a local disk or partition such as PhysicalDrive0 or - * C:. - */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class LocalDiskPanel extends JPanel { private static final Logger logger = Logger.getLogger(LocalDiskPanel.class.getName()); diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesPanel.java index 3ab4088094..872c647a94 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesPanel.java @@ -35,6 +35,7 @@ import org.sleuthkit.autopsy.coreutils.PathValidator; /** * A panel which allows the user to select local files and/or directories. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class LocalFilesPanel extends javax.swing.JPanel { private static final long serialVersionUID = 1L; diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalEvidenceFilePanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalEvidenceFilePanel.java index d38106a0ab..5e11d5dfa0 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalEvidenceFilePanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalEvidenceFilePanel.java @@ -32,7 +32,6 @@ import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang3.StringUtils; -import org.openide.util.Exceptions; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.PathValidator; @@ -40,6 +39,7 @@ import org.sleuthkit.autopsy.coreutils.PathValidator; /** * A panel which allows the user to select a Logical Evidence File (L01) */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class LogicalEvidenceFilePanel extends javax.swing.JPanel implements DocumentListener { private static final long serialVersionUID = 1L; diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalFilesDspPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalFilesDspPanel.java index 0298c98d69..5f96422a9e 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalFilesDspPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalFilesDspPanel.java @@ -28,13 +28,14 @@ import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; +/** + * Add input wizard subpanel for adding local files / dirs to the case + */ @Messages({ "LogicalFilesDspPanel.subTypeComboBox.localFilesOption.text=Local files and folders", "LogicalFilesDspPanel.subTypeComboBox.l01FileOption.text=Logical evidence file (L01)" }) -/** - * Add input wizard subpanel for adding local files / dirs to the case - */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class LogicalFilesDspPanel extends JPanel { private static final long serialVersionUID = 1L; diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/MissingImageDialog.java b/Core/src/org/sleuthkit/autopsy/casemodule/MissingImageDialog.java index 3b246e920b..5645e1d5d6 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/MissingImageDialog.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/MissingImageDialog.java @@ -35,6 +35,10 @@ import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; +/** + * Dialog to allow the examiner to locate an image when it cannot be found. + */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class MissingImageDialog extends javax.swing.JDialog { private static final Logger logger = Logger.getLogger(MissingImageDialog.class.getName()); @@ -50,7 +54,7 @@ class MissingImageDialog extends javax.swing.JDialog { } static final String allDesc = NbBundle.getMessage(MissingImageDialog.class, "MissingImageDialog.allDesc.text"); static final GeneralFilter allFilter = new GeneralFilter(allExt, allDesc); - private JFileChooser fc = new JFileChooser(); + private final JFileChooser fc = new JFileChooser(); /** * Instantiate a MissingImageDialog. diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCasesDialog.java b/Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCasesDialog.java index 8c90aeccb4..af95b0c5a0 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCasesDialog.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCasesDialog.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -28,6 +28,7 @@ import org.openide.windows.WindowManager; /** * This class extends a JDialog and maintains the MultiUserCasesPanel. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class MultiUserCasesDialog extends JDialog { private static final long serialVersionUID = 1L; diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCasesPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCasesPanel.java index 7a1ca81862..07d863bc2b 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCasesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/MultiUserCasesPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -36,9 +36,10 @@ import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; /** * A panel that allows a user to open cases created by auto ingest. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class MultiUserCasesPanel extends JPanel{ - private static final Logger LOGGER = Logger.getLogger(MultiUserCasesPanel.class.getName()); + private static final Logger logger = Logger.getLogger(MultiUserCasesPanel.class.getName()); private static final long serialVersionUID = 1L; private final JDialog parentDialog; private final CaseBrowser caseBrowserPanel; @@ -98,7 +99,7 @@ final class MultiUserCasesPanel extends JPanel{ Case.openAsCurrentCase(caseMetadataFilePath); } catch (CaseActionException ex) { if (null != ex.getCause() && !(ex.getCause() instanceof CaseActionCancelledException)) { - LOGGER.log(Level.SEVERE, String.format("Error opening case with metadata file path %s", caseMetadataFilePath), ex); //NON-NLS + logger.log(Level.SEVERE, String.format("Error opening case with metadata file path %s", caseMetadataFilePath), ex); //NON-NLS MessageNotifyUtil.Message.error(ex.getCause().getLocalizedMessage()); } SwingUtilities.invokeLater(() -> { diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java b/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java index cd1179b52b..6f365af86e 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2015 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,9 +18,9 @@ */ package org.sleuthkit.autopsy.casemodule; +import java.awt.Component; import org.openide.util.NbBundle; -import java.awt.*; import java.io.File; import javax.swing.JFileChooser; import javax.swing.JPanel; @@ -33,6 +33,7 @@ import org.sleuthkit.autopsy.coreutils.PathValidator; /** * The JPanel for the first page of the new case wizard. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class NewCaseVisualPanel1 extends JPanel implements DocumentListener { private final JFileChooser fileChooser = new JFileChooser(); diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel2.java b/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel2.java index 2098fc91b3..ec06648248 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel2.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel2.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,20 +16,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - - /* - * NewCaseVisualPanel2.java - * - * Created on Mar 7, 2012, 11:01:48 AM - */ package org.sleuthkit.autopsy.casemodule; import org.openide.util.NbBundle; /** - * - * @author dfickling + * The JPanel for the second page of the new case wizard. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class NewCaseVisualPanel2 extends javax.swing.JPanel { private static final long serialVersionUID = 1L; diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/OpenRecentCasePanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/OpenRecentCasePanel.java index c686bcad1c..5f164cd00f 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/OpenRecentCasePanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/OpenRecentCasePanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -35,6 +35,7 @@ import org.sleuthkit.autopsy.coreutils.ThreadConfined; /** * Panel used by the the open recent case option of the start window. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class OpenRecentCasePanel extends javax.swing.JPanel { private static final long serialVersionUID = 1L; diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/OptionalCasePropertiesPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/OptionalCasePropertiesPanel.java index 4625727d94..55c64d1f12 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/OptionalCasePropertiesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/OptionalCasePropertiesPanel.java @@ -36,9 +36,10 @@ import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; * Panel which allows for editing and setting of the case details which are * optional or otherwise able to be edited. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class OptionalCasePropertiesPanel extends javax.swing.JPanel { - private final static Logger LOGGER = Logger.getLogger(OptionalCasePropertiesPanel.class.getName()); + private final static Logger logger = Logger.getLogger(OptionalCasePropertiesPanel.class.getName()); private static final long serialVersionUID = 1L; private EamOrganization selectedOrg = null; private java.util.List orgs = null; @@ -66,7 +67,7 @@ final class OptionalCasePropertiesPanel extends javax.swing.JPanel { try { openCase = Case.getCurrentCaseThrows(); } catch (NoCurrentCaseException ex) { - LOGGER.log(Level.SEVERE, "Exception while getting open case.", ex); + logger.log(Level.SEVERE, "Exception while getting open case.", ex); return; } caseDisplayNameTextField.setText(openCase.getDisplayName()); @@ -100,9 +101,9 @@ final class OptionalCasePropertiesPanel extends javax.swing.JPanel { selectedOrg = dbManager.getCase(currentCase).getOrg(); } } catch (EamDbException ex) { - LOGGER.log(Level.SEVERE, "Unable to get Organization associated with the case from Central Repo", ex); + logger.log(Level.SEVERE, "Unable to get Organization associated with the case from Central Repo", ex); } catch (NoCurrentCaseException ex) { - LOGGER.log(Level.SEVERE, "Exception while getting open case.", ex); + logger.log(Level.SEVERE, "Exception while getting open case.", ex); } if (selectedOrg != null) { @@ -145,7 +146,7 @@ final class OptionalCasePropertiesPanel extends javax.swing.JPanel { }); comboBoxOrgName.setSelectedItem(selectedBeforeLoad); } catch (EamDbException ex) { - LOGGER.log(Level.WARNING, "Unable to populate list of Organizations from Central Repo", ex); + logger.log(Level.WARNING, "Unable to populate list of Organizations from Central Repo", ex); } } @@ -598,9 +599,9 @@ final class OptionalCasePropertiesPanel extends javax.swing.JPanel { correlationCase.setNotes(taNotesText.getText()); dbManager.updateCase(correlationCase); } catch (EamDbException ex) { - LOGGER.log(Level.SEVERE, "Error connecting to central repository database", ex); // NON-NLS + logger.log(Level.SEVERE, "Error connecting to central repository database", ex); // NON-NLS } catch (NoCurrentCaseException ex) { - LOGGER.log(Level.SEVERE, "Exception while getting open case.", ex); // NON-NLS + logger.log(Level.SEVERE, "Exception while getting open case.", ex); // NON-NLS } finally { setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagNameDialog.java b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagNameDialog.java index 276be583c1..eef8d08087 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagNameDialog.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagNameDialog.java @@ -19,8 +19,6 @@ package org.sleuthkit.autopsy.casemodule.services; import java.awt.BorderLayout; -import java.awt.Dimension; -import java.awt.Toolkit; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import javax.swing.JFrame; @@ -34,6 +32,7 @@ import org.sleuthkit.datamodel.TskData; @Messages({"TagNameDialog.descriptionLabel.text=Description:", "TagNameDialog.notableCheckbox.text=Tag indicates item is notable."}) +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class TagNameDialog extends javax.swing.JDialog { private static final long serialVersionUID = 1L; diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagOptionsPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagOptionsPanel.java index 419a92e8c9..7f63a9d4c4 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagOptionsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagOptionsPanel.java @@ -27,7 +27,6 @@ import java.util.TreeSet; import java.util.logging.Level; import javax.swing.DefaultListModel; import javax.swing.JOptionPane; -import javax.swing.SwingUtilities; import javax.swing.event.ListSelectionEvent; import org.netbeans.spi.options.OptionsPanelController; import org.openide.util.NbBundle; @@ -43,6 +42,7 @@ import org.sleuthkit.autopsy.coreutils.Logger; /** * A panel to allow the user to create and delete custom tag types. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class TagOptionsPanel extends javax.swing.JPanel implements OptionsPanel { private static final long serialVersionUID = 1L; diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/AddEditCentralRepoCommentAction.java b/Core/src/org/sleuthkit/autopsy/centralrepository/AddEditCentralRepoCommentAction.java new file mode 100755 index 0000000000..8c03883523 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/AddEditCentralRepoCommentAction.java @@ -0,0 +1,142 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2018 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.centralrepository; + +import java.awt.event.ActionEvent; +import java.util.logging.Level; +import javax.swing.AbstractAction; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamArtifactUtil; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * An AbstractAction to manage adding and modifying a Central Repository file + * instance comment. + */ +public final class AddEditCentralRepoCommentAction extends AbstractAction { + + private static final Logger logger = Logger.getLogger(AddEditCentralRepoCommentAction.class.getName()); + + private boolean addToDatabase; + private CorrelationAttribute correlationAttribute; + String title; + + /** + * Private constructor to create an instance given a CorrelationAttribute. + * + * @param correlationAttribute The correlation attribute to modify. + * @param title The text for the menu item. + */ + private AddEditCentralRepoCommentAction(CorrelationAttribute correlationAttribute, String title) { + super(title); + this.title = title; + this.correlationAttribute = correlationAttribute; + } + + /** + * Private constructor to create an instance given an AbstractFile. + * + * @param file The file from which a correlation attribute to modify is + * derived. + * @param title The text for the menu item. + */ + private AddEditCentralRepoCommentAction(AbstractFile file, String title) { + + super(title); + this.title = title; + correlationAttribute = EamArtifactUtil.getCorrelationAttributeFromContent(file); + if (correlationAttribute == null) { + addToDatabase = true; + correlationAttribute = EamArtifactUtil.makeCorrelationAttributeFromContent(file); + } + } + + @Override + public void actionPerformed(ActionEvent event) { + addEditCentralRepoComment(); + } + + /** + * Create a Add/Edit dialog for the correlation attribute file instance + * comment. The comment will be updated in the database if the file instance + * exists there, or a new file instance will be added to the database with + * the comment attached otherwise. + */ + public void addEditCentralRepoComment() { + CentralRepoCommentDialog centralRepoCommentDialog = new CentralRepoCommentDialog(correlationAttribute, title); + centralRepoCommentDialog.display(); + + if (centralRepoCommentDialog.isCommentUpdated()) { + EamDb dbManager; + + try { + dbManager = EamDb.getInstance(); + + if (addToDatabase) { + dbManager.addArtifact(correlationAttribute); + } else { + dbManager.updateAttributeInstanceComment(correlationAttribute); + } + } catch (EamDbException ex) { + logger.log(Level.SEVERE, "Error connecting to Central Repository database.", ex); + } + } + } + + /** + * Create an instance labeled "Add/Edit Central Repository Comment" given an + * AbstractFile. This is intended for the result view. + * + * @param file The file from which a correlation attribute to modify is + * derived. + * + * @return The instance. + * + * @throws EamDbException + * @throws NoCurrentCaseException + * @throws TskCoreException + */ + @Messages({"AddEditCentralRepoCommentAction.menuItemText.addEditCentralRepoComment=Add/Edit Central Repository Comment"}) + public static AddEditCentralRepoCommentAction createAddEditCentralRepoCommentAction(AbstractFile file) { + + return new AddEditCentralRepoCommentAction(file, + Bundle.AddEditCentralRepoCommentAction_menuItemText_addEditCentralRepoComment()); + } + + /** + * Create an instance labeled "Add/Edit Comment" given a + * CorrelationAttribute. This is intended for the content view. + * + * @param correlationAttribute The correlation attribute to modify. + * + * @return The instance. + */ + @Messages({"AddEditCentralRepoCommentAction.menuItemText.addEditComment=Add/Edit Comment"}) + public static AddEditCentralRepoCommentAction createAddEditCommentAction(CorrelationAttribute correlationAttribute) { + + return new AddEditCentralRepoCommentAction(correlationAttribute, + Bundle.AddEditCentralRepoCommentAction_menuItemText_addEditComment()); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/Bundle.properties b/Core/src/org/sleuthkit/autopsy/centralrepository/Bundle.properties index 9e873a514b..3223583037 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/Bundle.properties @@ -5,3 +5,8 @@ OpenIDE-Module-Long-Description=\ Correlation Engine ingest module and central database. \n\n\ The Correlation Engine ingest module stores attributes of artifacts matching selected correlation types into a central database.\n\ Stored attributes are used in future cases to correlate and analyzes files and artifacts during ingest. +CentralRepoCommentDialog.fileLabel.text=File: +CentralRepoCommentDialog.commentLabel.text=Comment: +CentralRepoCommentDialog.pathLabel.text= +CentralRepoCommentDialog.okButton.text=&OK +CentralRepoCommentDialog.cancelButton.text=C&ancel diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/CentralRepoCommentDialog.form b/Core/src/org/sleuthkit/autopsy/centralrepository/CentralRepoCommentDialog.form new file mode 100755 index 0000000000..5a9882d4cf --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/CentralRepoCommentDialog.form @@ -0,0 +1,138 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/CentralRepoCommentDialog.java b/Core/src/org/sleuthkit/autopsy/centralrepository/CentralRepoCommentDialog.java new file mode 100755 index 0000000000..4e42bfe9d7 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/CentralRepoCommentDialog.java @@ -0,0 +1,187 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2018 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.centralrepository; + +import org.openide.windows.WindowManager; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; + +/** + * Dialog to allow Central Repository file instance comments to be added and + * modified. + */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives +final class CentralRepoCommentDialog extends javax.swing.JDialog { + + private final CorrelationAttribute correlationAttribute; + private boolean commentUpdated = false; + + /** + * Create an instance. + * + * @param correlationAttribute The correlation attribute to be modified. + * @param title The title to assign the dialog. + */ + CentralRepoCommentDialog(CorrelationAttribute correlationAttribute, String title) { + super(WindowManager.getDefault().getMainWindow(), title); + + initComponents(); + + CorrelationAttributeInstance instance = correlationAttribute.getInstances().get(0); + + pathLabel.setText(instance.getFilePath()); + commentTextArea.setText(instance.getComment()); + + this.correlationAttribute = correlationAttribute; + } + + /** + * Display the dialog. + */ + void display() { + setModal(true); + setSize(getPreferredSize()); + setLocationRelativeTo(this.getParent()); + setAlwaysOnTop(false); + pack(); + setVisible(true); + } + + /** + * Has the comment been updated? + * + * @return True if the comment has been updated; otherwise false. + */ + boolean isCommentUpdated() { + return commentUpdated; + } + + /** + * 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() { + + jScrollPane1 = new javax.swing.JScrollPane(); + commentTextArea = new javax.swing.JTextArea(); + okButton = new javax.swing.JButton(); + cancelButton = new javax.swing.JButton(); + fileLabel = new javax.swing.JLabel(); + pathLabel = new javax.swing.JLabel(); + commentLabel = new javax.swing.JLabel(); + + setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); + setSize(getPreferredSize()); + + commentTextArea.setColumns(20); + commentTextArea.setLineWrap(true); + commentTextArea.setRows(5); + commentTextArea.setTabSize(4); + commentTextArea.setWrapStyleWord(true); + jScrollPane1.setViewportView(commentTextArea); + + org.openide.awt.Mnemonics.setLocalizedText(okButton, org.openide.util.NbBundle.getMessage(CentralRepoCommentDialog.class, "CentralRepoCommentDialog.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(CentralRepoCommentDialog.class, "CentralRepoCommentDialog.cancelButton.text")); // NOI18N + cancelButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + cancelButtonActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(fileLabel, org.openide.util.NbBundle.getMessage(CentralRepoCommentDialog.class, "CentralRepoCommentDialog.fileLabel.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(pathLabel, org.openide.util.NbBundle.getMessage(CentralRepoCommentDialog.class, "CentralRepoCommentDialog.pathLabel.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(commentLabel, org.openide.util.NbBundle.getMessage(CentralRepoCommentDialog.class, "CentralRepoCommentDialog.commentLabel.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) + .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 500, Short.MAX_VALUE) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(fileLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(pathLabel)) + .addComponent(commentLabel)) + .addGap(0, 451, Short.MAX_VALUE)) + .addGroup(layout.createSequentialGroup() + .addGap(0, 0, Short.MAX_VALUE) + .addComponent(okButton, javax.swing.GroupLayout.PREFERRED_SIZE, 75, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(cancelButton, javax.swing.GroupLayout.PREFERRED_SIZE, 75, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(fileLabel) + .addComponent(pathLabel)) + .addGap(19, 19, 19) + .addComponent(commentLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jScrollPane1) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(okButton) + .addComponent(cancelButton)) + .addContainerGap()) + ); + + pack(); + }// //GEN-END:initComponents + + private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed + dispose(); + }//GEN-LAST:event_cancelButtonActionPerformed + + private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed + String comment = commentTextArea.getText(); + correlationAttribute.getInstances().get(0).setComment(comment); + commentUpdated = true; + + dispose(); + }//GEN-LAST:event_okButtonActionPerformed + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton cancelButton; + private javax.swing.JLabel commentLabel; + private javax.swing.JTextArea commentTextArea; + private javax.swing.JLabel fileLabel; + private javax.swing.JScrollPane jScrollPane1; + private javax.swing.JButton okButton; + private javax.swing.JLabel pathLabel; + // End of variables declaration//GEN-END:variables +} diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/ArtifactKey.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/ArtifactKey.java deleted file mode 100644 index 1594456a97..0000000000 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/ArtifactKey.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Central Repository - * - * Copyright 2015-2017 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.centralrepository.contentviewer; - -import java.util.Objects; - -/** - * Used as a key to ensure we eliminate duplicates from the result set by not overwriting CR correlation instances. - */ -final class ArtifactKey { - - private final String dataSourceID; - private final String filePath; - - ArtifactKey(String theDataSource, String theFilePath) { - dataSourceID = theDataSource; - filePath = theFilePath.toLowerCase(); - } - - - /** - * - * @return the dataSourceID device ID - */ - String getDataSourceID() { - return dataSourceID; - } - - /** - * - * @return the filPath including the filename and extension. - */ - String getFilePath() { - return filePath; - } - - @Override - public boolean equals(Object other) { - if (other instanceof ArtifactKey) { - return ((ArtifactKey) other).getDataSourceID().equals(dataSourceID) && ((ArtifactKey) other).getFilePath().equals(filePath); - } - return false; - - } - - @Override - public int hashCode() { - //int hash = 7; - //hash = 67 * hash + this.dataSourceID.hashCode(); - //hash = 67 * hash + this.filePath.hashCode(); - - return Objects.hash(dataSourceID, filePath); - } -} diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/Bundle.properties b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/Bundle.properties index 8b18108d52..39759e3eb7 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/Bundle.properties @@ -3,3 +3,4 @@ DataContentViewerOtherCases.showCaseDetailsMenuItem.text=Show Case Details DataContentViewerOtherCases.table.toolTip.text=Click column name to sort. Right-click on the table for more options. DataContentViewerOtherCases.exportToCSVMenuItem.text=Export Selected Rows to CSV DataContentViewerOtherCases.showCommonalityMenuItem.text=Show Frequency +DataContentViewerOtherCases.addCommentMenuItem.text=Add/Edit Comment diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.form b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.form index 2d26ab3bc8..0097313943 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.form +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.form @@ -3,6 +3,9 @@
+ + + @@ -36,6 +39,13 @@ + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java index ca9b0e2c06..bf7fef5fa8 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java @@ -31,6 +31,7 @@ import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.logging.Level; import org.sleuthkit.autopsy.coreutils.Logger; import java.util.stream.Collectors; @@ -40,6 +41,7 @@ import javax.swing.JOptionPane; import static javax.swing.JOptionPane.DEFAULT_OPTION; import static javax.swing.JOptionPane.PLAIN_MESSAGE; import static javax.swing.JOptionPane.ERROR_MESSAGE; +import javax.swing.JPanel; import javax.swing.filechooser.FileNameExtensionFilter; import javax.swing.table.TableCellRenderer; import javax.swing.table.TableColumn; @@ -48,6 +50,7 @@ import org.openide.util.NbBundle.Messages; import org.openide.util.lookup.ServiceProvider; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.centralrepository.AddEditCentralRepoCommentAction; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; @@ -63,18 +66,21 @@ import org.sleuthkit.datamodel.ContentTag; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskException; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbUtil; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskData; +import org.sleuthkit.datamodel.TskDataException; /** * View correlation results from other cases */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives @ServiceProvider(service = DataContentViewer.class, position = 8) @Messages({"DataContentViewerOtherCases.title=Other Occurrences", "DataContentViewerOtherCases.toolTip=Displays instances of the selected file/artifact from other occurrences.",}) -public class DataContentViewerOtherCases extends javax.swing.JPanel implements DataContentViewer { +public class DataContentViewerOtherCases extends JPanel implements DataContentViewer { - private final static Logger LOGGER = Logger.getLogger(DataContentViewerOtherCases.class.getName()); + private final static Logger logger = Logger.getLogger(DataContentViewerOtherCases.class.getName()); private final DataContentViewerOtherCasesTableModel tableModel; private final Collection correlationAttributes; @@ -108,10 +114,15 @@ public class DataContentViewerOtherCases extends javax.swing.JPanel implements D try { saveToCSV(); } catch (NoCurrentCaseException ex) { - LOGGER.log(Level.SEVERE, "Exception while getting open case.", ex); // NON-NLS + logger.log(Level.SEVERE, "Exception while getting open case.", ex); // NON-NLS } } else if (jmi.equals(showCommonalityMenuItem)) { showCommonalityDetails(); + } else if (jmi.equals(addCommentMenuItem)) { + CorrelationAttribute selectedAttribute = (CorrelationAttribute) tableModel.getRow(otherCasesTable.getSelectedRow()); + AddEditCentralRepoCommentAction action = AddEditCentralRepoCommentAction.createAddEditCommentAction(selectedAttribute); + action.addEditCentralRepoComment(); + otherCasesTable.repaint(); } } }; @@ -120,6 +131,7 @@ public class DataContentViewerOtherCases extends javax.swing.JPanel implements D selectAllMenuItem.addActionListener(actList); showCaseDetailsMenuItem.addActionListener(actList); showCommonalityMenuItem.addActionListener(actList); + addCommentMenuItem.addActionListener(actList); // Set background of every nth row as light grey. TableCellRenderer renderer = new DataContentViewerOtherCasesTableCellRenderer(); @@ -135,7 +147,8 @@ public class DataContentViewerOtherCases extends javax.swing.JPanel implements D "DataContentViewerOtherCases.correlatedArtifacts.title=Attribute Frequency", "DataContentViewerOtherCases.correlatedArtifacts.failed=Failed to get frequency details."}) /** - * Show how common the selected correlationAttributes are with details dialog. + * Show how common the selected correlationAttributes are with details + * dialog. */ private void showCommonalityDetails() { if (correlationAttributes.isEmpty()) { @@ -159,7 +172,7 @@ public class DataContentViewerOtherCases extends javax.swing.JPanel implements D Bundle.DataContentViewerOtherCases_correlatedArtifacts_title(), DEFAULT_OPTION, PLAIN_MESSAGE); } catch (EamDbException ex) { - LOGGER.log(Level.SEVERE, "Error getting commonality details.", ex); + logger.log(Level.SEVERE, "Error getting commonality details.", ex); JOptionPane.showConfirmDialog(showCommonalityMenuItem, Bundle.DataContentViewerOtherCases_correlatedArtifacts_failed(), Bundle.DataContentViewerOtherCases_correlatedArtifacts_title(), @@ -283,7 +296,7 @@ public class DataContentViewerOtherCases extends javax.swing.JPanel implements D } } catch (IOException ex) { - LOGGER.log(Level.SEVERE, "Error writing selected rows to CSV.", ex); + logger.log(Level.SEVERE, "Error writing selected rows to CSV.", ex); } } @@ -374,7 +387,7 @@ public class DataContentViewerOtherCases extends javax.swing.JPanel implements D try { content = nodeBbArtifact.getSleuthkitCase().getContentById(nodeBbArtifact.getObjectID()); } catch (TskCoreException ex) { - LOGGER.log(Level.SEVERE, "Error retrieving blackboard artifact", ex); // NON-NLS + logger.log(Level.SEVERE, "Error retrieving blackboard artifact", ex); // NON-NLS return null; } @@ -401,7 +414,7 @@ public class DataContentViewerOtherCases extends javax.swing.JPanel implements D // correlate on blackboard artifact attributes if they exist and supported BlackboardArtifact bbArtifact = getBlackboardArtifactFromNode(node); - if (bbArtifact != null) { + if (bbArtifact != null && EamDb.isEnabled()) { ret.addAll(EamArtifactUtil.getCorrelationAttributeFromBlackboardArtifact(bbArtifact, false, false)); } @@ -420,20 +433,20 @@ public class DataContentViewerOtherCases extends javax.swing.JPanel implements D } } } catch (EamDbException ex) { - LOGGER.log(Level.SEVERE, "Error connecting to DB", ex); // NON-NLS + logger.log(Level.SEVERE, "Error connecting to DB", ex); // NON-NLS } } else { try { // If EamDb not enabled, get the Files default correlation type to allow Other Occurances to be enabled. - if(this.file != null) { + if (this.file != null) { String md5 = this.file.getMd5Hash(); - if(md5 != null && !md5.isEmpty()) { - ret.add(new CorrelationAttribute(CorrelationAttribute.getDefaultCorrelationTypes().get(0),md5)); + if (md5 != null && !md5.isEmpty()) { + ret.add(new CorrelationAttribute(CorrelationAttribute.getDefaultCorrelationTypes().get(0), md5)); } } } catch (EamDbException ex) { - LOGGER.log(Level.SEVERE, "Error connecting to DB", ex); // NON-NLS + logger.log(Level.SEVERE, "Error connecting to DB", ex); // NON-NLS } } @@ -441,35 +454,36 @@ public class DataContentViewerOtherCases extends javax.swing.JPanel implements D } /** - * Query the db for artifact instances from other cases correlated to the - * given central repository artifact. Will not show instances from the same - * datasource / device + * Query the database for artifact instances from other cases correlated to + * the given central repository artifact. Instances from the same datasource + * / device will also be included. * - * @param corAttr CorrelationAttribute to query for + * @param corAttr CorrelationAttribute to query for * @param dataSourceName Data source to filter results - * @param deviceId Device Id to filter results + * @param deviceId Device Id to filter results * - * @return A collection of correlated artifact instances from other cases + * @return A collection of correlated artifact instances */ - private Map getCorrelatedInstances(CorrelationAttribute corAttr, String dataSourceName, String deviceId) { + private Map getCorrelatedInstances(CorrelationAttribute corAttr, String dataSourceName, String deviceId) { // @@@ Check exception try { final Case openCase = Case.getCurrentCase(); String caseUUID = openCase.getName(); - HashMap artifactInstances = new HashMap<>(); + String filePath = (file.getParentPath() + file.getName()).toLowerCase(); + HashMap artifactInstances = new HashMap<>(); if (EamDb.isEnabled()) { EamDb dbManager = EamDb.getInstance(); artifactInstances.putAll(dbManager.getArtifactInstancesByTypeValue(corAttr.getCorrelationType(), corAttr.getCorrelationValue()).stream() - .filter(artifactInstance -> !artifactInstance.getCorrelationCase().getCaseUUID().equals(caseUUID) + .filter(artifactInstance -> !artifactInstance.getFilePath().equals(filePath) + || !artifactInstance.getCorrelationCase().getCaseUUID().equals(caseUUID) || !artifactInstance.getCorrelationDataSource().getName().equals(dataSourceName) || !artifactInstance.getCorrelationDataSource().getDeviceID().equals(deviceId)) - .collect(Collectors.toMap( - correlationAttr -> new ArtifactKey(correlationAttr.getCorrelationDataSource().getDeviceID(), correlationAttr.getFilePath()), + .collect(Collectors.toMap(correlationAttr -> new UniquePathKey(correlationAttr.getCorrelationDataSource().getDeviceID(), correlationAttr.getFilePath()), correlationAttr -> correlationAttr))); } - if (corAttr.getCorrelationType().getDisplayName().equals("Files")) { + if (corAttr.getCorrelationType().getDisplayName().equals("Files")) { List caseDbFiles = addCaseDbMatches(corAttr, openCase); for (AbstractFile caseDbFile : caseDbFiles) { addOrUpdateAttributeInstance(openCase, artifactInstances, caseDbFile); @@ -478,13 +492,13 @@ public class DataContentViewerOtherCases extends javax.swing.JPanel implements D return artifactInstances; } catch (EamDbException ex) { - LOGGER.log(Level.SEVERE, "Error getting artifact instances from database.", ex); // NON-NLS + logger.log(Level.SEVERE, "Error getting artifact instances from database.", ex); // NON-NLS } catch (NoCurrentCaseException ex) { - LOGGER.log(Level.SEVERE, "Exception while getting open case.", ex); // NON-NLS + logger.log(Level.SEVERE, "Exception while getting open case.", ex); // NON-NLS } catch (TskCoreException ex) { // do nothing. // @@@ Review this behavior - LOGGER.log(Level.SEVERE, "Exception while querying open case.", ex); // NON-NLS + logger.log(Level.SEVERE, "Exception while querying open case.", ex); // NON-NLS } return new HashMap<>(0); @@ -507,29 +521,59 @@ public class DataContentViewerOtherCases extends javax.swing.JPanel implements D } - private void addOrUpdateAttributeInstance(final Case openCase, Map artifactInstances, AbstractFile caseDbFile) throws TskCoreException, EamDbException { - CorrelationCase caze = new CorrelationCase(openCase.getNumber(), openCase.getDisplayName()); - CorrelationDataSource dataSource = CorrelationDataSource.fromTSKDataSource(caze, caseDbFile.getDataSource()); - String filePath = caseDbFile.getParentPath() + caseDbFile.getName(); - ArtifactKey instKey = new ArtifactKey(dataSource.getDeviceID(), filePath); - CorrelationAttributeInstance caseDbInstance = new CorrelationAttributeInstance(caze, dataSource, filePath, "", caseDbFile.getKnown()); - TskData.FileKnown knownStatus = caseDbInstance.getKnownStatus(); - // If not known, check Tags for known and set - TskData.FileKnown knownBad = TskData.FileKnown.BAD; - if (!knownStatus.equals(knownBad)) { - List fileMatchTags = openCase.getServices().getTagsManager().getContentTagsByContent(caseDbFile); + /** + * Adds the file to the artifactInstances map if it does not already exist + * + * @param autopsyCase + * @param artifactInstances + * @param newFile + * + * @throws TskCoreException + * @throws EamDbException + */ + private void addOrUpdateAttributeInstance(final Case autopsyCase, Map artifactInstances, AbstractFile newFile) throws TskCoreException, EamDbException { + + // figure out if the casedb file is known via either hash or tags + TskData.FileKnown localKnown = newFile.getKnown(); + + if (localKnown != TskData.FileKnown.BAD) { + List fileMatchTags = autopsyCase.getServices().getTagsManager().getContentTagsByContent(newFile); for (ContentTag tag : fileMatchTags) { TskData.FileKnown tagKnownStatus = tag.getName().getKnownStatus(); - if (tagKnownStatus.equals(knownBad)) { - caseDbInstance.setKnownStatus(knownBad); + if (tagKnownStatus.equals(TskData.FileKnown.BAD)) { + localKnown = TskData.FileKnown.BAD; break; } } } - // If known, or not in CR, add - if (caseDbInstance.getKnownStatus().equals(knownBad) || !artifactInstances.containsKey(instKey)) { - artifactInstances.put(instKey, caseDbInstance); + // make a key to see if the file is already in the map + String filePath = newFile.getParentPath() + newFile.getName(); + String deviceId; + try { + deviceId = autopsyCase.getSleuthkitCase().getDataSource(newFile.getDataSource().getId()).getDeviceId(); + } catch (TskDataException | TskCoreException ex) { + logger.log(Level.WARNING, "Error getting data source info: {0}", ex); + return; + } + UniquePathKey uniquePathKey = new UniquePathKey(deviceId, filePath); + + // double check that the CR version is BAD if the caseDB version is BAD. + if (artifactInstances.containsKey(uniquePathKey)) { + if (localKnown == TskData.FileKnown.BAD) { + CorrelationAttributeInstance prevInstance = artifactInstances.get(uniquePathKey); + prevInstance.setKnownStatus(localKnown); + } + } // add the data from the case DB by pushing data into CorrelationAttributeInstance class + else { + // NOTE: If we are in here, it is likely because CR is not enabled. So, we cannot rely + // on any of the methods that query the DB. + CorrelationCase correlationCase = new CorrelationCase(autopsyCase.getName(), autopsyCase.getDisplayName()); + + CorrelationDataSource correlationDataSource = CorrelationDataSource.fromTSKDataSource(correlationCase, newFile.getDataSource()); + + CorrelationAttributeInstance caseDbInstance = new CorrelationAttributeInstance(correlationCase, correlationDataSource, filePath, "", localKnown); + artifactInstances.put(uniquePathKey, caseDbInstance); } } @@ -539,9 +583,15 @@ public class DataContentViewerOtherCases extends javax.swing.JPanel implements D // Is supported if this node // has correlatable content (File, BlackboardArtifact) OR // other common files across datasources. - return this.file != null - && this.file.getSize() > 0 - && !getCorrelationAttributesFromNode(node).isEmpty(); + + if (EamDb.isEnabled()) { + return this.file != null + && this.file.getSize() > 0 + && !getCorrelationAttributesFromNode(node).isEmpty(); + } else { + return this.file != null + && this.file.getSize() > 0; + } } @Override @@ -582,7 +632,7 @@ public class DataContentViewerOtherCases extends javax.swing.JPanel implements D // get the attributes we can correlate on correlationAttributes.addAll(getCorrelationAttributesFromNode(node)); for (CorrelationAttribute corAttr : correlationAttributes) { - Map corAttrInstances = new HashMap<>(0); + Map corAttrInstances = new HashMap<>(0); // get correlation and reference set instances from DB corAttrInstances.putAll(getCorrelatedInstances(corAttr, dataSourceName, deviceId)); @@ -596,12 +646,13 @@ public class DataContentViewerOtherCases extends javax.swing.JPanel implements D newCeArtifact.addInstance(corAttrInstance); tableModel.addEamArtifact(newCeArtifact); } catch (EamDbException ex) { - LOGGER.log(Level.SEVERE, "Error creating correlation attribute", ex); + logger.log(Level.SEVERE, "Error creating correlation attribute", ex); } }); } if (correlationAttributes.isEmpty()) { + // @@@ BC: We should have a more descriptive message than this. Mention that the file didn't have a MD5, etc. displayMessageOnTableStatusPanel(Bundle.DataContentViewerOtherCases_table_noArtifacts()); } else if (0 == tableModel.getRowCount()) { displayMessageOnTableStatusPanel(Bundle.DataContentViewerOtherCases_table_isempty()); @@ -644,6 +695,7 @@ public class DataContentViewerOtherCases extends javax.swing.JPanel implements D exportToCSVMenuItem = new javax.swing.JMenuItem(); showCaseDetailsMenuItem = new javax.swing.JMenuItem(); showCommonalityMenuItem = new javax.swing.JMenuItem(); + addCommentMenuItem = new javax.swing.JMenuItem(); CSVFileChooser = new javax.swing.JFileChooser(); otherCasesPanel = new javax.swing.JPanel(); tableContainerPanel = new javax.swing.JPanel(); @@ -652,6 +704,16 @@ public class DataContentViewerOtherCases extends javax.swing.JPanel implements D tableStatusPanel = new javax.swing.JPanel(); tableStatusPanelLabel = new javax.swing.JLabel(); + rightClickPopupMenu.addPopupMenuListener(new javax.swing.event.PopupMenuListener() { + public void popupMenuCanceled(javax.swing.event.PopupMenuEvent evt) { + } + public void popupMenuWillBecomeInvisible(javax.swing.event.PopupMenuEvent evt) { + } + public void popupMenuWillBecomeVisible(javax.swing.event.PopupMenuEvent evt) { + rightClickPopupMenuPopupMenuWillBecomeVisible(evt); + } + }); + org.openide.awt.Mnemonics.setLocalizedText(selectAllMenuItem, org.openide.util.NbBundle.getMessage(DataContentViewerOtherCases.class, "DataContentViewerOtherCases.selectAllMenuItem.text")); // NOI18N rightClickPopupMenu.add(selectAllMenuItem); @@ -664,6 +726,9 @@ public class DataContentViewerOtherCases extends javax.swing.JPanel implements D org.openide.awt.Mnemonics.setLocalizedText(showCommonalityMenuItem, org.openide.util.NbBundle.getMessage(DataContentViewerOtherCases.class, "DataContentViewerOtherCases.showCommonalityMenuItem.text")); // NOI18N rightClickPopupMenu.add(showCommonalityMenuItem); + org.openide.awt.Mnemonics.setLocalizedText(addCommentMenuItem, org.openide.util.NbBundle.getMessage(DataContentViewerOtherCases.class, "DataContentViewerOtherCases.addCommentMenuItem.text")); // NOI18N + rightClickPopupMenu.add(addCommentMenuItem); + setMinimumSize(new java.awt.Dimension(1500, 10)); setOpaque(false); setPreferredSize(new java.awt.Dimension(1500, 44)); @@ -734,7 +799,7 @@ public class DataContentViewerOtherCases extends javax.swing.JPanel implements D .addGap(0, 60, Short.MAX_VALUE) .addGroup(otherCasesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(otherCasesPanelLayout.createSequentialGroup() - .addComponent(tableContainerPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 60, Short.MAX_VALUE) + .addComponent(tableContainerPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 52, Short.MAX_VALUE) .addGap(0, 0, 0))) ); @@ -746,13 +811,28 @@ public class DataContentViewerOtherCases extends javax.swing.JPanel implements D ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(otherCasesPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 60, Short.MAX_VALUE) + .addComponent(otherCasesPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 52, Short.MAX_VALUE) ); }// //GEN-END:initComponents + private void rightClickPopupMenuPopupMenuWillBecomeVisible(javax.swing.event.PopupMenuEvent evt) {//GEN-FIRST:event_rightClickPopupMenuPopupMenuWillBecomeVisible + boolean addCommentMenuItemVisible = false; + + if (EamDbUtil.useCentralRepo() && otherCasesTable.getSelectedRowCount() == 1) { + int rowIndex = otherCasesTable.getSelectedRow(); + CorrelationAttribute selectedAttribute = (CorrelationAttribute) tableModel.getRow(rowIndex); + if (selectedAttribute.getInstances().get(0).isDatabaseInstance() + && selectedAttribute.getCorrelationType().getId() == CorrelationAttribute.FILES_TYPE_ID) { + addCommentMenuItemVisible = true; + } + } + + addCommentMenuItem.setVisible(addCommentMenuItemVisible); + }//GEN-LAST:event_rightClickPopupMenuPopupMenuWillBecomeVisible // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JFileChooser CSVFileChooser; + private javax.swing.JMenuItem addCommentMenuItem; private javax.swing.JMenuItem exportToCSVMenuItem; private javax.swing.JPanel otherCasesPanel; private javax.swing.JTable otherCasesTable; @@ -766,4 +846,52 @@ public class DataContentViewerOtherCases extends javax.swing.JPanel implements D private javax.swing.JLabel tableStatusPanelLabel; // End of variables declaration//GEN-END:variables + /** + * Used as a key to ensure we eliminate duplicates from the result set by + * not overwriting CR correlation instances. + */ + static final class UniquePathKey { + + private final String dataSourceID; + private final String filePath; + + UniquePathKey(String theDataSource, String theFilePath) { + super(); + dataSourceID = theDataSource; + filePath = theFilePath.toLowerCase(); + } + + /** + * + * @return the dataSourceID device ID + */ + String getDataSourceID() { + return dataSourceID; + } + + /** + * + * @return the filPath including the filename and extension. + */ + String getFilePath() { + return filePath; + } + + @Override + public boolean equals(Object other) { + if (other instanceof UniquePathKey) { + return ((UniquePathKey) other).getDataSourceID().equals(dataSourceID) && ((UniquePathKey) other).getFilePath().equals(filePath); + } + return false; + } + + @Override + public int hashCode() { + //int hash = 7; + //hash = 67 * hash + this.dataSourceID.hashCode(); + //hash = 67 * hash + this.filePath.hashCode(); + return Objects.hash(dataSourceID, filePath); + } + } + } diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java index 60cbb519e6..45bc197283 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java @@ -1,7 +1,7 @@ /* * Central Repository * - * Copyright 2015-2017 Basis Technology Corp. + * Copyright 2015-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -35,7 +35,6 @@ import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.logging.Level; -import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; import static org.sleuthkit.autopsy.centralrepository.datamodel.EamDbUtil.updateSchemaVersion; import org.sleuthkit.autopsy.coreutils.Logger; @@ -46,19 +45,23 @@ import org.sleuthkit.datamodel.TskData; /** * - * SQLite manager implementation + * Generic JDBC methods * */ -public abstract class AbstractSqlEamDb implements EamDb { +abstract class AbstractSqlEamDb implements EamDb { - private final static Logger LOGGER = Logger.getLogger(AbstractSqlEamDb.class.getName()); + private final static Logger logger = Logger.getLogger(AbstractSqlEamDb.class.getName()); - protected final List DEFAULT_CORRELATION_TYPES; + protected final List defaultCorrelationTypes; private int bulkArtifactsCount; protected int bulkArtifactsThreshold; private final Map> bulkArtifacts; + // number of instances to keep in bulk queue before doing an insert. + // Update Test code if this changes. It's hard coded there. + static final int DEFAULT_BULK_THRESHHOLD = 1000; + /** * Connect to the DB and initialize it. * @@ -68,8 +71,8 @@ public abstract class AbstractSqlEamDb implements EamDb { bulkArtifactsCount = 0; bulkArtifacts = new HashMap<>(); - DEFAULT_CORRELATION_TYPES = CorrelationAttribute.getDefaultCorrelationTypes(); - DEFAULT_CORRELATION_TYPES.forEach((type) -> { + defaultCorrelationTypes = CorrelationAttribute.getDefaultCorrelationTypes(); + defaultCorrelationTypes.forEach((type) -> { bulkArtifacts.put(type.getDbTableName(), new ArrayList<>()); }); } @@ -82,7 +85,7 @@ public abstract class AbstractSqlEamDb implements EamDb { /** * Add a new name/value pair in the db_info table. * - * @param name Key to set + * @param name Key to set * @param value Value to set * * @throws EamDbException @@ -102,7 +105,7 @@ public abstract class AbstractSqlEamDb implements EamDb { } catch (SQLException ex) { throw new EamDbException("Error adding new name/value pair to db_info.", ex); } finally { - EamDbUtil.closePreparedStatement(preparedStatement); + EamDbUtil.closeStatement(preparedStatement); EamDbUtil.closeConnection(conn); } @@ -135,7 +138,7 @@ public abstract class AbstractSqlEamDb implements EamDb { } catch (SQLException ex) { throw new EamDbException("Error getting value for name.", ex); } finally { - EamDbUtil.closePreparedStatement(preparedStatement); + EamDbUtil.closeStatement(preparedStatement); EamDbUtil.closeResultSet(resultSet); EamDbUtil.closeConnection(conn); } @@ -146,7 +149,7 @@ public abstract class AbstractSqlEamDb implements EamDb { /** * Update the value for a name in the name/value db_info table. * - * @param name Name to find + * @param name Name to find * @param value Value to assign to name. * * @throws EamDbException @@ -165,7 +168,7 @@ public abstract class AbstractSqlEamDb implements EamDb { } catch (SQLException ex) { throw new EamDbException("Error updating value for name.", ex); } finally { - EamDbUtil.closePreparedStatement(preparedStatement); + EamDbUtil.closeStatement(preparedStatement); EamDbUtil.closeConnection(conn); } } @@ -176,11 +179,12 @@ public abstract class AbstractSqlEamDb implements EamDb { * Expects the Organization for this case to already exist in the database. * * @param eamCase The case to add + * * @returns New Case class with populated database ID */ @Override public synchronized CorrelationCase newCase(CorrelationCase eamCase) throws EamDbException { - + // check if there is already an existing CorrelationCase for this Case CorrelationCase cRCase = getCaseByUUID(eamCase.getCaseUUID()); if (cRCase != null) { @@ -236,7 +240,7 @@ public abstract class AbstractSqlEamDb implements EamDb { } catch (SQLException ex) { throw new EamDbException("Error inserting new case.", ex); // NON-NLS } finally { - EamDbUtil.closePreparedStatement(preparedStatement); + EamDbUtil.closeStatement(preparedStatement); EamDbUtil.closeConnection(conn); } @@ -281,10 +285,10 @@ public abstract class AbstractSqlEamDb implements EamDb { */ @Override public void updateCase(CorrelationCase eamCase) throws EamDbException { - if(eamCase == null) { - throw new EamDbException("CorrelationCase argument is null"); + if (eamCase == null) { + throw new EamDbException("Correlation case is null"); } - + Connection conn = connect(); PreparedStatement preparedStatement = null; @@ -335,7 +339,7 @@ public abstract class AbstractSqlEamDb implements EamDb { } catch (SQLException ex) { throw new EamDbException("Error updating case.", ex); // NON-NLS } finally { - EamDbUtil.closePreparedStatement(preparedStatement); + EamDbUtil.closeStatement(preparedStatement); EamDbUtil.closeConnection(conn); } } @@ -373,7 +377,7 @@ public abstract class AbstractSqlEamDb implements EamDb { } catch (SQLException ex) { throw new EamDbException("Error getting case details.", ex); // NON-NLS } finally { - EamDbUtil.closePreparedStatement(preparedStatement); + EamDbUtil.closeStatement(preparedStatement); EamDbUtil.closeResultSet(resultSet); EamDbUtil.closeConnection(conn); } @@ -410,7 +414,7 @@ public abstract class AbstractSqlEamDb implements EamDb { } catch (SQLException ex) { throw new EamDbException("Error getting all cases.", ex); // NON-NLS } finally { - EamDbUtil.closePreparedStatement(preparedStatement); + EamDbUtil.closeStatement(preparedStatement); EamDbUtil.closeResultSet(resultSet); EamDbUtil.closeConnection(conn); } @@ -425,6 +429,11 @@ public abstract class AbstractSqlEamDb implements EamDb { */ @Override public void newDataSource(CorrelationDataSource eamDataSource) throws EamDbException { + if (eamDataSource.getCaseID() == -1) { + throw new EamDbException("Case ID is -1"); + } else if (eamDataSource.getID() != -1) { + throw new EamDbException("Database ID is already set in object"); + } Connection conn = connect(); PreparedStatement preparedStatement = null; @@ -443,7 +452,7 @@ public abstract class AbstractSqlEamDb implements EamDb { } catch (SQLException ex) { throw new EamDbException("Error inserting new data source.", ex); // NON-NLS } finally { - EamDbUtil.closePreparedStatement(preparedStatement); + EamDbUtil.closeStatement(preparedStatement); EamDbUtil.closeConnection(conn); } } @@ -451,18 +460,18 @@ public abstract class AbstractSqlEamDb implements EamDb { /** * Retrieves Data Source details based on data source device ID * - * @param correlationCase the current CorrelationCase used for ensuring - * uniqueness of DataSource + * @param correlationCase the current CorrelationCase used for ensuring + * uniqueness of DataSource * @param dataSourceDeviceId the data source device ID number * * @return The data source */ @Override public CorrelationDataSource getDataSource(CorrelationCase correlationCase, String dataSourceDeviceId) throws EamDbException { - if(correlationCase == null) { - throw new EamDbException("CorrelationCase argument is null"); + if (correlationCase == null) { + throw new EamDbException("Correlation case is null"); } - + Connection conn = connect(); CorrelationDataSource eamDataSourceResult = null; @@ -482,7 +491,7 @@ public abstract class AbstractSqlEamDb implements EamDb { } catch (SQLException ex) { throw new EamDbException("Error getting data source.", ex); // NON-NLS } finally { - EamDbUtil.closePreparedStatement(preparedStatement); + EamDbUtil.closeStatement(preparedStatement); EamDbUtil.closeResultSet(resultSet); EamDbUtil.closeConnection(conn); } @@ -516,7 +525,7 @@ public abstract class AbstractSqlEamDb implements EamDb { } catch (SQLException ex) { throw new EamDbException("Error getting all data sources.", ex); // NON-NLS } finally { - EamDbUtil.closePreparedStatement(preparedStatement); + EamDbUtil.closeStatement(preparedStatement); EamDbUtil.closeResultSet(resultSet); EamDbUtil.closeConnection(conn); } @@ -532,16 +541,16 @@ public abstract class AbstractSqlEamDb implements EamDb { */ @Override public void addArtifact(CorrelationAttribute eamArtifact) throws EamDbException { - if(eamArtifact == null) { + if (eamArtifact == null) { throw new EamDbException("CorrelationAttribute is null"); } - if(eamArtifact.getCorrelationType() == null) { + if (eamArtifact.getCorrelationType() == null) { throw new EamDbException("Correlation type is null"); } - if(eamArtifact.getCorrelationValue() == null) { + if (eamArtifact.getCorrelationValue() == null) { throw new EamDbException("Correlation value is null"); } - + Connection conn = connect(); List eamInstances = eamArtifact.getInstances(); @@ -549,28 +558,28 @@ public abstract class AbstractSqlEamDb implements EamDb { // @@@ We should cache the case and data source IDs in memory String tableName = EamDbUtil.correlationTypeToInstanceTableName(eamArtifact.getCorrelationType()); - StringBuilder sql = new StringBuilder(); - sql.append("INSERT INTO "); - sql.append(tableName); - sql.append("(case_id, data_source_id, value, file_path, known_status, comment) "); - sql.append("VALUES ((SELECT id FROM cases WHERE case_uid=? LIMIT 1), "); - sql.append("(SELECT id FROM data_sources WHERE device_id=? AND case_id=? LIMIT 1), ?, ?, ?, ?) "); - sql.append(getConflictClause()); - + String sql + = "INSERT INTO " + + tableName + + "(case_id, data_source_id, value, file_path, known_status, comment) " + + "VALUES ((SELECT id FROM cases WHERE case_uid=? LIMIT 1), " + + "(SELECT id FROM data_sources WHERE device_id=? AND case_id=? LIMIT 1), ?, ?, ?, ?) " + + getConflictClause(); + try { - preparedStatement = conn.prepareStatement(sql.toString()); + preparedStatement = conn.prepareStatement(sql); for (CorrelationAttributeInstance eamInstance : eamInstances) { if (!eamArtifact.getCorrelationValue().isEmpty()) { - if(eamInstance.getCorrelationCase() == null) { - throw new EamDbException("CorrelationAttributeInstance has null case"); + if (eamInstance.getCorrelationCase() == null) { + throw new EamDbException("CorrelationAttributeInstance case is null"); } - if(eamInstance.getCorrelationDataSource() == null) { - throw new EamDbException("CorrelationAttributeInstance has null data source"); + if (eamInstance.getCorrelationDataSource() == null) { + throw new EamDbException("CorrelationAttributeInstance data source is null"); } - if(eamInstance.getKnownStatus() == null) { - throw new EamDbException("CorrelationAttributeInstance has null known status"); + if (eamInstance.getKnownStatus() == null) { + throw new EamDbException("CorrelationAttributeInstance known status is null"); } - + preparedStatement.setString(1, eamInstance.getCorrelationCase().getCaseUUID()); preparedStatement.setString(2, eamInstance.getCorrelationDataSource().getDeviceID()); preparedStatement.setInt(3, eamInstance.getCorrelationDataSource().getCaseID()); @@ -589,7 +598,7 @@ public abstract class AbstractSqlEamDb implements EamDb { } catch (SQLException ex) { throw new EamDbException("Error inserting new artifact into artifacts table.", ex); // NON-NLS } finally { - EamDbUtil.closePreparedStatement(preparedStatement); + EamDbUtil.closeStatement(preparedStatement); EamDbUtil.closeConnection(conn); } } @@ -607,7 +616,7 @@ public abstract class AbstractSqlEamDb implements EamDb { */ @Override public List getArtifactInstancesByTypeValue(CorrelationAttribute.Type aType, String value) throws EamDbException { - if(aType == null) { + if (aType == null) { throw new EamDbException("Correlation type is null"); } Connection conn = connect(); @@ -619,19 +628,21 @@ public abstract class AbstractSqlEamDb implements EamDb { ResultSet resultSet = null; String tableName = EamDbUtil.correlationTypeToInstanceTableName(aType); - StringBuilder sql = new StringBuilder(); - sql.append("SELECT cases.case_name, cases.case_uid, data_sources.name, device_id, file_path, known_status, comment, data_sources.case_id FROM "); - sql.append(tableName); - sql.append(" LEFT JOIN cases ON "); - sql.append(tableName); - sql.append(".case_id=cases.id"); - sql.append(" LEFT JOIN data_sources ON "); - sql.append(tableName); - sql.append(".data_source_id=data_sources.id"); - sql.append(" WHERE value=?"); + String sql + = "SELECT " + + tableName + + ".id, cases.case_name, cases.case_uid, data_sources.id AS data_source_id, data_sources.name, device_id, file_path, known_status, comment, data_sources.case_id FROM " + + tableName + + " LEFT JOIN cases ON " + + tableName + + ".case_id=cases.id" + + " LEFT JOIN data_sources ON " + + tableName + + ".data_source_id=data_sources.id" + + " WHERE value=?"; try { - preparedStatement = conn.prepareStatement(sql.toString()); + preparedStatement = conn.prepareStatement(sql); preparedStatement.setString(1, value); resultSet = preparedStatement.executeQuery(); while (resultSet.next()) { @@ -641,7 +652,7 @@ public abstract class AbstractSqlEamDb implements EamDb { } catch (SQLException ex) { throw new EamDbException("Error getting artifact instances by artifactType and artifactValue.", ex); // NON-NLS } finally { - EamDbUtil.closePreparedStatement(preparedStatement); + EamDbUtil.closeStatement(preparedStatement); EamDbUtil.closeResultSet(resultSet); EamDbUtil.closeConnection(conn); } @@ -653,7 +664,7 @@ public abstract class AbstractSqlEamDb implements EamDb { * Retrieves eamArtifact instances from the database that are associated * with the aType and filePath * - * @param aType EamArtifact.Type to search for + * @param aType EamArtifact.Type to search for * @param filePath File path to search for * * @return List of 0 or more EamArtifactInstances @@ -662,10 +673,10 @@ public abstract class AbstractSqlEamDb implements EamDb { */ @Override public List getArtifactInstancesByPath(CorrelationAttribute.Type aType, String filePath) throws EamDbException { - if(aType == null) { + if (aType == null) { throw new EamDbException("Correlation type is null"); } - if(filePath == null) { + if (filePath == null) { throw new EamDbException("Correlation value is null"); } Connection conn = connect(); @@ -677,19 +688,21 @@ public abstract class AbstractSqlEamDb implements EamDb { ResultSet resultSet = null; String tableName = EamDbUtil.correlationTypeToInstanceTableName(aType); - StringBuilder sql = new StringBuilder(); - sql.append("SELECT cases.case_name, cases.case_uid, data_sources.name, device_id, file_path, known_status, comment, data_sources.case_id FROM "); - sql.append(tableName); - sql.append(" LEFT JOIN cases ON "); - sql.append(tableName); - sql.append(".case_id=cases.id"); - sql.append(" LEFT JOIN data_sources ON "); - sql.append(tableName); - sql.append(".data_source_id=data_sources.id"); - sql.append(" WHERE file_path=?"); + String sql + = "SELECT " + + tableName + + ".id, cases.case_name, cases.case_uid, data_sources.id AS data_source_id, data_sources.name, device_id, file_path, known_status, comment, data_sources.case_id FROM " + + tableName + + " LEFT JOIN cases ON " + + tableName + + ".case_id=cases.id" + + " LEFT JOIN data_sources ON " + + tableName + + ".data_source_id=data_sources.id" + + " WHERE file_path=?"; try { - preparedStatement = conn.prepareStatement(sql.toString()); + preparedStatement = conn.prepareStatement(sql); preparedStatement.setString(1, filePath.toLowerCase()); resultSet = preparedStatement.executeQuery(); while (resultSet.next()) { @@ -699,7 +712,7 @@ public abstract class AbstractSqlEamDb implements EamDb { } catch (SQLException ex) { throw new EamDbException("Error getting artifact instances by artifactType and artifactValue.", ex); // NON-NLS } finally { - EamDbUtil.closePreparedStatement(preparedStatement); + EamDbUtil.closeStatement(preparedStatement); EamDbUtil.closeResultSet(resultSet); EamDbUtil.closeConnection(conn); } @@ -715,17 +728,17 @@ public abstract class AbstractSqlEamDb implements EamDb { * @param value The correlation value * * @return Number of artifact instances having ArtifactType and - * ArtifactValue. + * ArtifactValue. */ @Override public Long getCountArtifactInstancesByTypeValue(CorrelationAttribute.Type aType, String value) throws EamDbException { - if(aType == null) { + if (aType == null) { throw new EamDbException("Correlation type is null"); } - if(value == null) { + if (value == null) { throw new EamDbException("Correlation value is null"); } - + Connection conn = connect(); Long instanceCount = 0L; @@ -733,13 +746,13 @@ public abstract class AbstractSqlEamDb implements EamDb { ResultSet resultSet = null; String tableName = EamDbUtil.correlationTypeToInstanceTableName(aType); - StringBuilder sql = new StringBuilder(); - sql.append("SELECT count(*) FROM "); - sql.append(tableName); - sql.append(" WHERE value=?"); + String sql + = "SELECT count(*) FROM " + + tableName + + " WHERE value=?"; try { - preparedStatement = conn.prepareStatement(sql.toString()); + preparedStatement = conn.prepareStatement(sql); preparedStatement.setString(1, value.toLowerCase()); resultSet = preparedStatement.executeQuery(); resultSet.next(); @@ -747,7 +760,7 @@ public abstract class AbstractSqlEamDb implements EamDb { } catch (SQLException ex) { throw new EamDbException("Error getting count of artifact instances by artifactType and artifactValue.", ex); // NON-NLS } finally { - EamDbUtil.closePreparedStatement(preparedStatement); + EamDbUtil.closeStatement(preparedStatement); EamDbUtil.closeResultSet(resultSet); EamDbUtil.closeConnection(conn); } @@ -758,7 +771,7 @@ public abstract class AbstractSqlEamDb implements EamDb { @Override public int getFrequencyPercentage(CorrelationAttribute corAttr) throws EamDbException { if (corAttr == null) { - throw new EamDbException("Correlation attribute is null"); + throw new EamDbException("CorrelationAttribute is null"); } Double uniqueTypeValueTuples = getCountUniqueCaseDataSourceTuplesHavingTypeValue(corAttr.getCorrelationType(), corAttr.getCorrelationValue()).doubleValue(); Double uniqueCaseDataSourceTuples = getCountUniqueDataSources().doubleValue(); @@ -778,10 +791,10 @@ public abstract class AbstractSqlEamDb implements EamDb { */ @Override public Long getCountUniqueCaseDataSourceTuplesHavingTypeValue(CorrelationAttribute.Type aType, String value) throws EamDbException { - if(aType == null) { + if (aType == null) { throw new EamDbException("Correlation type is null"); } - + Connection conn = connect(); Long instanceCount = 0L; @@ -789,15 +802,15 @@ public abstract class AbstractSqlEamDb implements EamDb { ResultSet resultSet = null; String tableName = EamDbUtil.correlationTypeToInstanceTableName(aType); - StringBuilder sql = new StringBuilder(); - sql.append("SELECT count(*) FROM (SELECT DISTINCT case_id, data_source_id FROM "); - sql.append(tableName); - sql.append(" WHERE value=?) AS "); - sql.append(tableName); - sql.append("_distinct_case_data_source_tuple"); + String sql + = "SELECT count(*) FROM (SELECT DISTINCT case_id, data_source_id FROM " + + tableName + + " WHERE value=?) AS " + + tableName + + "_distinct_case_data_source_tuple"; try { - preparedStatement = conn.prepareStatement(sql.toString()); + preparedStatement = conn.prepareStatement(sql); preparedStatement.setString(1, value); resultSet = preparedStatement.executeQuery(); resultSet.next(); @@ -805,7 +818,7 @@ public abstract class AbstractSqlEamDb implements EamDb { } catch (SQLException ex) { throw new EamDbException("Error counting unique caseDisplayName/dataSource tuples having artifactType and artifactValue.", ex); // NON-NLS } finally { - EamDbUtil.closePreparedStatement(preparedStatement); + EamDbUtil.closeStatement(preparedStatement); EamDbUtil.closeResultSet(resultSet); EamDbUtil.closeConnection(conn); } @@ -831,7 +844,7 @@ public abstract class AbstractSqlEamDb implements EamDb { } catch (SQLException ex) { throw new EamDbException("Error counting data sources.", ex); // NON-NLS } finally { - EamDbUtil.closePreparedStatement(preparedStatement); + EamDbUtil.closeStatement(preparedStatement); EamDbUtil.closeResultSet(resultSet); EamDbUtil.closeConnection(conn); } @@ -844,11 +857,11 @@ public abstract class AbstractSqlEamDb implements EamDb { * associated with the caseDisplayName and dataSource of the given * eamArtifact instance. * - * @param caseUUID Case ID to search for + * @param caseUUID Case ID to search for * @param dataSourceID Data source ID to search for * * @return Number of artifact instances having caseDisplayName and - * dataSource + * dataSource */ @Override public Long getCountArtifactInstancesByCaseDataSource(String caseUUID, String dataSourceID) throws EamDbException { @@ -860,19 +873,19 @@ public abstract class AbstractSqlEamDb implements EamDb { ResultSet resultSet = null; // Figure out sql variables or subqueries - StringBuilder sql = new StringBuilder(); - sql.append("SELECT 0 "); + String sql = "SELECT 0 "; for (CorrelationAttribute.Type type : artifactTypes) { String table_name = EamDbUtil.correlationTypeToInstanceTableName(type); - sql.append("+ (SELECT count(*) FROM "); - sql.append(table_name); - sql.append(" WHERE case_id=(SELECT id FROM cases WHERE case_uid=?) and data_source_id=(SELECT id FROM data_sources WHERE device_id=?))"); + sql + += "+ (SELECT count(*) FROM " + + table_name + + " WHERE case_id=(SELECT id FROM cases WHERE case_uid=?) and data_source_id=(SELECT id FROM data_sources WHERE device_id=?))"; } try { - preparedStatement = conn.prepareStatement(sql.toString()); + preparedStatement = conn.prepareStatement(sql); for (int i = 0; i < artifactTypes.size(); ++i) { preparedStatement.setString(2 * i + 1, caseUUID); @@ -885,7 +898,7 @@ public abstract class AbstractSqlEamDb implements EamDb { } catch (SQLException ex) { throw new EamDbException("Error counting artifact instances by caseName/dataSource.", ex); // NON-NLS } finally { - EamDbUtil.closePreparedStatement(preparedStatement); + EamDbUtil.closeStatement(preparedStatement); EamDbUtil.closeResultSet(resultSet); EamDbUtil.closeConnection(conn); } @@ -903,10 +916,10 @@ public abstract class AbstractSqlEamDb implements EamDb { @Override public void prepareBulkArtifact(CorrelationAttribute eamArtifact) throws EamDbException { - if(eamArtifact.getCorrelationType() == null) { + if (eamArtifact.getCorrelationType() == null) { throw new EamDbException("Correlation type is null"); } - + synchronized (bulkArtifacts) { bulkArtifacts.get(eamArtifact.getCorrelationType().getDbTableName()).add(eamArtifact); bulkArtifactsCount++; @@ -940,21 +953,19 @@ public abstract class AbstractSqlEamDb implements EamDb { if (bulkArtifactsCount == 0) { return; } - - TimingMetric timingMetric = EnterpriseHealthMonitor.getTimingMetric("Correlation Engine: Bulk insert"); for (CorrelationAttribute.Type type : artifactTypes) { String tableName = EamDbUtil.correlationTypeToInstanceTableName(type); - StringBuilder sql = new StringBuilder(); - sql.append("INSERT INTO "); - sql.append(tableName); - sql.append(" (case_id, data_source_id, value, file_path, known_status, comment) "); - sql.append("VALUES ((SELECT id FROM cases WHERE case_uid=? LIMIT 1), "); - sql.append("(SELECT id FROM data_sources WHERE device_id=? AND case_id=? LIMIT 1), ?, ?, ?, ?) "); - sql.append(getConflictClause()); + String sql + = "INSERT INTO " + + tableName + + " (case_id, data_source_id, value, file_path, known_status, comment) " + + "VALUES ((SELECT id FROM cases WHERE case_uid=? LIMIT 1), " + + "(SELECT id FROM data_sources WHERE device_id=? AND case_id=? LIMIT 1), ?, ?, ?, ?) " + + getConflictClause(); - bulkPs = conn.prepareStatement(sql.toString()); + bulkPs = conn.prepareStatement(sql); Collection eamArtifacts = bulkArtifacts.get(type.getDbTableName()); for (CorrelationAttribute eamArtifact : eamArtifacts) { @@ -962,17 +973,17 @@ public abstract class AbstractSqlEamDb implements EamDb { for (CorrelationAttributeInstance eamInstance : eamInstances) { if (!eamArtifact.getCorrelationValue().isEmpty()) { - - if(eamInstance.getCorrelationCase() == null) { - throw new EamDbException("Correlation attribute instance has null case"); + + if (eamInstance.getCorrelationCase() == null) { + throw new EamDbException("CorrelationAttributeInstance case is null"); } - if(eamInstance.getCorrelationDataSource() == null) { - throw new EamDbException("Correlation attribute instance has null data source"); + if (eamInstance.getCorrelationDataSource() == null) { + throw new EamDbException("CorrelationAttributeInstance data source is null"); } - if(eamInstance.getKnownStatus()== null) { - throw new EamDbException("Correlation attribute instance has null known known status"); + if (eamInstance.getKnownStatus() == null) { + throw new EamDbException("CorrelationAttributeInstance known status is null"); } - + bulkPs.setString(1, eamInstance.getCorrelationCase().getCaseUUID()); bulkPs.setString(2, eamInstance.getCorrelationDataSource().getDeviceID()); bulkPs.setInt(3, eamInstance.getCorrelationDataSource().getCaseID()); @@ -992,7 +1003,8 @@ public abstract class AbstractSqlEamDb implements EamDb { bulkPs.executeBatch(); bulkArtifacts.get(type.getDbTableName()).clear(); } - + + TimingMetric timingMetric = EnterpriseHealthMonitor.getTimingMetric("Correlation Engine: Bulk insert"); EnterpriseHealthMonitor.submitTimingMetric(timingMetric); // Reset state @@ -1001,7 +1013,7 @@ public abstract class AbstractSqlEamDb implements EamDb { } catch (SQLException ex) { throw new EamDbException("Error inserting bulk artifacts.", ex); // NON-NLS } finally { - EamDbUtil.closePreparedStatement(bulkPs); + EamDbUtil.closeStatement(bulkPs); EamDbUtil.closeConnection(conn); } } @@ -1011,16 +1023,16 @@ public abstract class AbstractSqlEamDb implements EamDb { */ @Override public void bulkInsertCases(List cases) throws EamDbException { - if(cases == null) { + if (cases == null) { throw new EamDbException("cases argument is null"); } - + if (cases.isEmpty()) { return; } Connection conn = connect(); - + int counter = 0; PreparedStatement bulkPs = null; try { @@ -1081,44 +1093,177 @@ public abstract class AbstractSqlEamDb implements EamDb { } catch (SQLException ex) { throw new EamDbException("Error inserting bulk cases.", ex); // NON-NLS } finally { - EamDbUtil.closePreparedStatement(bulkPs); + EamDbUtil.closeStatement(bulkPs); EamDbUtil.closeConnection(conn); } } /** - * Sets an eamArtifact instance to the given knownStatus. - * knownStatus should be BAD if the file has been tagged with a notable tag and - * UNKNOWN otherwise. If eamArtifact - * exists, it is updated. If eamArtifact does not exist it is added with the - * given status. + * Update a correlation attribute instance in the database with that in the + * associated CorrelationAttribute object. + * + * @param eamArtifact The correlation attribute whose database instance will + * be updated. + * + * @throws EamDbException + */ + @Override + public void updateAttributeInstanceComment(CorrelationAttribute eamArtifact) throws EamDbException { + if (eamArtifact == null) { + throw new EamDbException("CorrelationAttribute is null"); + } + + CorrelationAttributeInstance eamInstance = eamArtifact.getInstances().get(0); + + if (eamInstance == null) { + throw new EamDbException("CorrelationAttributeInstance is null"); + } + if (eamInstance.getCorrelationCase() == null) { + throw new EamDbException("Correlation case is null"); + } + if (eamInstance.getCorrelationDataSource() == null) { + throw new EamDbException("Correlation data source is null"); + } + + Connection conn = connect(); + PreparedStatement preparedQuery = null; + String tableName = EamDbUtil.correlationTypeToInstanceTableName(eamArtifact.getCorrelationType()); + + String sqlUpdate + = "UPDATE " + + tableName + + " SET comment=? " + + "WHERE case_id=(SELECT id FROM cases WHERE case_uid=?) " + + "AND data_source_id=(SELECT id FROM data_sources WHERE device_id=?) " + + "AND value=? " + + "AND file_path=?"; + + try { + preparedQuery = conn.prepareStatement(sqlUpdate); + preparedQuery.setString(1, eamInstance.getComment()); + preparedQuery.setString(2, eamInstance.getCorrelationCase().getCaseUUID()); + preparedQuery.setString(3, eamInstance.getCorrelationDataSource().getDeviceID()); + preparedQuery.setString(4, eamArtifact.getCorrelationValue()); + preparedQuery.setString(5, eamInstance.getFilePath()); + preparedQuery.executeUpdate(); + } catch (SQLException ex) { + throw new EamDbException("Error getting/setting artifact instance comment=" + eamInstance.getComment(), ex); // NON-NLS + } finally { + EamDbUtil.closeStatement(preparedQuery); + EamDbUtil.closeConnection(conn); + } + } + + /** + * Find a correlation attribute in the Central Repository database given the + * instance type, case, data source, value, and file path. + * + * @param type The type of instance. + * @param correlationCase The case tied to the instance. + * @param correlationDataSource The data source tied to the instance. + * @param value The value tied to the instance. + * @param filePath The file path tied to the instance. + * + * @return The correlation attribute if it exists; otherwise null. + * + * @throws EamDbException + */ + @Override + public CorrelationAttribute getCorrelationAttribute(CorrelationAttribute.Type type, CorrelationCase correlationCase, + CorrelationDataSource correlationDataSource, String value, String filePath) throws EamDbException { + + if (type == null) { + throw new EamDbException("Correlation type is null"); + } + if (correlationCase == null) { + throw new EamDbException("Correlation case is null"); + } + if (correlationDataSource == null) { + throw new EamDbException("Correlation data source is null"); + } + if (value == null) { + throw new EamDbException("Correlation value is null"); + } + if (filePath == null) { + throw new EamDbException("Correlation file path is null"); + } + + Connection conn = connect(); + + PreparedStatement preparedStatement = null; + ResultSet resultSet = null; + CorrelationAttribute correlationAttribute = null; + + try { + String tableName = EamDbUtil.correlationTypeToInstanceTableName(type); + String sql + = "SELECT id, known_status, comment FROM " + + tableName + + " WHERE case_id=?" + + " AND data_source_id=?" + + " AND value=?" + + " AND file_path=?"; + + preparedStatement = conn.prepareStatement(sql); + preparedStatement.setInt(1, correlationCase.getID()); + preparedStatement.setInt(2, correlationDataSource.getID()); + preparedStatement.setString(3, value.toLowerCase()); + preparedStatement.setString(4, filePath.toLowerCase()); + resultSet = preparedStatement.executeQuery(); + if (resultSet.next()) { + int instanceId = resultSet.getInt(1); + int knownStatus = resultSet.getInt(2); + String comment = resultSet.getString(3); + + correlationAttribute = new CorrelationAttribute(type, value); + CorrelationAttributeInstance artifactInstance = new CorrelationAttributeInstance( + instanceId, correlationCase, correlationDataSource, filePath, comment, TskData.FileKnown.valueOf((byte) knownStatus)); + correlationAttribute.addInstance(artifactInstance); + } + } catch (SQLException ex) { + throw new EamDbException("Error getting notable artifact instances.", ex); // NON-NLS + } finally { + EamDbUtil.closeStatement(preparedStatement); + EamDbUtil.closeResultSet(resultSet); + EamDbUtil.closeConnection(conn); + } + + return correlationAttribute; + } + + /** + * Sets an eamArtifact instance to the given knownStatus. knownStatus should + * be BAD if the file has been tagged with a notable tag and UNKNOWN + * otherwise. If eamArtifact exists, it is updated. If eamArtifact does not + * exist it is added with the given status. * * @param eamArtifact Artifact containing exactly one (1) ArtifactInstance. - * @param knownStatus The status to change the artifact to. Should never be KNOWN + * @param knownStatus The status to change the artifact to. Should never be + * KNOWN */ @Override public void setArtifactInstanceKnownStatus(CorrelationAttribute eamArtifact, TskData.FileKnown knownStatus) throws EamDbException { - if(eamArtifact == null) { - throw new EamDbException("Correlation attribute is null"); + if (eamArtifact == null) { + throw new EamDbException("CorrelationAttribute is null"); } - if(knownStatus == null) { + if (knownStatus == null) { throw new EamDbException("Known status is null"); } if (1 != eamArtifact.getInstances().size()) { throw new EamDbException("Error: Artifact must have exactly one (1) Artifact Instance to set as notable."); // NON-NLS } - + List eamInstances = eamArtifact.getInstances(); CorrelationAttributeInstance eamInstance = eamInstances.get(0); - if(eamInstance.getCorrelationCase() == null) { + if (eamInstance.getCorrelationCase() == null) { throw new EamDbException("Correlation case is null"); } - if(eamInstance.getCorrelationDataSource() == null) { + if (eamInstance.getCorrelationDataSource() == null) { throw new EamDbException("Correlation data source is null"); } - - Connection conn = connect(); + + Connection conn = connect(); PreparedStatement preparedUpdate = null; PreparedStatement preparedQuery = null; @@ -1126,22 +1271,22 @@ public abstract class AbstractSqlEamDb implements EamDb { String tableName = EamDbUtil.correlationTypeToInstanceTableName(eamArtifact.getCorrelationType()); - StringBuilder sqlQuery = new StringBuilder(); - sqlQuery.append("SELECT id FROM "); - sqlQuery.append(tableName); - sqlQuery.append(" WHERE case_id=(SELECT id FROM cases WHERE case_uid=?) "); - sqlQuery.append("AND data_source_id=(SELECT id FROM data_sources WHERE device_id=?) "); - sqlQuery.append("AND value=? "); - sqlQuery.append("AND file_path=?"); + String sqlQuery + = "SELECT id FROM " + + tableName + + " WHERE case_id=(SELECT id FROM cases WHERE case_uid=?) " + + "AND data_source_id=(SELECT id FROM data_sources WHERE device_id=?) " + + "AND value=? " + + "AND file_path=?"; - StringBuilder sqlUpdate = new StringBuilder(); - sqlUpdate.append("UPDATE "); - sqlUpdate.append(tableName); - sqlUpdate.append(" SET known_status=?, comment=? "); - sqlUpdate.append("WHERE id=?"); + String sqlUpdate + = "UPDATE " + + tableName + + " SET known_status=?, comment=? " + + "WHERE id=?"; try { - preparedQuery = conn.prepareStatement(sqlQuery.toString()); + preparedQuery = conn.prepareStatement(sqlQuery); preparedQuery.setString(1, eamInstance.getCorrelationCase().getCaseUUID()); preparedQuery.setString(2, eamInstance.getCorrelationDataSource().getDeviceID()); preparedQuery.setString(3, eamArtifact.getCorrelationValue()); @@ -1149,7 +1294,7 @@ public abstract class AbstractSqlEamDb implements EamDb { resultSet = preparedQuery.executeQuery(); if (resultSet.next()) { int instance_id = resultSet.getInt("id"); - preparedUpdate = conn.prepareStatement(sqlUpdate.toString()); + preparedUpdate = conn.prepareStatement(sqlUpdate); preparedUpdate.setByte(1, knownStatus.getFileKnownValue()); // NOTE: if the user tags the same instance as BAD multiple times, @@ -1184,8 +1329,8 @@ public abstract class AbstractSqlEamDb implements EamDb { } catch (SQLException ex) { throw new EamDbException("Error getting/setting artifact instance knownStatus=" + knownStatus.getName(), ex); // NON-NLS } finally { - EamDbUtil.closePreparedStatement(preparedUpdate); - EamDbUtil.closePreparedStatement(preparedQuery); + EamDbUtil.closeStatement(preparedUpdate); + EamDbUtil.closeStatement(preparedQuery); EamDbUtil.closeResultSet(resultSet); EamDbUtil.closeConnection(conn); } @@ -1202,10 +1347,10 @@ public abstract class AbstractSqlEamDb implements EamDb { */ @Override public List getArtifactInstancesKnownBad(CorrelationAttribute.Type aType, String value) throws EamDbException { - if(aType == null) { + if (aType == null) { throw new EamDbException("Correlation type is null"); } - + Connection conn = connect(); List artifactInstances = new ArrayList<>(); @@ -1215,19 +1360,21 @@ public abstract class AbstractSqlEamDb implements EamDb { ResultSet resultSet = null; String tableName = EamDbUtil.correlationTypeToInstanceTableName(aType); - StringBuilder sql = new StringBuilder(); - sql.append("SELECT cases.case_name, cases.case_uid, data_sources.name, device_id, file_path, known_status, comment, data_sources.case_id FROM "); - sql.append(tableName); - sql.append(" LEFT JOIN cases ON "); - sql.append(tableName); - sql.append(".case_id=cases.id"); - sql.append(" LEFT JOIN data_sources ON "); - sql.append(tableName); - sql.append(".data_source_id=data_sources.id"); - sql.append(" WHERE value=? AND known_status=?"); + String sql + = "SELECT " + + tableName + + ".id, cases.case_name, cases.case_uid, data_sources.id AS data_source_id, data_sources.name, device_id, file_path, known_status, comment, data_sources.case_id FROM " + + tableName + + " LEFT JOIN cases ON " + + tableName + + ".case_id=cases.id" + + " LEFT JOIN data_sources ON " + + tableName + + ".data_source_id=data_sources.id" + + " WHERE value=? AND known_status=?"; try { - preparedStatement = conn.prepareStatement(sql.toString()); + preparedStatement = conn.prepareStatement(sql); preparedStatement.setString(1, value); preparedStatement.setByte(2, TskData.FileKnown.BAD.getFileKnownValue()); resultSet = preparedStatement.executeQuery(); @@ -1238,7 +1385,66 @@ public abstract class AbstractSqlEamDb implements EamDb { } catch (SQLException ex) { throw new EamDbException("Error getting notable artifact instances.", ex); // NON-NLS } finally { - EamDbUtil.closePreparedStatement(preparedStatement); + EamDbUtil.closeStatement(preparedStatement); + EamDbUtil.closeResultSet(resultSet); + EamDbUtil.closeConnection(conn); + } + + return artifactInstances; + } + + /** + * + * Gets list of matching eamArtifact instances that have knownStatus = + * "Bad". + * + * @param aType EamArtifact.Type to search for + * + * @return List with 0 or more matching eamArtifact instances. + * + * @throws EamDbException + */ + @Override + public List getArtifactInstancesKnownBad(CorrelationAttribute.Type aType) throws EamDbException { + if (aType == null) { + throw new EamDbException("Correlation type is null"); + } + + Connection conn = connect(); + + List artifactInstances = new ArrayList<>(); + + CorrelationAttributeInstance artifactInstance; + PreparedStatement preparedStatement = null; + ResultSet resultSet = null; + + String tableName = EamDbUtil.correlationTypeToInstanceTableName(aType); + String sql + = "SELECT cases.case_name, cases.case_uid, data_sources.name, device_id, file_path, known_status, comment, data_sources.case_id FROM " + + tableName + + " LEFT JOIN cases ON " + + tableName + + ".case_id=cases.id" + + " LEFT JOIN data_sources ON " + + tableName + + ".data_source_id=data_sources.id" + + " WHERE known_status=?" + + " GROUP BY " + + tableName + + ".value"; + + try { + preparedStatement = conn.prepareStatement(sql); + preparedStatement.setByte(1, TskData.FileKnown.BAD.getFileKnownValue()); + resultSet = preparedStatement.executeQuery(); + while (resultSet.next()) { + artifactInstance = getEamArtifactInstanceFromResultSet(resultSet); + artifactInstances.add(artifactInstance); + } + } catch (SQLException ex) { + throw new EamDbException("Error getting notable artifact instances.", ex); // NON-NLS + } finally { + EamDbUtil.closeStatement(preparedStatement); EamDbUtil.closeResultSet(resultSet); EamDbUtil.closeConnection(conn); } @@ -1256,10 +1462,10 @@ public abstract class AbstractSqlEamDb implements EamDb { */ @Override public Long getCountArtifactInstancesKnownBad(CorrelationAttribute.Type aType, String value) throws EamDbException { - if(aType == null) { + if (aType == null) { throw new EamDbException("Correlation type is null"); } - + Connection conn = connect(); Long badInstances = 0L; @@ -1267,13 +1473,13 @@ public abstract class AbstractSqlEamDb implements EamDb { ResultSet resultSet = null; String tableName = EamDbUtil.correlationTypeToInstanceTableName(aType); - StringBuilder sql = new StringBuilder(); - sql.append("SELECT count(*) FROM "); - sql.append(tableName); - sql.append(" WHERE value=? AND known_status=?"); + String sql + = "SELECT count(*) FROM " + + tableName + + " WHERE value=? AND known_status=?"; try { - preparedStatement = conn.prepareStatement(sql.toString()); + preparedStatement = conn.prepareStatement(sql); preparedStatement.setString(1, value); preparedStatement.setByte(2, TskData.FileKnown.BAD.getFileKnownValue()); resultSet = preparedStatement.executeQuery(); @@ -1282,7 +1488,7 @@ public abstract class AbstractSqlEamDb implements EamDb { } catch (SQLException ex) { throw new EamDbException("Error getting count of notable artifact instances.", ex); // NON-NLS } finally { - EamDbUtil.closePreparedStatement(preparedStatement); + EamDbUtil.closeStatement(preparedStatement); EamDbUtil.closeResultSet(resultSet); EamDbUtil.closeConnection(conn); } @@ -1298,16 +1504,16 @@ public abstract class AbstractSqlEamDb implements EamDb { * @param value Value to search for * * @return List of cases containing this artifact with instances marked as - * bad + * bad * * @throws EamDbException */ @Override public List getListCasesHavingArtifactInstancesKnownBad(CorrelationAttribute.Type aType, String value) throws EamDbException { - if(aType == null) { + if (aType == null) { throw new EamDbException("Correlation type is null"); } - + Connection conn = connect(); Collection caseNames = new LinkedHashSet<>(); @@ -1316,19 +1522,19 @@ public abstract class AbstractSqlEamDb implements EamDb { ResultSet resultSet = null; String tableName = EamDbUtil.correlationTypeToInstanceTableName(aType); - StringBuilder sql = new StringBuilder(); - sql.append("SELECT DISTINCT case_name FROM "); - sql.append(tableName); - sql.append(" INNER JOIN cases ON "); - sql.append(tableName); - sql.append(".case_id=cases.id WHERE "); - sql.append(tableName); - sql.append(".value=? AND "); - sql.append(tableName); - sql.append(".known_status=?"); + String sql + = "SELECT DISTINCT case_name FROM " + + tableName + + " INNER JOIN cases ON " + + tableName + + ".case_id=cases.id WHERE " + + tableName + + ".value=? AND " + + tableName + + ".known_status=?"; try { - preparedStatement = conn.prepareStatement(sql.toString()); + preparedStatement = conn.prepareStatement(sql); preparedStatement.setString(1, value); preparedStatement.setByte(2, TskData.FileKnown.BAD.getFileKnownValue()); resultSet = preparedStatement.executeQuery(); @@ -1338,7 +1544,7 @@ public abstract class AbstractSqlEamDb implements EamDb { } catch (SQLException ex) { throw new EamDbException("Error getting notable artifact instances.", ex); // NON-NLS } finally { - EamDbUtil.closePreparedStatement(preparedStatement); + EamDbUtil.closeStatement(preparedStatement); EamDbUtil.closeResultSet(resultSet); EamDbUtil.closeConnection(conn); } @@ -1350,6 +1556,7 @@ public abstract class AbstractSqlEamDb implements EamDb { * Remove a reference set and all entries contained in it. * * @param referenceSetID + * * @throws EamDbException */ @Override @@ -1362,6 +1569,7 @@ public abstract class AbstractSqlEamDb implements EamDb { * Remove the entry for this set from the reference_sets table * * @param referenceSetID + * * @throws EamDbException */ private void deleteReferenceSetEntry(int referenceSetID) throws EamDbException { @@ -1377,7 +1585,7 @@ public abstract class AbstractSqlEamDb implements EamDb { } catch (SQLException ex) { throw new EamDbException("Error deleting reference set " + referenceSetID, ex); // NON-NLS } finally { - EamDbUtil.closePreparedStatement(preparedStatement); + EamDbUtil.closeStatement(preparedStatement); EamDbUtil.closeConnection(conn); } } @@ -1387,6 +1595,7 @@ public abstract class AbstractSqlEamDb implements EamDb { * (Currently only removes entries from the reference_file table) * * @param referenceSetID + * * @throws EamDbException */ private void deleteReferenceSetEntries(int referenceSetID) throws EamDbException { @@ -1405,7 +1614,7 @@ public abstract class AbstractSqlEamDb implements EamDb { } catch (SQLException ex) { throw new EamDbException("Error deleting files from reference set " + referenceSetID, ex); // NON-NLS } finally { - EamDbUtil.closePreparedStatement(preparedStatement); + EamDbUtil.closeStatement(preparedStatement); EamDbUtil.closeConnection(conn); } } @@ -1418,13 +1627,15 @@ public abstract class AbstractSqlEamDb implements EamDb { * @param referenceSetID * @param setName * @param version + * * @return true if a matching entry exists in the central repository + * * @throws EamDbException */ @Override public boolean referenceSetIsValid(int referenceSetID, String setName, String version) throws EamDbException { EamGlobalSet refSet = this.getReferenceSetByID(referenceSetID); - if(refSet == null) { + if (refSet == null) { return false; } @@ -1437,7 +1648,9 @@ public abstract class AbstractSqlEamDb implements EamDb { * * @param hash * @param referenceSetID + * * @return true if the hash is found in the reference set + * * @throws EamDbException */ @Override @@ -1451,6 +1664,7 @@ public abstract class AbstractSqlEamDb implements EamDb { * @param value * @param referenceSetID * @param correlationTypeID + * * @return true if the value is found in the reference set */ @Override @@ -1475,7 +1689,7 @@ public abstract class AbstractSqlEamDb implements EamDb { } catch (SQLException ex) { throw new EamDbException("Error determining if value (" + value + ") is in reference set " + referenceSetID, ex); // NON-NLS } finally { - EamDbUtil.closePreparedStatement(preparedStatement); + EamDbUtil.closeStatement(preparedStatement); EamDbUtil.closeResultSet(resultSet); EamDbUtil.closeConnection(conn); } @@ -1493,8 +1707,8 @@ public abstract class AbstractSqlEamDb implements EamDb { */ @Override public boolean isArtifactKnownBadByReference(CorrelationAttribute.Type aType, String value) throws EamDbException { - if(aType == null) { - throw new EamDbException("null correlation type"); + if (aType == null) { + throw new EamDbException("Correlation type is null"); } // TEMP: Only support file correlation type @@ -1519,29 +1733,59 @@ public abstract class AbstractSqlEamDb implements EamDb { } catch (SQLException ex) { throw new EamDbException("Error determining if artifact is notable by reference.", ex); // NON-NLS } finally { - EamDbUtil.closePreparedStatement(preparedStatement); + EamDbUtil.closeStatement(preparedStatement); EamDbUtil.closeResultSet(resultSet); EamDbUtil.closeConnection(conn); } return 0 < badInstances; } - /** - * Add a new organization - * - * @return the Organization ID of the newly created organization. - * - * @param eamOrg The organization to add + * Process the Artifact instance in the EamDb * + * @param type EamArtifact.Type to search for + * @param instanceTableCallback callback to process the instance * @throws EamDbException */ @Override - public long newOrganization(EamOrganization eamOrg) throws EamDbException { - if(eamOrg == null) { - throw new EamDbException("EamOrganization is null"); + public void processInstanceTable(CorrelationAttribute.Type type, InstanceTableCallback instanceTableCallback) throws EamDbException { + if (type == null) { + throw new EamDbException("Correlation type is null"); } - + + if (instanceTableCallback == null) { + throw new EamDbException("Callback interface is null"); + } + + Connection conn = connect(); + PreparedStatement preparedStatement = null; + ResultSet resultSet = null; + String tableName = EamDbUtil.correlationTypeToInstanceTableName(type); + StringBuilder sql = new StringBuilder(); + sql.append("select * from "); + sql.append(tableName); + + try { + preparedStatement = conn.prepareStatement(sql.toString()); + resultSet = preparedStatement.executeQuery(); + instanceTableCallback.process(resultSet); + } catch (SQLException ex) { + throw new EamDbException("Error getting all artifact instances from instances table", ex); + } finally { + EamDbUtil.closePreparedStatement(preparedStatement); + EamDbUtil.closeResultSet(resultSet); + EamDbUtil.closeConnection(conn); + } + } + + @Override + public EamOrganization newOrganization(EamOrganization eamOrg) throws EamDbException { + if (eamOrg == null) { + throw new EamDbException("EamOrganization is null"); + } else if (eamOrg.getOrgID() != -1) { + throw new EamDbException("EamOrganization already has an ID"); + } + Connection conn = connect(); ResultSet generatedKeys = null; PreparedStatement preparedStatement = null; @@ -1558,14 +1802,15 @@ public abstract class AbstractSqlEamDb implements EamDb { preparedStatement.executeUpdate(); generatedKeys = preparedStatement.getGeneratedKeys(); if (generatedKeys.next()) { - return generatedKeys.getLong(1); + eamOrg.setOrgID((int) generatedKeys.getLong(1)); + return eamOrg; } else { throw new SQLException("Creating user failed, no ID obtained."); } } catch (SQLException ex) { throw new EamDbException("Error inserting new organization.", ex); // NON-NLS } finally { - EamDbUtil.closePreparedStatement(preparedStatement); + EamDbUtil.closeStatement(preparedStatement); EamDbUtil.closeResultSet(generatedKeys); EamDbUtil.closeConnection(conn); } @@ -1598,7 +1843,7 @@ public abstract class AbstractSqlEamDb implements EamDb { } catch (SQLException ex) { throw new EamDbException("Error getting all organizations.", ex); // NON-NLS } finally { - EamDbUtil.closePreparedStatement(preparedStatement); + EamDbUtil.closeStatement(preparedStatement); EamDbUtil.closeResultSet(resultSet); EamDbUtil.closeConnection(conn); } @@ -1631,7 +1876,7 @@ public abstract class AbstractSqlEamDb implements EamDb { } catch (SQLException ex) { throw new EamDbException("Error getting organization by id.", ex); // NON-NLS } finally { - EamDbUtil.closePreparedStatement(preparedStatement); + EamDbUtil.closeStatement(preparedStatement); EamDbUtil.closeResultSet(resultSet); EamDbUtil.closeConnection(conn); } @@ -1641,33 +1886,48 @@ public abstract class AbstractSqlEamDb implements EamDb { * Get the organization associated with the given reference set. * * @param referenceSetID ID of the reference set + * * @return The organization object + * * @throws EamDbException */ @Override public EamOrganization getReferenceSetOrganization(int referenceSetID) throws EamDbException { EamGlobalSet globalSet = getReferenceSetByID(referenceSetID); - if(globalSet == null) { + if (globalSet == null) { throw new EamDbException("Reference set with ID " + referenceSetID + " not found"); } return (getOrganizationByID(globalSet.getOrgID())); } + /** + * Tests that an organization passed in as an argument is valid + * + * @param org + * + * @throws EamDbException if invalid + */ + private void testArgument(EamOrganization org) throws EamDbException { + if (org == null) { + throw new EamDbException("EamOrganization is null"); + } else if (org.getOrgID() == -1) { + throw new EamDbException("Organization has -1 row ID"); + } + } + /** * Update an existing organization. * * @param updatedOrganization the values the Organization with the same ID - * will be updated to in the database. + * will be updated to in the database. * * @throws EamDbException */ @Override public void updateOrganization(EamOrganization updatedOrganization) throws EamDbException { - if(updatedOrganization == null) { - throw new EamDbException("null updatedOrganization"); - } - + testArgument(updatedOrganization); + Connection conn = connect(); PreparedStatement preparedStatement = null; String sql = "UPDATE organizations SET org_name = ?, poc_name = ?, poc_email = ?, poc_phone = ? WHERE id = ?"; @@ -1682,20 +1942,15 @@ public abstract class AbstractSqlEamDb implements EamDb { } catch (SQLException ex) { throw new EamDbException("Error updating organization.", ex); // NON-NLS } finally { - EamDbUtil.closePreparedStatement(preparedStatement); + EamDbUtil.closeStatement(preparedStatement); EamDbUtil.closeConnection(conn); } } - @Messages({"AbstractSqlEamDb.deleteOrganization.inUseException.message=Can not delete organization " - + "which is currently in use by a case or reference set in the central repository.", - "AbstractSqlEamDb.deleteOrganization.errorDeleting.message=Error executing query when attempting to delete organization by id."}) @Override public void deleteOrganization(EamOrganization organizationToDelete) throws EamDbException { - if(organizationToDelete == null) { - throw new EamDbException("Organization to delete is null"); - } - + testArgument(organizationToDelete); + Connection conn = connect(); PreparedStatement checkIfUsedStatement = null; ResultSet resultSet = null; @@ -1709,16 +1964,16 @@ public abstract class AbstractSqlEamDb implements EamDb { resultSet = checkIfUsedStatement.executeQuery(); resultSet.next(); if (resultSet.getLong(1) > 0) { - throw new EamDbException(Bundle.AbstractSqlEamDb_deleteOrganization_inUseException_message()); + throw new EamDbException("Can not delete organization which is currently in use by a case or reference set in the central repository."); } deleteOrgStatement = conn.prepareStatement(deleteOrgSql); deleteOrgStatement.setInt(1, organizationToDelete.getOrgID()); deleteOrgStatement.executeUpdate(); } catch (SQLException ex) { - throw new EamDbException(Bundle.AbstractSqlEamDb_deleteOrganization_errorDeleting_message(), ex); // NON-NLS + throw new EamDbException("Error executing query when attempting to delete organization by id.", ex); // NON-NLS } finally { - EamDbUtil.closePreparedStatement(checkIfUsedStatement); - EamDbUtil.closePreparedStatement(deleteOrgStatement); + EamDbUtil.closeStatement(checkIfUsedStatement); + EamDbUtil.closeStatement(deleteOrgStatement); EamDbUtil.closeResultSet(resultSet); EamDbUtil.closeConnection(conn); } @@ -1735,18 +1990,18 @@ public abstract class AbstractSqlEamDb implements EamDb { */ @Override public int newReferenceSet(EamGlobalSet eamGlobalSet) throws EamDbException { - if(eamGlobalSet == null){ - throw new EamDbException("EamGlobalSet argument is null"); + if (eamGlobalSet == null) { + throw new EamDbException("EamGlobalSet is null"); } - - if(eamGlobalSet.getFileKnownStatus() == null){ + + if (eamGlobalSet.getFileKnownStatus() == null) { throw new EamDbException("File known status on the EamGlobalSet is null"); } - - if(eamGlobalSet.getType() == null){ + + if (eamGlobalSet.getType() == null) { throw new EamDbException("Type on the EamGlobalSet is null"); } - + Connection conn = connect(); PreparedStatement preparedStatement1 = null; @@ -1781,8 +2036,8 @@ public abstract class AbstractSqlEamDb implements EamDb { } catch (SQLException ex) { throw new EamDbException("Error inserting new global set.", ex); // NON-NLS } finally { - EamDbUtil.closePreparedStatement(preparedStatement1); - EamDbUtil.closePreparedStatement(preparedStatement2); + EamDbUtil.closeStatement(preparedStatement1); + EamDbUtil.closeStatement(preparedStatement2); EamDbUtil.closeResultSet(resultSet); EamDbUtil.closeConnection(conn); } @@ -1809,7 +2064,7 @@ public abstract class AbstractSqlEamDb implements EamDb { preparedStatement1 = conn.prepareStatement(sql1); preparedStatement1.setInt(1, referenceSetID); resultSet = preparedStatement1.executeQuery(); - if(resultSet.next()) { + if (resultSet.next()) { return getEamGlobalSetFromResultSet(resultSet); } else { return null; @@ -1818,7 +2073,7 @@ public abstract class AbstractSqlEamDb implements EamDb { } catch (SQLException ex) { throw new EamDbException("Error getting reference set by id.", ex); // NON-NLS } finally { - EamDbUtil.closePreparedStatement(preparedStatement1); + EamDbUtil.closeStatement(preparedStatement1); EamDbUtil.closeResultSet(resultSet); EamDbUtil.closeConnection(conn); } @@ -1828,18 +2083,18 @@ public abstract class AbstractSqlEamDb implements EamDb { * Get all reference sets * * @param correlationType Type of sets to return - * + * * @return List of all reference sets in the central repository * * @throws EamDbException */ @Override public List getAllReferenceSets(CorrelationAttribute.Type correlationType) throws EamDbException { - - if(correlationType == null){ + + if (correlationType == null) { throw new EamDbException("Correlation type is null"); } - + List results = new ArrayList<>(); Connection conn = connect(); @@ -1857,7 +2112,7 @@ public abstract class AbstractSqlEamDb implements EamDb { } catch (SQLException ex) { throw new EamDbException("Error getting reference sets.", ex); // NON-NLS } finally { - EamDbUtil.closePreparedStatement(preparedStatement1); + EamDbUtil.closeStatement(preparedStatement1); EamDbUtil.closeResultSet(resultSet); EamDbUtil.closeConnection(conn); } @@ -1868,19 +2123,20 @@ public abstract class AbstractSqlEamDb implements EamDb { * Add a new reference instance * * @param eamGlobalFileInstance The reference instance to add - * @param correlationType Correlation Type that this Reference Instance is + * @param correlationType Correlation Type that this Reference + * Instance is * * @throws EamDbException */ @Override public void addReferenceInstance(EamGlobalFileInstance eamGlobalFileInstance, CorrelationAttribute.Type correlationType) throws EamDbException { - if(eamGlobalFileInstance.getKnownStatus() == null){ - throw new EamDbException("known status of EamGlobalFileInstance is null"); + if (eamGlobalFileInstance.getKnownStatus() == null) { + throw new EamDbException("Known status of EamGlobalFileInstance is null"); } - if(correlationType == null){ + if (correlationType == null) { throw new EamDbException("Correlation type is null"); } - + Connection conn = connect(); PreparedStatement preparedStatement = null; @@ -1898,7 +2154,7 @@ public abstract class AbstractSqlEamDb implements EamDb { } catch (SQLException ex) { throw new EamDbException("Error inserting new reference instance into reference_ table.", ex); // NON-NLS } finally { - EamDbUtil.closePreparedStatement(preparedStatement); + EamDbUtil.closeStatement(preparedStatement); EamDbUtil.closeConnection(conn); } } @@ -1910,7 +2166,9 @@ public abstract class AbstractSqlEamDb implements EamDb { * * @param referenceSetName * @param version + * * @return true if a matching set is found + * * @throws EamDbException */ @Override @@ -1932,7 +2190,7 @@ public abstract class AbstractSqlEamDb implements EamDb { throw new EamDbException("Error testing whether reference set exists (name: " + referenceSetName + " version: " + version, ex); // NON-NLS } finally { - EamDbUtil.closePreparedStatement(preparedStatement1); + EamDbUtil.closeStatement(preparedStatement1); EamDbUtil.closeResultSet(resultSet); EamDbUtil.closeConnection(conn); } @@ -1945,10 +2203,10 @@ public abstract class AbstractSqlEamDb implements EamDb { */ @Override public void bulkInsertReferenceTypeEntries(Set globalInstances, CorrelationAttribute.Type contentType) throws EamDbException { - if(contentType == null) { - throw new EamDbException("Null correlation type"); + if (contentType == null) { + throw new EamDbException("Correlation type is null"); } - if(globalInstances == null) { + if (globalInstances == null) { throw new EamDbException("Null set of EamGlobalFileInstance"); } @@ -1965,10 +2223,10 @@ public abstract class AbstractSqlEamDb implements EamDb { bulkPs = conn.prepareStatement(String.format(sql, EamDbUtil.correlationTypeToReferenceTableName(contentType))); for (EamGlobalFileInstance globalInstance : globalInstances) { - if(globalInstance.getKnownStatus() == null){ + if (globalInstance.getKnownStatus() == null) { throw new EamDbException("EamGlobalFileInstance with value " + globalInstance.getMD5Hash() + " has null known status"); } - + bulkPs.setInt(1, globalInstance.getGlobalSetID()); bulkPs.setString(2, globalInstance.getMD5Hash()); bulkPs.setByte(3, globalInstance.getKnownStatus().getFileKnownValue()); @@ -1986,7 +2244,7 @@ public abstract class AbstractSqlEamDb implements EamDb { } throw new EamDbException("Error inserting bulk artifacts.", ex); // NON-NLS } finally { - EamDbUtil.closePreparedStatement(bulkPs); + EamDbUtil.closeStatement(bulkPs); EamDbUtil.closeConnection(conn); } } @@ -1994,7 +2252,7 @@ public abstract class AbstractSqlEamDb implements EamDb { /** * Get all reference entries having a given correlation type and value * - * @param aType Type to use for matching + * @param aType Type to use for matching * @param aValue Value to use for matching * * @return List of all global file instances with a type and value @@ -2003,10 +2261,10 @@ public abstract class AbstractSqlEamDb implements EamDb { */ @Override public List getReferenceInstancesByTypeValue(CorrelationAttribute.Type aType, String aValue) throws EamDbException { - if(aType == null) { - throw new EamDbException("correlation type is null"); + if (aType == null) { + throw new EamDbException("Correlation type is null"); } - + Connection conn = connect(); List globalFileInstances = new ArrayList<>(); @@ -2026,7 +2284,7 @@ public abstract class AbstractSqlEamDb implements EamDb { } catch (SQLException ex) { throw new EamDbException("Error getting reference instances by type and value.", ex); // NON-NLS } finally { - EamDbUtil.closePreparedStatement(preparedStatement1); + EamDbUtil.closeStatement(preparedStatement1); EamDbUtil.closeResultSet(resultSet); EamDbUtil.closeConnection(conn); } @@ -2044,9 +2302,9 @@ public abstract class AbstractSqlEamDb implements EamDb { @Override public int newCorrelationType(CorrelationAttribute.Type newType) throws EamDbException { if (newType == null) { - throw new EamDbException("null correlation type"); + throw new EamDbException("Correlation type is null"); } - + Connection conn = connect(); PreparedStatement preparedStatement = null; @@ -2093,8 +2351,8 @@ public abstract class AbstractSqlEamDb implements EamDb { } catch (SQLException ex) { throw new EamDbException("Error inserting new correlation type.", ex); // NON-NLS } finally { - EamDbUtil.closePreparedStatement(preparedStatement); - EamDbUtil.closePreparedStatement(preparedStatementQuery); + EamDbUtil.closeStatement(preparedStatement); + EamDbUtil.closeStatement(preparedStatementQuery); EamDbUtil.closeResultSet(resultSet); EamDbUtil.closeConnection(conn); } @@ -2121,7 +2379,7 @@ public abstract class AbstractSqlEamDb implements EamDb { } catch (SQLException ex) { throw new EamDbException("Error getting all correlation types.", ex); // NON-NLS } finally { - EamDbUtil.closePreparedStatement(preparedStatement); + EamDbUtil.closeStatement(preparedStatement); EamDbUtil.closeResultSet(resultSet); EamDbUtil.closeConnection(conn); } @@ -2132,7 +2390,7 @@ public abstract class AbstractSqlEamDb implements EamDb { * artifacts. * * @return List of enabled EamArtifact.Type's. If none are defined in the - * database, the default list will be returned. + * database, the default list will be returned. * * @throws EamDbException */ @@ -2156,7 +2414,7 @@ public abstract class AbstractSqlEamDb implements EamDb { } catch (SQLException ex) { throw new EamDbException("Error getting enabled correlation types.", ex); // NON-NLS } finally { - EamDbUtil.closePreparedStatement(preparedStatement); + EamDbUtil.closeStatement(preparedStatement); EamDbUtil.closeResultSet(resultSet); EamDbUtil.closeConnection(conn); } @@ -2167,7 +2425,7 @@ public abstract class AbstractSqlEamDb implements EamDb { * correlate artifacts. * * @return List of supported EamArtifact.Type's. If none are defined in the - * database, the default list will be returned. + * database, the default list will be returned. * * @throws EamDbException */ @@ -2191,7 +2449,7 @@ public abstract class AbstractSqlEamDb implements EamDb { } catch (SQLException ex) { throw new EamDbException("Error getting supported correlation types.", ex); // NON-NLS } finally { - EamDbUtil.closePreparedStatement(preparedStatement); + EamDbUtil.closeStatement(preparedStatement); EamDbUtil.closeResultSet(resultSet); EamDbUtil.closeConnection(conn); } @@ -2223,7 +2481,7 @@ public abstract class AbstractSqlEamDb implements EamDb { } catch (SQLException ex) { throw new EamDbException("Error updating correlation type.", ex); // NON-NLS } finally { - EamDbUtil.closePreparedStatement(preparedStatement); + EamDbUtil.closeStatement(preparedStatement); EamDbUtil.closeConnection(conn); } @@ -2251,7 +2509,7 @@ public abstract class AbstractSqlEamDb implements EamDb { preparedStatement = conn.prepareStatement(sql); preparedStatement.setInt(1, typeId); resultSet = preparedStatement.executeQuery(); - if(resultSet.next()) { + if (resultSet.next()) { aType = getCorrelationTypeFromResultSet(resultSet); return aType; } else { @@ -2261,7 +2519,7 @@ public abstract class AbstractSqlEamDb implements EamDb { } catch (SQLException ex) { throw new EamDbException("Error getting correlation type by id.", ex); // NON-NLS } finally { - EamDbUtil.closePreparedStatement(preparedStatement); + EamDbUtil.closeStatement(preparedStatement); EamDbUtil.closeResultSet(resultSet); EamDbUtil.closeConnection(conn); } @@ -2271,7 +2529,7 @@ public abstract class AbstractSqlEamDb implements EamDb { * Convert a ResultSet to a EamCase object * * @param resultSet A resultSet with a set of values to create a EamCase - * object. + * object. * * @return fully populated EamCase object, or null * @@ -2341,7 +2599,7 @@ public abstract class AbstractSqlEamDb implements EamDb { * Convert a ResultSet to a EamArtifactInstance object * * @param resultSet A resultSet with a set of values to create a - * EamArtifactInstance object. + * EamArtifactInstance object. * * @return fully populated EamArtifactInstance, or null * @@ -2351,15 +2609,15 @@ public abstract class AbstractSqlEamDb implements EamDb { if (null == resultSet) { return null; } - CorrelationAttributeInstance eamArtifactInstance = new CorrelationAttributeInstance( + // @@@ We should have data source ID in the previous query instead of passing -1 into the below constructor + return new CorrelationAttributeInstance( + resultSet.getInt("id"), new CorrelationCase(resultSet.getInt("case_id"), resultSet.getString("case_uid"), resultSet.getString("case_name")), - new CorrelationDataSource(-1, resultSet.getInt("case_id"), resultSet.getString("device_id"), resultSet.getString("name")), + new CorrelationDataSource(resultSet.getInt("case_id"), resultSet.getInt("data_source_id"), resultSet.getString("device_id"), resultSet.getString("name")), resultSet.getString("file_path"), resultSet.getString("comment"), TskData.FileKnown.valueOf(resultSet.getByte("known_status")) ); - - return eamArtifactInstance; } private EamOrganization getEamOrganizationFromResultSet(ResultSet resultSet) throws SQLException { @@ -2367,15 +2625,13 @@ public abstract class AbstractSqlEamDb implements EamDb { return null; } - EamOrganization eamOrganization = new EamOrganization( + return new EamOrganization( resultSet.getInt("id"), resultSet.getString("org_name"), resultSet.getString("poc_name"), resultSet.getString("poc_email"), resultSet.getString("poc_phone") ); - - return eamOrganization; } private EamGlobalSet getEamGlobalSetFromResultSet(ResultSet resultSet) throws SQLException, EamDbException { @@ -2383,7 +2639,7 @@ public abstract class AbstractSqlEamDb implements EamDb { return null; } - EamGlobalSet eamGlobalSet = new EamGlobalSet( + return new EamGlobalSet( resultSet.getInt("id"), resultSet.getInt("org_id"), resultSet.getString("set_name"), @@ -2393,8 +2649,6 @@ public abstract class AbstractSqlEamDb implements EamDb { EamDb.getInstance().getCorrelationTypeById(resultSet.getInt("type")), LocalDate.parse(resultSet.getString("import_date")) ); - - return eamGlobalSet; } private EamGlobalFileInstance getEamGlobalFileInstanceFromResultSet(ResultSet resultSet) throws SQLException, EamDbException { @@ -2402,15 +2656,13 @@ public abstract class AbstractSqlEamDb implements EamDb { return null; } - EamGlobalFileInstance eamGlobalFileInstance = new EamGlobalFileInstance( + return new EamGlobalFileInstance( resultSet.getInt("id"), resultSet.getInt("reference_set_id"), resultSet.getString("value"), TskData.FileKnown.valueOf(resultSet.getByte("known_status")), resultSet.getString("comment") ); - - return eamGlobalFileInstance; } /** @@ -2422,7 +2674,7 @@ public abstract class AbstractSqlEamDb implements EamDb { public void upgradeSchema() throws EamDbException, SQLException { ResultSet resultSet = null; - Statement statement; + Statement statement = null; Connection conn = null; try { @@ -2431,30 +2683,30 @@ public abstract class AbstractSqlEamDb implements EamDb { statement = conn.createStatement(); int minorVersion = 0; - int majorVersion = 0; resultSet = statement.executeQuery("SELECT value FROM db_info WHERE name='SCHEMA_MINOR_VERSION'"); if (resultSet.next()) { String minorVersionStr = resultSet.getString("value"); try { minorVersion = Integer.parseInt(minorVersionStr); } catch (NumberFormatException ex) { - throw new EamDbException("Bad value for schema minor version (" + minorVersionStr + ") - database is corrupt"); + throw new EamDbException("Bad value for schema minor version (" + minorVersionStr + ") - database is corrupt", ex); } } + int majorVersion = 0; resultSet = statement.executeQuery("SELECT value FROM db_info WHERE name='SCHEMA_VERSION'"); if (resultSet.next()) { String majorVersionStr = resultSet.getString("value"); try { majorVersion = Integer.parseInt(majorVersionStr); } catch (NumberFormatException ex) { - throw new EamDbException("Bad value for schema version (" + majorVersionStr + ") - database is corrupt"); + throw new EamDbException("Bad value for schema version (" + majorVersionStr + ") - database is corrupt", ex); } } CaseDbSchemaVersionNumber dbSchemaVersion = new CaseDbSchemaVersionNumber(majorVersion, minorVersion); if (dbSchemaVersion.equals(CURRENT_DB_SCHEMA_VERSION)) { - LOGGER.log(Level.INFO, "Central Repository is up to date"); + logger.log(Level.INFO, "Central Repository is up to date"); return; } @@ -2475,18 +2727,19 @@ public abstract class AbstractSqlEamDb implements EamDb { } conn.commit(); - LOGGER.log(Level.INFO, "Central Repository upgraded to version " + CURRENT_DB_SCHEMA_VERSION); + logger.log(Level.INFO, "Central Repository upgraded to version " + CURRENT_DB_SCHEMA_VERSION); } catch (SQLException | EamDbException ex) { try { if (conn != null) { conn.rollback(); } } catch (SQLException ex2) { - LOGGER.log(Level.SEVERE, "Database rollback failed", ex2); + logger.log(Level.SEVERE, "Database rollback failed", ex2); } throw ex; } finally { EamDbUtil.closeResultSet(resultSet); + EamDbUtil.closeStatement(statement); EamDbUtil.closeConnection(conn); } } diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java index 9e0c3b6cec..3ccca9f293 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java @@ -1,7 +1,7 @@ /* * Central Repository * - * Copyright 2015-2017 Basis Technology Corp. + * Copyright 2015-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -24,8 +24,8 @@ import org.sleuthkit.datamodel.TskData; /** * - * Used to store details about a specific instance of a - * CorrelationAttribute. Includes its data source, path, etc. + * Used to store details about a specific instance of a CorrelationAttribute. + * Includes its data source, path, etc. * */ @Messages({ @@ -43,13 +43,6 @@ public class CorrelationAttributeInstance implements Serializable { private String comment; private TskData.FileKnown knownStatus; - public CorrelationAttributeInstance( - CorrelationCase eamCase, - CorrelationDataSource eamDataSource - ) throws EamDbException { - this(-1, eamCase, eamDataSource, "", null, TskData.FileKnown.UNKNOWN); - } - public CorrelationAttributeInstance( CorrelationCase eamCase, CorrelationDataSource eamDataSource, @@ -58,14 +51,6 @@ public class CorrelationAttributeInstance implements Serializable { this(-1, eamCase, eamDataSource, filePath, null, TskData.FileKnown.UNKNOWN); } - public CorrelationAttributeInstance( - CorrelationCase eamCase, - CorrelationDataSource eamDataSource, - String filePath, - String comment - ) throws EamDbException { - this(-1, eamCase, eamDataSource, filePath, comment, TskData.FileKnown.UNKNOWN); - } public CorrelationAttributeInstance( CorrelationCase eamCase, @@ -85,10 +70,10 @@ public class CorrelationAttributeInstance implements Serializable { String comment, TskData.FileKnown knownStatus ) throws EamDbException { - if(filePath == null) { + if (filePath == null) { throw new EamDbException("file path is null"); } - + this.ID = ID; this.correlationCase = eamCase; this.correlationDataSource = eamDataSource; @@ -117,6 +102,16 @@ public class CorrelationAttributeInstance implements Serializable { + this.getComment(); } + /** + * Is this a database instance? + * + * @return True if the instance ID is greater or equal to zero; otherwise + * false. + */ + public boolean isDatabaseInstance() { + return (ID >= 0); + } + /** * @return the database ID */ @@ -160,9 +155,9 @@ public class CorrelationAttributeInstance implements Serializable { } /** - * Get this knownStatus. This only indicates whether an item has been - * tagged as notable and should never return KNOWN. - * + * Get this knownStatus. This only indicates whether an item has been tagged + * as notable and should never return KNOWN. + * * @return BAD if the item has been tagged as notable, UNKNOWN otherwise */ public TskData.FileKnown getKnownStatus() { @@ -170,10 +165,11 @@ public class CorrelationAttributeInstance implements Serializable { } /** - * Set the knownStatus. This only indicates whether an item has been - * tagged as notable and should never be set to KNOWN. - * - * @param knownStatus Should be BAD if the item is tagged as notable, UNKNOWN otherwise + * Set the knownStatus. This only indicates whether an item has been tagged + * as notable and should never be set to KNOWN. + * + * @param knownStatus Should be BAD if the item is tagged as notable, + * UNKNOWN otherwise */ public void setKnownStatus(TskData.FileKnown knownStatus) { this.knownStatus = knownStatus; diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationDataSource.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationDataSource.java index 864bf50923..8cf07a42e9 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationDataSource.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationDataSource.java @@ -40,15 +40,21 @@ public class CorrelationDataSource implements Serializable { private final String name; /** - * - * @param caseId - * @param deviceId - * @param name + * @param correlationCase CorrelationCase object data source is associated with. Must have been created by EamDB and have a valid ID. + * @param deviceId User specified case-specific ID + * @param name Display name of data source */ - public CorrelationDataSource(int caseId, String deviceId, String name) { - this(caseId, -1, deviceId, name); + public CorrelationDataSource(CorrelationCase correlationCase, String deviceId, String name) { + this(correlationCase.getID(), -1, deviceId, name); } + /** + * + * @param caseId Row ID for Case in DB + * @param dataSourceId Row ID for this data source in DB (or -1) + * @param deviceId User specified ID for device (unique per case) + * @param name User specified name + */ CorrelationDataSource(int caseId, int dataSourceId, String deviceId, @@ -84,7 +90,18 @@ public class CorrelationDataSource implements Serializable { } catch (TskDataException | TskCoreException ex) { throw new EamDbException("Error getting data source info: " + ex.getMessage()); } - return new CorrelationDataSource(correlationCase.getID(), -1, deviceId, dataSource.getName()); + + CorrelationDataSource correlationDataSource = null; + if (EamDbUtil.useCentralRepo()) { + correlationDataSource = EamDb.getInstance().getDataSource(correlationCase, deviceId); + } + if (correlationDataSource == null) { + correlationDataSource = new CorrelationDataSource(correlationCase, deviceId, dataSource.getName()); + if (EamDbUtil.useCentralRepo()) { + EamDb.getInstance().newDataSource(correlationDataSource); + } + } + return correlationDataSource; } @Override @@ -102,7 +119,7 @@ public class CorrelationDataSource implements Serializable { /** * Get the database row ID * - * @return the ID + * @return the ID or -1 if unknown */ int getID() { return dataSourceID; diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamArtifactUtil.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamArtifactUtil.java index 84c23256f0..6aad75d38e 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamArtifactUtil.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamArtifactUtil.java @@ -39,7 +39,7 @@ import org.sleuthkit.datamodel.TskData; public class EamArtifactUtil { private static final long serialVersionUID = 1L; - private static final Logger LOGGER = Logger.getLogger(EamArtifactUtil.class.getName()); + private static final Logger logger = Logger.getLogger(EamArtifactUtil.class.getName()); public EamArtifactUtil() { } @@ -76,14 +76,14 @@ public class EamArtifactUtil { // have switch based on artifact type for (CorrelationAttribute.Type aType : EamDb.getInstance().getDefinedCorrelationTypes()) { if ((checkEnabled && aType.isEnabled()) || !checkEnabled) { - CorrelationAttribute eamArtifact = EamArtifactUtil.getCorrelationAttributeFromBlackboardArtifact(aType, bbArtifact); - if (eamArtifact != null) { - eamArtifacts.add(eamArtifact); + CorrelationAttribute correlationAttribute = EamArtifactUtil.getCorrelationAttributeFromBlackboardArtifact(aType, bbArtifact); + if (correlationAttribute != null) { + eamArtifacts.add(correlationAttribute); } } } } catch (EamDbException ex) { - LOGGER.log(Level.SEVERE, "Error getting defined correlation types.", ex); // NON-NLS + logger.log(Level.SEVERE, "Error getting defined correlation types.", ex); // NON-NLS return eamArtifacts; } @@ -115,10 +115,10 @@ public class EamArtifactUtil { eamArtifact.addInstance(eamInstance); } } catch (TskCoreException | EamDbException ex) { - LOGGER.log(Level.SEVERE, "Error creating artifact instance.", ex); // NON-NLS + logger.log(Level.SEVERE, "Error creating artifact instance.", ex); // NON-NLS return eamArtifacts; } catch (NoCurrentCaseException ex) { - LOGGER.log(Level.SEVERE, "Case is closed.", ex); // NON-NLS + logger.log(Level.SEVERE, "Case is closed.", ex); // NON-NLS return eamArtifacts; } } @@ -136,7 +136,7 @@ public class EamArtifactUtil { * @return the new EamArtifact, or null if one was not created because * bbArtifact did not contain the needed data */ - private static CorrelationAttribute getCorrelationAttributeFromBlackboardArtifact(CorrelationAttribute.Type correlationType, + private static CorrelationAttribute getCorrelationAttributeFromBlackboardArtifact(CorrelationAttribute.Type correlationType, BlackboardArtifact bbArtifact) throws EamDbException { String value = null; int artifactTypeID = bbArtifact.getArtifactTypeID(); @@ -202,10 +202,10 @@ public class EamArtifactUtil { } } catch (TskCoreException ex) { - LOGGER.log(Level.SEVERE, "Error getting attribute while getting type from BlackboardArtifact.", ex); // NON-NLS + logger.log(Level.SEVERE, "Error getting attribute while getting type from BlackboardArtifact.", ex); // NON-NLS return null; - } catch (NoCurrentCaseException ex) { - LOGGER.log(Level.SEVERE, "Exception while getting open case.", ex); // NON-NLS + } catch (NoCurrentCaseException ex) { + logger.log(Level.SEVERE, "Exception while getting open case.", ex); // NON-NLS return null; } @@ -216,6 +216,45 @@ public class EamArtifactUtil { } } + /** + * Retrieve CorrelationAttribute from the given Content. + * + * @param content The content object + * + * @return The new CorrelationAttribute, or null if retrieval failed. + */ + public static CorrelationAttribute getCorrelationAttributeFromContent(Content content) { + + if (!(content instanceof AbstractFile)) { + return null; + } + + final AbstractFile file = (AbstractFile) content; + + if (!isSupportedAbstractFileType(file)) { + return null; + } + + CorrelationAttribute correlationAttribute = null; + + try { + CorrelationAttribute.Type type = EamDb.getInstance().getCorrelationTypeById(CorrelationAttribute.FILES_TYPE_ID); + CorrelationCase correlationCase = EamDb.getInstance().getCase(Case.getCurrentCaseThrows()); + if (null == correlationCase) { + correlationCase = EamDb.getInstance().newCase(Case.getCurrentCaseThrows()); + } + CorrelationDataSource correlationDataSource = CorrelationDataSource.fromTSKDataSource(correlationCase, file.getDataSource()); + String value = file.getMd5Hash(); + String filePath = (file.getParentPath() + file.getName()).toLowerCase(); + + correlationAttribute = EamDb.getInstance().getCorrelationAttribute(type, correlationCase, correlationDataSource, value, filePath); + } catch (TskCoreException | EamDbException | NoCurrentCaseException ex) { + logger.log(Level.SEVERE, "Error retrieving correlation attribute.", ex); + } + + return correlationAttribute; + } + /** * Create an EamArtifact from the given Content. Will return null if an * artifact can not be created - this is not necessarily an error case, it @@ -225,7 +264,7 @@ public class EamArtifactUtil { * * Does not add the artifact to the database. * - * @param content The content object + * @param content The content object * * @return The new EamArtifact or null if creation failed */ @@ -237,7 +276,7 @@ public class EamArtifactUtil { final AbstractFile af = (AbstractFile) content; - if (!isValidCentralRepoFile(af)) { + if (!isSupportedAbstractFileType(af)) { return null; } @@ -262,7 +301,7 @@ public class EamArtifactUtil { eamArtifact.addInstance(cei); return eamArtifact; } catch (TskCoreException | EamDbException | NoCurrentCaseException ex) { - LOGGER.log(Level.SEVERE, "Error making correlation attribute.", ex); + logger.log(Level.SEVERE, "Error making correlation attribute.", ex); return null; } } @@ -271,21 +310,17 @@ public class EamArtifactUtil { * Check whether the given abstract file should be processed for the central * repository. * - * @param af The file to test + * @param file The file to test * * @return true if the file should be added to the central repo, false * otherwise */ - public static boolean isValidCentralRepoFile(AbstractFile af) { - if (af == null) { + public static boolean isSupportedAbstractFileType(AbstractFile file) { + if (file == null) { return false; } - if (af.getKnown() == TskData.FileKnown.KNOWN) { - return false; - } - - switch (af.getType()) { + switch (file.getType()) { case UNALLOC_BLOCKS: case UNUSED_BLOCKS: case SLACK: @@ -297,9 +332,9 @@ public class EamArtifactUtil { case LOCAL: return true; case FS: - return af.isMetaFlagSet(TskData.TSK_FS_META_FLAG_ENUM.ALLOC); + return file.isMetaFlagSet(TskData.TSK_FS_META_FLAG_ENUM.ALLOC); default: - LOGGER.log(Level.WARNING, "Unexpected file type {0}", af.getType().getName()); + logger.log(Level.WARNING, "Unexpected file type {0}", file.getType().getName()); return false; } } diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDb.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDb.java index 2b05cb1d70..7ced4a1d7d 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDb.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDb.java @@ -1,7 +1,7 @@ /* * Central Repository * - * Copyright 2015-2017 Basis Technology Corp. + * Copyright 2015-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -34,7 +34,8 @@ public interface EamDb { public static final int SCHEMA_VERSION = 1; public static final CaseDbSchemaVersionNumber CURRENT_DB_SCHEMA_VERSION = new CaseDbSchemaVersionNumber(1, 1); - + + /** * Get the instance * @@ -103,7 +104,7 @@ public interface EamDb { /** * Add a new name/value pair in the db_info table. * - * @param name Key to set + * @param name Key to set * @param value Value to set * * @throws EamDbException @@ -124,7 +125,7 @@ public interface EamDb { /** * Update the value for a name in the name/value db_info table. * - * @param name Name to find + * @param name Name to find * @param value Value to assign to name. * * @throws EamDbException @@ -158,7 +159,9 @@ public interface EamDb { * Retrieves Central Repo case based on an Autopsy Case * * @param autopsyCase Autopsy case to find corresponding CR case for + * * @return CR Case + * * @throws EamDbException */ CorrelationCase getCase(Case autopsyCase) throws EamDbException; @@ -189,8 +192,8 @@ public interface EamDb { /** * Retrieves Data Source details based on data source device ID * - * @param correlationCase the current CorrelationCase used for ensuring - * uniqueness of DataSource + * @param correlationCase the current CorrelationCase used for ensuring + * uniqueness of DataSource * @param dataSourceDeviceId the data source device ID number * * @return The data source @@ -227,7 +230,7 @@ public interface EamDb { * Retrieves eamArtifact instances from the database that are associated * with the aType and filePath * - * @param aType EamArtifact.Type to search for + * @param aType EamArtifact.Type to search for * @param filePath File path to search for * * @return List of 0 or more EamArtifactInstances @@ -244,7 +247,7 @@ public interface EamDb { * @param value Value to search for * * @return Number of artifact instances having ArtifactType and - * ArtifactValue. + * ArtifactValue. */ Long getCountArtifactInstancesByTypeValue(CorrelationAttribute.Type aType, String value) throws EamDbException; @@ -281,11 +284,11 @@ public interface EamDb { * associated with the caseDisplayName and dataSource of the given * eamArtifact instance. * - * @param caseUUID Case ID to search for + * @param caseUUID Case ID to search for * @param dataSourceID Data source ID to search for * * @return Number of artifact instances having caseDisplayName and - * dataSource + * dataSource */ Long getCountArtifactInstancesByCaseDataSource(String caseUUID, String dataSourceID) throws EamDbException; @@ -309,6 +312,34 @@ public interface EamDb { */ void bulkInsertCases(List cases) throws EamDbException; + /** + * Update a correlation attribute instance comment in the database with that + * in the associated CorrelationAttribute object. + * + * @param eamArtifact The correlation attribute whose database instance will + * be updated. + * + * @throws EamDbException + */ + void updateAttributeInstanceComment(CorrelationAttribute eamArtifact) throws EamDbException; + + /** + * Find a correlation attribute in the Central Repository database given the + * instance type, case, data source, value, and file path. + * + * @param type The type of instance. + * @param correlationCase The case tied to the instance. + * @param correlationDataSource The data source tied to the instance. + * @param value The value tied to the instance. + * @param filePath The file path tied to the instance. + * + * @return The correlation attribute if it exists; otherwise null. + * + * @throws EamDbException + */ + CorrelationAttribute getCorrelationAttribute(CorrelationAttribute.Type type, CorrelationCase correlationCase, + CorrelationDataSource correlationDataSource, String value, String filePath) throws EamDbException; + /** * Sets an eamArtifact instance to the given known status. If eamArtifact * exists, it is updated. If eamArtifact does not exist nothing happens @@ -329,6 +360,15 @@ public interface EamDb { */ List getArtifactInstancesKnownBad(CorrelationAttribute.Type aType, String value) throws EamDbException; + /** + * Gets list of matching eamArtifact instances that have knownStatus = + * "Bad". + * + * @param aType EamArtifact.Type to search for + * @return List with 0 or more matching eamArtifact instances. + * @throws EamDbException + */ + List getArtifactInstancesKnownBad(CorrelationAttribute.Type aType) throws EamDbException; /** * Count matching eamArtifacts instances that have knownStatus = "Bad". * @@ -347,7 +387,7 @@ public interface EamDb { * @param value Value to search for * * @return List of cases containing this artifact with instances marked as - * bad + * bad * * @throws EamDbException */ @@ -357,6 +397,7 @@ public interface EamDb { * Remove a reference set and all values contained in it. * * @param referenceSetID + * * @throws EamDbException */ public void deleteReferenceSet(int referenceSetID) throws EamDbException; @@ -369,7 +410,9 @@ public interface EamDb { * @param referenceSetID * @param referenceSetName * @param version + * * @return true if a matching entry exists in the central repository + * * @throws EamDbException */ public boolean referenceSetIsValid(int referenceSetID, String referenceSetName, String version) throws EamDbException; @@ -381,7 +424,9 @@ public interface EamDb { * * @param referenceSetName * @param version + * * @return true if a matching set is found + * * @throws EamDbException */ public boolean referenceSetExists(String referenceSetName, String version) throws EamDbException; @@ -392,7 +437,9 @@ public interface EamDb { * * @param hash * @param referenceSetID + * * @return true if the hash is found in the reference set + * * @throws EamDbException */ public boolean isFileHashInReferenceSet(String hash, int referenceSetID) throws EamDbException; @@ -403,6 +450,7 @@ public interface EamDb { * @param value * @param referenceSetID * @param correlationTypeID + * * @return true if the hash is found in the reference set */ public boolean isValueInReferenceSet(String value, int referenceSetID, int correlationTypeID) throws EamDbException; @@ -422,11 +470,11 @@ public interface EamDb { * * @param eamOrg The organization to add * - * @return the Organization ID of the newly created organization. + * @return The organization with the org ID set. * * @throws EamDbException */ - long newOrganization(EamOrganization eamOrg) throws EamDbException; + EamOrganization newOrganization(EamOrganization eamOrg) throws EamDbException; /** * Get all organizations @@ -452,7 +500,9 @@ public interface EamDb { * Get the organization associated with the given reference set. * * @param referenceSetID ID of the reference set + * * @return The organization object + * * @throws EamDbException */ EamOrganization getReferenceSetOrganization(int referenceSetID) throws EamDbException; @@ -461,7 +511,7 @@ public interface EamDb { * Update an existing organization. * * @param updatedOrganization the values the Organization with the same ID - * will be updated to in the database. + * will be updated to in the database. * * @throws EamDbException */ @@ -502,7 +552,7 @@ public interface EamDb { * Get all reference sets * * @param correlationType Type of sets to return - * + * * @return List of all reference sets in the central repository * * @throws EamDbException @@ -513,7 +563,8 @@ public interface EamDb { * Add a new reference instance * * @param eamGlobalFileInstance The reference instance to add - * @param correlationType Correlation Type that this Reference Instance is + * @param correlationType Correlation Type that this Reference + * Instance is * * @throws EamDbException */ @@ -523,8 +574,8 @@ public interface EamDb { * Insert the bulk collection of Global File Instances * * @param globalInstances a Set of EamGlobalFileInstances to insert into the - * db. - * @param contentType the Type of the global instances + * db. + * @param contentType the Type of the global instances * * @throws EamDbException */ @@ -533,7 +584,7 @@ public interface EamDb { /** * Get all reference entries having a given correlation type and value * - * @param aType Type to use for matching + * @param aType Type to use for matching * @param aValue Value to use for matching * * @return List of all global file instances with a type and value @@ -558,7 +609,7 @@ public interface EamDb { * used to correlate artifacts. * * @return List of EamArtifact.Type's. If none are defined in the database, - * the default list will be returned. + * the default list will be returned. * * @throws EamDbException */ @@ -569,7 +620,7 @@ public interface EamDb { * artifacts. * * @return List of enabled EamArtifact.Type's. If none are defined in the - * database, the default list will be returned. + * database, the default list will be returned. * * @throws EamDbException */ @@ -580,7 +631,7 @@ public interface EamDb { * correlate artifacts. * * @return List of supported EamArtifact.Type's. If none are defined in the - * database, the default list will be returned. + * database, the default list will be returned. * * @throws EamDbException */ @@ -620,8 +671,18 @@ public interface EamDb { * (meaning the database is in use). * * @return the lock, or null if locking is not supported + * * @throws EamDbException if the coordination service is running but we fail - * to get the lock + * to get the lock */ public CoordinationService.Lock getExclusiveMultiUserDbLock() throws EamDbException; + + /** + * Process the Artifact instance in the EamDb + * + * @param type EamArtifact.Type to search for + * @param instanceTableCallback callback to process the instance + * @throws EamDbException + */ + void processInstanceTable(CorrelationAttribute.Type type, InstanceTableCallback instanceTableCallback) throws EamDbException; } diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDbUtil.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDbUtil.java index e2fc4bf371..9b8a4e1ed5 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDbUtil.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDbUtil.java @@ -42,18 +42,18 @@ public class EamDbUtil { private static final String DEFAULT_ORG_NAME = "Not Specified"; /** - * Close the prepared statement. + * Close the statement. * - * @param preparedStatement + * @param statement The statement to be closed. * * @throws EamDbException */ - public static void closePreparedStatement(PreparedStatement preparedStatement) { - if (null != preparedStatement) { + public static void closeStatement(Statement statement) { + if (null != statement) { try { - preparedStatement.close(); + statement.close(); } catch (SQLException ex) { - LOGGER.log(Level.SEVERE, "Error closing PreparedStatement.", ex); + LOGGER.log(Level.SEVERE, "Error closing Statement.", ex); } } } @@ -361,4 +361,18 @@ public class EamDbUtil { return "reference_" + type.getDbTableName(); } + /** + * Close the prepared statement. + * + * @param preparedStatement The prepared statement to be closed. + * + * @deprecated Use closeStatement() instead. + * + * @throws EamDbException + */ + @Deprecated + public static void closePreparedStatement(PreparedStatement preparedStatement) { + closeStatement(preparedStatement); + } + } diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamOrganization.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamOrganization.java index 3bebf6d5f3..ba88798128 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamOrganization.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamOrganization.java @@ -29,7 +29,7 @@ public class EamOrganization { private String pocEmail; private String pocPhone; - public EamOrganization( + EamOrganization( int orgID, String name, String pocName, @@ -84,7 +84,7 @@ public class EamOrganization { /** * @param orgID the orgID to set */ - public void setOrgID(int orgID) { + void setOrgID(int orgID) { this.orgID = orgID; } diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/InstanceTableCallback.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/InstanceTableCallback.java new file mode 100644 index 0000000000..d7da43bbf4 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/InstanceTableCallback.java @@ -0,0 +1,109 @@ +/* + * Central Repository + * + * Copyright 2015-2017 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.centralrepository.datamodel; + +import java.sql.ResultSet; +import java.sql.SQLException; + +/** + * CallBack Interface to process attribute instance Table. Used in EamDb.processInstanceTable + * is called only once. The implementation of this method needs to call resultset.next to + * loop through each row of attribute instance table. + */ +public interface InstanceTableCallback { + + /** + * Process the attribute instance + * + * @param resultSet attribute instance table. + */ + void process(ResultSet resultSet); + + + /** + * + * @param resultSet attribute instance table + * @return ID of the instance + * @throws SQLException + */ + static int getId(ResultSet resultSet) throws SQLException{ + return resultSet.getInt("id"); + } + + /** + * + * @param resultSet attribute instance table + * @return Case ID of a given instance + * @throws SQLException + */ + static int getCaseId(ResultSet resultSet) throws SQLException { + return resultSet.getInt("case_id"); + } + + /** + * + * @param resultSet attribute instance table + * @return Data source id of a particular instance + * @throws SQLException + */ + static int getDataSourceId(ResultSet resultSet) throws SQLException { + return resultSet.getInt("data_source_id"); + } + + /** + * + * @param resultSet attribute instance table + * @return md5 hash value of the instance + * @throws SQLException + */ + static String getValue(ResultSet resultSet) throws SQLException { + return resultSet.getString("value"); + } + + /** + * + * @param resultSet attribute instance table + * @return file path of the instance + * @throws SQLException + */ + static String getFilePath(ResultSet resultSet) throws SQLException { + return resultSet.getString("file_path"); + } + + /** + * + * @param resultSet attribute instance table + * @return status integer based on whether instance is marked notable or not + * @throws SQLException + */ + static int getKnownStatus(ResultSet resultSet) throws SQLException { + return resultSet.getInt("known_status"); + } + + /** + * + * @param resultSet attribute instance table + * @return previous comment made for the instance + * @throws SQLException + */ + static String getComment(ResultSet resultSet) throws SQLException { + return resultSet.getString("comment"); + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresEamDb.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresEamDb.java index 46d579ad87..f008070cea 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresEamDb.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresEamDb.java @@ -24,8 +24,6 @@ import java.sql.Statement; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import org.apache.commons.dbcp2.BasicDataSource; -import org.sleuthkit.autopsy.casemodule.CaseActionCancelledException; -import org.sleuthkit.autopsy.casemodule.CaseActionException; import org.sleuthkit.autopsy.coordinationservice.CoordinationService; import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.coreutils.Logger; @@ -34,7 +32,7 @@ import org.sleuthkit.autopsy.coreutils.Logger; * Central Repository database implementation using Postgres as a * backend */ -public class PostgresEamDb extends AbstractSqlEamDb { +final class PostgresEamDb extends AbstractSqlEamDb { private final static Logger LOGGER = Logger.getLogger(PostgresEamDb.class.getName()); @@ -116,7 +114,7 @@ public class PostgresEamDb extends AbstractSqlEamDb { String instancesTemplate = "TRUNCATE TABLE %s_instances RESTART IDENTITY CASCADE"; String referencesTemplate = "TRUNCATE TABLE reference_%s RESTART IDENTITY CASCADE"; - for (CorrelationAttribute.Type type : DEFAULT_CORRELATION_TYPES) { + for (CorrelationAttribute.Type type : defaultCorrelationTypes) { dropContent.executeUpdate(String.format(instancesTemplate, type.getDbTableName())); // FUTURE: support other reference types if (type.getId() == CorrelationAttribute.FILES_TYPE_ID) { diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresEamDbSettings.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresEamDbSettings.java index 37a2970082..1154da273d 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresEamDbSettings.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresEamDbSettings.java @@ -35,6 +35,8 @@ import org.sleuthkit.autopsy.coreutils.TextConverterException; /** * Settings for the Postgres implementation of the Central Repository database + * + * NOTE: This is public scope because the options panel calls it directly to set/get */ public final class PostgresEamDbSettings { @@ -42,7 +44,6 @@ public final class PostgresEamDbSettings { private final String DEFAULT_HOST = ""; // NON-NLS private final int DEFAULT_PORT = 5432; private final String DEFAULT_DBNAME = "central_repository"; // NON-NLS - private final int DEFAULT_BULK_THRESHHOLD = 1000; private final String DEFAULT_USERNAME = ""; private final String DEFAULT_PASSWORD = ""; private final String VALIDATION_QUERY = "SELECT version()"; // NON-NLS @@ -89,15 +90,15 @@ public final class PostgresEamDbSettings { try { String bulkThresholdString = ModuleSettings.getConfigSetting("CentralRepository", "db.postgresql.bulkThreshold"); // NON-NLS if (bulkThresholdString == null || bulkThresholdString.isEmpty()) { - this.bulkThreshold = DEFAULT_BULK_THRESHHOLD; + this.bulkThreshold = AbstractSqlEamDb.DEFAULT_BULK_THRESHHOLD; } else { this.bulkThreshold = Integer.parseInt(bulkThresholdString); if (getBulkThreshold() <= 0) { - this.bulkThreshold = DEFAULT_BULK_THRESHHOLD; + this.bulkThreshold = AbstractSqlEamDb.DEFAULT_BULK_THRESHHOLD; } } } catch (NumberFormatException ex) { - this.bulkThreshold = DEFAULT_BULK_THRESHHOLD; + this.bulkThreshold = AbstractSqlEamDb.DEFAULT_BULK_THRESHHOLD; } userName = ModuleSettings.getConfigSetting("CentralRepository", "db.postgresql.user"); // NON-NLS @@ -139,7 +140,7 @@ public final class PostgresEamDbSettings { * * @return */ - public String getConnectionURL(boolean usePostgresDb) { + String getConnectionURL(boolean usePostgresDb) { StringBuilder url = new StringBuilder(); url.append(getJDBCBaseURI()); url.append(getHost()); @@ -231,7 +232,7 @@ public final class PostgresEamDbSettings { * Use the current settings and the schema version query to test the * database schema. * - * @return true if successfull connection, else false. + * @return true if successful connection, else false. */ public boolean verifyDatabaseSchema() { Connection conn = getEphemeralConnection(false); @@ -493,7 +494,7 @@ public final class PostgresEamDbSettings { return result; } - public boolean isChanged() { + boolean isChanged() { String hostString = ModuleSettings.getConfigSetting("CentralRepository", "db.postgresql.host"); // NON-NLS String portString = ModuleSettings.getConfigSetting("CentralRepository", "db.postgresql.port"); // NON-NLS String dbNameString = ModuleSettings.getConfigSetting("CentralRepository", "db.postgresql.dbName"); // NON-NLS @@ -568,7 +569,7 @@ public final class PostgresEamDbSettings { /** * @return the bulkThreshold */ - public int getBulkThreshold() { + int getBulkThreshold() { return bulkThreshold; } @@ -622,21 +623,21 @@ public final class PostgresEamDbSettings { /** * @return the VALIDATION_QUERY */ - public String getValidationQuery() { + String getValidationQuery() { return VALIDATION_QUERY; } /** * @return the POSTGRES_DRIVER */ - public String getDriver() { + String getDriver() { return JDBC_DRIVER; } /** * @return the JDBC_BASE_URI */ - public String getJDBCBaseURI() { + String getJDBCBaseURI() { return JDBC_BASE_URI; } diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDb.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDb.java index d9d1ea2fa3..4a3ef36530 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDb.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDb.java @@ -18,7 +18,6 @@ */ package org.sleuthkit.autopsy.centralrepository.datamodel; -import java.io.File; import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; @@ -38,7 +37,7 @@ import org.sleuthkit.autopsy.coordinationservice.CoordinationService; * All methods in AbstractSqlEamDb that read or write to the database should * be overriden here and use appropriate locking. */ -public class SqliteEamDb extends AbstractSqlEamDb { +final class SqliteEamDb extends AbstractSqlEamDb { private final static Logger LOGGER = Logger.getLogger(SqliteEamDb.class.getName()); @@ -125,7 +124,7 @@ public class SqliteEamDb extends AbstractSqlEamDb { String instancesTemplate = "DELETE FROM %s_instances"; String referencesTemplate = "DELETE FROM global_files"; - for (CorrelationAttribute.Type type : DEFAULT_CORRELATION_TYPES) { + for (CorrelationAttribute.Type type : defaultCorrelationTypes) { dropContent.executeUpdate(String.format(instancesTemplate, type.getDbTableName())); // FUTURE: support other reference types if (type.getId() == CorrelationAttribute.FILES_TYPE_ID) { @@ -417,8 +416,8 @@ public class SqliteEamDb extends AbstractSqlEamDb { } finally { releaseSharedLock(); } - } - + } + /** * Retrieves eamArtifact instances from the database that are associated * with the aType and filePath @@ -590,6 +589,24 @@ public class SqliteEamDb extends AbstractSqlEamDb { } } + /** + * + * Gets list of matching eamArtifact instances that have knownStatus = + * "Bad". + * @param aType EamArtifact.Type to search for + * @return List with 0 or more matching eamArtifact instances. + * @throws EamDbException + */ + @Override + public List getArtifactInstancesKnownBad(CorrelationAttribute.Type aType) throws EamDbException { + try{ + acquireSharedLock(); + return super.getArtifactInstancesKnownBad(aType); + } finally { + releaseSharedLock(); + } + } + /** * Count matching eamArtifacts instances that have knownStatus = "Bad". * @@ -662,6 +679,22 @@ public class SqliteEamDb extends AbstractSqlEamDb { } } + /** + * Process the Artifact instance in the EamDb + * + * @param type EamArtifact.Type to search for + * @param instanceTableCallback callback to process the instance + * @throws EamDbException + */ + @Override + public void processInstanceTable(CorrelationAttribute.Type type, InstanceTableCallback instanceTableCallback) throws EamDbException { + try { + acquireSharedLock(); + super.processInstanceTable(type, instanceTableCallback); + } finally { + releaseSharedLock(); + } + } /** * Check whether a reference set with the given name/version is in the central repo. * Used to check for name collisions when creating reference sets. @@ -708,7 +741,7 @@ public class SqliteEamDb extends AbstractSqlEamDb { * @throws EamDbException */ @Override - public long newOrganization(EamOrganization eamOrg) throws EamDbException { + public EamOrganization newOrganization(EamOrganization eamOrg) throws EamDbException { try{ acquireExclusiveLock(); return super.newOrganization(eamOrg); diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDbSettings.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDbSettings.java index f006cc7e24..c73fa55a0e 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDbSettings.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDbSettings.java @@ -35,13 +35,14 @@ import org.sleuthkit.autopsy.coreutils.PlatformUtil; /** * Settings for the sqlite implementation of the Central Repository database + * + * NOTE: This is public scope because the options panel calls it directly to set/get */ public final class SqliteEamDbSettings { private final static Logger LOGGER = Logger.getLogger(SqliteEamDbSettings.class.getName()); private final String DEFAULT_DBNAME = "central_repository.db"; // NON-NLS private final String DEFAULT_DBDIRECTORY = PlatformUtil.getUserDirectory() + File.separator + "central_repository"; // NON-NLS - private final int DEFAULT_BULK_THRESHHOLD = 1000; private final String JDBC_DRIVER = "org.sqlite.JDBC"; // NON-NLS private final String JDBC_BASE_URI = "jdbc:sqlite:"; // NON-NLS private final String VALIDATION_QUERY = "SELECT count(*) from sqlite_master"; // NON-NLS @@ -75,15 +76,15 @@ public final class SqliteEamDbSettings { try { String bulkThresholdString = ModuleSettings.getConfigSetting("CentralRepository", "db.sqlite.bulkThreshold"); // NON-NLS if (bulkThresholdString == null || bulkThresholdString.isEmpty()) { - this.bulkThreshold = DEFAULT_BULK_THRESHHOLD; + this.bulkThreshold = AbstractSqlEamDb.DEFAULT_BULK_THRESHHOLD; } else { this.bulkThreshold = Integer.parseInt(bulkThresholdString); if (getBulkThreshold() <= 0) { - this.bulkThreshold = DEFAULT_BULK_THRESHHOLD; + this.bulkThreshold = AbstractSqlEamDb.DEFAULT_BULK_THRESHHOLD; } } } catch (NumberFormatException ex) { - this.bulkThreshold = DEFAULT_BULK_THRESHHOLD; + this.bulkThreshold = AbstractSqlEamDb.DEFAULT_BULK_THRESHHOLD; } } @@ -162,7 +163,7 @@ public final class SqliteEamDbSettings { * * @return */ - public String getConnectionURL() { + String getConnectionURL() { StringBuilder url = new StringBuilder(); url.append(getJDBCBaseURI()); url.append(getFileNameWithPath()); @@ -439,7 +440,7 @@ public final class SqliteEamDbSettings { return result; } - public boolean isChanged() { + boolean isChanged() { String dbNameString = ModuleSettings.getConfigSetting("CentralRepository", "db.sqlite.dbName"); // NON-NLS String dbDirectoryString = ModuleSettings.getConfigSetting("CentralRepository", "db.sqlite.dbDirectory"); // NON-NLS String bulkThresholdString = ModuleSettings.getConfigSetting("CentralRepository", "db.sqlite.bulkThreshold"); // NON-NLS @@ -474,14 +475,14 @@ public final class SqliteEamDbSettings { /** * @return the bulkThreshold */ - public int getBulkThreshold() { + int getBulkThreshold() { return bulkThreshold; } /** * @param bulkThreshold the bulkThreshold to set */ - public void setBulkThreshold(int bulkThreshold) throws EamDbException { + void setBulkThreshold(int bulkThreshold) throws EamDbException { if (bulkThreshold > 0) { this.bulkThreshold = bulkThreshold; } else { @@ -525,21 +526,21 @@ public final class SqliteEamDbSettings { /** * @return the DRIVER */ - public String getDriver() { + String getDriver() { return JDBC_DRIVER; } /** * @return the VALIDATION_QUERY */ - public String getValidationQuery() { + String getValidationQuery() { return VALIDATION_QUERY; } /** * @return the JDBC_BASE_URI */ - public String getJDBCBaseURI() { + String getJDBCBaseURI() { return JDBC_BASE_URI; } diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestModule.java b/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestModule.java index 423f2b65a0..33f790d72e 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestModule.java @@ -102,10 +102,14 @@ final class IngestModule implements FileIngestModule { return ProcessResult.ERROR; } - if (!EamArtifactUtil.isValidCentralRepoFile(abstractFile)) { + if (!EamArtifactUtil.isSupportedAbstractFileType(abstractFile)) { return ProcessResult.OK; } + if (abstractFile.getKnown() == TskData.FileKnown.KNOWN) { + return ProcessResult.OK; + } + EamDb dbManager; try { dbManager = EamDb.getInstance(); diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestSettingsPanel.java index 57d4f0a098..ed36c71287 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestSettingsPanel.java @@ -24,6 +24,7 @@ import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettingsPanel; /** * Ingest job settings panel for the Correlation Engine module. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class IngestSettingsPanel extends IngestModuleIngestJobSettingsPanel { /** diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/AddNewOrganizationDialog.java b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/AddNewOrganizationDialog.java index 5ebfd1a629..79ce921dfe 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/AddNewOrganizationDialog.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/AddNewOrganizationDialog.java @@ -36,9 +36,10 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb; /** * Dialog to add a new organization to the Central Repository database */ -public class AddNewOrganizationDialog extends javax.swing.JDialog { +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives +class AddNewOrganizationDialog extends javax.swing.JDialog { - private static final Logger LOGGER = Logger.getLogger(AddNewOrganizationDialog.class.getName()); + private static final Logger logger = Logger.getLogger(AddNewOrganizationDialog.class.getName()); private static final long serialVersionUID = 1L; private final Collection textBoxes; @@ -51,7 +52,7 @@ public class AddNewOrganizationDialog extends javax.swing.JDialog { * Creates new form AddNewOrganizationDialog */ @Messages({"AddNewOrganizationDialog.addNewOrg.msg=Add New Organization"}) - public AddNewOrganizationDialog() { + AddNewOrganizationDialog() { super((JFrame) WindowManager.getDefault().getMainWindow(), Bundle.AddNewOrganizationDialog_addNewOrg_msg(), true); // NON-NLS @@ -65,6 +66,7 @@ public class AddNewOrganizationDialog extends javax.swing.JDialog { display(); } + // populates the dialog with existing case information to edit public AddNewOrganizationDialog(EamOrganization orgToEdit) { super((JFrame) WindowManager.getDefault().getMainWindow(), Bundle.AddNewOrganizationDialog_addNewOrg_msg(), @@ -193,10 +195,19 @@ public class AddNewOrganizationDialog extends javax.swing.JDialog { } } + /** + * + * @return True if new org was added or existing org changed + */ public boolean isChanged() { return hasChanged; } + /** + * Only valid if isChanged() is true. + * + * @return Org that was added or changed. null if nothing changed + */ public EamOrganization getNewOrg() { return newOrg; } @@ -332,12 +343,12 @@ public class AddNewOrganizationDialog extends javax.swing.JDialog { try { EamDb dbManager = EamDb.getInstance(); if (organizationToEdit != null) { - //check if new name exists with ID other than the one in use here - newOrg = new EamOrganization(organizationToEdit.getOrgID(), - tfOrganizationName.getText(), - tfPocName.getText(), - tfPocEmail.getText(), - tfPocPhone.getText()); + // make a copy in case the update fails + newOrg = dbManager.getOrganizationByID(organizationToEdit.getOrgID()); + newOrg.setName(tfOrganizationName.getText()); + newOrg.setPocName(tfPocName.getText()); + newOrg.setPocEmail(tfPocEmail.getText()); + newOrg.setPocPhone(tfPocPhone.getText()); dbManager.updateOrganization(newOrg); } else { newOrg = new EamOrganization( @@ -345,13 +356,14 @@ public class AddNewOrganizationDialog extends javax.swing.JDialog { tfPocName.getText(), tfPocEmail.getText(), tfPocPhone.getText()); - newOrg.setOrgID((int)dbManager.newOrganization(newOrg)); + newOrg = dbManager.newOrganization(newOrg); } hasChanged = true; dispose(); } catch (EamDbException ex) { lbWarningMsg.setText(Bundle.AddNewOrganizationDialog_bnOk_addFailed_text()); - LOGGER.log(Level.SEVERE, "Failed adding new organization.", ex); + logger.log(Level.SEVERE, "Failed adding new organization.", ex); + newOrg = null; } }//GEN-LAST:event_bnOKActionPerformed diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/EamDbSettingsDialog.java b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/EamDbSettingsDialog.java index 55ea941d33..c504f666e4 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/EamDbSettingsDialog.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/EamDbSettingsDialog.java @@ -47,9 +47,9 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.PostgresEamDbSettings; import org.sleuthkit.autopsy.centralrepository.datamodel.SqliteEamDbSettings; /** - * - * @author nick + * Configuration dialog for Central Repository database settings. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public class EamDbSettingsDialog extends JDialog { private static final Logger logger = Logger.getLogger(EamDbSettingsDialog.class.getName()); @@ -96,11 +96,8 @@ public class EamDbSettingsDialog extends JDialog { public boolean accept(File pathname) { if (pathname.isDirectory()) { return true; - } else if (pathname.getName().toLowerCase().equals((CENTRAL_REPO_DB_NAME + CENTRAL_REPO_SQLITE_EXT).toLowerCase())) { - return true; - } else { - return false; } + return pathname.getName().toLowerCase().equals((CENTRAL_REPO_DB_NAME + CENTRAL_REPO_SQLITE_EXT).toLowerCase()); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.java index a470247a66..a1daad3a71 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.java @@ -41,6 +41,7 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.SqliteEamDbSettings; /** * Main settings panel for the Central Repository */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel implements OptionsPanel { private static final long serialVersionUID = 1L; diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageCorrelationPropertiesDialog.java b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageCorrelationPropertiesDialog.java index a7c3016d96..3fa46b3306 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageCorrelationPropertiesDialog.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageCorrelationPropertiesDialog.java @@ -1,7 +1,7 @@ /* * Central Repository * - * Copyright 2015-2017 Basis Technology Corp. + * Copyright 2015-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,8 +18,6 @@ */ package org.sleuthkit.autopsy.centralrepository.optionspanel; -import java.awt.Dimension; -import java.awt.Toolkit; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; @@ -38,8 +36,9 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb; /** * Dialog to handle management of artifact types handled by the Central - * Repository + * Repository */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class ManageCorrelationPropertiesDialog extends javax.swing.JDialog { private static final Logger LOGGER = Logger.getLogger(ManageCorrelationPropertiesDialog.class.getName()); diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageOrganizationsDialog.java b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageOrganizationsDialog.java index 3f3e89f5d5..26a63f03a4 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageOrganizationsDialog.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageOrganizationsDialog.java @@ -37,6 +37,10 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbUtil; import org.sleuthkit.autopsy.centralrepository.datamodel.EamOrganization; import org.sleuthkit.autopsy.coreutils.Logger; +/** + * Configuration dialog to manage organizations for the Central Repository. + */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public final class ManageOrganizationsDialog extends JDialog { private static final long serialVersionUID = 1L; diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/AllDataSourcesCommonFilesAlgorithm.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/AllDataSourcesCommonFilesAlgorithm.java index f752fbabad..044241ccf5 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/AllDataSourcesCommonFilesAlgorithm.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/AllDataSourcesCommonFilesAlgorithm.java @@ -24,7 +24,7 @@ import java.util.Map; /** * Provides logic for selecting common files from all data sources. */ -final class AllDataSourcesCommonFilesAlgorithm extends CommonFilesMetadataBuilder { +final public class AllDataSourcesCommonFilesAlgorithm extends CommonFilesMetadataBuilder { private static final String WHERE_CLAUSE = "%s md5 in (select md5 from tsk_files where (known != 1 OR known IS NULL)%s GROUP BY md5 HAVING COUNT(*) > 1) order by md5"; //NON-NLS @@ -36,7 +36,7 @@ final class AllDataSourcesCommonFilesAlgorithm extends CommonFilesMetadataBuilde * @param filterByMediaMimeType match only on files whose mime types can be broadly categorized as media types * @param filterByDocMimeType match only on files whose mime types can be broadly categorized as document types */ - AllDataSourcesCommonFilesAlgorithm(Map dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) { + public AllDataSourcesCommonFilesAlgorithm(Map dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) { super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType); } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesMetadata.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesMetadata.java index 7b7b10b828..de06db45a4 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesMetadata.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesMetadata.java @@ -26,7 +26,7 @@ import java.util.Map; * Utility and wrapper model around data required for Common Files Search results. * Subclass this to implement different selections of files from the case. */ -final class CommonFilesMetadata { +final public class CommonFilesMetadata { private final Map metadata; @@ -52,7 +52,7 @@ final class CommonFilesMetadata { return this.metadata.get(md5); } - Map getMetadata() { + public Map getMetadata() { return Collections.unmodifiableMap(this.metadata); } @@ -60,7 +60,7 @@ final class CommonFilesMetadata { * How many distinct file instances exist for this metadata? * @return number of file instances */ - int size() { + public int size() { int count = 0; for (Md5Metadata data : this.metadata.values()) { count += data.size(); diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesMetadataBuilder.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesMetadataBuilder.java index d137368cfe..6fee774102 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesMetadataBuilder.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesMetadataBuilder.java @@ -46,7 +46,7 @@ import org.sleuthkit.datamodel.TskCoreException; * This entire thing runs on a background thread where exceptions are handled. */ @SuppressWarnings("PMD.AbstractNaming") -abstract class CommonFilesMetadataBuilder { +public abstract class CommonFilesMetadataBuilder { private final Map dataSourceIdToNameMap; private final boolean filterByMedia; diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesPanel.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesPanel.java index ad278a4cb5..3ce2c34e26 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesPanel.java @@ -18,12 +18,9 @@ */ package org.sleuthkit.autopsy.commonfilesearch; -import java.io.File; -import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.ExecutionException; @@ -33,7 +30,6 @@ import javax.swing.SwingUtilities; import javax.swing.SwingWorker; import org.openide.explorer.ExplorerManager; import org.openide.util.NbBundle; -import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer; import org.sleuthkit.autopsy.corecomponents.DataResultTopComponent; @@ -42,9 +38,6 @@ import org.sleuthkit.autopsy.corecomponents.TableFilterNode; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.directorytree.DataResultFilterNode; -import org.sleuthkit.autopsy.directorytree.DirectoryTreeTopComponent; -import org.sleuthkit.datamodel.SleuthkitCase; -import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery; import org.sleuthkit.datamodel.TskCoreException; /** @@ -77,7 +70,7 @@ public final class CommonFilesPanel extends javax.swing.JPanel { initComponents(); this.setupDataSources(); - + this.errorText.setVisible(false); } @@ -98,10 +91,6 @@ public final class CommonFilesPanel extends javax.swing.JPanel { new SwingWorker, Void>() { - private static final String SELECT_DATA_SOURCES_LOGICAL = "select obj_id, name from tsk_files where obj_id in (SELECT obj_id FROM tsk_objects WHERE obj_id in (select obj_id from data_source_info))"; - - private static final String SELECT_DATA_SOURCES_IMAGE = "select obj_id, name from tsk_image_names where obj_id in (SELECT obj_id FROM tsk_objects WHERE obj_id in (select obj_id from data_source_info))"; - private void updateUi() { String[] dataSourcesNames = new String[CommonFilesPanel.this.dataSourceMap.size()]; @@ -132,48 +121,10 @@ public final class CommonFilesPanel extends javax.swing.JPanel { return CommonFilesPanel.this.dataSourceMap.size() >= 2; } - private void loadLogicalSources(SleuthkitCase tskDb, Map dataSouceMap) throws TskCoreException, SQLException { - //try block releases resources - exceptions are handled in done() - try ( - CaseDbQuery query = tskDb.executeQuery(SELECT_DATA_SOURCES_LOGICAL); - ResultSet resultSet = query.getResultSet()) { - while (resultSet.next()) { - Long objectId = resultSet.getLong(1); - String dataSourceName = resultSet.getString(2); - dataSouceMap.put(objectId, dataSourceName); - } - } - } - - private void loadImageSources(SleuthkitCase tskDb, Map dataSouceMap) throws SQLException, TskCoreException { - //try block releases resources - exceptions are handled in done() - try ( - CaseDbQuery query = tskDb.executeQuery(SELECT_DATA_SOURCES_IMAGE); - ResultSet resultSet = query.getResultSet()) { - - while (resultSet.next()) { - Long objectId = resultSet.getLong(1); - String dataSourceName = resultSet.getString(2); - File image = new File(dataSourceName); - String dataSourceNameTrimmed = image.getName(); - dataSouceMap.put(objectId, dataSourceNameTrimmed); - } - } - } - @Override protected Map doInBackground() throws NoCurrentCaseException, TskCoreException, SQLException { - - Map dataSouceMap = new HashMap<>(); - - Case currentCase = Case.getCurrentCaseThrows(); - SleuthkitCase tskDb = currentCase.getSleuthkitCase(); - - loadLogicalSources(tskDb, dataSouceMap); - - loadImageSources(tskDb, dataSouceMap); - - return dataSouceMap; + DataSourceLoader loader = new DataSourceLoader(); + return loader.getDataSourceMap(); } @Override @@ -296,10 +247,10 @@ public final class CommonFilesPanel extends javax.swing.JPanel { TableFilterNode tableFilterWithDescendantsNode = new TableFilterNode(dataResultFilterNode); DataResultViewerTable table = new DataResultViewerTable(); - + Collection viewers = new ArrayList<>(1); viewers.add(table); - + DataResultTopComponent.createInstance(tabTitle, pathText, tableFilterWithDescendantsNode, metadata.size(), viewers); } catch (InterruptedException ex) { @@ -591,7 +542,7 @@ public final class CommonFilesPanel extends javax.swing.JPanel { this.pictureVideoCheckbox.setEnabled(true); this.documentsCheckbox.setEnabled(true); - + this.toggleErrorTextAndSearchBox(); } } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/DataSourceLoader.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/DataSourceLoader.java new file mode 100644 index 0000000000..69af771dad --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/DataSourceLoader.java @@ -0,0 +1,95 @@ +/* + * + * Autopsy Forensic Browser + * + * Copyright 2018 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.commonfilesearch; + +import java.io.File; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Encapsulates logic required to create a mapping of data sources in the + * current case to their data source IDs. + * + * Intended to be used within the context of a SwingWorker or other background + * thread. + */ +public class DataSourceLoader { + + private static final String SELECT_DATA_SOURCES_LOGICAL = "select obj_id, name from tsk_files where obj_id in (SELECT obj_id FROM tsk_objects WHERE obj_id in (select obj_id from data_source_info))"; + + private static final String SELECT_DATA_SOURCES_IMAGE = "select obj_id, name from tsk_image_names where obj_id in (SELECT obj_id FROM tsk_objects WHERE obj_id in (select obj_id from data_source_info))"; + + private void loadLogicalSources(SleuthkitCase tskDb, Map dataSouceMap) throws TskCoreException, SQLException { + //try block releases resources - exceptions are handled in done() + try ( + SleuthkitCase.CaseDbQuery query = tskDb.executeQuery(SELECT_DATA_SOURCES_LOGICAL); + ResultSet resultSet = query.getResultSet() + ) { + while (resultSet.next()) { + Long objectId = resultSet.getLong(1); + String dataSourceName = resultSet.getString(2); + dataSouceMap.put(objectId, dataSourceName); + } + } + } + + private void loadImageSources(SleuthkitCase tskDb, Map dataSouceMap) throws SQLException, TskCoreException { + //try block releases resources - exceptions are handled in done() + try ( + SleuthkitCase.CaseDbQuery query = tskDb.executeQuery(SELECT_DATA_SOURCES_IMAGE); + ResultSet resultSet = query.getResultSet()) { + + while (resultSet.next()) { + Long objectId = resultSet.getLong(1); + String dataSourceName = resultSet.getString(2); + File image = new File(dataSourceName); + String dataSourceNameTrimmed = image.getName(); + dataSouceMap.put(objectId, dataSourceNameTrimmed); + } + } + } + + /** + * Get a map of data source Ids to their string names for the current case. + * + * @return Map of Long (id) to String (name) + * @throws NoCurrentCaseException + * @throws TskCoreException + * @throws SQLException + */ + public Map getDataSourceMap() throws NoCurrentCaseException, TskCoreException, SQLException { + Map dataSouceMap = new HashMap<>(); + + Case currentCase = Case.getCurrentCaseThrows(); + SleuthkitCase tskDb = currentCase.getSleuthkitCase(); + + loadLogicalSources(tskDb, dataSouceMap); + + loadImageSources(tskDb, dataSouceMap); + + return dataSouceMap; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/SingleDataSource.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/SingleDataSource.java index bc506f5988..f3f8ef689d 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/SingleDataSource.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/SingleDataSource.java @@ -24,7 +24,7 @@ import java.util.Map; /** * Provides logic for selecting common files from a single data source. */ -final class SingleDataSource extends CommonFilesMetadataBuilder { +final public class SingleDataSource extends CommonFilesMetadataBuilder { private static final String WHERE_CLAUSE = "%s md5 in (select md5 from tsk_files where md5 in (select md5 from tsk_files where (known != 1 OR known IS NULL) and data_source_obj_id=%s%s) GROUP BY md5 HAVING COUNT(*) > 1) order by md5"; //NON-NLS private final Long selectedDataSourceId; @@ -35,10 +35,12 @@ final class SingleDataSource extends CommonFilesMetadataBuilder { * once in the given data source * @param dataSourceId data source id for which common files must appear at least once * @param dataSourceIdMap a map of obj_id to datasource name - * @param filterByMediaMimeType match only on files whose mime types can be broadly categorized as media types - * @param filterByDocMimeType match only on files whose mime types can be broadly categorized as document types + * @param filterByMediaMimeType match only on files whose mime types can be + * broadly categorized as media types + * @param filterByDocMimeType match only on files whose mime types can be + * broadly categorized as document types */ - SingleDataSource(Long dataSourceId, Map dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) { + public SingleDataSource(Long dataSourceId, Map dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) { super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType); this.selectedDataSourceId = dataSourceId; this.dataSourceName = dataSourceIdMap.get(this.selectedDataSourceId); diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java b/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java index a0417855fe..006fbd846f 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2017-18 Basis Technology Corp. + * Copyright 2017-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -48,6 +48,7 @@ import org.sleuthkit.datamodel.TskCoreException; * CVTTopComponent when this tab is active allowing for context sensitive * actions to work correctly. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public final class AccountsBrowser extends JPanel implements ExplorerManager.Provider, Lookup.Provider { private static final long serialVersionUID = 1L; diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java index 09052d9d9e..1e1004de25 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2017-18 Basis Technology Corp. + * Copyright 2017-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -42,6 +42,7 @@ import org.sleuthkit.autopsy.coreutils.ThreadConfined; @TopComponent.Registration(mode = "cvt", openAtStartup = false) @RetainLocation("cvt") @NbBundle.Messages("CVTTopComponent.name= Communications Visualization") +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public final class CVTTopComponent extends TopComponent { private static final long serialVersionUID = 1L; diff --git a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java index 266c0d91f2..81ffcb4805 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2017-18 Basis Technology Corp. + * Copyright 2017-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -57,6 +57,7 @@ import org.sleuthkit.datamodel.TskCoreException; * Panel that holds the Filter control widgets and triggers queries against the * CommunicationsManager on user filtering changes. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final public class FiltersPanel extends JPanel { private static final long serialVersionUID = 1L; diff --git a/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java b/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java index dca1770463..339e842280 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java +++ b/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2017-18 Basis Technology Corp. + * Copyright 2017-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -41,6 +41,7 @@ import org.sleuthkit.autopsy.directorytree.DataResultFilterNode; * messages and other account details, and a ContentViewer to show individual * messages. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public final class MessageBrowser extends JPanel implements ExplorerManager.Provider, Lookup.Provider { private static final long serialVersionUID = 1L; diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index eb33b557e2..8f5e6e5c94 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -112,6 +112,7 @@ import org.sleuthkit.datamodel.TskCoreException; * CVTTopComponent when this tab is active allowing for context sensitive * actions to work correctly. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final public class VisualizationPanel extends JPanel implements Lookup.Provider { private static final long serialVersionUID = 1L; diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties index c6bd24f38e..f2abc866da 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties @@ -75,3 +75,4 @@ SQLiteViewer.jLabel2.text=Page SQLiteViewer.numEntriesField.text=num Entries SQLiteViewer.jLabel1.text=Table PListViewer.exportButton.text=Export +SQLiteViewer.exportCsvButton.text=Export to CSV diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/FXVideoPanel.java b/Core/src/org/sleuthkit/autopsy/contentviewers/FXVideoPanel.java index cc5e2008b5..07736120c7 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/FXVideoPanel.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/FXVideoPanel.java @@ -76,6 +76,7 @@ import org.sleuthkit.datamodel.TskData; @ServiceProviders(value = { @ServiceProvider(service = FrameCapture.class) }) +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public class FXVideoPanel extends MediaViewVideoPanel { // Refer to https://docs.oracle.com/javafx/2/api/javafx/scene/media/package-summary.html diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/FileViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/FileViewer.java index 53be8ddbcd..c33a653292 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/FileViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/FileViewer.java @@ -36,6 +36,7 @@ import org.sleuthkit.datamodel.AbstractFile; * Generic Application content viewer */ @ServiceProvider(service = DataContentViewer.class, position = 3) +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public class FileViewer extends javax.swing.JPanel implements DataContentViewer { private static final int CONFIDENCE_LEVEL = 5; diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/GstVideoPanel.java b/Core/src/org/sleuthkit/autopsy/contentviewers/GstVideoPanel.java index 5ebd894517..a1ca715a19 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/GstVideoPanel.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/GstVideoPanel.java @@ -69,6 +69,7 @@ import org.sleuthkit.datamodel.TskData; @ServiceProviders(value = { @ServiceProvider(service = FrameCapture.class) }) +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public class GstVideoPanel extends MediaViewVideoPanel { private static final String[] EXTENSIONS = new String[]{".mov", ".m4v", ".flv", ".mp4", ".3gp", ".avi", ".mpg", ".mpeg", ".wmv"}; //NON-NLS @@ -365,7 +366,7 @@ public class GstVideoPanel extends MediaViewVideoPanel { playbin.getState(); if (!playbin.seek(timeStamp, unit)) { - logger.log(Level.INFO, "There was a problem seeking to " + timeStamp + " " + unit.name().toLowerCase()); //NON-NLS + logger.log(Level.INFO, "There was a problem seeking to {0} {1}", new Object[]{timeStamp, unit.name().toLowerCase()}); //NON-NLS } ret = playbin.play(); @@ -395,7 +396,7 @@ public class GstVideoPanel extends MediaViewVideoPanel { } if (image == null) { - logger.log(Level.WARNING, "There was a problem while trying to capture a frame from file " + file.getName()); //NON-NLS + logger.log(Level.WARNING, "There was a problem while trying to capture a frame from file {0}", file.getName()); //NON-NLS badVideoFiles.add(file.getName()); break; } @@ -672,7 +673,7 @@ public class GstVideoPanel extends MediaViewVideoPanel { try { get(); } catch (InterruptedException | ExecutionException ex) { - logger.log(Level.WARNING, "Error updating video progress: " + ex.getMessage()); //NON-NLS + logger.log(Level.WARNING, "Error updating video progress: {0}", ex.getMessage()); //NON-NLS infoLabel.setText(NbBundle.getMessage(this.getClass(), "GstVideoPanel.progress.infoLabel.updateErr", ex.getMessage())); } // catch and ignore if we were cancelled diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaFileViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaFileViewer.java index 92da0733d8..963e3f8b39 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaFileViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaFileViewer.java @@ -30,6 +30,7 @@ import org.sleuthkit.datamodel.AbstractFile; /** * Media content viewer for videos, sounds and images. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class MediaFileViewer extends javax.swing.JPanel implements FileTypeViewer { private static final Logger LOGGER = Logger.getLogger(MediaFileViewer.class.getName()); diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java index b04473990c..da526803a8 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java @@ -48,7 +48,6 @@ import org.openide.util.NbBundle; import org.python.google.common.collect.Lists; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.coreutils.ImageUtils; -import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datamodel.FileNode; import org.sleuthkit.autopsy.directorytree.ExternalViewerAction; import org.sleuthkit.datamodel.AbstractFile; @@ -60,12 +59,11 @@ import org.sleuthkit.datamodel.AbstractFile; @NbBundle.Messages({"MediaViewImagePanel.externalViewerButton.text=Open in External Viewer", "MediaViewImagePanel.errorLabel.text=Could not load file into Media View.", "MediaViewImagePanel.errorLabel.OOMText=Could not load file into Media View: insufficent memory."}) +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPanel { private static final Image EXTERNAL = new Image(MediaViewImagePanel.class.getResource("/org/sleuthkit/autopsy/images/external.png").toExternalForm()); - private static final Logger LOGGER = Logger.getLogger(MediaViewImagePanel.class.getName()); - private final boolean fxInited; private JFXPanel fxPanel; diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java index 62b9996f22..8f08b248fd 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java @@ -71,6 +71,7 @@ import org.sleuthkit.datamodel.TskCoreException; * Shows SMS/MMS/EMail messages */ @ServiceProvider(service = DataContentViewer.class, position = 5) +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public class MessageContentViewer extends javax.swing.JPanel implements DataContentViewer { private static final long serialVersionUID = 1L; diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/Metadata.java b/Core/src/org/sleuthkit/autopsy/contentviewers/Metadata.java index 1bfa05d5b8..03a84d4181 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/Metadata.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/Metadata.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013-2014 Basis Technology Corp. + * Copyright 2013-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -36,6 +36,7 @@ import org.sleuthkit.datamodel.TskData.TSK_DB_FILES_TYPE_ENUM; * different order and allows the full path to be visible in the bottom area. */ @ServiceProvider(service = DataContentViewer.class, position = 6) +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public class Metadata extends javax.swing.JPanel implements DataContentViewer { /** @@ -222,7 +223,6 @@ public class Metadata extends javax.swing.JPanel implements DataContentViewer { @Override public void resetComponent() { setText(""); - return; } @Override diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/PListViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/PListViewer.java index 6478114314..506084f0c3 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/PListViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/PListViewer.java @@ -63,11 +63,12 @@ import org.xml.sax.SAXException; * PListViewer - a file viewer for binary plist files. * */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class PListViewer extends javax.swing.JPanel implements FileTypeViewer, ExplorerManager.Provider { private static final long serialVersionUID = 1L; private static final String[] MIMETYPES = new String[]{"application/x-bplist"}; - private static final Logger LOGGER = Logger.getLogger(PListViewer.class.getName()); + private static final Logger logger = Logger.getLogger(PListViewer.class.getName()); private final org.openide.explorer.view.OutlineView outlineView; private final Outline outline; @@ -198,7 +199,7 @@ class PListViewer extends javax.swing.JPanel implements FileTypeViewer, Explorer Bundle.PListViewer_ExportFailed_message(), JOptionPane.ERROR_MESSAGE); - LOGGER.log(Level.SEVERE, "Exception while getting open case.", ex); + logger.log(Level.SEVERE, "Exception while getting open case.", ex); return; } @@ -227,7 +228,7 @@ class PListViewer extends javax.swing.JPanel implements FileTypeViewer, Explorer Bundle.PListViewer_ExportFailed_message(), JOptionPane.ERROR_MESSAGE); - LOGGER.log(Level.SEVERE, "Error exporting plist to XML file " + selectedFile.getName(), ex); + logger.log(Level.SEVERE, "Error exporting plist to XML file " + selectedFile.getName(), ex); } } }//GEN-LAST:event_exportButtonActionPerformed @@ -305,7 +306,7 @@ class PListViewer extends javax.swing.JPanel implements FileTypeViewer, Explorer setColumnWidths(); }); } catch (InterruptedException ex) { - LOGGER.log(Level.SEVERE, "Interruption while parsing/dislaying plist file " + plistFile.getName(), ex); + logger.log(Level.SEVERE, "Interruption while parsing/dislaying plist file " + plistFile.getName(), ex); JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), ex.getMessage(), @@ -313,7 +314,7 @@ class PListViewer extends javax.swing.JPanel implements FileTypeViewer, Explorer JOptionPane.ERROR_MESSAGE); } catch (ExecutionException ex) { - LOGGER.log(Level.SEVERE, "Exception while parsing/dislaying plist file " + plistFile.getName(), ex); + logger.log(Level.SEVERE, "Exception while parsing/dislaying plist file " + plistFile.getName(), ex); JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), ex.getCause().getMessage(), Bundle.PListViewer_processPlist_errorMessage(), @@ -407,7 +408,7 @@ class PListViewer extends javax.swing.JPanel implements FileTypeViewer, Explorer pkv.setChildren(children.toArray(new PropKeyValue[children.size()])); return pkv; } else { - LOGGER.log(Level.SEVERE, "Can''t parse Plist for key = {0} value of type {1}", new Object[]{key, value.getClass()}); + logger.log(Level.SEVERE, "Can''t parse Plist for key = {0} value of type {1}", new Object[]{key, value.getClass()}); } return null; diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteTableView.java b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteTableView.java index 77c0814c55..7861a7e05d 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteTableView.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteTableView.java @@ -49,6 +49,7 @@ import org.openide.util.actions.Presenter; /** * Panel to display a SQLite table */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class SQLiteTableView extends JPanel implements ExplorerManager.Provider { private final org.openide.explorer.view.OutlineView outlineView; diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.form b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.form index 0469da7b73..d1629bdd2c 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.form +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.form @@ -16,7 +16,7 @@ - + @@ -60,7 +60,9 @@ - + + + @@ -69,6 +71,7 @@ + @@ -199,6 +202,16 @@ + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java index 250b74f36a..b7a5498bf8 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java @@ -22,6 +22,7 @@ import java.awt.BorderLayout; import java.awt.Component; import java.awt.Cursor; import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; import java.sql.Connection; import java.sql.DriverManager; @@ -39,6 +40,10 @@ import java.util.Objects; import java.util.TreeMap; import java.util.logging.Level; import javax.swing.JComboBox; +import javax.swing.JFileChooser; +import javax.swing.JOptionPane; +import javax.swing.filechooser.FileNameExtensionFilter; +import org.apache.commons.io.FilenameUtils; import org.openide.util.NbBundle; import org.openide.windows.WindowManager; import org.sleuthkit.autopsy.casemodule.Case; @@ -51,10 +56,12 @@ import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; +import org.sleuthkit.autopsy.coreutils.TimeStampUtils; /** * A file content viewer for SQLite database files. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { private static final long serialVersionUID = 1L; @@ -95,6 +102,7 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { numPagesLabel = new javax.swing.JLabel(); prevPageButton = new javax.swing.JButton(); nextPageButton = new javax.swing.JButton(); + exportCsvButton = new javax.swing.JButton(); jTableDataPanel = new javax.swing.JPanel(); jHdrPanel.setPreferredSize(new java.awt.Dimension(536, 40)); @@ -146,6 +154,13 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { } }); + org.openide.awt.Mnemonics.setLocalizedText(exportCsvButton, org.openide.util.NbBundle.getMessage(SQLiteViewer.class, "SQLiteViewer.exportCsvButton.text")); // NOI18N + exportCsvButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + exportCsvButtonActionPerformed(evt); + } + }); + javax.swing.GroupLayout jHdrPanelLayout = new javax.swing.GroupLayout(jHdrPanel); jHdrPanel.setLayout(jHdrPanelLayout); jHdrPanelLayout.setHorizontalGroup( @@ -169,13 +184,16 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { .addComponent(prevPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(0, 0, 0) .addComponent(nextPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap(133, Short.MAX_VALUE)) + .addGap(29, 29, 29) + .addComponent(exportCsvButton) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); jHdrPanelLayout.setVerticalGroup( jHdrPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(jHdrPanelLayout.createSequentialGroup() .addContainerGap() .addGroup(jHdrPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(exportCsvButton) .addComponent(nextPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(prevPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE) .addGroup(jHdrPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) @@ -195,7 +213,7 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jHdrPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(jHdrPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 569, Short.MAX_VALUE) .addComponent(jTableDataPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) ); layout.setVerticalGroup( @@ -249,9 +267,56 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); }//GEN-LAST:event_tablesDropdownListActionPerformed + /** + * The action when the Export Csv button is pressed. The file chooser window will pop + * up to choose where the user wants to save the csv file. The default location is case export directory. + * + * @param evt the action event + */ + + @NbBundle.Messages({"SQLiteViewer.csvExport.fileName.empty=Please input a file name for exporting.", + "SQLiteViewer.csvExport.title=Export to csv file", + "SQLiteViewer.csvExport.confirm.msg=Do you want to overwrite the existing file?"}) + private void exportCsvButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_exportCsvButtonActionPerformed + Case openCase = Case.getCurrentCase(); + File caseDirectory = new File(openCase.getExportDirectory()); + JFileChooser fileChooser = new JFileChooser(); + fileChooser.setDragEnabled(false); + fileChooser.setCurrentDirectory(caseDirectory); + //Set a filter to let the filechooser only work for csv files + FileNameExtensionFilter csvFilter = new FileNameExtensionFilter("*.csv", "csv"); + fileChooser.addChoosableFileFilter(csvFilter); + fileChooser.setAcceptAllFileFilterUsed(true); + fileChooser.setFileFilter(csvFilter); + fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); + int choice = fileChooser.showSaveDialog((Component) evt.getSource()); //TODO + if (JFileChooser.APPROVE_OPTION == choice) { + boolean overwrite = false; + File file = fileChooser.getSelectedFile(); + if (file == null) { + JOptionPane.showMessageDialog(this, + Bundle.SQLiteViewer_csvExport_fileName_empty(), + Bundle.SQLiteViewer_csvExport_title(), + JOptionPane.WARNING_MESSAGE); + return; + } else if (file.exists() && FilenameUtils.getExtension(file.getName()).equalsIgnoreCase("csv")) { + if (JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(this, + Bundle.SQLiteViewer_csvExport_confirm_msg(), + Bundle.SQLiteViewer_csvExport_title(), + JOptionPane.YES_NO_OPTION)) { + overwrite = true; + } else { + return; + } + } + + exportTableToCsv(file, overwrite); + } + }//GEN-LAST:event_exportCsvButtonActionPerformed // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JLabel currPageLabel; + private javax.swing.JButton exportCsvButton; private javax.swing.JPanel jHdrPanel; private javax.swing.JLabel jLabel1; private javax.swing.JLabel jLabel2; @@ -436,9 +501,11 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { prevPageButton.setEnabled(false); if (numRows > 0) { + exportCsvButton.setEnabled(true); nextPageButton.setEnabled(((numRows > ROWS_PER_PAGE))); readTable(tableName, (currPage - 1) * ROWS_PER_PAGE + 1, ROWS_PER_PAGE); } else { + exportCsvButton.setEnabled(false); nextPageButton.setEnabled(false); selectedTableView.setupTable(Collections.emptyList()); } @@ -495,4 +562,68 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { return rowlist; } + + @NbBundle.Messages({"SQLiteViewer.exportTableToCsv.write.errText=Failed to export table content to csv file.", + "SQLiteViewer.exportTableToCsv.FileName=File name: ", + "SQLiteViewer.exportTableToCsv.TableName=Table name: " + }) + private void exportTableToCsv(File file, boolean overwrite) { + String tableName = (String) this.tablesDropdownList.getSelectedItem(); + String csvFileSuffix = "_" + tableName + "_" + TimeStampUtils.createTimeStamp() + ".csv"; + try ( + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery("SELECT * FROM " + tableName)) { + List> currentTableRows = resultSetToArrayList(resultSet); + + if (Objects.isNull(currentTableRows) || currentTableRows.isEmpty()) { + logger.log(Level.INFO, String.format("The table %s is empty. (objId=%d)", tableName, sqliteDbFile.getId())); //NON-NLS + } else { + String fileName = file.getName(); + File csvFile; + if (overwrite) { + csvFile = file; + } else if (FilenameUtils.getExtension(fileName).equalsIgnoreCase("csv")) { + csvFile = new File(file.getParentFile(), FilenameUtils.removeExtension(fileName) + csvFileSuffix); + } else { + csvFile = new File(file.toString() + csvFileSuffix); + } + FileOutputStream out = new FileOutputStream(csvFile, false); + + out.write((Bundle.SQLiteViewer_exportTableToCsv_FileName() + csvFile.getName() + "\n").getBytes()); + out.write((Bundle.SQLiteViewer_exportTableToCsv_TableName() + tableName + "\n").getBytes()); + // Set up the column names + Map row = currentTableRows.get(0); + StringBuffer header = new StringBuffer(); + for (Map.Entry col : row.entrySet()) { + String colName = col.getKey(); + if (header.length() > 0) { + header.append(',').append(colName); + } else { + header.append(colName); + } + } + out.write(header.append('\n').toString().getBytes()); + + for (Map maps : currentTableRows) { + StringBuffer valueLine = new StringBuffer(); + maps.values().forEach((value) -> { + if (valueLine.length() > 0) { + valueLine.append(',').append(value.toString()); + } else { + valueLine.append(value.toString()); + } + }); + out.write(valueLine.append('\n').toString().getBytes()); + } + } + } catch (SQLException ex) { + logger.log(Level.SEVERE, String.format("Failed to read table %s from DB file '%s' (objId=%d)", tableName, sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS + MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_readTable_errorText(tableName)); + } catch (IOException ex) { + logger.log(Level.SEVERE, String.format("Failed to export table %s to file '%s'", tableName, file.getName()), ex); //NON-NLS + MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_exportTableToCsv_write_errText()); + } + } + + } diff --git a/Core/src/org/sleuthkit/autopsy/core/Installer.java b/Core/src/org/sleuthkit/autopsy/core/Installer.java index 63ac880171..e45055b077 100644 --- a/Core/src/org/sleuthkit/autopsy/core/Installer.java +++ b/Core/src/org/sleuthkit/autopsy/core/Installer.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -206,7 +206,7 @@ public class Installer extends ModuleInstall { // Prevent the Autopsy UI from shrinking on high DPI displays System.setProperty("sun.java2d.dpiaware", "false"); System.setProperty("prism.allowhidpi", "false"); - + // Update existing configuration in case of unsupported settings updateConfig(); @@ -218,16 +218,16 @@ public class Installer extends ModuleInstall { packageInstallers.add(org.sleuthkit.autopsy.centralrepository.eventlisteners.Installer.getDefault()); packageInstallers.add(org.sleuthkit.autopsy.healthmonitor.Installer.getDefault()); } - + /** * If the mode in the configuration file is 'REVIEW' (2, now invalid), this * method will set it to 'STANDALONE' (0) and disable auto ingest. */ private void updateConfig() { String mode = ModuleSettings.getConfigSetting(SETTINGS_PROPERTIES, "AutopsyMode"); - if(mode != null) { + if (mode != null) { int ordinal = Integer.parseInt(mode); - if(ordinal > 1) { + if (ordinal > 1) { UserPreferences.setMode(UserPreferences.SelectedMode.STANDALONE); ModuleSettings.setConfigSetting(UserPreferences.SETTINGS_PROPERTIES, "JoinAutoModeCluster", Boolean.toString(false)); } @@ -268,6 +268,19 @@ public class Installer extends ModuleInstall { } } + /** + * Make a folder in the config directory for object detection classifiers if one does not + * exist. + */ + private static void ensureClassifierFolderExists() { + File objectDetectionClassifierDir = new File(PlatformUtil.getObjectDetectionClassifierPath()); + objectDetectionClassifierDir.mkdir(); + } + + /** + * Make a folder in the config directory for Python Modules if one does not + * exist. + */ private static void ensurePythonModulesFolderExists() { File pythonModulesDir = new File(PlatformUtil.getUserPythonModulesPath()); pythonModulesDir.mkdir(); @@ -277,6 +290,7 @@ public class Installer extends ModuleInstall { public void restored() { super.restored(); ensurePythonModulesFolderExists(); + ensureClassifierFolderExists(); initJavaFx(); for (ModuleInstall mi : packageInstallers) { try { diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/AboutWindowPanel.java b/Core/src/org/sleuthkit/autopsy/corecomponents/AboutWindowPanel.java index 9506058b66..a7f8d2c095 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/AboutWindowPanel.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/AboutWindowPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2016 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,14 +19,12 @@ package org.sleuthkit.autopsy.corecomponents; import java.awt.Cursor; -import java.awt.Image; import java.awt.Window; import java.io.File; import java.net.MalformedURLException; import java.net.URL; import java.text.MessageFormat; import java.util.Locale; -import org.sleuthkit.autopsy.coreutils.Logger; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JPanel; @@ -45,6 +43,7 @@ import org.sleuthkit.datamodel.SleuthkitJNI; /** * Custom "About" window panel. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public final class AboutWindowPanel extends JPanel implements HyperlinkListener { private static final long serialVersionUID = 1L; @@ -212,7 +211,7 @@ private void logoLabelMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:e private void showUrl() { if (url != null) { org.openide.awt.StatusDisplayer.getDefault().setStatusText( - NbBundle.getBundle(HTMLViewAction.class).getString("CTL_OpeningBrowser")); //NON-NLS + NbBundle.getMessage(HTMLViewAction.class, "CTL_OpeningBrowser")); //NON-NLS HtmlBrowser.URLDisplayer.getDefault().showURL(url); } } diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/AdvancedConfigurationCleanDialog.java b/Core/src/org/sleuthkit/autopsy/corecomponents/AdvancedConfigurationCleanDialog.java index cccb6e1a46..4589c43755 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/AdvancedConfigurationCleanDialog.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/AdvancedConfigurationCleanDialog.java @@ -1,12 +1,24 @@ /* - * To change this template, choose Tools | Templates - * and open the template in the editor. + * Autopsy Forensic Browser + * + * Copyright 2011-2018 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.corecomponents; import java.awt.Component; -import java.awt.Dimension; -import java.awt.Toolkit; import javax.swing.JFrame; import javax.swing.JPanel; import org.openide.windows.WindowManager; @@ -16,6 +28,7 @@ import org.openide.windows.WindowManager; * the panel given to it. No additional buttons or features, except the default * close operation, which is set to dispose. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public class AdvancedConfigurationCleanDialog extends javax.swing.JDialog { /** @@ -27,6 +40,8 @@ public class AdvancedConfigurationCleanDialog extends javax.swing.JDialog { /** * Creates new form AdvancedConfigurationDialog + * + * @param resizable Is the dialog resizable? */ public AdvancedConfigurationCleanDialog(boolean resizable) { super((JFrame) WindowManager.getDefault().getMainWindow(), true); diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/AdvancedConfigurationDialog.java b/Core/src/org/sleuthkit/autopsy/corecomponents/AdvancedConfigurationDialog.java index 35a26344ea..a62d3a1459 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/AdvancedConfigurationDialog.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/AdvancedConfigurationDialog.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,26 +16,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -/* - * AdvancedConfigurationDialog.java - * - * Created on Feb 28, 2012, 4:47:31 PM - */ package org.sleuthkit.autopsy.corecomponents; import java.awt.Component; -import java.awt.Dimension; -import java.awt.Toolkit; import java.awt.event.ActionListener; import javax.swing.JFrame; import javax.swing.JPanel; import org.openide.windows.WindowManager; /** - * - * @author dfickling + * Advanced configuration dialog. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public class AdvancedConfigurationDialog extends javax.swing.JDialog { /** @@ -47,6 +39,8 @@ public class AdvancedConfigurationDialog extends javax.swing.JDialog { /** * Creates new form AdvancedConfigurationDialog + * + * @param resizable Is the dialog resizable? */ public AdvancedConfigurationDialog(boolean resizable) { super((JFrame) WindowManager.getDefault().getMainWindow(), true); diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.java b/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.java index de9a8b8e69..6c5a67a3f7 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.java @@ -66,7 +66,7 @@ import org.sleuthkit.autopsy.report.ReportBranding; "AutopsyOptionsPanel.agencyLogoPathFieldValidationLabel.pathNotSet.text=Agency logo path must be set.", "AutopsyOptionsPanel.logNumAlert.invalidInput.text=A positive integer is required here." }) - +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class AutopsyOptionsPanel extends javax.swing.JPanel { private static final long serialVersionUID = 1L; diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/CriterionChooser.java b/Core/src/org/sleuthkit/autopsy/corecomponents/CriterionChooser.java index d23091c007..29dcdbd7e8 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/CriterionChooser.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/CriterionChooser.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-17 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -32,6 +32,7 @@ import org.sleuthkit.autopsy.corecomponents.ResultViewerPersistence.SortCriterio /** * A Gui for choosing a SortCriterion from a list of available properties. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class CriterionChooser extends javax.swing.JPanel { private DefaultListCellRenderer defaultListCellRenderer = new DefaultListCellRenderer(); diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentPanel.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentPanel.java index 9b94c9a7b5..642ba5f4b0 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentPanel.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2014 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -38,8 +38,9 @@ import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.TskCoreException; /** - * + * Data content panel. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public class DataContentPanel extends javax.swing.JPanel implements DataContent, ChangeListener { private static Logger logger = Logger.getLogger(DataContentPanel.class.getName()); @@ -241,7 +242,7 @@ public class DataContentPanel extends javax.swing.JPanel implements DataContent, private static class UpdateWrapper { - private DataContentViewer wrapped; + private final DataContentViewer wrapped; private boolean outdated; UpdateWrapper(DataContentViewer wrapped) { diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentTopComponent.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentTopComponent.java index 6331d67023..af015d0b4a 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentTopComponent.java @@ -43,6 +43,7 @@ import org.sleuthkit.autopsy.coreutils.Logger; //@TopComponent.Description(preferredID = "DataContentTopComponent") //@TopComponent.Registration(mode = "output", openAtStartup = true) //@TopComponent.OpenActionRegistration(displayName = "#CTL_DataContentAction", preferredID = "DataContentTopComponent") +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public final class DataContentTopComponent extends TopComponent implements DataContent, ExplorerManager.Provider { private static final Logger logger = Logger.getLogger(DataContentTopComponent.class.getName()); diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.java index e2775df40e..a5760a26aa 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2013 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -63,6 +63,7 @@ import org.netbeans.swing.etable.ETable; * in a JTable representation of its BlackboardAttributes. */ @ServiceProvider(service = DataContentViewer.class, position = 7) +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public class DataContentViewerArtifact extends javax.swing.JPanel implements DataContentViewer { @NbBundle.Messages({ @@ -484,7 +485,8 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat if ((artifact == null) || (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID()) || (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()) - || (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID())) { + || (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID()) + || (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_OBJECT_DETECTED.getTypeID())) { return 3; } else { return 6; diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerHex.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerHex.java index 2b75990d68..6639856099 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerHex.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerHex.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2016 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -27,7 +27,6 @@ import org.openide.util.NbBundle; import org.sleuthkit.autopsy.coreutils.Logger; import javax.swing.JMenuItem; import javax.swing.JOptionPane; -import javax.swing.JTextPane; import javax.swing.text.BadLocationException; import javax.swing.text.Utilities; import org.openide.nodes.Node; @@ -40,6 +39,7 @@ import org.sleuthkit.datamodel.TskException; /** * Hex view of file contents. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives @ServiceProvider(service = DataContentViewer.class, position = 1) public class DataContentViewerHex extends javax.swing.JPanel implements DataContentViewer { diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerString.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerString.java index ff48a678f2..bc0980c58c 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerString.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerString.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -28,9 +28,7 @@ import org.openide.util.NbBundle; import org.sleuthkit.autopsy.coreutils.Logger; import javax.swing.JMenuItem; import javax.swing.JOptionPane; -import javax.swing.JTextPane; import org.openide.nodes.Node; -import org.openide.util.Lookup; import org.openide.util.lookup.ServiceProvider; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer; import org.sleuthkit.autopsy.coreutils.StringExtract; @@ -44,11 +42,12 @@ import org.sleuthkit.datamodel.TskException; * Viewer displays strings extracted from contents. */ @ServiceProvider(service = DataContentViewer.class, position = 2) +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public class DataContentViewerString extends javax.swing.JPanel implements DataContentViewer { private static long currentOffset = 0; - private static final long pageLength = 16384; - private final byte[] data = new byte[(int) pageLength]; + private static final long PAGE_LENGTH = 16384; + private final byte[] data = new byte[(int) PAGE_LENGTH]; private static int currentPage = 1; private Content dataSource; //string extract utility @@ -62,7 +61,7 @@ public class DataContentViewerString extends javax.swing.JPanel implements DataC initComponents(); customizeComponents(); this.resetComponent(); - logger.log(Level.INFO, "Created StringView instance: " + this); //NON-NLS + logger.log(Level.INFO, "Created StringView instance: {0}", this); //NON-NLS } private void customizeComponents() { @@ -264,7 +263,7 @@ public class DataContentViewerString extends javax.swing.JPanel implements DataC private void prevPageButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_prevPageButtonActionPerformed //@@@ this is part of the code dealing with the data viewer. could be copied/removed to implement the scrollbar - currentOffset -= pageLength; + currentOffset -= PAGE_LENGTH; currentPage = currentPage - 1; currentPageLabel.setText(Integer.toString(currentPage)); setDataView(dataSource, currentOffset); @@ -272,7 +271,7 @@ public class DataContentViewerString extends javax.swing.JPanel implements DataC private void nextPageButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_nextPageButtonActionPerformed //@@@ this is part of the code dealing with the data viewer. could be copied/removed to implement the scrollbar - currentOffset += pageLength; + currentOffset += PAGE_LENGTH; currentPage = currentPage + 1; currentPageLabel.setText(Integer.toString(currentPage)); setDataView(dataSource, currentOffset); @@ -281,7 +280,7 @@ public class DataContentViewerString extends javax.swing.JPanel implements DataC private void goToPageTextFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_goToPageTextFieldActionPerformed String pageNumberStr = goToPageTextField.getText(); int pageNumber; - int maxPage = Math.round((dataSource.getSize() - 1) / pageLength) + 1; + int maxPage = Math.round((dataSource.getSize() - 1) / PAGE_LENGTH) + 1; try { pageNumber = Integer.parseInt(pageNumberStr); } catch (NumberFormatException ex) { @@ -297,7 +296,7 @@ public class DataContentViewerString extends javax.swing.JPanel implements DataC JOptionPane.WARNING_MESSAGE); return; } - currentOffset = (pageNumber - 1) * pageLength; + currentOffset = (pageNumber - 1) * PAGE_LENGTH; currentPage = pageNumber; currentPageLabel.setText(Integer.toString(currentPage)); setDataView(dataSource, currentOffset); @@ -352,11 +351,11 @@ public class DataContentViewerString extends javax.swing.JPanel implements DataC String text = ""; if (dataSource.getSize() > 0) { try { - bytesRead = dataSource.read(data, offset, pageLength); // read the data + bytesRead = dataSource.read(data, offset, PAGE_LENGTH); // read the data } catch (TskException ex) { text = NbBundle.getMessage(this.getClass(), "DataContentViewerString.setDataView.errorText", currentOffset, - currentOffset + pageLength); + currentOffset + PAGE_LENGTH); logger.log(Level.WARNING, "Error while trying to show the String content.", ex); //NON-NLS } } @@ -370,15 +369,15 @@ public class DataContentViewerString extends javax.swing.JPanel implements DataC if (text.trim().isEmpty()) { text = NbBundle.getMessage(this.getClass(), "DataContentViewerString.setDataView.errorNoText", currentOffset, - currentOffset + pageLength); + currentOffset + PAGE_LENGTH); } } else { text = NbBundle.getMessage(this.getClass(), "DataContentViewerString.setDataView.errorText", currentOffset, - currentOffset + pageLength); + currentOffset + PAGE_LENGTH); } // disable or enable the next button - if (offset + pageLength < dataSource.getSize()) { + if (offset + PAGE_LENGTH < dataSource.getSize()) { nextPageButton.setEnabled(true); } else { nextPageButton.setEnabled(false); @@ -391,7 +390,7 @@ public class DataContentViewerString extends javax.swing.JPanel implements DataC prevPageButton.setEnabled(true); } - int totalPage = Math.round((dataSource.getSize() - 1) / pageLength) + 1; + int totalPage = Math.round((dataSource.getSize() - 1) / PAGE_LENGTH) + 1; totalPageLabel.setText(Integer.toString(totalPage)); currentPageLabel.setText(Integer.toString(currentPage)); outputViewPane.setText(text); // set the output view @@ -500,11 +499,7 @@ public class DataContentViewerString extends javax.swing.JPanel implements DataC return false; } Content content = node.getLookup().lookup(Content.class); - if (content != null && content.getSize() > 0) { - return true; - } - - return false; + return (content != null && content.getSize() > 0); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultPanel.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultPanel.java index 4aa7a63117..ced154edf7 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultPanel.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultPanel.java @@ -72,6 +72,7 @@ import org.sleuthkit.autopsy.datamodel.NodeSelectionInfo; * (DataContentTopComponent) that is normally docked into the lower right hand * side of the main application window, or it could be a custom content view. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public class DataResultPanel extends javax.swing.JPanel implements DataResult, ChangeListener, ExplorerManager.Provider { private static final long serialVersionUID = 1L; @@ -451,7 +452,7 @@ public class DataResultPanel extends javax.swing.JPanel implements DataResult, C } } } - }; + } if (tabToSelect == NO_TAB_SELECTED) { tabToSelect = resultViewerTabs.getSelectedIndex(); if ((tabToSelect == NO_TAB_SELECTED) || (!resultViewerTabs.isEnabledAt(tabToSelect))) { diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultTopComponent.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultTopComponent.java index dc88be9fee..29e289d67e 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultTopComponent.java @@ -68,6 +68,7 @@ import org.sleuthkit.autopsy.coreutils.Logger; * viewers to the actions global context. */ @RetainLocation("editor") +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public final class DataResultTopComponent extends TopComponent implements DataResult, ExplorerManager.Provider { private static final Logger logger = Logger.getLogger(DataResultTopComponent.class.getName()); diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java index 1089bc62f9..0e36b8fc00 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java @@ -77,6 +77,7 @@ import org.sleuthkit.autopsy.datamodel.NodeSelectionInfo; * ancestor top component's explorer manager at runtime. */ @ServiceProvider(service = DataResultViewer.class) +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public final class DataResultViewerTable extends AbstractDataResultViewer { private static final long serialVersionUID = 1L; diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.java index c3f1d19b64..ea4f4d3136 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.java @@ -71,6 +71,7 @@ import org.sleuthkit.datamodel.TskCoreException; * ancestor top component's explorer manager at runtime. */ @ServiceProvider(service = DataResultViewer.class) +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public final class DataResultViewerThumbnail extends AbstractDataResultViewer { private static final long serialVersionUID = 1L; @@ -372,10 +373,7 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer { @Override public boolean isSupported(Node selectedNode) { - if (selectedNode == null) { - return false; - } - return true; + return (selectedNode != null); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/MultiUserSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/corecomponents/MultiUserSettingsPanel.java index 20948ac77d..97a645da3c 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/MultiUserSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/MultiUserSettingsPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013-2016 Basis Technology Corp. + * Copyright 2013-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -43,6 +43,10 @@ import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchServiceException; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; +/** + * Configuration panel for multi-user settings. + */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public final class MultiUserSettingsPanel extends javax.swing.JPanel { private static final String HOST_NAME_OR_IP_PROMPT = NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.tbDbHostname.toolTipText"); diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/OfflineHelpAction.java b/Core/src/org/sleuthkit/autopsy/corecomponents/OfflineHelpAction.java index e06d61d778..3981ac63b9 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/OfflineHelpAction.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/OfflineHelpAction.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2015 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -52,8 +52,7 @@ import org.sleuthkit.autopsy.coreutils.Logger; @Messages("CTL_OfflineHelpAction=Offline Autopsy Documentation") public final class OfflineHelpAction implements ActionListener { - private URI uri; - private static final Logger Logger + private static final Logger logger = org.sleuthkit.autopsy.coreutils.Logger.getLogger(AboutWindowPanel.class.getName()); @Override @@ -71,7 +70,8 @@ public final class OfflineHelpAction implements ActionListener { String fileForHelp = ""; String indexForHelp = ""; String currentDirectory = ""; - + URI uri = null; + try { // Match the form: file:///C:/some/directory/AutopsyXYZ/docs/index.html fileForHelp = NbBundle.getMessage(OfflineHelpAction.class, "FILE_FOR_LOCAL_HELP"); @@ -79,7 +79,7 @@ public final class OfflineHelpAction implements ActionListener { currentDirectory = System.getProperty("user.dir").replace("\\", "/").replace(" ", "%20"); //NON-NLS uri = new URI(fileForHelp + currentDirectory + indexForHelp); } catch (Exception ex) { - Logger.log(Level.SEVERE, "Unable to load Offline Documentation: " + logger.log(Level.SEVERE, "Unable to load Offline Documentation: " + fileForHelp + currentDirectory + indexForHelp, ex); //NON-NLS } if (uri != null) { @@ -89,7 +89,7 @@ public final class OfflineHelpAction implements ActionListener { try { desktop.browse(uri); } catch (IOException ex) { - Logger.log(Level.SEVERE, "Unable to launch the system browser: " + logger.log(Level.SEVERE, "Unable to launch the system browser: " + fileForHelp + currentDirectory + indexForHelp, ex); //NON-NLS } } else { @@ -98,7 +98,7 @@ public final class OfflineHelpAction implements ActionListener { try { HtmlBrowser.URLDisplayer.getDefault().showURL(uri.toURL()); } catch (MalformedURLException ex) { - Logger.log(Level.SEVERE, "Unable to launch the built-in browser: " + logger.log(Level.SEVERE, "Unable to launch the built-in browser: " + fileForHelp + currentDirectory + indexForHelp, ex); //NON-NLS } } diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/SortChooser.java b/Core/src/org/sleuthkit/autopsy/corecomponents/SortChooser.java index 2c73a50a67..8c7bda3e6e 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/SortChooser.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/SortChooser.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-17 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -29,6 +29,7 @@ import org.sleuthkit.autopsy.coreutils.ThreadConfined; * A dialog that allows the user to choose sort criteria for the thumbnail * viewer. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class SortChooser extends javax.swing.JPanel { /** diff --git a/Core/src/org/sleuthkit/autopsy/coreutils/ImageUtils.java b/Core/src/org/sleuthkit/autopsy/coreutils/ImageUtils.java index 11e92836ac..5089ed41e2 100644 --- a/Core/src/org/sleuthkit/autopsy/coreutils/ImageUtils.java +++ b/Core/src/org/sleuthkit/autopsy/coreutils/ImageUtils.java @@ -59,10 +59,11 @@ import javax.imageio.stream.ImageInputStream; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.concurrent.BasicThreadFactory; -import org.opencv.core.Core; import org.openide.util.NbBundle; +import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.corelibs.OpenCvLoader; import org.sleuthkit.autopsy.corelibs.ScalrWrapper; import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector; import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector.FileTypeDetectorInitException; @@ -96,7 +97,7 @@ public class ImageUtils { private static final List SUPPORTED_IMAGE_EXTENSIONS = new ArrayList<>(); private static final SortedSet SUPPORTED_IMAGE_MIME_TYPES; - private static final boolean OPEN_CV_LOADED; + private static final boolean FFMPEG_LOADED; /** * Map from tsk object id to Java File object. Used to get the same File for @@ -105,6 +106,8 @@ public class ImageUtils { * * NOTE: Must be cleared when the case is changed. */ + @Messages({"ImageUtils.ffmpegLoadedError.title=OpenCV FFMpeg", + "ImageUtils.ffmpegLoadedError.msg=OpenCV FFMpeg library failed to load, see log for more details"}) private static final ConcurrentHashMap cacheFileMap = new ConcurrentHashMap<>(); static { @@ -117,25 +120,23 @@ public class ImageUtils { tempImage = null; } DEFAULT_THUMBNAIL = tempImage; - - //load opencv libraries - boolean openCVLoadedTemp; - try { - System.loadLibrary(Core.NATIVE_LIBRARY_NAME); - if (System.getProperty("os.arch").equals("amd64") || System.getProperty("os.arch").equals("x86_64")) { //NON-NLS - System.loadLibrary("opencv_ffmpeg248_64"); //NON-NLS - } else { - System.loadLibrary("opencv_ffmpeg248"); //NON-NLS + boolean tempFfmpegLoaded = false; + if (OpenCvLoader.isOpenCvLoaded()) { + try { + if (System.getProperty("os.arch").equals("amd64") || System.getProperty("os.arch").equals("x86_64")) { //NON-NLS + System.loadLibrary("opencv_ffmpeg2413_64"); //NON-NLS + } else { + System.loadLibrary("opencv_ffmpeg2413"); //NON-NLS + } + tempFfmpegLoaded = true; + } catch (UnsatisfiedLinkError e) { + tempFfmpegLoaded = false; + LOGGER.log(Level.SEVERE, Bundle.ImageUtils_ffmpegLoadedError_msg(), e); //NON-NLS + MessageNotifyUtil.Notify.show(Bundle.ImageUtils_ffmpegLoadedError_title(), Bundle.ImageUtils_ffmpegLoadedError_msg(), MessageNotifyUtil.MessageType.WARNING); } - - openCVLoadedTemp = true; - } catch (UnsatisfiedLinkError e) { - openCVLoadedTemp = false; - LOGGER.log(Level.SEVERE, "OpenCV Native code library failed to load", e); //NON-NLS - MessageNotifyUtil.Notify.show("Open CV", "OpenCV native library failed to load, see log for more details", MessageNotifyUtil.MessageType.WARNING); } + FFMPEG_LOADED = tempFfmpegLoaded; - OPEN_CV_LOADED = openCVLoadedTemp; SUPPORTED_IMAGE_EXTENSIONS.addAll(Arrays.asList(ImageIO.getReaderFileSuffixes())); SUPPORTED_IMAGE_EXTENSIONS.add("tec"); // Add JFIF .tec files SUPPORTED_IMAGE_EXTENSIONS.removeIf("db"::equals); // remove db files @@ -165,8 +166,8 @@ public class ImageUtils { /** * Thread/Executor that saves generated thumbnails to disk in the background */ - private static final Executor imageSaver = - Executors.newSingleThreadExecutor(new BasicThreadFactory.Builder() + private static final Executor imageSaver + = Executors.newSingleThreadExecutor(new BasicThreadFactory.Builder() .namingPattern("thumbnail-saver-%d").build()); //NON-NLS public static List getSupportedImageExtensions() { @@ -687,7 +688,7 @@ public class ImageUtils { //There was no correctly-sized cached thumbnail so make one. BufferedImage thumbnail = null; if (VideoUtils.isVideoThumbnailSupported(file)) { - if (OPEN_CV_LOADED) { + if (FFMPEG_LOADED) { updateMessage(Bundle.GetOrGenerateThumbnailTask_generatingPreviewFor(file.getName())); if (isCancelled()) { return null; diff --git a/Core/src/org/sleuthkit/autopsy/coreutils/PlatformUtil.java b/Core/src/org/sleuthkit/autopsy/coreutils/PlatformUtil.java index 7d66dc4e3c..f0a3086588 100644 --- a/Core/src/org/sleuthkit/autopsy/coreutils/PlatformUtil.java +++ b/Core/src/org/sleuthkit/autopsy/coreutils/PlatformUtil.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2012-2014 Basis Technology Corp. + * Copyright 2012-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -51,6 +51,7 @@ import org.sleuthkit.datamodel.TskCoreException; public class PlatformUtil { private static final String PYTHON_MODULES_SUBDIRECTORY = "python_modules"; //NON-NLS + private static final String CLASSIFIERS_SUBDIRECTORY = "object_detection_classifiers"; //NON-NLS private static String javaPath = null; public static final String OS_NAME_UNKNOWN = NbBundle.getMessage(PlatformUtil.class, "PlatformUtil.nameUnknown"); public static final String OS_VERSION_UNKNOWN = NbBundle.getMessage(PlatformUtil.class, "PlatformUtil.verUnknown"); @@ -116,6 +117,15 @@ public class PlatformUtil { return getUserDirectory().getAbsolutePath() + File.separator + PYTHON_MODULES_SUBDIRECTORY; } + /** + * Get root path where the user's object detection classifiers are stored. + * + * @return Absolute path to the object detection classifiers root directory. + */ + public static String getObjectDetectionClassifierPath() { + return getUserDirectory().getAbsolutePath() + File.separator + CLASSIFIERS_SUBDIRECTORY; + } + /** * get file path to the java executable binary use embedded java if * available, otherwise use system java in PATH no validation is done if @@ -310,19 +320,17 @@ public class PlatformUtil { return (System.getProperty("os.arch").contains("64")); //NON-NLS } } - - + /** - * Attempts to determine whether the JVM is 64-bit or 32-bit. - * May not be completely reliable for non-Windows operating systems. + * Attempts to determine whether the JVM is 64-bit or 32-bit. May not be + * completely reliable for non-Windows operating systems. * * @return True if the JVM is 64-bit. False otherwise. */ public static boolean is64BitJVM() { return (System.getProperty("sun.arch.data.model").equals("64")); } - - + /** * Get a list of all physical drives attached to the client's machine. Error * threshold of 4 non-existent physical drives before giving up. diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java index 3f1ca56e47..c9b271a68e 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java @@ -21,12 +21,14 @@ package org.sleuthkit.autopsy.datamodel; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; +import java.util.Arrays; import java.util.EnumSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Level; import java.util.stream.Collectors; +import javax.swing.Action; import org.apache.commons.lang3.StringUtils; import org.openide.nodes.Children; import org.openide.nodes.Sheet; @@ -36,6 +38,9 @@ import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent; import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent; +import org.sleuthkit.autopsy.centralrepository.AddEditCentralRepoCommentAction; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamArtifactUtil; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbUtil; import org.sleuthkit.autopsy.coreutils.Logger; import static org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode.AbstractFilePropertyType.*; import static org.sleuthkit.autopsy.datamodel.Bundle.*; @@ -253,6 +258,21 @@ public abstract class AbstractAbstractFileNode extends A map.put(EXTENSION.toString(), content.getNameExtension()); } + @Override + public Action[] getActions(boolean context) { + List actionsList = new ArrayList<>(); + + actionsList.addAll(Arrays.asList(super.getActions(true))); + + // Create the "Add/Edit Central Repository Comment" menu item if the enabled. + AbstractFile file = content; + if (EamDbUtil.useCentralRepo() && EamArtifactUtil.isSupportedAbstractFileType(file) && file.isFile()) { + actionsList.add(AddEditCentralRepoCommentAction.createAddEditCentralRepoCommentAction(file)); + } + + return actionsList.toArray(new Action[actionsList.size()]); + } + /** * Used by subclasses of AbstractAbstractFileNode to add the tags property * to their sheets. diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java index ab64cead5e..9e003c5be4 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java @@ -48,6 +48,8 @@ import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagAddedEvent; import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagDeletedEvent; import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent; import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent; +import org.sleuthkit.autopsy.centralrepository.AddEditCentralRepoCommentAction; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbUtil; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import static org.sleuthkit.autopsy.datamodel.DisplayableItemNode.findLinked; @@ -68,7 +70,7 @@ import org.sleuthkit.datamodel.TskCoreException; */ public class BlackboardArtifactNode extends AbstractContentNode { - private static final Logger LOGGER = Logger.getLogger(BlackboardArtifactNode.class.getName()); + private static final Logger logger = Logger.getLogger(BlackboardArtifactNode.class.getName()); private static final Set CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED, Case.Events.BLACKBOARD_ARTIFACT_TAG_DELETED, Case.Events.CONTENT_TAG_ADDED, @@ -217,6 +219,12 @@ public class BlackboardArtifactNode extends AbstractContentNode actionsList = new ArrayList<>(); actionsList.addAll(Arrays.asList(super.getActions(context))); + AbstractFile file = getLookup().lookup(AbstractFile.class); + + // Create the "Add/Edit Central Repository Comment" menu item if the enabled. + if (file != null && file.isFile() && EamDbUtil.useCentralRepo()) { + actionsList.add(AddEditCentralRepoCommentAction.createAddEditCentralRepoCommentAction(file)); + } //if this artifact has a time stamp add the action to view it in the timeline try { @@ -224,7 +232,7 @@ public class BlackboardArtifactNode extends AbstractContentNode("Tags", Bundle.BlackboardArtifactNode_createSheet_tags_displayName(), NO_DESCR, tags.stream().map(t -> t.getName().getDisplayName()).collect(Collectors.joining(", ")))); @@ -490,7 +497,7 @@ public class BlackboardArtifactNode extends AbstractContentNode sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -28,7 +28,9 @@ import org.openide.nodes.Children; import org.openide.nodes.Sheet; import org.openide.util.Lookup; import org.openide.util.NbBundle; +import org.openide.util.NbBundle.Messages; import org.openide.util.lookup.Lookups; +import org.sleuthkit.autopsy.directorytree.ViewContextAction; import org.sleuthkit.autopsy.timeline.actions.ViewFileInTimelineAction; import org.sleuthkit.datamodel.AbstractFile; @@ -107,17 +109,21 @@ public class KeyValueNode extends AbstractNode { * * @return actions */ + @Messages({ + "KeyValueNode.menuItemText.viewFileInDir=View Source File in Directory" + }) @Override public Action[] getActions(boolean popup) { - List actions = new ArrayList<>(); - actions.addAll(Arrays.asList(super.getActions(popup))); - //if this artifact has associated content, add the action to view the content in the timeline + List actionsList = new ArrayList<>(); + actionsList.addAll(Arrays.asList(super.getActions(popup))); + // If this artifact has associated content, add the actions. AbstractFile file = getLookup().lookup(AbstractFile.class); if (null != file) { - actions.add(ViewFileInTimelineAction.createViewSourceFileAction(file)); + actionsList.add(ViewFileInTimelineAction.createViewSourceFileAction(file)); + actionsList.add(new ViewContextAction(Bundle.KeyValueNode_menuItemText_viewFileInDir(), file)); } - actions.add(null); // creates a menu separator + actionsList.add(null); // creates a menu separator - return actions.toArray(new Action[actions.size()]); + return actionsList.toArray(new Action[actionsList.size()]); } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/LocalFileNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/LocalFileNode.java index c723506363..133f90c291 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/LocalFileNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/LocalFileNode.java @@ -96,8 +96,8 @@ public class LocalFileNode extends AbstractAbstractFileNode { @Override public Action[] getActions(boolean context) { List actionsList = new ArrayList<>(); - actionsList.addAll(Arrays.asList(super.getActions(true))); + actionsList.add(new ViewContextAction(NbBundle.getMessage(this.getClass(), "LocalFileNode.viewFileInDir.text"), this.content)); actionsList.add(null); // creates a menu separator actionsList.add(new NewWindowViewAction( diff --git a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/RawDSInputPanel.java b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/RawDSInputPanel.java index baf9b15223..431d71d19f 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/RawDSInputPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/RawDSInputPanel.java @@ -33,6 +33,10 @@ import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; import org.sleuthkit.autopsy.coreutils.ModuleSettings; import org.sleuthkit.autopsy.coreutils.PathValidator; +/** + * Allows examiner to supply a raw data source. + */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class RawDSInputPanel extends JPanel implements DocumentListener { private static final long TWO_GB = 2000000000L; private static final long serialVersionUID = 1L; //default diff --git a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/RawDSProcessor.java b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/RawDSProcessor.java index d98cf86527..790c4ae11e 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/RawDSProcessor.java +++ b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/RawDSProcessor.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2016 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -47,7 +47,6 @@ import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; public class RawDSProcessor implements DataSourceProcessor, AutoIngestDataSourceProcessor { private final RawDSInputPanel configPanel; - private AddRawImageTask addImageTask; private static final GeneralFilter rawFilter = new GeneralFilter(GeneralFilter.RAW_IMAGE_EXTS, GeneralFilter.RAW_IMAGE_DESC); private static final GeneralFilter encaseFilter = new GeneralFilter(GeneralFilter.ENCASE_IMAGE_EXTS, GeneralFilter.ENCASE_IMAGE_DESC); private static final List filtersList = new ArrayList<>(); @@ -163,7 +162,7 @@ public class RawDSProcessor implements DataSourceProcessor, AutoIngestDataSource * @param callback Callback to call when processing is done. */ private void run(String deviceId, String imageFilePath, String timeZone, long chunkSize, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { - addImageTask = new AddRawImageTask(deviceId, imageFilePath, timeZone, chunkSize, progressMonitor, callback); + AddRawImageTask addImageTask = new AddRawImageTask(deviceId, imageFilePath, timeZone, chunkSize, progressMonitor, callback); new Thread(addImageTask).start(); } diff --git a/Core/src/org/sleuthkit/autopsy/diagnostics/PerformancePanel.java b/Core/src/org/sleuthkit/autopsy/diagnostics/PerformancePanel.java index d50f31e383..00cd992eb1 100644 --- a/Core/src/org/sleuthkit/autopsy/diagnostics/PerformancePanel.java +++ b/Core/src/org/sleuthkit/autopsy/diagnostics/PerformancePanel.java @@ -42,6 +42,10 @@ import org.sleuthkit.datamodel.Image; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; +/** + * Display statistics on system performance. + */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public class PerformancePanel extends javax.swing.JDialog { /** diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/AddExternalViewerRuleDialog.java b/Core/src/org/sleuthkit/autopsy/directorytree/AddExternalViewerRuleDialog.java index 073f7b4eee..fad5d90b61 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/AddExternalViewerRuleDialog.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/AddExternalViewerRuleDialog.java @@ -35,6 +35,7 @@ import org.openide.windows.WindowManager; /** * A dialog for adding or editing an external viewer rule */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class AddExternalViewerRuleDialog extends JDialog { private ExternalViewerRule rule; diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/AddExternalViewerRulePanel.java b/Core/src/org/sleuthkit/autopsy/directorytree/AddExternalViewerRulePanel.java index 66705cc174..3cf1d2445a 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/AddExternalViewerRulePanel.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/AddExternalViewerRulePanel.java @@ -31,6 +31,7 @@ import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector; /** * Panel found in an AddRuleDialog */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class AddExternalViewerRulePanel extends javax.swing.JPanel { private static final Logger logger = Logger.getLogger(AddExternalViewerRulePanel.class.getName()); diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java index b188da42fc..596552262d 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java @@ -92,6 +92,7 @@ import org.sleuthkit.datamodel.TskCoreException; @Messages({ "DirectoryTreeTopComponent.resultsView.title=Listing" }) +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public final class DirectoryTreeTopComponent extends TopComponent implements DataExplorer, ExplorerManager.Provider { private final transient ExplorerManager em = new ExplorerManager(); @@ -1114,10 +1115,15 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat DisplayableItemNode undecoratedParentNode = (DisplayableItemNode) ((DirectoryTreeFilterNode) treeNode).getOriginal(); undecoratedParentNode.setChildNodeSelectionInfo(new ArtifactNodeSelectionInfo(art)); getTree().expandNode(treeNode); - try { - em.setExploredContextAndSelection(treeNode, new Node[]{treeNode}); - } catch (PropertyVetoException ex) { - LOGGER.log(Level.WARNING, "Property Veto: ", ex); //NON-NLS + if (this.getSelectedNode().equals(treeNode)) { + this.setDirectoryListingActive(); + this.respondSelection(em.getSelectedNodes(), new Node[]{treeNode}); + } else { + try { + em.setExploredContextAndSelection(treeNode, new Node[]{treeNode}); + } catch (PropertyVetoException ex) { + LOGGER.log(Level.WARNING, "Property Veto: ", ex); //NON-NLS + } } // Another thread is needed because we have to wait for dataResult to populate } diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/ExternalViewerGlobalSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/directorytree/ExternalViewerGlobalSettingsPanel.java index 29241cf02e..f5351393dc 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/ExternalViewerGlobalSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/ExternalViewerGlobalSettingsPanel.java @@ -34,6 +34,7 @@ import org.sleuthkit.autopsy.coreutils.Logger; * opening files in external viewers. Users can associate a file by either MIME * type or by extension to an executable file. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class ExternalViewerGlobalSettingsPanel extends javax.swing.JPanel implements OptionsPanel { private static final Logger logger = Logger.getLogger(ExternalViewerGlobalSettingsPanel.class.getName()); diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/FileSystemDetailsPanel.java b/Core/src/org/sleuthkit/autopsy/directorytree/FileSystemDetailsPanel.java index ce5b915b41..29fd7d3bf4 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/FileSystemDetailsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/FileSystemDetailsPanel.java @@ -27,9 +27,8 @@ import org.sleuthkit.datamodel.TskCoreException; /** * This is the form / panel to show the File System Details. - * - * @author jantonius */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class FileSystemDetailsPanel extends javax.swing.JPanel { private static final Logger logger = Logger.getLogger(FileSystemDetailsPanel.class.getName()); private static final long serialVersionUID = 1L; diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/ImageDetailsPanel.java b/Core/src/org/sleuthkit/autopsy/directorytree/ImageDetailsPanel.java index b551c11c69..e26c0605ab 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/ImageDetailsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/ImageDetailsPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,21 +16,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -/* - * ImageDetailsPanel.java - * - * Created on May 2, 2011, 3:53:49 PM - */ package org.sleuthkit.autopsy.directorytree; -import java.awt.*; import java.awt.event.ActionListener; /** - * - * @author jantonius + * Image details panel. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class ImageDetailsPanel extends javax.swing.JPanel { /** diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/VolumeDetailsPanel.java b/Core/src/org/sleuthkit/autopsy/directorytree/VolumeDetailsPanel.java index cea09df745..825de50796 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/VolumeDetailsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/VolumeDetailsPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,12 +18,12 @@ */ package org.sleuthkit.autopsy.directorytree; -import java.awt.*; import java.awt.event.ActionListener; /** * This is the form / panel to show the Volume Details. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class VolumeDetailsPanel extends javax.swing.JPanel { /** diff --git a/Core/src/org/sleuthkit/autopsy/events/RemoteEventPublisher.java b/Core/src/org/sleuthkit/autopsy/events/RemoteEventPublisher.java index b2b7267225..4afb45117a 100644 --- a/Core/src/org/sleuthkit/autopsy/events/RemoteEventPublisher.java +++ b/Core/src/org/sleuthkit/autopsy/events/RemoteEventPublisher.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -56,7 +56,6 @@ final class RemoteEventPublisher { private final MessageProducer producer; @GuardedBy("this") private final MessageConsumer consumer; - private final MessageReceiver receiver; /** * Constructs an object for publishing events to registered subscribers on @@ -84,7 +83,7 @@ final class RemoteEventPublisher { producer = session.createProducer(topic); producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT); consumer = session.createConsumer(topic, "events = '" + ALL_MESSAGE_SELECTOR + "'", true); //NON-NLS - receiver = new MessageReceiver(); + MessageReceiver receiver = new MessageReceiver(); consumer.setMessageListener(receiver); } catch (URISyntaxException | JMSException ex) { logger.log(Level.SEVERE, "Failed to connect to event channel", ex); //NON-NLS diff --git a/Core/src/org/sleuthkit/autopsy/examples/SampleContentViewer.java b/Core/src/org/sleuthkit/autopsy/examples/SampleContentViewer.java index 17c73388aa..f8f2e14902 100644 --- a/Core/src/org/sleuthkit/autopsy/examples/SampleContentViewer.java +++ b/Core/src/org/sleuthkit/autopsy/examples/SampleContentViewer.java @@ -31,7 +31,6 @@ package org.sleuthkit.autopsy.examples; import java.awt.Component; import org.openide.nodes.Node; -import org.openide.util.lookup.ServiceProvider; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.TskCoreException; @@ -45,6 +44,7 @@ import org.sleuthkit.datamodel.TskCoreException; * compiled each time to ensure that it is compliant with the API. */ // @ServiceProvider(service = DataContentViewer.class) +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class SampleContentViewer extends javax.swing.JPanel implements DataContentViewer { /** diff --git a/Core/src/org/sleuthkit/autopsy/examples/SampleIngestModuleIngestJobSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/examples/SampleIngestModuleIngestJobSettingsPanel.java index 2d0f511963..22a7cb3e2f 100644 --- a/Core/src/org/sleuthkit/autopsy/examples/SampleIngestModuleIngestJobSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/examples/SampleIngestModuleIngestJobSettingsPanel.java @@ -36,6 +36,7 @@ import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettingsPanel; /** * UI component used to make per ingest job settings for sample ingest modules. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public class SampleIngestModuleIngestJobSettingsPanel extends IngestModuleIngestJobSettingsPanel { /** diff --git a/Core/src/org/sleuthkit/autopsy/filesearch/DataSourcePanel.form b/Core/src/org/sleuthkit/autopsy/filesearch/DataSourcePanel.form index e1725962ea..2287e3b4df 100755 --- a/Core/src/org/sleuthkit/autopsy/filesearch/DataSourcePanel.form +++ b/Core/src/org/sleuthkit/autopsy/filesearch/DataSourcePanel.form @@ -6,7 +6,7 @@ - + @@ -46,8 +46,8 @@ - - + + diff --git a/Core/src/org/sleuthkit/autopsy/filesearch/DataSourcePanel.java b/Core/src/org/sleuthkit/autopsy/filesearch/DataSourcePanel.java index beb3a05277..96d7973d8a 100755 --- a/Core/src/org/sleuthkit/autopsy/filesearch/DataSourcePanel.java +++ b/Core/src/org/sleuthkit/autopsy/filesearch/DataSourcePanel.java @@ -41,6 +41,7 @@ import org.sleuthkit.datamodel.TskCoreException; /** * Subpanel with controls for data source filtering. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public class DataSourcePanel extends javax.swing.JPanel { private static final Logger logger = Logger.getLogger(DataSourcePanel.class.getName()); @@ -65,10 +66,12 @@ public class DataSourcePanel extends javax.swing.JPanel { @Override public void mouseMoved(MouseEvent evt) { - JList DsList = (JList) evt.getSource(); - int index = DsList.locationToIndex(evt.getPoint()); - if (index > -1) { - DsList.setToolTipText(toolTipList.get(index)); + if (evt.getSource() instanceof JList) { + JList dsList = (JList) evt.getSource(); + int index = dsList.locationToIndex(evt.getPoint()); + if (index > -1) { + dsList.setToolTipText(toolTipList.get(index)); + } } } }); @@ -151,7 +154,7 @@ public class DataSourcePanel extends javax.swing.JPanel { dataSourceNoteLabel = new javax.swing.JLabel(); setMinimumSize(new java.awt.Dimension(150, 150)); - setPreferredSize(new java.awt.Dimension(100, 100)); + setPreferredSize(new java.awt.Dimension(150, 150)); dataSourceList.setModel(new javax.swing.AbstractListModel() { List strings = getDataSourceArray(); @@ -194,8 +197,8 @@ public class DataSourcePanel extends javax.swing.JPanel { .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() .addComponent(dataSourceCheckBox) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 95, Short.MAX_VALUE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 103, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(dataSourceNoteLabel) .addContainerGap()) ); diff --git a/Core/src/org/sleuthkit/autopsy/filesearch/DateSearchPanel.java b/Core/src/org/sleuthkit/autopsy/filesearch/DateSearchPanel.java index dedc5a2899..d13b4490a2 100644 --- a/Core/src/org/sleuthkit/autopsy/filesearch/DateSearchPanel.java +++ b/Core/src/org/sleuthkit/autopsy/filesearch/DateSearchPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -39,6 +39,7 @@ import java.util.Date; /** * Subpanel with controls for file data filtering. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class DateSearchPanel extends javax.swing.JPanel { private final DatePickerSettings fromDateSettings = new DatePickerSettings(); diff --git a/Core/src/org/sleuthkit/autopsy/filesearch/FileSearchDialog.java b/Core/src/org/sleuthkit/autopsy/filesearch/FileSearchDialog.java index 9171104238..845c39f06c 100644 --- a/Core/src/org/sleuthkit/autopsy/filesearch/FileSearchDialog.java +++ b/Core/src/org/sleuthkit/autopsy/filesearch/FileSearchDialog.java @@ -16,24 +16,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -/* - * FileSearchDialog.java - * - * Created on Mar 5, 2012, 1:57:33 PM - */ package org.sleuthkit.autopsy.filesearch; -import org.openide.util.NbBundle; - import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JFrame; +import org.openide.util.NbBundle; import org.openide.windows.WindowManager; /** * File search dialog */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class FileSearchDialog extends javax.swing.JDialog { /** diff --git a/Core/src/org/sleuthkit/autopsy/filesearch/FileSearchPanel.form b/Core/src/org/sleuthkit/autopsy/filesearch/FileSearchPanel.form index 89fe3dc17f..f6fd00903f 100644 --- a/Core/src/org/sleuthkit/autopsy/filesearch/FileSearchPanel.form +++ b/Core/src/org/sleuthkit/autopsy/filesearch/FileSearchPanel.form @@ -70,12 +70,12 @@ - - - + + + diff --git a/Core/src/org/sleuthkit/autopsy/filesearch/FileSearchPanel.java b/Core/src/org/sleuthkit/autopsy/filesearch/FileSearchPanel.java index 0219b76367..a81afa8dca 100644 --- a/Core/src/org/sleuthkit/autopsy/filesearch/FileSearchPanel.java +++ b/Core/src/org/sleuthkit/autopsy/filesearch/FileSearchPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,17 +16,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - - /* - * FileSearchPanel.java - * - * Created on Mar 5, 2012, 1:51:50 PM - */ package org.sleuthkit.autopsy.filesearch; import java.awt.Component; import java.awt.Cursor; -import java.awt.Dimension; +import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.beans.PropertyChangeEvent; @@ -37,6 +31,7 @@ import java.util.Collections; import java.util.List; import java.util.logging.Level; import javax.swing.JLabel; +import javax.swing.JPanel; import javax.swing.border.EmptyBorder; import org.openide.DialogDisplayer; import org.openide.NotifyDescriptor; @@ -56,9 +51,10 @@ import org.sleuthkit.datamodel.TskCoreException; /** * FileSearchPanel that present search options */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class FileSearchPanel extends javax.swing.JPanel { - private final List filterAreas = new ArrayList<>(); + private final List filters = new ArrayList<>(); private static int resultWindowCount = 0; //keep track of result windows so they get unique names private static final String EMPTY_WHERE_CLAUSE = NbBundle.getMessage(DateSearchFilter.class, "FileSearchPanel.emptyWhereClause.text"); @@ -79,32 +75,57 @@ class FileSearchPanel extends javax.swing.JPanel { * This method is called from within the constructor to initialize the form. */ private void customizeComponents() { - + JLabel label = new JLabel(NbBundle.getMessage(this.getClass(), "FileSearchPanel.custComp.label.text")); label.setAlignmentX(Component.LEFT_ALIGNMENT); label.setBorder(new EmptyBorder(0, 0, 10, 0)); - filterPanel.add(label); + + JPanel panel1 = new JPanel(); + panel1.setLayout(new GridLayout(1,2)); + panel1.add(new JLabel("")); + JPanel panel2 = new JPanel(); + panel2.setLayout(new GridLayout(1,2, 20, 0)); + JPanel panel3 = new JPanel(); + panel3.setLayout(new GridLayout(1,2, 20, 0)); + JPanel panel4 = new JPanel(); + panel4.setLayout(new GridLayout(1,2, 20, 0)); + JPanel panel5 = new JPanel(); + panel5.setLayout(new GridLayout(1,2, 20, 0)); // Create and add filter areas - this.filterAreas.add(new FilterArea(NbBundle.getMessage(this.getClass(), "FileSearchPanel.filterTitle.name"), new NameSearchFilter())); - - List metadataFilters = new ArrayList<>(); - metadataFilters.add(new SizeSearchFilter()); - metadataFilters.add(new MimeTypeFilter()); - metadataFilters.add(new DateSearchFilter()); + NameSearchFilter nameFilter = new NameSearchFilter(); + SizeSearchFilter sizeFilter = new SizeSearchFilter(); + DateSearchFilter dateFilter = new DateSearchFilter(); + KnownStatusSearchFilter knowStatusFilter = new KnownStatusSearchFilter(); + HashSearchFilter hashFilter = new HashSearchFilter(); + MimeTypeFilter mimeTypeFilter = new MimeTypeFilter(); + DataSourceFilter dataSourceFilter = new DataSourceFilter(); - this.filterAreas.add(new FilterArea(NbBundle.getMessage(this.getClass(), "FileSearchPanel.filterTitle.metadata"), metadataFilters)); - - this.filterAreas.add(new FilterArea(NbBundle.getMessage(this.getClass(), "FileSearchPanel.filterTitle.knownStatus"), new KnownStatusSearchFilter())); + panel2.add(new FilterArea(NbBundle.getMessage(this.getClass(), "FileSearchPanel.filterTitle.name"),nameFilter)); + + panel3.add(new FilterArea(NbBundle.getMessage(this.getClass(), "FileSearchPanel.filterTitle.metadata"),sizeFilter)); + + panel2.add(new FilterArea(NbBundle.getMessage(this.getClass(), "FileSearchPanel.filterTitle.metadata"), dateFilter)); + panel3.add(new FilterArea(NbBundle.getMessage(this.getClass(), "FileSearchPanel.filterTitle.knownStatus"), knowStatusFilter)); + + panel5.add(new FilterArea(NbBundle.getMessage(this.getClass(), "HashSearchPanel.md5CheckBox.text"), hashFilter)); + panel5.add(new JLabel("")); + panel4.add(new FilterArea(NbBundle.getMessage(this.getClass(), "FileSearchPanel.filterTitle.metadata"), mimeTypeFilter)); + panel4.add(new FilterArea(NbBundle.getMessage(this.getClass(), "DataSourcePanel.dataSourceCheckBox.text"), dataSourceFilter)); + filterPanel.add(panel1); + filterPanel.add(panel2); + filterPanel.add(panel3); + filterPanel.add(panel4); + filterPanel.add(panel5); + + filters.add(nameFilter); + filters.add(sizeFilter); + filters.add(dateFilter); + filters.add(knowStatusFilter); + filters.add(hashFilter); + filters.add(mimeTypeFilter); + filters.add(dataSourceFilter); - this.filterAreas.add(new FilterArea(NbBundle.getMessage(this.getClass(), "HashSearchPanel.md5CheckBox.text"), new HashSearchFilter())); - this.filterAreas.add(new FilterArea(NbBundle.getMessage(this.getClass(), "DataSourcePanel.dataSourceCheckBox.text"), new DataSourceFilter())); - for (FilterArea fa : this.filterAreas) { - fa.setMaximumSize(new Dimension(Integer.MAX_VALUE, fa.getMinimumSize().height)); - fa.setAlignmentX(Component.LEFT_ALIGNMENT); - filterPanel.add(fa); - } - for (FileSearchFilter filter : this.getFilters()) { filter.addPropertyChangeListener(new PropertyChangeListener() { @Override @@ -242,12 +263,6 @@ class FileSearchPanel extends javax.swing.JPanel { } private Collection getFilters() { - Collection filters = new ArrayList<>(); - - for (FilterArea fa : this.filterAreas) { - filters.addAll(fa.getFilters()); - } - return filters; } @@ -265,10 +280,8 @@ class FileSearchPanel extends javax.swing.JPanel { void addListenerToAll(ActionListener l) { searchButton.addActionListener(l); - for (FilterArea fa : this.filterAreas) { - for (FileSearchFilter fsf : fa.getFilters()) { - fsf.addActionListener(l); - } + for (FileSearchFilter fsf : getFilters()) { + fsf.addActionListener(l); } } diff --git a/Core/src/org/sleuthkit/autopsy/filesearch/HashSearchPanel.java b/Core/src/org/sleuthkit/autopsy/filesearch/HashSearchPanel.java index 3f0d5176ac..6a5491ea08 100644 --- a/Core/src/org/sleuthkit/autopsy/filesearch/HashSearchPanel.java +++ b/Core/src/org/sleuthkit/autopsy/filesearch/HashSearchPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -27,8 +27,9 @@ import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; /** - * + * Panel to allow examiner to search for a hash value. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class HashSearchPanel extends javax.swing.JPanel { private static final long serialVersionUID = 1L; diff --git a/Core/src/org/sleuthkit/autopsy/filesearch/KnownStatusSearchPanel.java b/Core/src/org/sleuthkit/autopsy/filesearch/KnownStatusSearchPanel.java index 57d968ed23..b1a68652b2 100644 --- a/Core/src/org/sleuthkit/autopsy/filesearch/KnownStatusSearchPanel.java +++ b/Core/src/org/sleuthkit/autopsy/filesearch/KnownStatusSearchPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,20 +16,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - - /* - * KnownStatusSearchPanel.java - * - * Created on Oct 19, 2011, 11:45:44 AM - */ package org.sleuthkit.autopsy.filesearch; import javax.swing.JCheckBox; /** - * - * @author pmartel + * Search for known, unknown, and bad files. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class KnownStatusSearchPanel extends javax.swing.JPanel { /** diff --git a/Core/src/org/sleuthkit/autopsy/filesearch/MimeTypePanel.form b/Core/src/org/sleuthkit/autopsy/filesearch/MimeTypePanel.form index 772e74f9fc..fd2cc895f6 100644 --- a/Core/src/org/sleuthkit/autopsy/filesearch/MimeTypePanel.form +++ b/Core/src/org/sleuthkit/autopsy/filesearch/MimeTypePanel.form @@ -6,7 +6,7 @@ - + @@ -46,10 +46,10 @@ - - + + - + @@ -89,12 +89,12 @@ - - - + + + diff --git a/Core/src/org/sleuthkit/autopsy/filesearch/MimeTypePanel.java b/Core/src/org/sleuthkit/autopsy/filesearch/MimeTypePanel.java index 0f775a8320..4dda56ce21 100644 --- a/Core/src/org/sleuthkit/autopsy/filesearch/MimeTypePanel.java +++ b/Core/src/org/sleuthkit/autopsy/filesearch/MimeTypePanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -25,6 +25,10 @@ import javax.swing.event.ListSelectionEvent; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector; +/** + * Enter MIME types for search. + */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public class MimeTypePanel extends javax.swing.JPanel { private static final Logger logger = Logger.getLogger(MimeTypePanel.class.getName()); @@ -81,7 +85,7 @@ public class MimeTypePanel extends javax.swing.JPanel { jLabel1 = new javax.swing.JLabel(); setMinimumSize(new java.awt.Dimension(150, 150)); - setPreferredSize(new java.awt.Dimension(100, 100)); + setPreferredSize(new java.awt.Dimension(150, 150)); mimeTypeList.setModel(new javax.swing.AbstractListModel() { String[] strings = getMimeTypeArray(); @@ -98,8 +102,8 @@ public class MimeTypePanel extends javax.swing.JPanel { } }); - org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(MimeTypePanel.class, "MimeTypePanel.jLabel1.text")); // NOI18N jLabel1.setFont(new java.awt.Font("Tahoma", 0, 10)); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(MimeTypePanel.class, "MimeTypePanel.jLabel1.text")); // NOI18N javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); @@ -122,10 +126,10 @@ public class MimeTypePanel extends javax.swing.JPanel { .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() .addComponent(mimeTypeCheckBox) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 94, Short.MAX_VALUE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 103, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(jLabel1) - .addContainerGap()) + .addGap(40, 40, 40)) ); }// //GEN-END:initComponents diff --git a/Core/src/org/sleuthkit/autopsy/filesearch/NameSearchPanel.form b/Core/src/org/sleuthkit/autopsy/filesearch/NameSearchPanel.form index af80aa264a..6bcc3522bf 100644 --- a/Core/src/org/sleuthkit/autopsy/filesearch/NameSearchPanel.form +++ b/Core/src/org/sleuthkit/autopsy/filesearch/NameSearchPanel.form @@ -54,14 +54,16 @@ - - + - + + + + - + @@ -117,9 +119,11 @@ + + diff --git a/Core/src/org/sleuthkit/autopsy/filesearch/NameSearchPanel.java b/Core/src/org/sleuthkit/autopsy/filesearch/NameSearchPanel.java index 81ff01fd0f..dce02e5d90 100644 --- a/Core/src/org/sleuthkit/autopsy/filesearch/NameSearchPanel.java +++ b/Core/src/org/sleuthkit/autopsy/filesearch/NameSearchPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,12 +16,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - - /* - * NameSearchPanel.java - * - * Created on Oct 19, 2011, 11:58:53 AM - */ package org.sleuthkit.autopsy.filesearch; import java.awt.event.ActionEvent; @@ -33,9 +27,9 @@ import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; /** - * - * @author pmartel + * Provide a name for search. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class NameSearchPanel extends javax.swing.JPanel { private static final long serialVersionUID = 1L; @@ -146,7 +140,9 @@ class NameSearchPanel extends javax.swing.JPanel { searchTextField.setText(org.openide.util.NbBundle.getMessage(NameSearchPanel.class, "NameSearchPanel.searchTextField.text")); // NOI18N noteNameLabel.setFont(noteNameLabel.getFont().deriveFont(noteNameLabel.getFont().getStyle() & ~java.awt.Font.BOLD, 10)); + noteNameLabel.setHorizontalAlignment(javax.swing.SwingConstants.TRAILING); noteNameLabel.setText(org.openide.util.NbBundle.getMessage(NameSearchPanel.class, "NameSearchPanel.noteNameLabel.text")); // NOI18N + noteNameLabel.setVerticalAlignment(javax.swing.SwingConstants.TOP); noteNameLabel.setMaximumSize(new java.awt.Dimension(250, 30)); noteNameLabel.setMinimumSize(new java.awt.Dimension(250, 30)); noteNameLabel.setPreferredSize(new java.awt.Dimension(250, 40)); @@ -155,14 +151,15 @@ class NameSearchPanel extends javax.swing.JPanel { this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addGap(0, 0, 0) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addComponent(noteNameLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addComponent(noteNameLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)) .addGroup(layout.createSequentialGroup() .addComponent(nameCheckBox) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(searchTextField))) + .addComponent(searchTextField, javax.swing.GroupLayout.DEFAULT_SIZE, 191, Short.MAX_VALUE))) .addGap(0, 0, 0)) ); layout.setVerticalGroup( diff --git a/Core/src/org/sleuthkit/autopsy/filesearch/SizeSearchPanel.java b/Core/src/org/sleuthkit/autopsy/filesearch/SizeSearchPanel.java index 089b83530a..6a69ba36f9 100644 --- a/Core/src/org/sleuthkit/autopsy/filesearch/SizeSearchPanel.java +++ b/Core/src/org/sleuthkit/autopsy/filesearch/SizeSearchPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -29,9 +29,9 @@ import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; /** - * - * @author pmartel + * Provide file size for search. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class SizeSearchPanel extends javax.swing.JPanel { private static final long serialVersionUID = 1L; diff --git a/Core/src/org/sleuthkit/autopsy/healthmonitor/EnterpriseHealthMonitor.java b/Core/src/org/sleuthkit/autopsy/healthmonitor/EnterpriseHealthMonitor.java index d28ef1b475..57a5ae452d 100644 --- a/Core/src/org/sleuthkit/autopsy/healthmonitor/EnterpriseHealthMonitor.java +++ b/Core/src/org/sleuthkit/autopsy/healthmonitor/EnterpriseHealthMonitor.java @@ -34,8 +34,6 @@ import java.util.ArrayList; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.UUID; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -69,16 +67,14 @@ public final class EnterpriseHealthMonitor implements PropertyChangeListener { private final static String DATABASE_NAME = "EnterpriseHealthMonitor"; private final static long DATABASE_WRITE_INTERVAL = 60; // Minutes public static final CaseDbSchemaVersionNumber CURRENT_DB_SCHEMA_VERSION - = new CaseDbSchemaVersionNumber(1, 0); + = new CaseDbSchemaVersionNumber(1, 1); private static final AtomicBoolean isEnabled = new AtomicBoolean(false); private static EnterpriseHealthMonitor instance; - private final ExecutorService healthMonitorExecutor; - private static final String HEALTH_MONITOR_EVENT_THREAD_NAME = "Health-Monitor-Event-Listener-%d"; - private ScheduledThreadPoolExecutor healthMonitorOutputTimer; private final Map timingInfoMap; + private final List userInfoList; private static final int CONN_POOL_SIZE = 10; private BasicDataSource connectionPool = null; private CaseDbConnectionInfo connectionSettingsInUse = null; @@ -90,8 +86,9 @@ public final class EnterpriseHealthMonitor implements PropertyChangeListener { // of whether the monitor is enabled. timingInfoMap = new HashMap<>(); - // Set up the executor to handle case events - healthMonitorExecutor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat(HEALTH_MONITOR_EVENT_THREAD_NAME).build()); + // Create the list to hold user information. The list will exist regardless + // of whether the monitor is enabled. + userInfoList = new ArrayList<>(); // Get the host name try { @@ -156,12 +153,70 @@ public final class EnterpriseHealthMonitor implements PropertyChangeListener { initializeDatabaseSchema(); } + if( ! CURRENT_DB_SCHEMA_VERSION.equals(getVersion())) { + upgradeDatabaseSchema(); + } + } catch (CoordinationService.CoordinationServiceException ex) { throw new HealthMonitorException("Error releasing database lock", ex); } // Clear out any old data timingInfoMap.clear(); + userInfoList.clear(); + } + + /** + * Upgrade an older database + */ + private void upgradeDatabaseSchema() throws HealthMonitorException { + + logger.log(Level.INFO, "Upgrading Health Monitor database"); + CaseDbSchemaVersionNumber currentSchema = getVersion(); + + Connection conn = connect(); + if(conn == null) { + throw new HealthMonitorException("Error getting database connection"); + } + + try (Statement statement = conn.createStatement()) { + conn.setAutoCommit(false); + + // Upgrade from 1.0 to 1.1 + // Changes: user_data table added + if(currentSchema.compareTo(new CaseDbSchemaVersionNumber(1,1)) < 0) { + + // Add the user_data table + statement.execute("CREATE TABLE IF NOT EXISTS user_data ("+ + "id SERIAL PRIMARY KEY," + + "host text NOT NULL," + + "timestamp bigint NOT NULL," + + "event_type int NOT NULL," + + "is_examiner boolean NOT NULL," + + "case_name text NOT NULL" + + ")"); + } + + // Update the schema version + statement.execute("UPDATE db_info SET value='" + CURRENT_DB_SCHEMA_VERSION.getMajor() + "' WHERE name='SCHEMA_VERSION'"); + statement.execute("UPDATE db_info SET value='" + CURRENT_DB_SCHEMA_VERSION.getMinor() + "' WHERE name='SCHEMA_MINOR_VERSION'"); + + conn.commit(); + logger.log(Level.INFO, "Health Monitor database upgraded to version {0}", CURRENT_DB_SCHEMA_VERSION.toString()); + } catch (SQLException ex) { + try { + conn.rollback(); + } catch (SQLException ex2) { + logger.log(Level.SEVERE, "Rollback error"); + } + throw new HealthMonitorException("Error upgrading database", ex); + } finally { + try { + conn.close(); + } catch (SQLException ex) { + logger.log(Level.SEVERE, "Error closing connection.", ex); + } + } } /** @@ -207,7 +262,17 @@ public final class EnterpriseHealthMonitor implements PropertyChangeListener { * @throws HealthMonitorException */ static synchronized void startUpIfEnabled() throws HealthMonitorException { - getInstance(); + getInstance().addUserEvent(UserEvent.LOG_ON); + } + + /** + * Called when the application is closing. + * Create a log off event and write all existing metrics to the database + * @throws HealthMonitorException + */ + static synchronized void shutdown() throws HealthMonitorException { + getInstance().addUserEvent(UserEvent.LOG_OFF); + recordMetrics(); } /** @@ -318,6 +383,17 @@ public final class EnterpriseHealthMonitor implements PropertyChangeListener { } } + /** + * Add a user event to the list. + * @param eventType + */ + private void addUserEvent(UserEvent eventType) { + UserData userInfo = new UserData(eventType); + synchronized(this) { + userInfoList.add(userInfo); + } + } + /** * Time a database query. * Database queries are hard to test in normal processing because the time @@ -359,7 +435,6 @@ public final class EnterpriseHealthMonitor implements PropertyChangeListener { * @throws HealthMonitorException */ private void gatherTimerBasedMetrics() throws HealthMonitorException { - // Time a database query performDatabaseQuery(); } @@ -370,6 +445,7 @@ public final class EnterpriseHealthMonitor implements PropertyChangeListener { private void writeCurrentStateToDatabase() throws HealthMonitorException { Map timingMapCopy; + List userDataCopy; // Do as little as possible within the synchronized block since it will // block threads attempting to record metrics. @@ -382,10 +458,13 @@ public final class EnterpriseHealthMonitor implements PropertyChangeListener { // per metric name. timingMapCopy = new HashMap<>(timingInfoMap); timingInfoMap.clear(); + + userDataCopy = new ArrayList<>(userInfoList); + userInfoList.clear(); } - // Check if there's anything to report (right now we only have the timing map) - if(timingMapCopy.keySet().isEmpty()) { + // Check if there's anything to report + if(timingMapCopy.keySet().isEmpty() && userDataCopy.isEmpty()) { return; } @@ -402,22 +481,33 @@ public final class EnterpriseHealthMonitor implements PropertyChangeListener { throw new HealthMonitorException("Error getting database connection"); } - // Add timing metrics to the database + // Add metrics to the database String addTimingInfoSql = "INSERT INTO timing_data (name, host, timestamp, count, average, max, min) VALUES (?, ?, ?, ?, ?, ?, ?)"; - try (PreparedStatement statement = conn.prepareStatement(addTimingInfoSql)) { + String addUserInfoSql = "INSERT INTO user_data (host, timestamp, event_type, is_examiner, case_name) VALUES (?, ?, ?, ?, ?)"; + try (PreparedStatement timingStatement = conn.prepareStatement(addTimingInfoSql); + PreparedStatement userStatement = conn.prepareStatement(addUserInfoSql)) { for(String name:timingMapCopy.keySet()) { TimingInfo info = timingMapCopy.get(name); - statement.setString(1, name); - statement.setString(2, hostName); - statement.setLong(3, System.currentTimeMillis()); - statement.setLong(4, info.getCount()); - statement.setDouble(5, info.getAverage()); - statement.setDouble(6, info.getMax()); - statement.setDouble(7, info.getMin()); + timingStatement.setString(1, name); + timingStatement.setString(2, hostName); + timingStatement.setLong(3, System.currentTimeMillis()); + timingStatement.setLong(4, info.getCount()); + timingStatement.setDouble(5, info.getAverage()); + timingStatement.setDouble(6, info.getMax()); + timingStatement.setDouble(7, info.getMin()); - statement.execute(); + timingStatement.execute(); + } + + for(UserData userInfo:userDataCopy) { + userStatement.setString(1, hostName); + userStatement.setLong(2, userInfo.getTimestamp()); + userStatement.setInt(3, userInfo.getEventType().getEventValue()); + userStatement.setBoolean(4, userInfo.isExaminerNode()); + userStatement.setString(5, userInfo.getCaseName()); + userStatement.execute(); } } catch (SQLException ex) { @@ -757,9 +847,8 @@ public final class EnterpriseHealthMonitor implements PropertyChangeListener { try (Statement statement = conn.createStatement()) { conn.setAutoCommit(false); - - String createTimingTable = - "CREATE TABLE IF NOT EXISTS timing_data (" + + + statement.execute("CREATE TABLE IF NOT EXISTS timing_data (" + "id SERIAL PRIMARY KEY," + "name text NOT NULL," + "host text NOT NULL," + @@ -768,16 +857,23 @@ public final class EnterpriseHealthMonitor implements PropertyChangeListener { "average double precision NOT NULL," + "max double precision NOT NULL," + "min double precision NOT NULL" + - ")"; - statement.execute(createTimingTable); + ")"); - String createDbInfoTable = - "CREATE TABLE IF NOT EXISTS db_info (" + + statement.execute("CREATE TABLE IF NOT EXISTS db_info (" + "id SERIAL PRIMARY KEY NOT NULL," + "name text NOT NULL," + "value text NOT NULL" + - ")"; - statement.execute(createDbInfoTable); + ")"); + + statement.execute("CREATE TABLE IF NOT EXISTS user_data ("+ + "id SERIAL PRIMARY KEY," + + "host text NOT NULL," + + "timestamp bigint NOT NULL," + + "event_type int NOT NULL," + + "is_examiner BOOLEAN NOT NULL," + + "case_name text NOT NULL" + + ")"); + statement.execute("INSERT INTO db_info (name, value) VALUES ('SCHEMA_VERSION', '" + CURRENT_DB_SCHEMA_VERSION.getMajor() + "')"); statement.execute("INSERT INTO db_info (name, value) VALUES ('SCHEMA_MINOR_VERSION', '" + CURRENT_DB_SCHEMA_VERSION.getMinor() + "')"); @@ -802,25 +898,32 @@ public final class EnterpriseHealthMonitor implements PropertyChangeListener { /** * The task called by the ScheduledThreadPoolExecutor to handle - * the database checks/writes. + * the periodic database update */ static final class PeriodicHealthMonitorTask implements Runnable { - /** - * Perform all periodic tasks: - * - Check if monitoring has been enabled / disabled in the database - * - Gather any additional metrics - * - Write current metric data to the database - */ @Override public void run() { - try { - getInstance().updateFromGlobalEnabledStatus(); + recordMetrics(); + } + } + + /** + * Perform all periodic tasks: + * - Check if monitoring has been enabled / disabled in the database + * - Gather any additional metrics + * - Write current metric data to the database + * Do not run this from a new thread if the case/application is closing. + */ + private static void recordMetrics() { + try { + getInstance().updateFromGlobalEnabledStatus(); + if(monitorIsEnabled()) { getInstance().gatherTimerBasedMetrics(); getInstance().writeCurrentStateToDatabase(); - } catch (HealthMonitorException ex) { - logger.log(Level.SEVERE, "Error performing periodic task", ex); //NON-NLS } + } catch (HealthMonitorException ex) { + logger.log(Level.SEVERE, "Error performing periodic task", ex); //NON-NLS } } @@ -831,8 +934,12 @@ public final class EnterpriseHealthMonitor implements PropertyChangeListener { case CURRENT_CASE: if ((null == evt.getNewValue()) && (evt.getOldValue() instanceof Case)) { - // When a case is closed, write the current metrics to the database - healthMonitorExecutor.submit(new EnterpriseHealthMonitor.PeriodicHealthMonitorTask()); + // Case is closing + addUserEvent(UserEvent.CASE_CLOSE); + + } else if((null == evt.getOldValue()) && (evt.getNewValue() instanceof Case)) { + // Case is opening + addUserEvent(UserEvent.CASE_OPEN); } break; } @@ -855,7 +962,8 @@ public final class EnterpriseHealthMonitor implements PropertyChangeListener { throw new HealthMonitorException("Error getting database lock"); } - String[] metricNames = {"Disk Reads: Hash calculation", "Database: getImages query", "Solr: Index chunk", "Solr: Connectivity check"}; // NON-NLS + String[] metricNames = {"Disk Reads: Hash calculation", "Database: getImages query", "Solr: Index chunk", "Solr: Connectivity check", + "Correlation Engine: Notable artifact query", "Correlation Engine: Bulk insert"}; // NON-NLS Random rand = new Random(); @@ -1046,6 +1154,47 @@ public final class EnterpriseHealthMonitor implements PropertyChangeListener { } } + /** + * Get user metrics currently stored in the database. + * @param timeRange Maximum age for returned metrics (in milliseconds) + * @return A list of user metrics + * @throws HealthMonitorException + */ + List getUserMetricsFromDatabase(long timeRange) throws HealthMonitorException { + + // Make sure the monitor is enabled. It could theoretically get disabled after this + // check but it doesn't seem worth holding a lock to ensure that it doesn't since that + // may slow down ingest. + if(! isEnabled.get()) { + throw new HealthMonitorException("Health Monitor is not enabled"); + } + + // Calculate the smallest timestamp we should return + long minimumTimestamp = System.currentTimeMillis() - timeRange; + + try (CoordinationService.Lock lock = getSharedDbLock()) { + if(lock == null) { + throw new HealthMonitorException("Error getting database lock"); + } + + List resultList = new ArrayList<>(); + + try (Connection conn = connect(); + Statement statement = conn.createStatement(); + ResultSet resultSet = statement.executeQuery("SELECT * FROM user_data WHERE timestamp > " + minimumTimestamp)) { + + while (resultSet.next()) { + resultList.add(new UserData(resultSet)); + } + return resultList; + } catch (SQLException ex) { + throw new HealthMonitorException("Error reading user metrics from database", ex); + } + } catch (CoordinationService.CoordinationServiceException ex) { + throw new HealthMonitorException("Error getting database lock", ex); + } + } + /** * Get an exclusive lock for the health monitor database. * Acquire this before creating, initializing, or updating the database schema. @@ -1085,6 +1234,164 @@ public final class EnterpriseHealthMonitor implements PropertyChangeListener { } } + /** + * Types of user events being logged + */ + enum UserEvent { + LOG_ON(0), + LOG_OFF(1), + CASE_OPEN(2), + CASE_CLOSE(3); + + int value; + + UserEvent(int value) { + this.value = value; + } + + /** + * Get the integer value of the event to store in the database. + * @return value corresponding to the event + */ + int getEventValue() { + return value; + } + + /** + * Get the UserEvent from the value stored in the database + * @param value + * @return the corresponding UserEvent object + * @throws HealthMonitorException + */ + static UserEvent valueOf(int value) throws HealthMonitorException { + for (UserEvent v : UserEvent.values()) { + if (v.value == value) { + return v; + } + } + throw new HealthMonitorException("Can not create UserEvent from unknown value " + value); + } + + /** + * Return whether a case is considered to be open given this event + * as the last recorded event. + * @return true if a case is open, false otherwise + */ + boolean caseIsOpen() { + return(this.equals(CASE_OPEN)); + } + + /** + * Return whether a user is considered to be logged in given this event + * as the last recorded event. + * @return true if a the user is logged in, false otherwise + */ + boolean userIsLoggedIn() { + // LOG_ON, CASE_OPEN, and CASE_CLOSED events all imply that the user + // is logged in + return( ! this.equals(LOG_OFF)); + } + } + + /** + * Class holding user metric data. + * Can be used for storing new events or retrieving + * events out of the database. + */ + static class UserData { + private final UserEvent eventType; + private long timestamp; + private final boolean isExaminer; + private final String hostname; + private String caseName; + + /** + * Create a new UserData object using the given event type + * and the current settings. + * @param eventType The type of event being recorded + */ + private UserData(UserEvent eventType) { + this.eventType = eventType; + this.timestamp = System.currentTimeMillis(); + this.isExaminer = (UserPreferences.SelectedMode.STANDALONE == UserPreferences.getMode()); + this.hostname = ""; + + // If there's a case open, record the name + try { + this.caseName = Case.getCurrentCaseThrows().getDisplayName(); + } catch (NoCurrentCaseException ex) { + // It's not an error if there's no case open + this.caseName = ""; + } + } + + /** + * Create a UserData object from a database result set. + * @param resultSet The result set containing the data + * @throws SQLException + * @throws HealthMonitorException + */ + UserData(ResultSet resultSet) throws SQLException, HealthMonitorException { + this.timestamp = resultSet.getLong("timestamp"); + this.hostname = resultSet.getString("host"); + this.eventType = UserEvent.valueOf(resultSet.getInt("event_type")); + this.isExaminer = resultSet.getBoolean("is_examiner"); + this.caseName = resultSet.getString("case_name"); + } + + /** + * This should only be used to make a dummy object to use for timestamp + * comparisons. + * @param timestamp + * @return A UserData object with the given timestamp + */ + static UserData createDummyUserData(long timestamp) { + UserData userData = new UserData(UserEvent.CASE_CLOSE); + userData.timestamp = timestamp; + return userData; + } + + /** + * Get the timestamp for the event + * @return Timestamp in milliseconds + */ + long getTimestamp() { + return timestamp; + } + + /** + * Get the host that created the metric + * @return the host name + */ + String getHostname() { + return hostname; + } + + /** + * Get the type of event + * @return the event type + */ + UserEvent getEventType() { + return eventType; + } + + /** + * Check whether this node is an examiner node or an auto ingest node + * @return true if it is an examiner node + */ + boolean isExaminerNode() { + return isExaminer; + } + + /** + * Get the name of the case for this metric + * @return the case name. Will be the empty string if no case was open. + */ + String getCaseName() { + return caseName; + } + } + /** * Internal class for collecting timing metrics. * Instead of storing each TimingMetric, we only store the min and max diff --git a/Core/src/org/sleuthkit/autopsy/healthmonitor/HealthMonitorDashboard.java b/Core/src/org/sleuthkit/autopsy/healthmonitor/HealthMonitorDashboard.java index 9dbd08233e..07f8fcced7 100644 --- a/Core/src/org/sleuthkit/autopsy/healthmonitor/HealthMonitorDashboard.java +++ b/Core/src/org/sleuthkit/autopsy/healthmonitor/HealthMonitorDashboard.java @@ -25,6 +25,7 @@ import java.util.Set; import java.util.HashSet; import java.util.HashMap; import java.util.Arrays; +import java.util.ArrayList; import java.util.List; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -61,13 +62,16 @@ public class HealthMonitorDashboard { private final static String ADMIN_ACCESS_FILE_PATH = Paths.get(Places.getUserDirectory().getAbsolutePath(), ADMIN_ACCESS_FILE_NAME).toString(); Map> timingData; + List userData; - private JComboBox dateComboBox = null; - private JComboBox hostComboBox = null; - private JCheckBox hostCheckBox = null; - private JCheckBox showTrendLineCheckBox = null; - private JCheckBox skipOutliersCheckBox = null; - private JPanel graphPanel = null; + private JComboBox timingDateComboBox = null; + private JComboBox timingHostComboBox = null; + private JCheckBox timingHostCheckBox = null; + private JCheckBox timingShowTrendLineCheckBox = null; + private JCheckBox timingSkipOutliersCheckBox = null; + private JPanel timingGraphPanel = null; + private JComboBox userDateComboBox = null; + private JPanel userGraphPanel = null; private JDialog dialog = null; private final Container parentWindow; @@ -78,6 +82,7 @@ public class HealthMonitorDashboard { */ public HealthMonitorDashboard(Container parent) { timingData = new HashMap<>(); + userData = new ArrayList<>(); parentWindow = parent; } @@ -91,10 +96,12 @@ public class HealthMonitorDashboard { // Update the enabled status and get the timing data, then create all // the sub panels. JPanel timingPanel; + JPanel userPanel; JPanel adminPanel; try { updateData(); timingPanel = createTimingPanel(); + userPanel = createUserPanel(); adminPanel = createAdminPanel(); } catch (HealthMonitorException ex) { logger.log(Level.SEVERE, "Error creating panels for health monitor dashboard", ex); @@ -109,6 +116,9 @@ public class HealthMonitorDashboard { // Add the timing panel mainPanel.add(timingPanel); + // Add the user panel + mainPanel.add(userPanel); + // Add the admin panel if the admin file is present File adminFile = new File(ADMIN_ACCESS_FILE_PATH); if(adminFile.exists()) { @@ -148,6 +158,9 @@ public class HealthMonitorDashboard { if(EnterpriseHealthMonitor.monitorIsEnabled()) { // Get a copy of the timing data from the database timingData = EnterpriseHealthMonitor.getInstance().getTimingMetricsFromDatabase(DateRange.getMaximumTimestampRange()); + + // Get a copy of the user data from the database + userData = EnterpriseHealthMonitor.getInstance().getUserMetricsFromDatabase(DateRange.getMaximumTimestampRange()); } } @@ -185,12 +198,12 @@ public class HealthMonitorDashboard { timingMetricPanel.add(new JSeparator()); // Create panel to hold graphs - graphPanel = new JPanel(); - graphPanel.setLayout(new GridLayout(0,2)); + timingGraphPanel = new JPanel(); + timingGraphPanel.setLayout(new GridLayout(0,2)); // Update the graph panel, put it in a scroll pane, and add to the timing metric panel updateTimingMetricGraphs(); - JScrollPane scrollPane = new JScrollPane(graphPanel, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + JScrollPane scrollPane = new JScrollPane(timingGraphPanel, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); timingMetricPanel.add(scrollPane); timingMetricPanel.revalidate(); timingMetricPanel.repaint(); @@ -216,11 +229,11 @@ public class HealthMonitorDashboard { // Create the combo box for selecting how much data to display String[] dateOptionStrings = Arrays.stream(DateRange.values()).map(e -> e.getLabel()).toArray(String[]::new); - dateComboBox = new JComboBox<>(dateOptionStrings); - dateComboBox.setSelectedItem(DateRange.ONE_DAY.getLabel()); + timingDateComboBox = new JComboBox<>(dateOptionStrings); + timingDateComboBox.setSelectedItem(DateRange.ONE_DAY.getLabel()); // Set up the listener on the date combo box - dateComboBox.addActionListener(new ActionListener() { + timingDateComboBox.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent arg0) { try { @@ -240,14 +253,14 @@ public class HealthMonitorDashboard { } // Load the host names into the combo box - hostComboBox = new JComboBox<>(hostNameSet.toArray(new String[hostNameSet.size()])); + timingHostComboBox = new JComboBox<>(hostNameSet.toArray(new String[hostNameSet.size()])); // Set up the listener on the combo box - hostComboBox.addActionListener(new ActionListener() { + timingHostComboBox.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent arg0) { try { - if((hostCheckBox != null) && hostCheckBox.isSelected()) { + if((timingHostCheckBox != null) && timingHostCheckBox.isSelected()) { updateTimingMetricGraphs(); } } catch (HealthMonitorException ex) { @@ -257,16 +270,16 @@ public class HealthMonitorDashboard { }); // Create the host checkbox - hostCheckBox = new JCheckBox(Bundle.HealthMonitorDashboard_createTimingControlPanel_filterByHost()); - hostCheckBox.setSelected(false); - hostComboBox.setEnabled(false); + timingHostCheckBox = new JCheckBox(Bundle.HealthMonitorDashboard_createTimingControlPanel_filterByHost()); + timingHostCheckBox.setSelected(false); + timingHostComboBox.setEnabled(false); // Set up the listener on the checkbox - hostCheckBox.addActionListener(new ActionListener() { + timingHostCheckBox.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent arg0) { try { - hostComboBox.setEnabled(hostCheckBox.isSelected()); + timingHostComboBox.setEnabled(timingHostCheckBox.isSelected()); updateTimingMetricGraphs(); } catch (HealthMonitorException ex) { logger.log(Level.SEVERE, "Error populating timing metric panel", ex); @@ -275,11 +288,11 @@ public class HealthMonitorDashboard { }); // Create the checkbox for showing the trend line - showTrendLineCheckBox = new JCheckBox(Bundle.HealthMonitorDashboard_createTimingControlPanel_showTrendLine()); - showTrendLineCheckBox.setSelected(true); + timingShowTrendLineCheckBox = new JCheckBox(Bundle.HealthMonitorDashboard_createTimingControlPanel_showTrendLine()); + timingShowTrendLineCheckBox.setSelected(true); // Set up the listener on the checkbox - showTrendLineCheckBox.addActionListener(new ActionListener() { + timingShowTrendLineCheckBox.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent arg0) { try { @@ -291,11 +304,11 @@ public class HealthMonitorDashboard { }); // Create the checkbox for omitting outliers - skipOutliersCheckBox = new JCheckBox(Bundle.HealthMonitorDashboard_createTimingControlPanel_skipOutliers()); - skipOutliersCheckBox.setSelected(false); + timingSkipOutliersCheckBox = new JCheckBox(Bundle.HealthMonitorDashboard_createTimingControlPanel_skipOutliers()); + timingSkipOutliersCheckBox.setSelected(false); // Set up the listener on the checkbox - skipOutliersCheckBox.addActionListener(new ActionListener() { + timingSkipOutliersCheckBox.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent arg0) { try { @@ -308,26 +321,26 @@ public class HealthMonitorDashboard { // Add the date range combo box and label to the panel timingControlPanel.add(new JLabel(Bundle.HealthMonitorDashboard_createTimingControlPanel_maxDays())); - timingControlPanel.add(dateComboBox); + timingControlPanel.add(timingDateComboBox); // Put some space between the elements timingControlPanel.add(Box.createHorizontalStrut(100)); // Add the host combo box and checkbox to the panel - timingControlPanel.add(hostCheckBox); - timingControlPanel.add(hostComboBox); + timingControlPanel.add(timingHostCheckBox); + timingControlPanel.add(timingHostComboBox); // Put some space between the elements timingControlPanel.add(Box.createHorizontalStrut(100)); // Add the skip outliers checkbox - timingControlPanel.add(this.showTrendLineCheckBox); + timingControlPanel.add(this.timingShowTrendLineCheckBox); // Put some space between the elements timingControlPanel.add(Box.createHorizontalStrut(100)); // Add the skip outliers checkbox - timingControlPanel.add(this.skipOutliersCheckBox); + timingControlPanel.add(this.timingSkipOutliersCheckBox); return timingControlPanel; } @@ -340,11 +353,11 @@ public class HealthMonitorDashboard { private void updateTimingMetricGraphs() throws HealthMonitorException { // Clear out any old graphs - graphPanel.removeAll(); + timingGraphPanel.removeAll(); if(timingData.keySet().isEmpty()) { // There are no timing metrics in the database - graphPanel.add(new JLabel(Bundle.HealthMonitorDashboard_updateTimingMetricGraphs_noData())); + timingGraphPanel.add(new JLabel(Bundle.HealthMonitorDashboard_updateTimingMetricGraphs_noData())); return; } @@ -352,8 +365,8 @@ public class HealthMonitorDashboard { // If necessary, trim down the list of results to fit the selected time range List intermediateTimingDataForDisplay; - if(dateComboBox.getSelectedItem() != null) { - DateRange selectedDateRange = DateRange.fromLabel(dateComboBox.getSelectedItem().toString()); + if(timingDateComboBox.getSelectedItem() != null) { + DateRange selectedDateRange = DateRange.fromLabel(timingDateComboBox.getSelectedItem().toString()); long threshold = System.currentTimeMillis() - selectedDateRange.getTimestampRange(); intermediateTimingDataForDisplay = timingData.get(metricName).stream() .filter(t -> t.getTimestamp() > threshold) @@ -366,25 +379,150 @@ public class HealthMonitorDashboard { // The graph always uses the data from all hosts to generate the x and y scales // so we don't filter anything out here. String hostToDisplay = null; - if(hostCheckBox.isSelected() && (hostComboBox.getSelectedItem() != null)) { - hostToDisplay = hostComboBox.getSelectedItem().toString(); + if(timingHostCheckBox.isSelected() && (timingHostComboBox.getSelectedItem() != null)) { + hostToDisplay = timingHostComboBox.getSelectedItem().toString(); } // Generate the graph TimingMetricGraphPanel singleTimingGraphPanel = new TimingMetricGraphPanel(intermediateTimingDataForDisplay, - hostToDisplay, true, metricName, skipOutliersCheckBox.isSelected(), showTrendLineCheckBox.isSelected()); + hostToDisplay, true, metricName, timingSkipOutliersCheckBox.isSelected(), timingShowTrendLineCheckBox.isSelected()); singleTimingGraphPanel.setPreferredSize(new Dimension(700,200)); - graphPanel.add(singleTimingGraphPanel); + timingGraphPanel.add(singleTimingGraphPanel); } - graphPanel.revalidate(); - graphPanel.repaint(); + timingGraphPanel.revalidate(); + timingGraphPanel.repaint(); + } + + /** + * Create the user panel. + * This displays cases open and users logged in + * @return the user panel + */ + @NbBundle.Messages({"HealthMonitorDashboard.createUserPanel.noData=No data to display - monitor is not enabled", + "HealthMonitorDashboard.createUserPanel.userMetricsTitle=User Metrics"}) + private JPanel createUserPanel() throws HealthMonitorException { + // If the monitor isn't enabled, just add a message + if(! EnterpriseHealthMonitor.monitorIsEnabled()) { + JPanel emptyUserMetricPanel = new JPanel(); + emptyUserMetricPanel.add(new JLabel(Bundle.HealthMonitorDashboard_createUserPanel_userMetricsTitle())); + emptyUserMetricPanel.add(new JLabel(" ")); + emptyUserMetricPanel.add(new JLabel(Bundle.HealthMonitorDashboard_createUserPanel_noData())); + + return emptyUserMetricPanel; + } + + JPanel userMetricPanel = new JPanel(); + userMetricPanel.setLayout(new BoxLayout(userMetricPanel, BoxLayout.PAGE_AXIS)); + userMetricPanel.setBorder(BorderFactory.createEtchedBorder()); + + // Add title + JLabel userMetricTitle = new JLabel(Bundle.HealthMonitorDashboard_createUserPanel_userMetricsTitle()); + userMetricPanel.add(userMetricTitle); + userMetricPanel.add(new JSeparator()); + + // Add the controls + userMetricPanel.add(createUserControlPanel()); + userMetricPanel.add(new JSeparator()); + + // Create panel to hold graphs + userGraphPanel = new JPanel(); + userGraphPanel.setLayout(new GridLayout(0,2)); + + // Update the graph panel, put it in a scroll pane, and add to the timing metric panel + updateUserMetricGraphs(); + JScrollPane scrollPane = new JScrollPane(userGraphPanel, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + userMetricPanel.add(scrollPane); + userMetricPanel.revalidate(); + userMetricPanel.repaint(); + + return userMetricPanel; + } + + /** + * Create the panel with controls for the user panel + * @return the control panel + */ + @NbBundle.Messages({"HealthMonitorDashboard.createUserControlPanel.maxDays=Max days to display"}) + private JPanel createUserControlPanel() { + JPanel userControlPanel = new JPanel(); + + // If the monitor is not enabled, don't add any components + if(! EnterpriseHealthMonitor.monitorIsEnabled()) { + return userControlPanel; + } + + // Create the combo box for selecting how much data to display + String[] dateOptionStrings = Arrays.stream(DateRange.values()).map(e -> e.getLabel()).toArray(String[]::new); + userDateComboBox = new JComboBox<>(dateOptionStrings); + userDateComboBox.setSelectedItem(DateRange.ONE_DAY.getLabel()); + + // Set up the listener on the date combo box + userDateComboBox.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent arg0) { + try { + updateUserMetricGraphs(); + } catch (HealthMonitorException ex) { + logger.log(Level.SEVERE, "Error updating user metric panel", ex); + } + } + }); + + // Add the date range combo box and label to the panel + userControlPanel.add(new JLabel(Bundle.HealthMonitorDashboard_createUserControlPanel_maxDays())); + userControlPanel.add(userDateComboBox); + + return userControlPanel; + } + + /** + * Update the user graphs. + * @throws HealthMonitorException + */ + @NbBundle.Messages({"HealthMonitorDashboard.updateUserMetricGraphs.noData=No data to display"}) + private void updateUserMetricGraphs() throws HealthMonitorException { + + // Clear out any old graphs + userGraphPanel.removeAll(); + + if(userData.isEmpty()) { + // There are no user metrics in the database + userGraphPanel.add(new JLabel(Bundle.HealthMonitorDashboard_updateUserMetricGraphs_noData())); + return; + } + + // Calculate the minimum timestamp for the graph. + // Unlike the timing graphs, we do not filter the list of user metrics here. + // This is because even if we're only displaying one day, the + // last metric for a host may be that it logged on two days ago, so we would want + // to show that node as logged on. + long timestampThreshold; + if(userDateComboBox.getSelectedItem() != null) { + DateRange selectedDateRange = DateRange.fromLabel(userDateComboBox.getSelectedItem().toString()); + timestampThreshold = System.currentTimeMillis() - selectedDateRange.getTimestampRange(); + + } else { + timestampThreshold = System.currentTimeMillis() - DateRange.getMaximumTimestampRange(); + } + + // Generate the graphs + UserMetricGraphPanel caseGraphPanel = new UserMetricGraphPanel(userData, timestampThreshold, true); + caseGraphPanel.setPreferredSize(new Dimension(700,200)); + + UserMetricGraphPanel logonGraphPanel = new UserMetricGraphPanel(userData, timestampThreshold, false); + logonGraphPanel.setPreferredSize(new Dimension(700,200)); + + userGraphPanel.add(caseGraphPanel); + userGraphPanel.add(logonGraphPanel); + userGraphPanel.revalidate(); + userGraphPanel.repaint(); } /** * Create the admin panel. * This allows the health monitor to be enabled and disabled. - * @return + * @return the admin panel */ @NbBundle.Messages({"HealthMonitorDashboard.createAdminPanel.enableButton=Enable monitor", "HealthMonitorDashboard.createAdminPanel.disableButton=Disable monitor"}) diff --git a/Core/src/org/sleuthkit/autopsy/healthmonitor/Installer.java b/Core/src/org/sleuthkit/autopsy/healthmonitor/Installer.java index 61ea5a5244..187513a84a 100644 --- a/Core/src/org/sleuthkit/autopsy/healthmonitor/Installer.java +++ b/Core/src/org/sleuthkit/autopsy/healthmonitor/Installer.java @@ -50,4 +50,13 @@ public class Installer extends ModuleInstall { logger.log(Level.SEVERE, "Error starting health services monitor", ex); } } + + @Override + public void close() { + try { + EnterpriseHealthMonitor.shutdown(); + } catch (HealthMonitorException ex) { + logger.log(Level.SEVERE, "Error stopping health services monitor", ex); + } + } } \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/healthmonitor/TimingMetricGraphPanel.java b/Core/src/org/sleuthkit/autopsy/healthmonitor/TimingMetricGraphPanel.java index 6d995ed9ac..a540ea8c6b 100644 --- a/Core/src/org/sleuthkit/autopsy/healthmonitor/TimingMetricGraphPanel.java +++ b/Core/src/org/sleuthkit/autopsy/healthmonitor/TimingMetricGraphPanel.java @@ -44,6 +44,7 @@ import org.sleuthkit.autopsy.healthmonitor.EnterpriseHealthMonitor.DatabaseTimin /** * Creates a graph of the given timing metric data */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class TimingMetricGraphPanel extends JPanel { private final static Logger logger = Logger.getLogger(TimingMetricGraphPanel.class.getName()); @@ -373,7 +374,7 @@ class TimingMetricGraphPanel extends JPanel { } } else if (y0value > maxValueOnYAxis) { try { - y0value = minValueOnYAxis; + y0value = maxValueOnYAxis; x0value = trendLine.getXGivenY(y0value); } catch (HealthMonitorException ex) { // The exception is caused by a slope of zero on the trend line, which diff --git a/Core/src/org/sleuthkit/autopsy/healthmonitor/UserMetricGraphPanel.java b/Core/src/org/sleuthkit/autopsy/healthmonitor/UserMetricGraphPanel.java new file mode 100644 index 0000000000..d8da7afe97 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/healthmonitor/UserMetricGraphPanel.java @@ -0,0 +1,455 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2018 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.healthmonitor; + +import java.awt.Color; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.util.Collections; +import java.util.Comparator; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.HashMap; +import java.util.HashSet; +import java.util.TreeSet; +import java.util.Calendar; +import java.util.GregorianCalendar; +import javax.swing.JPanel; +import java.util.TimeZone; +import java.util.concurrent.TimeUnit; +import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.healthmonitor.EnterpriseHealthMonitor.UserData; + +/** + * Creates graphs using the given user metric data + */ +class UserMetricGraphPanel extends JPanel { + + private static final int padding = 25; + private static final int labelPadding = 25; + private final Color examinerColor = new Color(0x12, 0x20, 0xdb, 255); + private final Color autoIngestColor = new Color(0x12, 0x80, 0x20, 255); + private final Color gridColor = new Color(200, 200, 200, 200); + private static final int pointWidth = 4; + private static final int numberYDivisions = 10; + private final List dataToPlot; + private final String graphLabel; + private final long dataInterval; + private final long MILLISECONDS_PER_HOUR = 1000 * 60 * 60; + private final long MILLISECONDS_PER_DAY = MILLISECONDS_PER_HOUR * 24; + private final long maxTimestamp; + private final long minTimestamp; + private int maxCount; + private static final int minCount = 0; // The bottom of the graph will always be zero + + @NbBundle.Messages({"UserMetricGraphPanel.constructor.casesOpen=Cases open", + "UserMetricGraphPanel.constructor.loggedIn=Users logged in - examiner nodes in blue, auto ingest nodes in green" + }) + UserMetricGraphPanel(List userResults, long timestampThreshold, boolean plotCases) { + + maxTimestamp = System.currentTimeMillis(); + minTimestamp = timestampThreshold; + + // Make the label + if (plotCases) { + graphLabel = Bundle.UserMetricGraphPanel_constructor_casesOpen(); + } else { + graphLabel = Bundle.UserMetricGraphPanel_constructor_loggedIn(); + } + + // Comparator for the set of UserData objects + Comparator sortOnTimestamp = new Comparator() { + @Override + public int compare(UserData o1, UserData o2) { + return Long.compare(o1.getTimestamp(), o2.getTimestamp()); + } + }; + + // Create a map from host name to data and get the timestamp bounds. + // We're using TreeSets here because they support the floor function. + Map> userDataMap = new HashMap<>(); + for(UserData result:userResults) { + if(userDataMap.containsKey(result.getHostname())) { + userDataMap.get(result.getHostname()).add(result); + } else { + TreeSet resultTreeSet = new TreeSet<>(sortOnTimestamp); + resultTreeSet.add(result); + userDataMap.put(result.getHostname(), resultTreeSet); + } + } + + // Create a list of data points to plot + // The idea here is that starting at maxTimestamp, we go backwards in increments, + // see what the state of each node was at that time and make the counts of nodes + // that are logged in/ have a case open. + // A case is open if the last event was "case open"; closed otherwise + // A user is logged in if the last event was anything but "log out";logged out otherwise + dataToPlot = new ArrayList<>(); + dataInterval = MILLISECONDS_PER_HOUR; + maxCount = Integer.MIN_VALUE; + for (long timestamp = maxTimestamp;timestamp > minTimestamp;timestamp -= dataInterval) { + + // Collect both counts so that we can use the same scale in the open case graph and + // the logged in users graph + UserCount openCaseCount = new UserCount(timestamp); + UserCount loggedInUserCount = new UserCount(timestamp); + + Set openCaseNames = new HashSet<>(); + UserData timestampUserData = UserData.createDummyUserData(timestamp); + + for (String hostname:userDataMap.keySet()) { + // Get the most recent record before this timestamp + UserData lastRecord = userDataMap.get(hostname).floor(timestampUserData); + + if (lastRecord != null) { + + // Update the case count. + if (lastRecord.getEventType().caseIsOpen()) { + + // Only add each case once regardless of how many users have it open + if ( ! openCaseNames.contains(lastRecord.getCaseName())) { + + // Store everything as examiner nodes. The graph will represent + // the number of distinct cases open, not anything about the + // nodes that have them open. + openCaseCount.addExaminer(); + openCaseNames.add(lastRecord.getCaseName()); + } + } + + // Update the logged in user count + if (lastRecord.getEventType().userIsLoggedIn()) { + if(lastRecord.isExaminerNode()) { + loggedInUserCount.addExaminer(); + } else { + loggedInUserCount.addAutoIngestNode(); + } + } + } + } + + // Check if this is a new maximum. + // Assuming we log all the events, there should never be more cases open than + // there are logged in users, but it could happen if we lose data. + maxCount = Integer.max(maxCount, openCaseCount.getTotalNodeCount()); + maxCount = Integer.max(maxCount, loggedInUserCount.getTotalNodeCount()); + + // Add the count to be plotted + if(plotCases) { + dataToPlot.add(openCaseCount); + } else { + dataToPlot.add(loggedInUserCount); + } + } + } + + /** + * Setup of the graphics panel: + * Origin (0,0) is at the top left corner + * + * Horizontally (from the left): (padding)(label padding)(the graph)(padding) + * For plotting data on the x-axis, we scale it to the size of the graph and then add the padding and label padding + * + * Vertically (from the top): (padding)(the graph)(label padding)(padding) + * For plotting data on the y-axis, we subtract from the max value in the graph and then scale to the size of the graph + * @param g + */ + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + Graphics2D g2 = (Graphics2D) g; + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + // Get the max and min timestamps to create the x-axis. + // We add a small buffer to each side so the data won't overwrite the axes. + double maxValueOnXAxis = maxTimestamp + TimeUnit.HOURS.toMillis(2); // Two hour buffer (the last bar graph will take up one of the hours) + double minValueOnXAxis = minTimestamp - TimeUnit.HOURS.toMillis(1); // One hour buffer + + // Get the max and min times to create the y-axis + // To make the intervals even, make sure the maximum is a multiple of five + if((maxCount % 5) != 0) { + maxCount += (5 - (maxCount % 5)); + } + int maxValueOnYAxis = Integer.max(maxCount, 5); + int minValueOnYAxis = minCount; + + // The graph itself has the following corners: + // (padding + label padding, padding + font height) -> top left + // (padding + label padding, getHeight() - label padding - padding) -> bottom left + // (getWidth() - padding, padding + font height) -> top right + // (padding + label padding, getHeight() - label padding - padding) -> bottom right + int leftGraphPadding = padding + labelPadding; + int rightGraphPadding = padding; + int topGraphPadding = padding + g2.getFontMetrics().getHeight(); + int bottomGraphPadding = labelPadding; + + // Calculate the scale for each axis. + // The size of the graph area is the width/height of the panel minus any padding. + // The scale is calculated based on this size of the graph compared to the data range. + // For example: + // getWidth() = 575 => graph width = 500 + // If our max x value to plot is 10000 and our min is 0, then the xScale would be 0.05 - i.e., + // our original x values will be multipled by 0.05 to translate them to an x-coordinate in the + // graph (plus the padding) + int graphWidth = getWidth() - leftGraphPadding - rightGraphPadding; + int graphHeight = getHeight() - topGraphPadding - bottomGraphPadding; + double xScale = ((double) graphWidth) / (maxValueOnXAxis - minValueOnXAxis); + double yScale = ((double) graphHeight) / (maxValueOnYAxis - minValueOnYAxis); + + // Draw white background + g2.setColor(Color.WHITE); + g2.fillRect(leftGraphPadding, topGraphPadding, graphWidth, graphHeight); + + // Create hatch marks and grid lines for y axis. + int labelWidth; + int positionForMetricNameLabel = 0; + Map countToGraphPosition = new HashMap<>(); + for (int i = 0; i < numberYDivisions + 1; i++) { + int x0 = leftGraphPadding; + int x1 = pointWidth + leftGraphPadding; + int y0 = getHeight() - ((i * graphHeight) / numberYDivisions + bottomGraphPadding); + int y1 = y0; + + if ( ! dataToPlot.isEmpty()) { + // Draw the grid line + g2.setColor(gridColor); + g2.drawLine(leftGraphPadding + 1 + pointWidth, y0, getWidth() - rightGraphPadding, y1); + + // Create the label + g2.setColor(Color.BLACK); + double yValue = minValueOnYAxis + ((maxValueOnYAxis - minValueOnYAxis) * ((i * 1.0) / numberYDivisions)); + int intermediateLabelVal = (int) (yValue * 100); + if ((i == numberYDivisions) || ((intermediateLabelVal % 100) == 0)) { + countToGraphPosition.put(intermediateLabelVal / 100, y0); + String yLabel = Integer.toString(intermediateLabelVal / 100); + FontMetrics fontMetrics = g2.getFontMetrics(); + labelWidth = fontMetrics.stringWidth(yLabel); + g2.drawString(yLabel, x0 - labelWidth - 5, y0 + (fontMetrics.getHeight() / 2) - 3); + + // The nicest looking alignment for this label seems to be left-aligned with the top + // y-axis label. Save this position to be used to write the label later. + if (i == numberYDivisions) { + positionForMetricNameLabel = x0 - labelWidth - 5; + } + } + } + + // Draw the small hatch mark + g2.setColor(Color.BLACK); + g2.drawLine(x0, y0, x1, y1); + } + + // On the x-axis, the farthest right grid line should represent midnight preceding the last recorded value + Calendar maxDate = new GregorianCalendar(); + maxDate.setTimeInMillis(maxTimestamp); + maxDate.set(Calendar.HOUR_OF_DAY, 0); + maxDate.set(Calendar.MINUTE, 0); + maxDate.set(Calendar.SECOND, 0); + maxDate.set(Calendar.MILLISECOND, 0); + long maxMidnightInMillis = maxDate.getTimeInMillis(); + + // We don't want to display more than 20 grid lines. If we have more + // data then that, put multiple days within one division + long totalDays = (maxMidnightInMillis - (long)minValueOnXAxis) / MILLISECONDS_PER_DAY; + long daysPerDivision; + if(totalDays <= 20) { + daysPerDivision = 1; + } else { + daysPerDivision = (totalDays / 20); + if((totalDays % 20) != 0) { + daysPerDivision++; + } + } + + // Draw the vertical grid lines and labels + // The vertical grid lines will be at midnight, and display the date underneath them + // At present we use GMT because of some complications with daylight savings time. + for (long currentDivision = maxMidnightInMillis; currentDivision >= minValueOnXAxis; currentDivision -= MILLISECONDS_PER_DAY * daysPerDivision) { + + int x0 = (int) ((currentDivision - minValueOnXAxis) * xScale + leftGraphPadding); + int x1 = x0; + int y0 = getHeight() - bottomGraphPadding; + int y1 = y0 - pointWidth; + + // Draw the light grey grid line + g2.setColor(gridColor); + g2.drawLine(x0, getHeight() - bottomGraphPadding - 1 - pointWidth, x1, topGraphPadding); + + // Draw the hatch mark + g2.setColor(Color.BLACK); + g2.drawLine(x0, y0, x1, y1); + + // Draw the label + Calendar thisDate = new GregorianCalendar(); + thisDate.setTimeZone(TimeZone.getTimeZone("GMT")); // Stick with GMT to avoid daylight savings issues + thisDate.setTimeInMillis(currentDivision); + int month = thisDate.get(Calendar.MONTH) + 1; + int day = thisDate.get(Calendar.DAY_OF_MONTH); + + String xLabel = month + "/" + day; + FontMetrics metrics = g2.getFontMetrics(); + labelWidth = metrics.stringWidth(xLabel); + g2.drawString(xLabel, x0 - labelWidth / 2, y0 + metrics.getHeight() + 3); + } + + // Create x and y axes + g2.setColor(Color.BLACK); + g2.drawLine(leftGraphPadding, getHeight() - bottomGraphPadding, leftGraphPadding, topGraphPadding); + g2.drawLine(leftGraphPadding, getHeight() - bottomGraphPadding, getWidth() - rightGraphPadding, getHeight() - bottomGraphPadding); + + // Sort dataToPlot on timestamp + Collections.sort(dataToPlot, new Comparator(){ + @Override + public int compare(UserCount o1, UserCount o2){ + return Long.compare(o1.getTimestamp(), o2.getTimestamp()); + } + }); + + // Create the bars + for(int i = 0;i < dataToPlot.size();i++) { + UserCount userCount = dataToPlot.get(i); + int x = (int) ((userCount.getTimestamp() - minValueOnXAxis) * xScale + leftGraphPadding); + int yTopOfExaminerBox; + if(countToGraphPosition.containsKey(userCount.getTotalNodeCount())) { + // If we've drawn a grid line for this count, use the recorded value. If we don't do + // this, rounding differences lead to the bar graph not quite lining up with the existing grid. + yTopOfExaminerBox = countToGraphPosition.get(userCount.getTotalNodeCount()); + } else { + yTopOfExaminerBox = (int) ((maxValueOnYAxis - userCount.getTotalNodeCount()) * yScale + topGraphPadding); + } + + // Calculate the width. If this isn't the last column, set this to one less than + // the distance to the next column starting point. + int width; + if(i < dataToPlot.size() - 1) { + width = Integer.max((int)((dataToPlot.get(i + 1).getTimestamp() - minValueOnXAxis) * xScale + leftGraphPadding) - x - 1, + 1); + } else { + width = Integer.max((int)(dataInterval * xScale), 1); + } + + // The examiner bar goes all the way to the bottom of the graph. + // The bottom will be overwritten by the auto ingest bar for displaying + // logged in users. + int heightExaminerBox = (getHeight() - bottomGraphPadding) - yTopOfExaminerBox; + + // Plot the examiner bar + g2.setColor(examinerColor); + g2.fillRect(x, yTopOfExaminerBox, width, heightExaminerBox); + + // Check that there is an auto ingest node count before plotting its bar. + // For the cases open graph, this will always be empty. + if (userCount.getAutoIngestNodeCount() > 0) { + int yTopOfAutoIngestBox; + if(countToGraphPosition.containsKey(userCount.getAutoIngestNodeCount())) { + // As above, if we've drawn a grid line for this count, use the recorded value. If we don't do + // this, rounding differences lead to the bar graph not quite lining up with the existing grid. + yTopOfAutoIngestBox =countToGraphPosition.get(userCount.getAutoIngestNodeCount()); + } else { + yTopOfAutoIngestBox = yTopOfExaminerBox + heightExaminerBox; + } + int heightAutoIngestBox = (getHeight() - bottomGraphPadding) - yTopOfAutoIngestBox; + + // Plot the auto ingest bar + g2.setColor(autoIngestColor); + g2.fillRect(x, yTopOfAutoIngestBox, width, heightAutoIngestBox); + } + } + + // The graph lines may have extended up past the bounds of the graph. Overwrite that + // area with the original background color. + g2.setColor(this.getBackground()); + g2.fillRect(leftGraphPadding, 0, graphWidth, topGraphPadding); + + // Write the scale. Do this after we erase the top block of the graph. + g2.setColor(Color.BLACK); + String titleStr = graphLabel; + g2.drawString(titleStr, positionForMetricNameLabel, padding); + } + + /** + * Utility class to keep track of the data to be graphed. + * Used for tracking logging in users and open cases. + */ + private class UserCount { + private final long timestamp; + private int examinerCount; + private int autoIngestCount; + + /** + * Create a UserCount object with counts initialized to zero. + * @param timestamp + */ + UserCount(long timestamp) { + this.timestamp = timestamp; + this.examinerCount = 0; + this.autoIngestCount = 0; + } + + /** + * Add one examiner node to the count. + */ + void addExaminer() { + examinerCount++; + } + + /** + * Add one auto ingest node to the count. + */ + void addAutoIngestNode() { + autoIngestCount++; + } + + /** + * Get the number of examiner nodes. + * @return number of examiner nodes + */ + int getExaminerNodeCount() { + return examinerCount; + } + + /** + * Get the number of auto ingest nodes. + * @return number of auto ingest nodes + */ + int getAutoIngestNodeCount() { + return autoIngestCount; + } + + /** + * Get the total number of nodes + * @return the sum of the examiner and auto ingest nodes + */ + int getTotalNodeCount() { + return examinerCount + autoIngestCount; + } + + /** + * Get the timestamp for this metric + * @return the timestamp + */ + long getTimestamp() { + return timestamp; + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/ingest/DataSourceIngestCancellationPanel.java b/Core/src/org/sleuthkit/autopsy/ingest/DataSourceIngestCancellationPanel.java index 2d13e3fadb..318dc6e05a 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/DataSourceIngestCancellationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/DataSourceIngestCancellationPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2014 Basis Technology Corp. + * Copyright 2014-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,6 +22,7 @@ package org.sleuthkit.autopsy.ingest; * A UI panel that allows a user to make data source ingest cancellation * requests. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class DataSourceIngestCancellationPanel extends javax.swing.JPanel { private boolean cancelAllIngestModules; diff --git a/Core/src/org/sleuthkit/autopsy/ingest/DataSourceIngestJob.java b/Core/src/org/sleuthkit/autopsy/ingest/DataSourceIngestJob.java index 93a1f1505d..6563dd3540 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/DataSourceIngestJob.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/DataSourceIngestJob.java @@ -22,7 +22,7 @@ import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.Date; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; @@ -224,8 +224,8 @@ public final class DataSourceIngestJob { /** * Make mappings of ingest module factory class names to templates. */ - Map dataSourceModuleTemplates = new HashMap<>(); - Map fileModuleTemplates = new HashMap<>(); + Map dataSourceModuleTemplates = new LinkedHashMap<>(); + Map fileModuleTemplates = new LinkedHashMap<>(); for (IngestModuleTemplate template : ingestModuleTemplates) { if (template.isDataSourceIngestModuleTemplate()) { dataSourceModuleTemplates.put(template.getModuleFactory().getClass().getCanonicalName(), template); diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettingsPanel.java index 88fb0b347e..dcaaca9c35 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettingsPanel.java @@ -58,6 +58,7 @@ import org.sleuthkit.autopsy.modules.interestingitems.FilesSetsManager; /** * A panel to allow a user to make ingest job settings. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public final class IngestJobSettingsPanel extends javax.swing.JPanel { private static final long serialVersionUID = 1L; diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestMessageDetailsPanel.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestMessageDetailsPanel.java index 17645ccb68..27400766b0 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestMessageDetailsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestMessageDetailsPanel.java @@ -18,7 +18,7 @@ */ package org.sleuthkit.autopsy.ingest; -import java.awt.*; +import java.awt.Cursor; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JMenuItem; @@ -36,6 +36,7 @@ import org.sleuthkit.datamodel.TskException; /** * Details panel within IngestMessagePanel */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class IngestMessageDetailsPanel extends javax.swing.JPanel { private final IngestMessageMainPanel mainPanel; @@ -240,7 +241,7 @@ class IngestMessageDetailsPanel extends javax.swing.JPanel { this.messageDetailsPane.setText(""); } //show artifact/content only for a message group with a single message - BlackboardArtifact artifact = messageGroup.getData();; + BlackboardArtifact artifact = messageGroup.getData(); if (artifact != null && messageGroup.getCount() == 1) { viewArtifactButton.setEnabled(true); diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestMessageMainPanel.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestMessageMainPanel.java index 1fa70462cc..d2dd0c2ad2 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestMessageMainPanel.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestMessageMainPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2013 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -25,6 +25,7 @@ import org.sleuthkit.autopsy.coreutils.Logger; * the main layered pane container for messages table (IngestMessagePanel) and * details view (IngestMessageDetailsPanel) */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class IngestMessageMainPanel extends javax.swing.JPanel { private IngestMessagePanel messagePanel; diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestMessagePanel.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestMessagePanel.java index f9063b289c..a8f4d39dea 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestMessagePanel.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestMessagePanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2014 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -36,6 +36,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.logging.Level; +import javax.swing.DefaultComboBoxModel; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTable; @@ -51,14 +52,13 @@ import javax.swing.table.TableCellRenderer; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; -import org.sleuthkit.autopsy.ingest.IngestMessage.*; import org.sleuthkit.autopsy.ingest.IngestMessage.MessageType; import org.sleuthkit.datamodel.BlackboardArtifact; /** * Notification window showing messages from modules to user - * */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class IngestMessagePanel extends JPanel implements TableModelListener { private final MessageTableModel tableModel; @@ -245,7 +245,7 @@ class IngestMessagePanel extends JPanel implements TableModelListener { * It is not possible to internationalize the list of options in a ComboBox * inside of the generated form code. So, it is done here. */ - sortByComboBox.setModel(new javax.swing.DefaultComboBoxModel(new String[] { + sortByComboBox.setModel(new DefaultComboBoxModel<>(new String[] { NbBundle.getMessage(this.getClass(), "IngestMessagePanel.sortByComboBox.model.time"), NbBundle.getMessage(this.getClass(), "IngestMessagePanel.sortByComboBox.model.priority")})); @@ -519,7 +519,7 @@ class IngestMessagePanel extends JPanel implements TableModelListener { if (moduleName != null && m.getMessageType() == IngestMessage.MessageType.DATA) { //not a manager message, a data message, then group if (!groupings.containsKey(moduleName)) { - groupings.put(moduleName, new HashMap>()); + groupings.put(moduleName, new HashMap<>()); } final Map> groups = groupings.get(moduleName); //groups for this uniqueness @@ -564,7 +564,7 @@ class IngestMessagePanel extends JPanel implements TableModelListener { messageGroup = first; //move to bottom of table //remove from existing position - int toRemove = 0; + int toRemove; while ((toRemove = getTableEntryIndex(uniqueness)) != -1) { messageData.remove(toRemove); //remove the row, will be added to the bottom diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestMessageTopComponent.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestMessageTopComponent.java index 04052b1ce6..767c19b52f 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestMessageTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestMessageTopComponent.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2014 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -40,6 +40,7 @@ import org.sleuthkit.datamodel.Content; /** * Top component which displays something. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class IngestMessageTopComponent extends TopComponent { private static IngestMessageTopComponent instance; diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestMessagesToolbar.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestMessagesToolbar.java index 432cb48810..261ef90352 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestMessagesToolbar.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestMessagesToolbar.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2015 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -37,6 +37,7 @@ import org.sleuthkit.autopsy.core.RuntimeProperties; * Tool bar for an ingest messages button that allows a user to open the ingest * messages inbox top component. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class IngestMessagesToolbar extends javax.swing.JPanel { private IngestMessagesButton ingestMessagesButton = new IngestMessagesButton(); diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleFactoryLoader.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleFactoryLoader.java index 3d2c66deb9..a8df988a01 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleFactoryLoader.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleFactoryLoader.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2014 Basis Technology Corp. + * Copyright 2014-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -38,6 +38,7 @@ import org.sleuthkit.autopsy.modules.hashdatabase.HashLookupModuleFactory; import org.sleuthkit.autopsy.modules.interestingitems.InterestingItemsIngestModuleFactory; import org.sleuthkit.autopsy.modules.photoreccarver.PhotoRecCarverIngestModuleFactory; import org.sleuthkit.autopsy.modules.embeddedfileextractor.EmbeddedFileExtractorModuleFactory; +import org.sleuthkit.autopsy.modules.encryptiondetection.EncryptionDetectionModuleFactory; import org.sleuthkit.autopsy.python.JythonModuleLoader; /** @@ -60,6 +61,7 @@ final class IngestModuleFactoryLoader { add("org.sleuthkit.autopsy.thunderbirdparser.EmailParserModuleFactory"); //NON-NLS add(FileExtMismatchDetectorModuleFactory.class.getCanonicalName()); add(E01VerifierModuleFactory.class.getCanonicalName()); + add(EncryptionDetectionModuleFactory.class.getCanonicalName()); add(InterestingItemsIngestModuleFactory.class.getCanonicalName()); add(PhotoRecCarverIngestModuleFactory.class.getCanonicalName()); } diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestOptionsPanel.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestOptionsPanel.java index 40f3d1c298..cbfd996473 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestOptionsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestOptionsPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -32,6 +32,7 @@ import org.sleuthkit.autopsy.modules.interestingitems.FilesSetDefsPanel.PANEL_TY /** * Global options panel for keyword searching. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public class IngestOptionsPanel extends IngestModuleGlobalSettingsPanel implements OptionsPanel { @NbBundle.Messages({"IngestOptionsPanel.settingsTab.text=Settings", diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestProgressSnapshotDialog.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestProgressSnapshotDialog.java index eddebd59c4..c86f90ad4b 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestProgressSnapshotDialog.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestProgressSnapshotDialog.java @@ -35,6 +35,7 @@ import org.openide.windows.WindowManager; /** * A dialog that displays ingest task progress snapshots. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public final class IngestProgressSnapshotDialog extends JDialog { private static final String TITLE = NbBundle.getMessage(IngestProgressSnapshotDialog.class, "IngestProgressSnapshotDialog.title.text"); diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestProgressSnapshotPanel.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestProgressSnapshotPanel.java index be4661ce5b..0448382309 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestProgressSnapshotPanel.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestProgressSnapshotPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2014 Basis Technology Corp. + * Copyright 2014-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -33,6 +33,7 @@ import org.openide.util.NbBundle; /** * A panel that displays ingest task progress snapshots. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class IngestProgressSnapshotPanel extends javax.swing.JPanel { private final JDialog parent; diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestSettingsPanel.java index ed02db96cb..11b3d1f349 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestSettingsPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013-2017 Basis Technology Corp. + * Copyright 2013-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -30,6 +30,7 @@ import org.sleuthkit.autopsy.core.UserPreferences; /** * Options panel that allow users to set application preferences. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class IngestSettingsPanel extends IngestModuleGlobalSettingsPanel { IngestSettingsPanel() { diff --git a/Core/src/org/sleuthkit/autopsy/ingest/ProfilePanel.java b/Core/src/org/sleuthkit/autopsy/ingest/ProfilePanel.java index ec09fbca6e..dc145a6824 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/ProfilePanel.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/ProfilePanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -31,6 +31,7 @@ import org.sleuthkit.autopsy.ingest.IngestProfiles.IngestProfile; /** * Panel to display options for profile creation and editing. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class ProfilePanel extends IngestModuleGlobalSettingsPanel { @NbBundle.Messages({"ProfilePanel.title.text=Profile", diff --git a/Core/src/org/sleuthkit/autopsy/ingest/ProfileSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/ingest/ProfileSettingsPanel.java index 7e97fe0e7d..bb0c75e750 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/ProfileSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/ProfileSettingsPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -35,6 +35,10 @@ import org.sleuthkit.autopsy.ingest.IngestProfiles.IngestProfile; import org.sleuthkit.autopsy.modules.interestingitems.FilesSet; import org.sleuthkit.autopsy.modules.interestingitems.FilesSetsManager; +/** + * Panel for managing ingest module profiles. + */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class ProfileSettingsPanel extends IngestModuleGlobalSettingsPanel implements OptionsPanel { @NbBundle.Messages({"ProfileSettingsPanel.title=Profile Settings", diff --git a/Core/src/org/sleuthkit/autopsy/ingest/runIngestModuleWizard/IngestProfileSelectionPanel.java b/Core/src/org/sleuthkit/autopsy/ingest/runIngestModuleWizard/IngestProfileSelectionPanel.java index e16fc98aa0..12cc78ecd5 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/runIngestModuleWizard/IngestProfileSelectionPanel.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/runIngestModuleWizard/IngestProfileSelectionPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -44,6 +44,7 @@ import org.sleuthkit.autopsy.ingest.IngestProfiles.IngestProfile; * Visual panel for the choosing of ingest profiles by the user when running * ingest. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class IngestProfileSelectionPanel extends JPanel { @Messages({"IngestProfileSelectionPanel.customSettings.name=Custom Settings", diff --git a/Core/src/org/sleuthkit/autopsy/ingest/runIngestModuleWizard/RunIngestModulesWizardIterator.java b/Core/src/org/sleuthkit/autopsy/ingest/runIngestModuleWizard/RunIngestModulesWizardIterator.java index add27f8146..451b0e4bc7 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/runIngestModuleWizard/RunIngestModulesWizardIterator.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/runIngestModuleWizard/RunIngestModulesWizardIterator.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -35,7 +35,6 @@ import org.sleuthkit.datamodel.Content; final class RunIngestModulesWizardIterator implements WizardDescriptor.Iterator { private final static String PROP_LASTPROFILE_NAME = "RIMW_LASTPROFILE_NAME"; //NON-NLS - private final IngestJobSettings.IngestType ingestType; private final List panels; private int currentPanelIndex; @@ -47,14 +46,13 @@ final class RunIngestModulesWizardIterator implements WizardDescriptor.Iterator< * @param ingestType The type of ingest to be configured. */ RunIngestModulesWizardIterator(String executionContext, IngestJobSettings.IngestType ingestType, List dataSources) { - this.ingestType = ingestType; panels = new ArrayList<>(); List profiles = IngestProfiles.getIngestProfiles(); - if (!profiles.isEmpty() && IngestJobSettings.IngestType.FILES_ONLY != this.ingestType) { + if (!profiles.isEmpty() && IngestJobSettings.IngestType.FILES_ONLY != ingestType) { panels.add(new IngestProfileSelectionWizardPanel(executionContext, PROP_LASTPROFILE_NAME)); } - panels.add(new IngestModulesConfigWizardPanel(executionContext, this.ingestType, dataSources)); + panels.add(new IngestModulesConfigWizardPanel(executionContext, ingestType, dataSources)); String[] steps = new String[panels.size()]; for (int i = 0; i < panels.size(); i++) { Component c = panels.get(i).getComponent(); diff --git a/Core/src/org/sleuthkit/autopsy/livetriage/SelectDriveDialog.java b/Core/src/org/sleuthkit/autopsy/livetriage/SelectDriveDialog.java index c13d82d324..2b973aa92f 100644 --- a/Core/src/org/sleuthkit/autopsy/livetriage/SelectDriveDialog.java +++ b/Core/src/org/sleuthkit/autopsy/livetriage/SelectDriveDialog.java @@ -1,7 +1,20 @@ /* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * Autopsy Forensic Browser + * + * Copyright 2018 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.livetriage; @@ -22,8 +35,9 @@ import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.PlatformUtil; /** - * + * Dialog to allow for live triage drive selection. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class SelectDriveDialog extends javax.swing.JDialog { private List disks = new ArrayList<>(); diff --git a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/Bundle.properties b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/Bundle.properties index c80ccaa863..dfda2e6061 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/Bundle.properties @@ -9,8 +9,8 @@ OpenIDE-Module-Name=Embedded File Extraction OpenIDE-Module-Short-Description=Embedded File Extraction Ingest Module EmbeddedFileExtractorIngestModule.SevenZipContentReadStream.seek.exception.invalidOrigin=Invalid seek origin\: {0} EmbeddedFileExtractorIngestModule.SevenZipContentReadStream.read.exception.errReadStream=Error reading content stream. -EmbeddedFileExtractorIngestModule.ArchiveExtractor.encryptionFileLevel=File-level Encryption -EmbeddedFileExtractorIngestModule.ArchiveExtractor.encryptionFull=Full Encryption +EmbeddedFileExtractorIngestModule.ArchiveExtractor.encryptionFileLevel=Content-only Encryption (Archive File) +EmbeddedFileExtractorIngestModule.ArchiveExtractor.encryptionFull=Full Encryption (Archive File) EmbeddedFileExtractorIngestModule.ArchiveExtractor.init.errInitModule.details=Error initializing output dir\: {0}\: {1} EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnMsg=Possible ZIP bomb detected in archive\: {0}, item\: {1} EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnDetails=Compression ratio is {0}, skipping item in {1}. diff --git a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/EmbeddedFileExtractorIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/EmbeddedFileExtractorIngestModule.java index 5e67e9da8b..8c443b28ee 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/EmbeddedFileExtractorIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/EmbeddedFileExtractorIngestModule.java @@ -45,8 +45,6 @@ import org.sleuthkit.autopsy.ingest.FileIngestModuleAdapter; public final class EmbeddedFileExtractorIngestModule extends FileIngestModuleAdapter { static final String[] SUPPORTED_EXTENSIONS = {"zip", "rar", "arj", "7z", "7zip", "gzip", "gz", "bzip2", "tar", "tgz",}; // "iso"}; NON-NLS - private String moduleDirRelative; - private String moduleDirAbsolute; private MSOfficeEmbeddedContentExtractor officeExtractor; private SevenZipExtractor archiveExtractor; private FileTypeDetector fileTypeDetector; @@ -66,10 +64,13 @@ public final class EmbeddedFileExtractorIngestModule extends FileIngestModuleAda * case database for extracted (derived) file paths. The absolute path * is used to write the extracted (derived) files to local storage. */ + String moduleDirRelative = null; + String moduleDirAbsolute = null; + try { - final Case currentCase = Case.getCurrentCaseThrows(); - moduleDirRelative = Paths.get(currentCase.getModuleOutputDirectoryRelativePath(), EmbeddedFileExtractorModuleFactory.getModuleName()).toString(); - moduleDirAbsolute = Paths.get(currentCase.getModuleDirectory(), EmbeddedFileExtractorModuleFactory.getModuleName()).toString(); + final Case currentCase = Case.getCurrentCaseThrows(); + moduleDirRelative = Paths.get(currentCase.getModuleOutputDirectoryRelativePath(), EmbeddedFileExtractorModuleFactory.getModuleName()).toString(); + moduleDirAbsolute = Paths.get(currentCase.getModuleDirectory(), EmbeddedFileExtractorModuleFactory.getModuleName()).toString(); } catch (NoCurrentCaseException ex) { throw new IngestModuleException(Bundle.EmbeddedFileExtractorIngestModule_NoOpenCase_errMsg(), ex); } diff --git a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionFileIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionFileIngestModule.java index 1d5d5d05d1..ea1f4042bc 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionFileIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionFileIngestModule.java @@ -75,6 +75,8 @@ final class EncryptionDetectionFileIngestModule extends FileIngestModuleAdapter private static final String MIME_TYPE_MSACCESS = "application/x-msaccess"; private static final String MIME_TYPE_PDF = "application/pdf"; + private static final String[] FILE_IGNORE_LIST = {"hiberfile.sys", "pagefile.sys"}; + private final IngestServices services = IngestServices.getInstance(); private final Logger logger = services.getLogger(EncryptionDetectionModuleFactory.getModuleName()); private FileTypeDetector fileTypeDetector; @@ -122,32 +124,40 @@ final class EncryptionDetectionFileIngestModule extends FileIngestModuleAdapter try { /* - * Qualify the file type. + * Qualify the file type, qualify it against hash databases, and + * verify the file hasn't been deleted. */ if (!file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS) && !file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS) && !file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR) && !file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.LOCAL_DIR) - && (!file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.SLACK) || slackFilesAllowed)) { + && (!file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.SLACK) || slackFilesAllowed) + && !file.getKnown().equals(TskData.FileKnown.KNOWN) + && !file.isMetaFlagSet(TskData.TSK_FS_META_FLAG_ENUM.UNALLOC)) { /* - * Qualify the file against hash databases. + * Is the file in FILE_IGNORE_LIST? */ - if (!file.getKnown().equals(TskData.FileKnown.KNOWN)) { - /* - * Qualify the MIME type. - */ - String mimeType = fileTypeDetector.getMIMEType(file); - if (mimeType.equals("application/octet-stream")) { - if (isFileEncryptionSuspected(file)) { - return flagFile(file, BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_SUSPECTED, - String.format(Bundle.EncryptionDetectionFileIngestModule_artifactComment_suspected(), calculatedEntropy)); - } - } else { - if (isFilePasswordProtected(file)) { - return flagFile(file, BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED, Bundle.EncryptionDetectionFileIngestModule_artifactComment_password()); + String filePath = file.getParentPath(); + if (filePath.equals("/")) { + String fileName = file.getName(); + for (String listEntry : FILE_IGNORE_LIST) { + if (fileName.equalsIgnoreCase(listEntry)) { + // Skip this file. + return IngestModule.ProcessResult.OK; } } } + + /* + * Qualify the MIME type. + */ + String mimeType = fileTypeDetector.getMIMEType(file); + if (mimeType.equals("application/octet-stream") && isFileEncryptionSuspected(file)) { + return flagFile(file, BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_SUSPECTED, + String.format(Bundle.EncryptionDetectionFileIngestModule_artifactComment_suspected(), calculatedEntropy)); + } else if (isFilePasswordProtected(file)) { + return flagFile(file, BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED, Bundle.EncryptionDetectionFileIngestModule_artifactComment_password()); + } } } catch (ReadContentInputStreamException | SAXException | TikaException | UnsupportedCodecException ex) { logger.log(Level.WARNING, String.format("Unable to read file '%s'", file.getParentPath() + file.getName()), ex); @@ -376,7 +386,7 @@ final class EncryptionDetectionFileIngestModule extends FileIngestModuleAdapter fileSizeQualified = true; } } - + if (fileSizeQualified) { /* * Qualify the entropy. @@ -386,7 +396,7 @@ final class EncryptionDetectionFileIngestModule extends FileIngestModuleAdapter possiblyEncrypted = true; } } - + return possiblyEncrypted; } } diff --git a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionIngestJobSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionIngestJobSettingsPanel.java index b4e2ed487a..ca92d1b256 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionIngestJobSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionIngestJobSettingsPanel.java @@ -29,6 +29,7 @@ import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettingsPanel; /** * Ingest job settings panel for the Encryption Detection module. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class EncryptionDetectionIngestJobSettingsPanel extends IngestModuleIngestJobSettingsPanel { private static final int MEGABYTE_SIZE = 1048576; diff --git a/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/FileExtMismatchModuleSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/FileExtMismatchModuleSettingsPanel.java index 7c141f8733..442511a557 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/FileExtMismatchModuleSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/FileExtMismatchModuleSettingsPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2016 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -25,6 +25,7 @@ import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettingsPanel; * UI component used to set ingest job options for the file extension mismatch * detection ingest module. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class FileExtMismatchModuleSettingsPanel extends IngestModuleIngestJobSettingsPanel { private static final long serialVersionUID = 1L; diff --git a/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/FileExtMismatchSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/FileExtMismatchSettingsPanel.java index 3306d57192..508e1048d1 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/FileExtMismatchSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/FileExtMismatchSettingsPanel.java @@ -40,6 +40,7 @@ import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector; * Container panel for File Extension Mismatch Ingest Module advanced * configuration options */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class FileExtMismatchSettingsPanel extends IngestModuleGlobalSettingsPanel implements OptionsPanel { private static final Logger logger = Logger.getLogger(FileExtMismatchSettingsPanel.class.getName()); diff --git a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/AddFileTypeDialog.java b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/AddFileTypeDialog.java index 5095f4948e..e98460684e 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/AddFileTypeDialog.java +++ b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/AddFileTypeDialog.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,6 @@ package org.sleuthkit.autopsy.modules.filetypeid; import java.awt.BorderLayout; import java.awt.Dimension; -import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; @@ -39,6 +38,7 @@ import org.openide.windows.WindowManager; /** * Dialog used for editing or adding file types. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class AddFileTypeDialog extends JDialog { /** @@ -52,7 +52,7 @@ class AddFileTypeDialog extends JDialog { private static final long serialVersionUID = 1L; private FileType fileType; - private AddFileTypePanel addMimeTypePanel; + final private AddFileTypePanel addMimeTypePanel; private BUTTON_PRESSED result; private JButton okButton; private JButton closeButton; diff --git a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/AddFileTypePanel.java b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/AddFileTypePanel.java index 7008f76ef9..557bf1f8fd 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/AddFileTypePanel.java +++ b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/AddFileTypePanel.java @@ -32,11 +32,11 @@ import static org.sleuthkit.autopsy.modules.filetypeid.AddFileTypePanel.EVENT.SI import org.sleuthkit.autopsy.modules.filetypeid.AddFileTypeSignatureDialog.BUTTON_PRESSED; import org.sleuthkit.autopsy.modules.filetypeid.FileType.Signature; -@Messages("AddFileTypePanel.mimeFormatLabel.text=Form of MIME type should be: media type/media subtype") - /** * Panel for adding or editing file types. */ +@Messages("AddFileTypePanel.mimeFormatLabel.text=Form of MIME type should be: media type/media subtype") +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class AddFileTypePanel extends javax.swing.JPanel { private static final long serialVersionUID = 1L; diff --git a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/AddFileTypeSignatureDialog.java b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/AddFileTypeSignatureDialog.java index 372c62769a..e5373bc555 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/AddFileTypeSignatureDialog.java +++ b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/AddFileTypeSignatureDialog.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,6 @@ package org.sleuthkit.autopsy.modules.filetypeid; import java.awt.BorderLayout; import java.awt.Dimension; -import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; @@ -40,6 +39,7 @@ import org.sleuthkit.autopsy.modules.filetypeid.FileType.Signature; * A dialog box that allows a user to create a file type signature, to be added * to a selected file type. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class AddFileTypeSignatureDialog extends JDialog { private static final long serialVersionUID = 1L; diff --git a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/AddFileTypeSignaturePanel.java b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/AddFileTypeSignaturePanel.java index c22ecd94a7..bd047b86e0 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/AddFileTypeSignaturePanel.java +++ b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/AddFileTypeSignaturePanel.java @@ -30,6 +30,7 @@ import org.sleuthkit.autopsy.modules.filetypeid.FileType.Signature; /** * Panel for creating a file type signature to be added to a file type. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class AddFileTypeSignaturePanel extends javax.swing.JPanel { private static final String RAW_SIGNATURE_TYPE_COMBO_BOX_ITEM = NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.signatureComboBox.rawItem"); diff --git a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeIdGlobalSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeIdGlobalSettingsPanel.java index 600bf48a2f..5e788c04d3 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeIdGlobalSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeIdGlobalSettingsPanel.java @@ -43,6 +43,7 @@ import org.sleuthkit.autopsy.modules.filetypeid.FileType.Signature; * being an ingest module global settings panel, an instance of this class also * appears in the NetBeans options dialog as an options panel. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class FileTypeIdGlobalSettingsPanel extends IngestModuleGlobalSettingsPanel implements OptionsPanel { private static final long serialVersionUID = 1L; private static final Logger logger = Logger.getLogger(FileTypeIdGlobalSettingsPanel.class.getName()); diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/AddHashValuesToDatabaseDialog.java b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/AddHashValuesToDatabaseDialog.java index 9f7061b618..68535928c7 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/AddHashValuesToDatabaseDialog.java +++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/AddHashValuesToDatabaseDialog.java @@ -33,9 +33,9 @@ import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.HashDb; import org.sleuthkit.datamodel.HashEntry; /** - * - * @author sidhesh + * Dialog for supplying MD5 hash values for the hash database. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public class AddHashValuesToDatabaseDialog extends javax.swing.JDialog { HashDb hashDb; diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/AddHashValuesToDatabaseProgressDialog.java b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/AddHashValuesToDatabaseProgressDialog.java index 06c584e643..3715816801 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/AddHashValuesToDatabaseProgressDialog.java +++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/AddHashValuesToDatabaseProgressDialog.java @@ -36,9 +36,9 @@ import org.sleuthkit.datamodel.HashEntry; import org.sleuthkit.datamodel.TskCoreException; /** - * - * @author sidhesh + * Progress dialog for MD5 hash values being added to the hash database. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public class AddHashValuesToDatabaseProgressDialog extends javax.swing.JDialog { private final AddHashValuesToDatabaseDialog parentRef; diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbCreateDatabaseDialog.java b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbCreateDatabaseDialog.java index 4aa976be97..873dc5e32b 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbCreateDatabaseDialog.java +++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbCreateDatabaseDialog.java @@ -50,6 +50,7 @@ import org.sleuthkit.datamodel.TskCoreException; * to the set of hash databases used to classify files as unknown, known or * notable. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class HashDbCreateDatabaseDialog extends javax.swing.JDialog { private static final String DEFAULT_FILE_NAME = NbBundle diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbImportDatabaseDialog.java b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbImportDatabaseDialog.java index a2d041280d..d779629846 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbImportDatabaseDialog.java +++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbImportDatabaseDialog.java @@ -47,9 +47,10 @@ import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.HashDb; * add it to the set of hash databases used to classify files as unknown, known, * or notable. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class HashDbImportDatabaseDialog extends javax.swing.JDialog { - private JFileChooser fileChooser = new JFileChooser(); + private final JFileChooser fileChooser; private String selectedFilePath = ""; private HashDb selectedHashDb = null; private final static String LAST_FILE_PATH_KEY = "HashDbImport_Path"; @@ -65,6 +66,7 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog { super((JFrame) WindowManager.getDefault().getMainWindow(), NbBundle.getMessage(HashDbImportDatabaseDialog.class, "HashDbImportDatabaseDialog.importHashDbMsg"), true); + this.fileChooser = new JFileChooser(); initComponents(); enableComponents(); initFileChooser(); diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbSearchPanel.java b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbSearchPanel.java index a75b47646e..93ee3ce836 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbSearchPanel.java +++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbSearchPanel.java @@ -38,6 +38,7 @@ import org.sleuthkit.autopsy.ingest.IngestManager; /** * Searches for files by md5 hash, based off the hash given in this panel. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class HashDbSearchPanel extends javax.swing.JPanel implements ActionListener { private static final Logger logger = Logger.getLogger(HashDbSearchPanel.class.getName()); @@ -329,7 +330,7 @@ class HashDbSearchPanel extends javax.swing.JPanel implements ActionListener { errorField.setVisible(false); // Get all the rows in the table int numRows = hashTable.getRowCount(); - ArrayList hashes = new ArrayList(); + ArrayList hashes = new ArrayList<>(); for (int i = 0; i < numRows; i++) { hashes.add((String) hashTable.getValueAt(i, 0)); } diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupModuleFactory.java b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupModuleFactory.java index 257f90ad2f..068c52cf5e 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupModuleFactory.java +++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupModuleFactory.java @@ -18,8 +18,6 @@ */ package org.sleuthkit.autopsy.modules.hashdatabase; -import java.util.ArrayList; -import java.util.List; import org.openide.util.NbBundle; import org.openide.util.lookup.ServiceProvider; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupModuleSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupModuleSettingsPanel.java index fbac01235e..6dcd567226 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupModuleSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupModuleSettingsPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -37,6 +37,7 @@ import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.HashDb; /** * Ingest job settings panel for hash lookup file ingest modules. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public final class HashLookupModuleSettingsPanel extends IngestModuleIngestJobSettingsPanel implements PropertyChangeListener { private static final long serialVersionUID = 1L; diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupSettingsPanel.java index 1b7bf461d4..9c2962797f 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupSettingsPanel.java @@ -18,7 +18,10 @@ */ package org.sleuthkit.autopsy.modules.hashdatabase; -import java.awt.*; +import java.awt.Color; +import java.awt.Component; +import java.awt.EventQueue; +import java.awt.Frame; import java.awt.event.KeyEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; @@ -55,6 +58,7 @@ import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.HashDb; * Instances of this class provide a comprehensive UI for managing the hash sets * configuration. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public final class HashLookupSettingsPanel extends IngestModuleGlobalSettingsPanel implements OptionsPanel { private static final String NO_SELECTION_TEXT = NbBundle diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/ImportCentralRepoDbProgressDialog.java b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/ImportCentralRepoDbProgressDialog.java index 88251f1f6e..d793f1dc2d 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/ImportCentralRepoDbProgressDialog.java +++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/ImportCentralRepoDbProgressDialog.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2016 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -45,6 +45,7 @@ import org.sleuthkit.datamodel.TskData; /** * Imports a hash set into the central repository and updates a progress dialog */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class ImportCentralRepoDbProgressDialog extends javax.swing.JDialog implements PropertyChangeListener { private CentralRepoImportWorker worker; // Swing worker that will import the file and send updates to the dialog diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/ModalNoButtons.java b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/ModalNoButtons.java index 2d0d4f9bf4..874a5c136b 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/ModalNoButtons.java +++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/ModalNoButtons.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011 - 2013 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,7 +18,6 @@ */ package org.sleuthkit.autopsy.modules.hashdatabase; -import java.awt.*; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; @@ -41,6 +40,7 @@ import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.SleuthkitHashSet * completion. Furthermore, it does not delete any files left over from a * half-indexed state, forcing the user to perform cleanup. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class ModalNoButtons extends javax.swing.JDialog implements PropertyChangeListener { List unindexed; diff --git a/Core/src/org/sleuthkit/autopsy/modules/iOS/CallLogAnalyzer.java b/Core/src/org/sleuthkit/autopsy/modules/iOS/CallLogAnalyzer.java index ec8c84dc0b..0c36938eb6 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/iOS/CallLogAnalyzer.java +++ b/Core/src/org/sleuthkit/autopsy/modules/iOS/CallLogAnalyzer.java @@ -50,7 +50,6 @@ final class CallLogAnalyzer { private Connection connection = null; private ResultSet resultSet = null; private Statement statement = null; - private String dbPath = ""; private long fileId = 0; private java.io.File jFile = null; private final String moduleName = iOSModuleFactory.getModuleName(); @@ -79,6 +78,7 @@ final class CallLogAnalyzer { return; } for (AbstractFile file : absFiles) { + String dbPath = ""; try { jFile = new java.io.File(Case.getCurrentCaseThrows().getTempDirectory(), file.getName().replaceAll("[<>%|\"/:*\\\\]", "")); dbPath = jFile.toString(); //path of file as string diff --git a/Core/src/org/sleuthkit/autopsy/modules/iOS/ContactAnalyzer.java b/Core/src/org/sleuthkit/autopsy/modules/iOS/ContactAnalyzer.java index 1adb60d4dd..85fa762300 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/iOS/ContactAnalyzer.java +++ b/Core/src/org/sleuthkit/autopsy/modules/iOS/ContactAnalyzer.java @@ -54,8 +54,6 @@ import org.sleuthkit.datamodel.TskCoreException; final class ContactAnalyzer { private Connection connection = null; - private ResultSet resultSet = null; - private Statement statement = null; private String dbPath = ""; private long fileId = 0; private java.io.File jFile = null; @@ -114,13 +112,16 @@ final class ContactAnalyzer { if (DatabasePath == null || DatabasePath.isEmpty()) { return; } + Case currentCase; try { currentCase = Case.getCurrentCaseThrows(); } catch (NoCurrentCaseException ex) { logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS return; - } + } + + Statement statement = null; try { Class.forName("org.sqlite.JDBC"); //NON-NLS //load JDBC driver connection = DriverManager.getConnection("jdbc:sqlite:" + DatabasePath); //NON-NLS @@ -137,6 +138,7 @@ final class ContactAnalyzer { return; } + ResultSet resultSet = null; try { // get display_name, mimetype(email or phone number) and data1 (phonenumber or email address depending on mimetype) //sorted by name, so phonenumber/email would be consecutive for a person if they exist. diff --git a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesIdentifierIngestJobSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesIdentifierIngestJobSettingsPanel.java index 934fde0fef..70aa5c4795 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesIdentifierIngestJobSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesIdentifierIngestJobSettingsPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2014 Basis Technology Corp. + * Copyright 2014-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -34,6 +34,7 @@ import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettingsPanel; /** * Ingest job settings panel for interesting files identifier ingest modules. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class FilesIdentifierIngestJobSettingsPanel extends IngestModuleIngestJobSettingsPanel implements Observer { @Messages({ diff --git a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetDefsPanel.java b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetDefsPanel.java index fc930fadd0..71a4be086a 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetDefsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetDefsPanel.java @@ -49,6 +49,7 @@ import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector; /** * A panel that allows a user to make interesting item definitions. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel implements OptionsPanel { private static final long serialVersionUID = 1L; diff --git a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetPanel.java b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetPanel.java index daf3bede83..85b4ac19e3 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetPanel.java +++ b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2014-2017 Basis Technology Corp. + * Copyright 2014-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -27,6 +27,7 @@ import org.sleuthkit.autopsy.modules.interestingitems.FilesSetDefsPanel.PANEL_TY * A panel that allows a user to create and edit interesting files set * definitions. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public class FilesSetPanel extends javax.swing.JPanel { @NbBundle.Messages({"FilesSetPanel.filter.title=File Filter", "FilesSetPanel.rule.title=File Filter Rule", "FilesSetPanel.ingest.createNewFilter=Create/edit file ingest filters...", "FilesSetPanel.ingest.messages.filtersMustBeNamed=File ingest filters must be named."}) diff --git a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetRulePanel.java b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetRulePanel.java index 4965652c5f..36888279a9 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetRulePanel.java +++ b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetRulePanel.java @@ -38,6 +38,7 @@ import org.sleuthkit.autopsy.modules.interestingitems.FilesSetDefsPanel.PANEL_TY /** * A panel that allows a user to create and edit files set membership rules. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class FilesSetRulePanel extends javax.swing.JPanel { @Messages({ diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java index 9af6902a2d..48f6109950 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java @@ -24,6 +24,7 @@ import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettingsPanel; /** * Ingest job settings panel for the Encryption Detection module. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class PhotoRecCarverIngestJobSettingsPanel extends IngestModuleIngestJobSettingsPanel { /** diff --git a/Core/src/org/sleuthkit/autopsy/modules/stix/STIXReportModuleConfigPanel.java b/Core/src/org/sleuthkit/autopsy/modules/stix/STIXReportModuleConfigPanel.java index c24cba6447..b8d03d7cb4 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/stix/STIXReportModuleConfigPanel.java +++ b/Core/src/org/sleuthkit/autopsy/modules/stix/STIXReportModuleConfigPanel.java @@ -23,8 +23,9 @@ import javax.swing.JFileChooser; import org.sleuthkit.autopsy.coreutils.ModuleSettings; /** - * + * Configuration panel for STIX report generation. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public class STIXReportModuleConfigPanel extends javax.swing.JPanel { String stixFile = null; diff --git a/Core/src/org/sleuthkit/autopsy/progress/ProgressPanel.java b/Core/src/org/sleuthkit/autopsy/progress/ProgressPanel.java index b918d1f9f0..f6794e1637 100644 --- a/Core/src/org/sleuthkit/autopsy/progress/ProgressPanel.java +++ b/Core/src/org/sleuthkit/autopsy/progress/ProgressPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 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.progress; /** * A progress panel consisting of a message label and a progress bar. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class ProgressPanel extends javax.swing.JPanel { private static final long serialVersionUID = 1L; diff --git a/Core/src/org/sleuthkit/autopsy/report/ArtifactSelectionDialog.java b/Core/src/org/sleuthkit/autopsy/report/ArtifactSelectionDialog.java index 50e9715a16..b0655dfa2b 100644 --- a/Core/src/org/sleuthkit/autopsy/report/ArtifactSelectionDialog.java +++ b/Core/src/org/sleuthkit/autopsy/report/ArtifactSelectionDialog.java @@ -41,6 +41,10 @@ import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.TskCoreException; +/** + * Allow examiner to select artifacts on which to report. + */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public class ArtifactSelectionDialog extends javax.swing.JDialog { private ArtifactModel model; diff --git a/Core/src/org/sleuthkit/autopsy/report/Bundle.properties b/Core/src/org/sleuthkit/autopsy/report/Bundle.properties index 2da0862826..8efbfd9178 100644 --- a/Core/src/org/sleuthkit/autopsy/report/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/report/Bundle.properties @@ -199,6 +199,10 @@ ReportHTML.writeSum.examiner=Examiner\: ReportHTML.writeSum.noExaminer=No examiner ReportHTML.writeSum.numImages=Number of Images\: ReportHTML.writeSum.imageInfoHeading=

Image Information\:

+ReportHTML.writeSum.softwareInfoHeading=

Software Information\:

+ReportHTML.writeSum.ingestHistoryHeading=

Ingest History\:

+ReportHTML.writeSum.modulesEnabledHeading=Enabled Modules\: +ReportHTML.writeSum.autopsyVersion=Autopsy Version\: ReportHTML.writeSum.timezone=Timezone\: ReportHTML.writeSum.path=Path\: ReportProgressPanel.progress.queuing=Queuing... @@ -249,4 +253,4 @@ ReportGenerator.errList.coreExceptionWhileGenRptRow=Core exception while generat ReportKML.latLongStartPoint={0};{1};;{2} (Start)\n ReportKML.latLongEndPoint={0};{1};;{2} (End)\n ReportGenerationPanel.cancelButton.actionCommand=Cancel -ReportGenerationPanel.cancelButton.text=Cancel +ReportGenerationPanel.cancelButton.text=Cancel \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/report/DefaultReportConfigurationPanel.java b/Core/src/org/sleuthkit/autopsy/report/DefaultReportConfigurationPanel.java index ad1ce529dc..2f18ddf3d2 100644 --- a/Core/src/org/sleuthkit/autopsy/report/DefaultReportConfigurationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/report/DefaultReportConfigurationPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2012 Basis Technology Corp. + * Copyright 2012-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,11 +18,10 @@ */ package org.sleuthkit.autopsy.report; -import java.awt.*; - /** * The panel shown for all TableReportModules when configuring report modules. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public class DefaultReportConfigurationPanel extends javax.swing.JPanel { /** diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportGenerationPanel.java b/Core/src/org/sleuthkit/autopsy/report/ReportGenerationPanel.java index d08acea3a6..c9bf291314 100644 --- a/Core/src/org/sleuthkit/autopsy/report/ReportGenerationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/report/ReportGenerationPanel.java @@ -32,6 +32,7 @@ import org.sleuthkit.autopsy.report.ReportProgressPanel.ReportStatus; * A panel that displays a panel used by a report generation module to show * progress. It provides OK and Cancel buttons. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class ReportGenerationPanel extends javax.swing.JPanel { private static final long serialVersionUID = 1L; diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java b/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java index 2f0cb5f770..b69d84b629 100644 --- a/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java +++ b/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java @@ -40,6 +40,7 @@ import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -55,6 +56,7 @@ import org.sleuthkit.autopsy.casemodule.services.TagsManager; import org.sleuthkit.autopsy.coreutils.EscapeUtil; import org.sleuthkit.autopsy.coreutils.ImageUtils; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.Version; import org.sleuthkit.autopsy.datamodel.ContentUtils.ExtractFscContentVisitor; import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.datamodel.AbstractFile; @@ -63,6 +65,8 @@ import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.ContentTag; import org.sleuthkit.datamodel.Image; +import org.sleuthkit.datamodel.IngestJobInfo; +import org.sleuthkit.datamodel.IngestModuleInfo; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; @@ -1049,15 +1053,19 @@ class ReportHTML implements TableReportModule { head.append("h1 { color: #07A; font-size: 36px; line-height: 42px; font-weight: normal; margin: 0px; border-bottom: 1px solid #81B9DB; }\n"); //NON-NLS head.append("h1 span { color: #F00; display: block; font-size: 16px; font-weight: bold; line-height: 22px;}\n"); //NON-NLS head.append("h2 { padding: 0 0 3px 0; margin: 0px; color: #07A; font-weight: normal; border-bottom: 1px dotted #81B9DB; }\n"); //NON-NLS - head.append("table td { padding-right: 25px; }\n"); //NON-NLS + head.append("h3 { padding: 5 0 3px 0; margin: 0px; color: #07A; font-weight: normal; }\n"); + head.append("table td { padding: 5px 25px 5px 0px; vertical-align:top;}\n"); //NON-NLS head.append("p.subheadding { padding: 0px; margin: 0px; font-size: 11px; color: #B5B5B5; }\n"); //NON-NLS head.append(".title { width: 660px; margin-bottom: 50px; }\n"); //NON-NLS head.append(".left { float: left; width: 250px; margin-top: 20px; text-align: center; }\n"); //NON-NLS head.append(".left img { max-width: 250px; max-height: 250px; min-width: 200px; min-height: 200px; }\n"); //NON-NLS head.append(".right { float: right; width: 385px; margin-top: 25px; font-size: 14px; }\n"); //NON-NLS head.append(".clear { clear: both; }\n"); //NON-NLS + head.append(".info { padding: 10px 0;}\n"); head.append(".info p { padding: 3px 10px; background: #e5e5e5; color: #777; font-size: 12px; font-weight: bold; text-shadow: #e9f9fd 0 1px 0; border-top: 1px solid #dedede; border-bottom: 2px solid #dedede; }\n"); //NON-NLS - head.append(".info table { margin: 0 25px 20px 25px; }\n"); //NON-NLS + head.append(".info table { margin: 10px 25px 10px 25px; }\n"); //NON-NLS + head.append("ul {padding: 0;margin: 0;list-style-type: none;}"); + head.append("li {padding-bottom: 5px;}"); head.append("\n"); //NON-NLS head.append("\n\n"); //NON-NLS out.write(head.toString()); @@ -1066,25 +1074,15 @@ class ReportHTML implements TableReportModule { Date date = new Date(); String datetime = datetimeFormat.format(date); - String caseName = currentCase.getDisplayName(); - String caseNumber = currentCase.getNumber(); - String examiner = currentCase.getExaminer(); - int imagecount; - try { - imagecount = currentCase.getDataSources().size(); - } catch (TskCoreException ex) { - imagecount = 0; - } - StringBuilder summary = new StringBuilder(); boolean running = false; if (IngestManager.getInstance().isIngestRunning()) { running = true; } - + SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase(); + List ingestJobs = skCase.getIngestJobs(); final String reportTitle = reportBranding.getReportTitle(); final String reportFooter = reportBranding.getReportFooter(); - final boolean agencyLogoSet = reportBranding.getAgencyLogoPath() != null && !reportBranding.getAgencyLogoPath().isEmpty(); final boolean generatorLogoSet = reportBranding.getGeneratorLogoPath() != null && !reportBranding.getGeneratorLogoPath().isEmpty(); summary.append("
\n"); //NON-NLS @@ -1094,6 +1092,60 @@ class ReportHTML implements TableReportModule { summary.append("

").append( //NON-NLS NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.reportGenOn.text", datetime)).append("

\n"); //NON-NLS summary.append("
\n"); //NON-NLS + summary.append(writeSummaryCaseDetails()); + summary.append(writeSummaryImageInfo()); + summary.append(writeSummarySoftwareInfo(skCase,ingestJobs)); + summary.append(writeSummaryIngestHistoryInfo(skCase,ingestJobs)); + if (generatorLogoSet) { + summary.append("
\n"); //NON-NLS + summary.append("\n"); //NON-NLS + summary.append("
\n"); //NON-NLS + } + summary.append("
\n"); //NON-NLS + if (reportFooter != null) { + summary.append("

").append(reportFooter).append("

\n"); //NON-NLS + } + summary.append("
\n"); //NON-NLS + summary.append(""); //NON-NLS + out.write(summary.toString()); + } catch (FileNotFoundException ex) { + logger.log(Level.SEVERE, "Could not find summary.html file to write to."); //NON-NLS + } catch (UnsupportedEncodingException ex) { + logger.log(Level.SEVERE, "Did not recognize encoding when writing summary.hmtl."); //NON-NLS + } catch (IOException ex) { + logger.log(Level.SEVERE, "Error creating Writer for summary.html."); //NON-NLS + } catch (NoCurrentCaseException | TskCoreException ex) { + logger.log(Level.WARNING, "Unable to get current sleuthkit Case for the HTML report."); + } finally { + try { + if (out != null) { + out.flush(); + out.close(); + } + } catch (IOException ex) { + } + } + } + + /** + * Write the case details section of the summary for this report. + * + * @return StringBuilder updated html report with case details + */ + + private StringBuilder writeSummaryCaseDetails(){ + StringBuilder summary = new StringBuilder(); + String caseName = currentCase.getDisplayName(); + String caseNumber = currentCase.getNumber(); + String examiner = currentCase.getExaminer(); + final boolean agencyLogoSet = reportBranding.getAgencyLogoPath() != null && !reportBranding.getAgencyLogoPath().isEmpty(); + int imagecount; + try { + imagecount = currentCase.getDataSources().size(); + } catch (TskCoreException ex) { + imagecount = 0; + } + summary.append("
\n"); //NON-NLS if (agencyLogoSet) { summary.append("
\n"); //NON-NLS summary.append("\n"); //NON-NLS summary.append("
\n"); //NON-NLS summary.append("
\n"); //NON-NLS - summary.append(NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.imageInfoHeading")); - summary.append("
\n"); //NON-NLS - try { - for (Content c : currentCase.getDataSources()) { - summary.append("

").append(c.getName()).append("

\n"); //NON-NLS - if (c instanceof Image) { - Image img = (Image) c; + return summary; + } + + /** + * Write the Image Information section of the summary for this report. + * + * @return StringBuilder updated html report with Image Information + */ + + private StringBuilder writeSummaryImageInfo() { + StringBuilder summary = new StringBuilder(); + summary.append(NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.imageInfoHeading")); + summary.append("
\n"); //NON-NLS + try { + for (Content c : currentCase.getDataSources()) { + summary.append("

").append(c.getName()).append("

\n"); //NON-NLS + if (c instanceof Image) { + Image img = (Image) c; - summary.append("\n"); //NON-NLS + summary.append("
\n"); //NON-NLS + summary.append("\n"); //NON-NLS + for (String imgPath : img.getPaths()) { summary.append("\n"); //NON-NLS - for (String imgPath : img.getPaths()) { - summary.append("\n"); //NON-NLS - } - summary.append("
").append( //NON-NLS + NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.timezone")) + .append("").append(img.getTimeZone()).append("
").append( //NON-NLS - NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.timezone")) - .append("").append(img.getTimeZone()).append("
").append( //NON-NLS - NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.path")) - .append("").append(imgPath).append("
\n"); //NON-NLS + NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.path")) + .append("").append(imgPath).append("\n"); //NON-NLS } + summary.append("\n"); //NON-NLS } - } catch (TskCoreException ex) { - logger.log(Level.WARNING, "Unable to get image information for the HTML report."); //NON-NLS } - summary.append("
\n"); //NON-NLS - if (generatorLogoSet) { - summary.append("
\n"); //NON-NLS - summary.append("\n"); //NON-NLS - summary.append("
\n"); //NON-NLS - } - summary.append("
\n"); //NON-NLS - if (reportFooter != null) { - summary.append("

").append(reportFooter).append("

\n"); //NON-NLS - } - summary.append("
\n"); //NON-NLS - summary.append(""); //NON-NLS - out.write(summary.toString()); - } catch (FileNotFoundException ex) { - logger.log(Level.SEVERE, "Could not find summary.html file to write to."); //NON-NLS - } catch (UnsupportedEncodingException ex) { - logger.log(Level.SEVERE, "Did not recognize encoding when writing summary.hmtl."); //NON-NLS - } catch (IOException ex) { - logger.log(Level.SEVERE, "Error creating Writer for summary.html."); //NON-NLS - } finally { - try { - if (out != null) { - out.flush(); - out.close(); + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Unable to get image information for the HTML report."); //NON-NLS + } + summary.append("
\n"); //NON-NLS + return summary; + } + + /** + * Write the software information section of the summary for this report. + * + * @return StringBuilder updated html report with software information + */ + + private StringBuilder writeSummarySoftwareInfo(SleuthkitCase skCase, List ingestJobs) { + StringBuilder summary = new StringBuilder(); + summary.append(NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.softwareInfoHeading")); + summary.append("
\n"); + summary.append("\n"); + summary.append("\n"); + Map moduleInfoHashMap = new HashMap<>(); + for (IngestJobInfo ingestJob : ingestJobs) { + List ingestModules = ingestJob.getIngestModuleInfo(); + for (IngestModuleInfo ingestModule : ingestModules) { + if (!moduleInfoHashMap.containsKey(ingestModule.getIngestModuleId())) { + moduleInfoHashMap.put(ingestModule.getIngestModuleId(), ingestModule); } - } catch (IOException ex) { } } + TreeMap modules = new TreeMap<>(); + for (IngestModuleInfo moduleinfo : moduleInfoHashMap.values()) { + modules.put(moduleinfo.getDisplayName(), moduleinfo.getVersion()); + } + for (Map.Entry module : modules.entrySet()) { + summary.append("\n"); + } + summary.append("
").append(NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.autopsyVersion")) + .append("").append(Version.getVersion()).append("
").append(module.getKey()).append(" Module:") + .append("").append(module.getValue()).append("
\n"); + summary.append("
\n"); + summary.append("
\n"); //NON-NLS + return summary; + } + + /** + * Write the Ingest History section of the summary for this report. + * + * @return StringBuilder updated html report with ingest history + */ + + private StringBuilder writeSummaryIngestHistoryInfo(SleuthkitCase skCase, List ingestJobs) { + StringBuilder summary = new StringBuilder(); + try { + summary.append(NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.ingestHistoryHeading")); + summary.append("
\n"); + int jobnumber = 1; + + for (IngestJobInfo ingestJob : ingestJobs) { + summary.append("

Job ").append(jobnumber).append(":

\n"); + summary.append("\n"); + summary.append("\n"); + summary.append("\n"); + summary.append("\n"); + summary.append("
").append("Data Source:") + .append("").append(skCase.getContentById(ingestJob.getObjectId()).getName()).append("
").append("Status:") + .append("").append(ingestJob.getStatus()).append("
").append(NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.modulesEnabledHeading")) + .append(""); + List ingestModules = ingestJob.getIngestModuleInfo(); + summary.append("
    \n"); + for (IngestModuleInfo ingestModule : ingestModules) { + summary.append("
  • ").append(ingestModule.getDisplayName()).append("
  • "); + } + summary.append("
\n"); + jobnumber++; + summary.append("
\n"); + } + summary.append("
\n"); + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Unable to get ingest jobs for the HTML report."); + } + return summary; } private String prepareThumbnail(AbstractFile file) { @@ -1192,4 +1304,4 @@ class ReportHTML implements TableReportModule { + thumbFile.getName(); } -} +} \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportProgressPanel.java b/Core/src/org/sleuthkit/autopsy/report/ReportProgressPanel.java index c6fd374c36..9d3fd1bf41 100644 --- a/Core/src/org/sleuthkit/autopsy/report/ReportProgressPanel.java +++ b/Core/src/org/sleuthkit/autopsy/report/ReportProgressPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2016 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,7 +19,10 @@ package org.sleuthkit.autopsy.report; import org.openide.util.NbBundle; -import java.awt.*; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.Desktop; +import java.awt.EventQueue; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.io.File; @@ -30,6 +33,7 @@ import org.sleuthkit.autopsy.coreutils.Logger; /** * A panel used by a report generation module to show progress. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public class ReportProgressPanel extends javax.swing.JPanel { private static final long serialVersionUID = 1L; diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportVisualPanel1.java b/Core/src/org/sleuthkit/autopsy/report/ReportVisualPanel1.java index 93acae830a..a032f44381 100644 --- a/Core/src/org/sleuthkit/autopsy/report/ReportVisualPanel1.java +++ b/Core/src/org/sleuthkit/autopsy/report/ReportVisualPanel1.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2012 Basis Technology Corp. + * Copyright 2012-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -40,14 +40,18 @@ import org.openide.util.NbBundle; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.python.JythonModuleLoader; +/** + * Display reports modules. + */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class ReportVisualPanel1 extends JPanel implements ListSelectionListener { private static final Logger logger = Logger.getLogger(ReportVisualPanel1.class.getName()); - private ReportWizardPanel1 wizPanel; - private List modules = new ArrayList<>(); - private List generalModules = new ArrayList<>(); - private List tableModules = new ArrayList<>(); - private List fileModules = new ArrayList<>(); + private final ReportWizardPanel1 wizPanel; + private final List modules = new ArrayList<>(); + private final List generalModules = new ArrayList<>(); + private final List tableModules = new ArrayList<>(); + private final List fileModules = new ArrayList<>(); private Integer selectedIndex; /** diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportVisualPanel2.java b/Core/src/org/sleuthkit/autopsy/report/ReportVisualPanel2.java index 98160874c9..82de40aac9 100644 --- a/Core/src/org/sleuthkit/autopsy/report/ReportVisualPanel2.java +++ b/Core/src/org/sleuthkit/autopsy/report/ReportVisualPanel2.java @@ -49,11 +49,15 @@ import org.sleuthkit.datamodel.TagName; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; +/** + * Display data on which to allow reports module to report. + */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class ReportVisualPanel2 extends JPanel { - private ReportWizardPanel2 wizPanel; - private Map tagStates = new LinkedHashMap<>(); - private List tags = new ArrayList<>(); + private final ReportWizardPanel2 wizPanel; + private final Map tagStates = new LinkedHashMap<>(); + private final List tags = new ArrayList<>(); ArtifactSelectionDialog dialog = new ArtifactSelectionDialog((JFrame) WindowManager.getDefault().getMainWindow(), true); private Map artifactStates = new HashMap<>(); private List artifacts = new ArrayList<>(); @@ -367,11 +371,11 @@ final class ReportVisualPanel2 extends JPanel { public Component getListCellRendererComponent(JList list, String value, int index, boolean isSelected, boolean cellHasFocus) { if (value != null) { setEnabled(list.isEnabled()); - setSelected(tagStates.get(value.toString())); + setSelected(tagStates.get(value)); setFont(list.getFont()); setBackground(list.getBackground()); setForeground(list.getForeground()); - setText(value.toString()); + setText(value); return this; } return new JLabel(); diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportWizardFileOptionsVisualPanel.java b/Core/src/org/sleuthkit/autopsy/report/ReportWizardFileOptionsVisualPanel.java index 5dceb24650..eb844b2bba 100644 --- a/Core/src/org/sleuthkit/autopsy/report/ReportWizardFileOptionsVisualPanel.java +++ b/Core/src/org/sleuthkit/autopsy/report/ReportWizardFileOptionsVisualPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013 Basis Technology Corp. + * Copyright 2013-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -35,15 +35,14 @@ import org.openide.util.NbBundle; /** * Visual component of the File Report Configuration panel of the Report Wizard. - * - * @author jwallace */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class ReportWizardFileOptionsVisualPanel extends javax.swing.JPanel { private List options; - private Map optionStates = new EnumMap<>(FileReportDataTypes.class); + private final Map optionStates = new EnumMap<>(FileReportDataTypes.class); private ListModel model; - private ReportWizardFileOptionsPanel wizPanel; + private final ReportWizardFileOptionsPanel wizPanel; public ReportWizardFileOptionsVisualPanel(ReportWizardFileOptionsPanel wizPanel) { this.wizPanel = wizPanel; diff --git a/Core/src/org/sleuthkit/autopsy/report/taggedhashes/AddTaggedHashesToHashDbConfigPanel.java b/Core/src/org/sleuthkit/autopsy/report/taggedhashes/AddTaggedHashesToHashDbConfigPanel.java index 06484468a4..5f95f18bbd 100644 --- a/Core/src/org/sleuthkit/autopsy/report/taggedhashes/AddTaggedHashesToHashDbConfigPanel.java +++ b/Core/src/org/sleuthkit/autopsy/report/taggedhashes/AddTaggedHashesToHashDbConfigPanel.java @@ -46,6 +46,7 @@ import org.sleuthkit.datamodel.TskCoreException; * Instances of this class are used to configure the report module plug in that * provides a convenient way to add content hashes to hash set databases. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class AddTaggedHashesToHashDbConfigPanel extends javax.swing.JPanel { private static final long serialVersionUID = 1L; diff --git a/Core/src/org/sleuthkit/autopsy/timeline/PerCaseTimelineProperties.java b/Core/src/org/sleuthkit/autopsy/timeline/PerCaseTimelineProperties.java index 6861dac340..d54ce01120 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/PerCaseTimelineProperties.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/PerCaseTimelineProperties.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2016 Basis Technology Corp. + * Copyright 2016-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -37,13 +37,11 @@ class PerCaseTimelineProperties { private static final String STALE_KEY = "stale"; //NON-NLS private static final String WAS_INGEST_RUNNING_KEY = "was_ingest_running"; // NON-NLS - private final Case autoCase; private final Path propertiesPath; - PerCaseTimelineProperties(Case c) { - Objects.requireNonNull(c, "Case must not be null"); - this.autoCase = c; - propertiesPath = Paths.get(autoCase.getModuleDirectory(), "Timeline", "timeline.properties"); //NON-NLS + PerCaseTimelineProperties(Case autopsyCase) { + Objects.requireNonNull(autopsyCase, "Case must not be null"); + propertiesPath = Paths.get(autopsyCase.getModuleDirectory(), "Timeline", "timeline.properties"); //NON-NLS } /** diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ShowInTimelineDialog.java b/Core/src/org/sleuthkit/autopsy/timeline/ShowInTimelineDialog.java index 82e479c4ab..dede3d537a 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ShowInTimelineDialog.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ShowInTimelineDialog.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2016 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -72,6 +72,7 @@ import org.sleuthkit.datamodel.TskCoreException; * to choose a specific event and a time range around it to show in the Timeline * List View. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class ShowInTimelineDialog extends Dialog { private static final Logger LOGGER = Logger.getLogger(ShowInTimelineDialog.class.getName()); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java index 3d19bd4226..2f6c6ed559 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java @@ -87,9 +87,10 @@ import org.sleuthkit.datamodel.TskCoreException; persistenceType = TopComponent.PERSISTENCE_NEVER) @TopComponent.Registration(mode = "timeline", openAtStartup = false) @RetainLocation("timeline") +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public final class TimeLineTopComponent extends TopComponent implements ExplorerManager.Provider { - private static final Logger LOGGER = Logger.getLogger(TimeLineTopComponent.class.getName()); + private static final Logger logger = Logger.getLogger(TimeLineTopComponent.class.getName()); @ThreadConfined(type = ThreadConfined.ThreadType.AWT) private final DataContentExplorerPanel contentViewerPanel; @@ -182,7 +183,7 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer explorerManager.setSelectedNodes(childArray); } catch (PropertyVetoException ex) { //I don't know why this would ever happen. - LOGGER.log(Level.SEVERE, "Selecting the event node was vetoed.", ex); // NON-NLS + logger.log(Level.SEVERE, "Selecting the event node was vetoed.", ex); // NON-NLS } //if there is only one event selected push it into content viewer. if (childArray.length == 1) { @@ -193,9 +194,9 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer }); } catch (NoCurrentCaseException ex) { //Since the case is closed, the user probably doesn't care about this, just log it as a precaution. - LOGGER.log(Level.SEVERE, "There was no case open to lookup the Sleuthkit object backing a SingleEvent.", ex); // NON-NLS + logger.log(Level.SEVERE, "There was no case open to lookup the Sleuthkit object backing a SingleEvent.", ex); // NON-NLS } catch (TskCoreException ex) { - LOGGER.log(Level.SEVERE, "Failed to lookup Sleuthkit object backing a SingleEvent.", ex); // NON-NLS + logger.log(Level.SEVERE, "Failed to lookup Sleuthkit object backing a SingleEvent.", ex); // NON-NLS Platform.runLater(() -> { Notifications.create() .owner(jFXViewPanel.getScene().getWindow()) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/actions/SaveSnapshotAsReport.java b/Core/src/org/sleuthkit/autopsy/timeline/actions/SaveSnapshotAsReport.java index b7fe2de313..ac0d9d10a7 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/actions/SaveSnapshotAsReport.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/actions/SaveSnapshotAsReport.java @@ -66,7 +66,6 @@ public class SaveSnapshotAsReport extends Action { private static final ButtonType OPEN = new ButtonType(Bundle.OpenReportAction_DisplayName(), ButtonBar.ButtonData.NO); private static final ButtonType OK = new ButtonType(ButtonType.OK.getText(), ButtonBar.ButtonData.CANCEL_CLOSE); - private final TimeLineController controller; private final Case currentCase; /** @@ -96,7 +95,6 @@ public class SaveSnapshotAsReport extends Action { setLongText(Bundle.SaveSnapShotAsReport_action_longText()); setGraphic(new ImageView(SNAP_SHOT)); - this.controller = controller; this.currentCase = controller.getAutopsyCase(); setEventHandler(actionEvent -> { diff --git a/Core/src/org/sleuthkit/autopsy/timeline/filters/TagNameFilter.java b/Core/src/org/sleuthkit/autopsy/timeline/filters/TagNameFilter.java index e80df2de3c..0139614836 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/filters/TagNameFilter.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/filters/TagNameFilter.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2015 Basis Technology Corp. + * Copyright 2015-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,8 +20,6 @@ package org.sleuthkit.autopsy.timeline.filters; import java.util.Objects; import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.casemodule.services.TagsManager; -import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TagName; /** @@ -31,13 +29,9 @@ public class TagNameFilter extends AbstractFilter { private final TagName tagName; private final Case autoCase; - private final TagsManager tagsManager; - private final SleuthkitCase sleuthkitCase; public TagNameFilter(TagName tagName, Case autoCase) { this.autoCase = autoCase; - sleuthkitCase = autoCase.getSleuthkitCase(); - tagsManager = autoCase.getServices().getTagsManager(); this.tagName = tagName; setSelected(Boolean.TRUE); } diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepoDatamodelTest.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepoDatamodelTest.java index 99dbd9204d..d47efa3995 100755 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepoDatamodelTest.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepoDatamodelTest.java @@ -27,6 +27,8 @@ import java.util.Set; import java.util.HashSet; import java.nio.file.Path; import java.nio.file.Paths; +import java.sql.ResultSet; +import java.sql.SQLException; import java.util.stream.Collectors; import junit.framework.Test; import junit.framework.TestCase; @@ -51,6 +53,8 @@ public class CentralRepoDatamodelTest extends TestCase { private static final String PROPERTIES_FILE = "CentralRepository"; private static final String CR_DB_NAME = "testcentralrepo.db"; private static final Path testDirectory = Paths.get(System.getProperty("java.io.tmpdir"), "CentralRepoDatamodelTest"); + private static final int DEFAULT_BULK_THRESHOLD = 1000; // hard coded from EamDb + SqliteEamDbSettings dbSettingsSqlite; private CorrelationCase case1; @@ -61,6 +65,7 @@ public class CentralRepoDatamodelTest extends TestCase { private EamOrganization org1; private EamOrganization org2; CorrelationAttribute.Type fileType; + CorrelationAttribute.Type usbDeviceType; private Map propertiesMap = null; @@ -128,30 +133,32 @@ public class CentralRepoDatamodelTest extends TestCase { case2 = EamDb.getInstance().newCase(case2); assertTrue("Failed to create test object case2", case2 != null); - dataSource1fromCase1 = new CorrelationDataSource(case1.getID(), "dataSource1_deviceID", "dataSource1"); + dataSource1fromCase1 = new CorrelationDataSource(case1, "dataSource1_deviceID", "dataSource1"); EamDb.getInstance().newDataSource(dataSource1fromCase1); dataSource1fromCase1 = EamDb.getInstance().getDataSource(case1, dataSource1fromCase1.getDeviceID()); assertTrue("Failed to create test object dataSource1fromCase1", dataSource1fromCase1 != null); - dataSource2fromCase1 = new CorrelationDataSource(case1.getID(), "dataSource2_deviceID", "dataSource2"); + dataSource2fromCase1 = new CorrelationDataSource(case1, "dataSource2_deviceID", "dataSource2"); EamDb.getInstance().newDataSource(dataSource2fromCase1); dataSource2fromCase1 = EamDb.getInstance().getDataSource(case1, dataSource2fromCase1.getDeviceID()); assertTrue("Failed to create test object dataSource2fromCase1", dataSource2fromCase1 != null); - dataSource1fromCase2 = new CorrelationDataSource(case2.getID(), "dataSource3_deviceID", "dataSource3"); + dataSource1fromCase2 = new CorrelationDataSource(case2, "dataSource3_deviceID", "dataSource3"); EamDb.getInstance().newDataSource(dataSource1fromCase2); dataSource1fromCase2 = EamDb.getInstance().getDataSource(case2, dataSource1fromCase2.getDeviceID()); assertTrue("Failed to create test object dataSource1fromCase2", dataSource1fromCase2 != null); org1 = new EamOrganization("org1"); - org1.setOrgID((int) EamDb.getInstance().newOrganization(org1)); + org1 = EamDb.getInstance().newOrganization(org1); org2 = new EamOrganization("org2"); - org2.setOrgID((int) EamDb.getInstance().newOrganization(org2)); + org2 = EamDb.getInstance().newOrganization(org2); // Store the file type object for later use fileType = EamDb.getInstance().getCorrelationTypeById(CorrelationAttribute.FILES_TYPE_ID); assertTrue("getCorrelationTypeById(FILES_TYPE_ID) returned null", fileType != null); + usbDeviceType = EamDb.getInstance().getCorrelationTypeById(CorrelationAttribute.USBID_TYPE_ID); + assertTrue("getCorrelationTypeById(USBID_TYPE_ID) returned null", usbDeviceType != null); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); @@ -467,7 +474,7 @@ public class CentralRepoDatamodelTest extends TestCase { // Create the first list, which will have (bulkThreshold / 2) entries List list1 = new ArrayList<>(); - for (int i = 0; i < dbSettingsSqlite.getBulkThreshold() / 2; i++) { + for (int i = 0; i < DEFAULT_BULK_THRESHOLD / 2; i++) { String value = "bulkInsertValue1_" + String.valueOf(i); String path = "C:\\bulkInsertPath1\\file" + String.valueOf(i); @@ -487,7 +494,7 @@ public class CentralRepoDatamodelTest extends TestCase { // Make a second list with length equal to bulkThreshold List list2 = new ArrayList<>(); - for (int i = 0; i < dbSettingsSqlite.getBulkThreshold(); i++) { + for (int i = 0; i < DEFAULT_BULK_THRESHOLD; i++) { String value = "bulkInsertValue2_" + String.valueOf(i); String path = "C:\\bulkInsertPath2\\file" + String.valueOf(i); @@ -503,7 +510,7 @@ public class CentralRepoDatamodelTest extends TestCase { // There should now be bulkThreshold artifacts in the database long count = EamDb.getInstance().getCountArtifactInstancesByCaseDataSource(case1.getCaseUUID(), dataSource1fromCase1.getDeviceID()); - assertTrue("Artifact count " + count + " does not match bulkThreshold " + dbSettingsSqlite.getBulkThreshold(), count == dbSettingsSqlite.getBulkThreshold()); + assertTrue("Artifact count " + count + " does not match bulkThreshold " + DEFAULT_BULK_THRESHOLD, count == DEFAULT_BULK_THRESHOLD); // Now call bulkInsertArtifacts() to insert the rest of queue EamDb.getInstance().bulkInsertArtifacts(); @@ -629,6 +636,9 @@ public class CentralRepoDatamodelTest extends TestCase { String inDataSource1twicePath2 = "C:\\files\\path2.txt"; String onlyInDataSource3Hash = "2af54305f183778d87de0c70c591fae4"; String onlyInDataSource3Path = "C:\\files\\path3.txt"; + String callbackTestFilePath1 = "C:\\files\\processinstancecallback\\path1.txt"; + String callbackTestFilePath2 = "C:\\files\\processinstancecallback\\path2.txt"; + String callbackTestFileHash = "fb9dd8f04dacd3e82f4917f1a002223c"; // These will all go in dataSource1fromCase1 String emailValue = "test@gmail.com"; @@ -781,7 +791,7 @@ public class CentralRepoDatamodelTest extends TestCase { // Test adding instance with invalid data source ID try { - CorrelationDataSource badDS = new CorrelationDataSource(case1.getID(), "badDSUuid", "badDSName"); + CorrelationDataSource badDS = new CorrelationDataSource(case1, "badDSUuid", "badDSName"); CorrelationAttributeInstance inst = new CorrelationAttributeInstance(case1, badDS, "badPath"); failAttr.addInstance(inst); EamDb.getInstance().addArtifact(failAttr); @@ -995,6 +1005,25 @@ public class CentralRepoDatamodelTest extends TestCase { } catch (EamDbException ex) { // This is the expected behavior } + + // Test updating a correlation attribute instance comment + try { + CorrelationAttribute correlationAttribute = EamDb.getInstance().getCorrelationAttribute( + usbDeviceType, case1, dataSource1fromCase1, devIdValue, devIdPath); + assertNotNull("getCorrelationAttribute returned null", correlationAttribute); + + correlationAttribute.getInstances().get(0).setComment("new comment"); + EamDb.getInstance().updateAttributeInstanceComment(correlationAttribute); + + // Get a fresh copy to verify the update. + correlationAttribute = EamDb.getInstance().getCorrelationAttribute( + usbDeviceType, case1, dataSource1fromCase1, devIdValue, devIdPath); + assertEquals("updateAttributeInstanceComment did not set comment to \"new comment\".", + "new comment", correlationAttribute.getInstances().get(0).getComment()); + } catch (EamDbException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex); + } // Test getting count for dataSource1fromCase1 (includes all types) try { @@ -1066,6 +1095,34 @@ public class CentralRepoDatamodelTest extends TestCase { Exceptions.printStackTrace(ex); Assert.fail(ex); } + + // Test running processinstance which queries all rows from instances table + try { + // Add two instances to the central repository and use the callback query to verify we can see them + CorrelationAttribute attr = new CorrelationAttribute(fileType, callbackTestFileHash); + CorrelationAttributeInstance inst1 = new CorrelationAttributeInstance(case1, dataSource1fromCase1, callbackTestFilePath1); + CorrelationAttributeInstance inst2 = new CorrelationAttributeInstance(case1, dataSource1fromCase1, callbackTestFilePath2); + attr.addInstance(inst1); + attr.addInstance(inst2); + EamDb DbManager = EamDb.getInstance(); + DbManager.addArtifact(attr); + AttributeInstanceTableCallback instancetableCallback = new AttributeInstanceTableCallback(); + DbManager.processInstanceTable(fileType, instancetableCallback); + int count1 = instancetableCallback.getCounter(); + int count2 = instancetableCallback.getCounterNamingConvention(); + assertTrue("Process Instance count with filepath naming convention: " + count2 + "-expected 2", count2 == 2); + assertTrue("Process Instance count with filepath without naming convention: " + count1 + "-expected greater than 0", count1 > 0); + } catch (EamDbException ex) { + Exceptions.printStackTrace(ex); + } + + try { + //test null inputs + EamDb.getInstance().processInstanceTable(null, null); + Assert.fail("processinstance method failed to throw exception for null type value"); + } catch (EamDbException ex) { + // This is the expected + } } /** @@ -1283,7 +1340,7 @@ public class CentralRepoDatamodelTest extends TestCase { // Test adding a basic organization try { orgA = new EamOrganization(orgAname); - orgA.setOrgID((int) EamDb.getInstance().newOrganization(orgA)); + orgA = EamDb.getInstance().newOrganization(orgA); assertTrue("Organization ID is still -1 after adding to db", orgA.getOrgID() != -1); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); @@ -1294,7 +1351,7 @@ public class CentralRepoDatamodelTest extends TestCase { // Test adding an organization with additional fields try { orgB = new EamOrganization(orgBname, orgBpocName, orgBpocEmail, orgBpocPhone); - orgB.setOrgID((int) EamDb.getInstance().newOrganization(orgB)); + orgB = EamDb.getInstance().newOrganization(orgB); assertTrue("Organization ID is still -1 after adding to db", orgB.getOrgID() != -1); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); @@ -1386,14 +1443,13 @@ public class CentralRepoDatamodelTest extends TestCase { } // Test updating invalid org - // Shouldn't do anything + try { EamOrganization temp = new EamOrganization("invalidOrg"); - temp.setOrgID(3434); EamDb.getInstance().updateOrganization(temp); + Assert.fail("updateOrganization worked for invalid ID"); } catch (EamDbException ex) { - Exceptions.printStackTrace(ex); - Assert.fail(ex); + // this is the expected behavior } // Test updating null org @@ -1417,7 +1473,7 @@ public class CentralRepoDatamodelTest extends TestCase { // Test deleting existing org that isn't in use try { EamOrganization orgToDelete = new EamOrganization("deleteThis"); - orgToDelete.setOrgID((int) EamDb.getInstance().newOrganization(orgToDelete)); + orgToDelete = EamDb.getInstance().newOrganization(orgToDelete); int orgCount = EamDb.getInstance().getOrganizations().size(); EamDb.getInstance().deleteOrganization(orgToDelete); @@ -1431,7 +1487,7 @@ public class CentralRepoDatamodelTest extends TestCase { try { // Make a new org EamOrganization inUseOrg = new EamOrganization("inUseOrg"); - inUseOrg.setOrgID((int) EamDb.getInstance().newOrganization(inUseOrg)); + inUseOrg = EamDb.getInstance().newOrganization(inUseOrg); // Make a reference set that uses it EamGlobalSet tempSet = new EamGlobalSet(inUseOrg.getOrgID(), "inUseOrgTest", "1.0", TskData.FileKnown.BAD, false, fileType); @@ -1445,14 +1501,12 @@ public class CentralRepoDatamodelTest extends TestCase { } // Test deleting non-existent org - // Should do nothing try { EamOrganization temp = new EamOrganization("temp"); - temp.setOrgID(9876); EamDb.getInstance().deleteOrganization(temp); + Assert.fail("deleteOrganization failed to throw exception for non-existent organization"); } catch (EamDbException ex) { - Exceptions.printStackTrace(ex); - Assert.fail(ex); + // This is the expected behavior } // Test deleting null org @@ -1610,7 +1664,7 @@ public class CentralRepoDatamodelTest extends TestCase { // Create a list of global file instances. Make enough that the bulk threshold should be hit once. Set instances = new HashSet<>(); String bulkTestHash = "bulktesthash_"; - for (int i = 0; i < dbSettingsSqlite.getBulkThreshold() * 1.5; i++) { + for (int i = 0; i < DEFAULT_BULK_THRESHOLD * 1.5; i++) { String hash = bulkTestHash + String.valueOf(i); instances.add(new EamGlobalFileInstance(notableSet2id, hash, TskData.FileKnown.BAD, null)); } @@ -1619,7 +1673,7 @@ public class CentralRepoDatamodelTest extends TestCase { EamDb.getInstance().bulkInsertReferenceTypeEntries(instances, fileType); // There's no way to get a count of the number of entries in the database, so just do a spot check - if (dbSettingsSqlite.getBulkThreshold() > 10) { + if (DEFAULT_BULK_THRESHOLD > 10) { String hash = bulkTestHash + "10"; assertTrue("Sample bulk insert instance not found", EamDb.getInstance().isFileHashInReferenceSet(hash, notableSet2id)); } @@ -2141,7 +2195,7 @@ public class CentralRepoDatamodelTest extends TestCase { // Test creating a data source with valid case, name, and ID try { - dataSourceA = new CorrelationDataSource(case2.getID(), dataSourceAid, dataSourceAname); + dataSourceA = new CorrelationDataSource(case2, dataSourceAid, dataSourceAname); EamDb.getInstance().newDataSource(dataSourceA); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); @@ -2151,7 +2205,7 @@ public class CentralRepoDatamodelTest extends TestCase { // Test creating a data source with the same case, name, and ID try { - CorrelationDataSource temp = new CorrelationDataSource(case2.getID(), dataSourceAid, dataSourceAname); + CorrelationDataSource temp = new CorrelationDataSource(case2, dataSourceAid, dataSourceAname); EamDb.getInstance().newDataSource(temp); Assert.fail("newDataSource did not throw exception from duplicate data source"); } catch (EamDbException ex) { @@ -2160,7 +2214,7 @@ public class CentralRepoDatamodelTest extends TestCase { // Test creating a data source with the same name and ID but different case try { - dataSourceB = new CorrelationDataSource(case1.getID(), dataSourceAid, dataSourceAname); + dataSourceB = new CorrelationDataSource(case1, dataSourceAid, dataSourceAname); EamDb.getInstance().newDataSource(dataSourceB); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); @@ -2170,7 +2224,8 @@ public class CentralRepoDatamodelTest extends TestCase { // Test creating a data source with an invalid case ID try { - CorrelationDataSource temp = new CorrelationDataSource(5000, "tempID", "tempName"); + CorrelationCase correlationCase = new CorrelationCase("1", "test"); + CorrelationDataSource temp = new CorrelationDataSource(correlationCase, "tempID", "tempName"); EamDb.getInstance().newDataSource(temp); Assert.fail("newDataSource did not throw exception from invalid case ID"); } catch (EamDbException ex) { @@ -2179,7 +2234,7 @@ public class CentralRepoDatamodelTest extends TestCase { // Test creating a data source with null device ID try { - CorrelationDataSource temp = new CorrelationDataSource(case2.getID(), null, "tempName"); + CorrelationDataSource temp = new CorrelationDataSource(case2, null, "tempName"); EamDb.getInstance().newDataSource(temp); Assert.fail("newDataSource did not throw exception from null device ID"); } catch (EamDbException ex) { @@ -2188,7 +2243,7 @@ public class CentralRepoDatamodelTest extends TestCase { // Test creating a data source with null name try { - CorrelationDataSource temp = new CorrelationDataSource(case2.getID(), "tempID", null); + CorrelationDataSource temp = new CorrelationDataSource(case2, "tempID", null); EamDb.getInstance().newDataSource(temp); Assert.fail("newDataSource did not throw exception from null name"); } catch (EamDbException ex) { @@ -2467,7 +2522,7 @@ public class CentralRepoDatamodelTest extends TestCase { List cases = new ArrayList<>(); String bulkTestUuid = "bulkTestUUID_"; String bulkTestName = "bulkTestName_"; - for (int i = 0; i < dbSettingsSqlite.getBulkThreshold() * 1.5; i++) { + for (int i = 0; i < DEFAULT_BULK_THRESHOLD * 1.5; i++) { String name = bulkTestUuid + String.valueOf(i); String uuid = bulkTestName + String.valueOf(i); cases.add(new CorrelationCase(uuid, name)); @@ -2618,4 +2673,34 @@ public class CentralRepoDatamodelTest extends TestCase { } } + public class AttributeInstanceTableCallback implements InstanceTableCallback { + + int counterNamingConvention = 0; + int counter = 0; + + @Override + public void process(ResultSet resultSet) { + try { + while(resultSet.next()){ + if(InstanceTableCallback.getFilePath(resultSet).contains("processinstancecallback")){ + counterNamingConvention++; + }else{ + counter++; + } + } + } catch (SQLException ex) { + Exceptions.printStackTrace(ex); + } + } + + public int getCounter() { + return counter; + } + + public int getCounterNamingConvention(){ + return counterNamingConvention; + } + + } + } diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithHashAndFileType.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithHashAndFileType.java new file mode 100644 index 0000000000..a599763346 --- /dev/null +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithHashAndFileType.java @@ -0,0 +1,465 @@ +/* + * + * Autopsy Forensic Browser + * + * Copyright 2018 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.commonfilessearch; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import junit.framework.Test; +import org.netbeans.junit.NbModuleSuite; +import org.netbeans.junit.NbTestCase; +import org.openide.util.Exceptions; +import org.python.icu.impl.Assert; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.commonfilesearch.AllDataSourcesCommonFilesAlgorithm; +import org.sleuthkit.autopsy.commonfilesearch.CommonFilesMetadata; +import org.sleuthkit.autopsy.commonfilesearch.CommonFilesMetadataBuilder; +import org.sleuthkit.autopsy.commonfilesearch.SingleDataSource; +import static org.sleuthkit.autopsy.commonfilessearch.IntraCaseUtils.*; +import org.sleuthkit.autopsy.ingest.IngestJobSettings; +import org.sleuthkit.autopsy.ingest.IngestJobSettings.IngestType; +import org.sleuthkit.autopsy.ingest.IngestModuleTemplate; +import org.sleuthkit.autopsy.modules.filetypeid.FileTypeIdModuleFactory; +import org.sleuthkit.autopsy.modules.hashdatabase.HashLookupModuleFactory; +import org.sleuthkit.autopsy.testutils.IngestUtils; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Add set 1, set 2, set 3, and set 4 to case and ingest with hash algorithm. + */ +public class IngestedWithHashAndFileType extends NbTestCase { + + public static Test suite() { + NbModuleSuite.Configuration conf = NbModuleSuite.createConfiguration(IngestedWithHashAndFileType.class). + clusters(".*"). + enableModules(".*"); + return conf.suite(); + } + + private final IntraCaseUtils utils; + + public IngestedWithHashAndFileType(String name) { + super(name); + + this.utils = new IntraCaseUtils(this, "IngestedWithHashAndFileTypeTests"); + } + + @Override + public void setUp() { + this.utils.setUp(); + + IngestModuleTemplate hashLookupTemplate = IngestUtils.getIngestModuleTemplate(new HashLookupModuleFactory()); + IngestModuleTemplate mimeTypeLookupTemplate = IngestUtils.getIngestModuleTemplate(new FileTypeIdModuleFactory()); + + ArrayList templates = new ArrayList<>(); + templates.add(hashLookupTemplate); + templates.add(mimeTypeLookupTemplate); + + IngestJobSettings ingestJobSettings = new IngestJobSettings(IngestedWithHashAndFileType.class.getCanonicalName(), IngestType.FILES_ONLY, templates); + + try { + IngestUtils.runIngestJob(Case.getCurrentCaseThrows().getDataSources(), ingestJobSettings); + } catch (NoCurrentCaseException | TskCoreException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex); + } + } + + @Override + public void tearDown() { + this.utils.tearDown(); + } + + /** + * Find all matches & all file types. Confirm file.jpg is found on all three + * and file.docx is found on two. + */ + public void testOneA() { + try { + Map dataSources = this.utils.getDataSourceMap(); + + CommonFilesMetadataBuilder allSourcesBuilder = new AllDataSourcesCommonFilesAlgorithm(dataSources, false, false); + CommonFilesMetadata metadata = allSourcesBuilder.findCommonFiles(); + + Map objectIdToDataSource = IntraCaseUtils.mapFileInstancesToDataSources(metadata); + + List files = IntraCaseUtils.getFiles(objectIdToDataSource.keySet()); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 2)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 1)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 1)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 1)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 1)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0)); + assertTrue(IntraCaseUtils.verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0)); + + } catch (NoCurrentCaseException | TskCoreException | SQLException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex); + } + } + + /** + * Find all matches & only image types. Confirm file.jpg is found on all + * three. + */ + public void testOneB() { + try { + Map dataSources = this.utils.getDataSourceMap(); + + CommonFilesMetadataBuilder allSourcesBuilder = new AllDataSourcesCommonFilesAlgorithm(dataSources, true, false); + CommonFilesMetadata metadata = allSourcesBuilder.findCommonFiles(); + + Map objectIdToDataSource = mapFileInstancesToDataSources(metadata); + + List files = getFiles(objectIdToDataSource.keySet()); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 2)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 1)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 1)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0)); + + } catch (NoCurrentCaseException | TskCoreException | SQLException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex); + } + } + + /** + * Find all matches & only image types. Confirm file.jpg is found on all + * three. + */ + public void testOneC() { + try { + Map dataSources = this.utils.getDataSourceMap(); + + CommonFilesMetadataBuilder allSourcesBuilder = new AllDataSourcesCommonFilesAlgorithm(dataSources, false, true); + CommonFilesMetadata metadata = allSourcesBuilder.findCommonFiles(); + + Map objectIdToDataSource = mapFileInstancesToDataSources(metadata); + + List files = getFiles(objectIdToDataSource.keySet()); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 1)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 1)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0)); + + } catch (NoCurrentCaseException | TskCoreException | SQLException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex); + } + } + + /** + * Find matches on set 1 & all file types. Confirm same results. + * + */ + public void testTwoA() { + try { + Map dataSources = this.utils.getDataSourceMap(); + Long first = getDataSourceIdByName(SET1, dataSources); + + CommonFilesMetadataBuilder singleSourceBuilder = new SingleDataSource(first, dataSources, false, false); + CommonFilesMetadata metadata = singleSourceBuilder.findCommonFiles(); + + Map objectIdToDataSource = mapFileInstancesToDataSources(metadata); + + List files = getFiles(objectIdToDataSource.keySet()); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 2)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 1)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 1)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 1)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 1)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0)); + + } catch (NoCurrentCaseException | TskCoreException | SQLException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex); + } + } + + /** + * Find matches on set 1 & only media types. Confirm same results. + * + */ + public void testTwoB() { + try { + Map dataSources = this.utils.getDataSourceMap(); + Long first = getDataSourceIdByName(SET1, dataSources); + + CommonFilesMetadataBuilder singleSourceBuilder = new SingleDataSource(first, dataSources, true, false); + CommonFilesMetadata metadata = singleSourceBuilder.findCommonFiles(); + + Map objectIdToDataSource = mapFileInstancesToDataSources(metadata); + + List files = getFiles(objectIdToDataSource.keySet()); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 2)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 1)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 1)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0)); + + } catch (NoCurrentCaseException | TskCoreException | SQLException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex); + } + } + + /** + * Find matches on set 1 & all file types. Confirm same results. + * + */ + public void testTwoC() { + try { + Map dataSources = this.utils.getDataSourceMap(); + Long first = getDataSourceIdByName(SET1, dataSources); + + CommonFilesMetadataBuilder singleSourceBuilder = new SingleDataSource(first, dataSources, false, true); + CommonFilesMetadata metadata = singleSourceBuilder.findCommonFiles(); + + Map objectIdToDataSource = mapFileInstancesToDataSources(metadata); + + List files = getFiles(objectIdToDataSource.keySet()); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 1)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 1)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0)); + + } catch (NoCurrentCaseException | TskCoreException | SQLException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex); + } + } + + /** + * Find matches on set 2 & all file types: Confirm file.jpg. + * + */ + public void testThree() { + try { + Map dataSources = this.utils.getDataSourceMap(); + Long second = getDataSourceIdByName(SET2, dataSources); + + CommonFilesMetadataBuilder singleSourceBuilder = new SingleDataSource(second, dataSources, false, false); + CommonFilesMetadata metadata = singleSourceBuilder.findCommonFiles(); + + Map objectIdToDataSource = mapFileInstancesToDataSources(metadata); + + List files = getFiles(objectIdToDataSource.keySet()); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 2)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 1)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 1)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0)); + + } catch (NoCurrentCaseException | TskCoreException | SQLException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex); + } + } + + /** + * Find matches on set 4 & all file types: Confirm nothing is found. + */ + public void testFour() { + try { + Map dataSources = this.utils.getDataSourceMap(); + Long last = getDataSourceIdByName(SET4, dataSources); + + CommonFilesMetadataBuilder singleSourceBuilder = new SingleDataSource(last, dataSources, false, false); + CommonFilesMetadata metadata = singleSourceBuilder.findCommonFiles(); + + Map objectIdToDataSource = mapFileInstancesToDataSources(metadata); + + List files = getFiles(objectIdToDataSource.keySet()); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0)); + + } catch (NoCurrentCaseException | TskCoreException | SQLException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex); + } + } + + /** + * Find matches on set 3 & all file types: Confirm file.jpg and file.docx. + */ + public void testFive() { + try { + Map dataSources = this.utils.getDataSourceMap(); + Long third = getDataSourceIdByName(SET3, dataSources); + + CommonFilesMetadataBuilder singleSourceBuilder = new SingleDataSource(third, dataSources, false, false); + CommonFilesMetadata metadata = singleSourceBuilder.findCommonFiles(); + + Map objectIdToDataSource = mapFileInstancesToDataSources(metadata); + + List files = getFiles(objectIdToDataSource.keySet()); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 2)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 1)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 1)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 1)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 1)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0)); + + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0)); + assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0)); + + } catch (NoCurrentCaseException | TskCoreException | SQLException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex); + } + } +} diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithNoFileTypes.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithNoFileTypes.java new file mode 100644 index 0000000000..0090d0f699 --- /dev/null +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithNoFileTypes.java @@ -0,0 +1,140 @@ +/* + * + * Autopsy Forensic Browser + * + * Copyright 2018 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.commonfilessearch; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import static junit.framework.Assert.assertTrue; +import junit.framework.Test; +import org.netbeans.junit.NbModuleSuite; +import org.netbeans.junit.NbTestCase; +import org.openide.util.Exceptions; +import org.python.icu.impl.Assert; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.commonfilesearch.AllDataSourcesCommonFilesAlgorithm; +import org.sleuthkit.autopsy.commonfilesearch.CommonFilesMetadata; +import org.sleuthkit.autopsy.commonfilesearch.CommonFilesMetadataBuilder; +import org.sleuthkit.autopsy.commonfilesearch.SingleDataSource; +import org.sleuthkit.autopsy.ingest.IngestJobSettings; +import org.sleuthkit.autopsy.ingest.IngestModuleTemplate; +import org.sleuthkit.autopsy.modules.hashdatabase.HashLookupModuleFactory; +import org.sleuthkit.autopsy.testutils.IngestUtils; +import static org.sleuthkit.autopsy.testutils.IngestUtils.getIngestModuleTemplate; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Ingested w/o mime type info added to DB. + * + * Setup: + * + * Add images set 1, set 2, set 3, and set 4 to case. Do not run mime type + * module. + */ +public class IngestedWithNoFileTypes extends NbTestCase { + + public static Test suite() { + NbModuleSuite.Configuration conf = NbModuleSuite.createConfiguration(IngestedWithNoFileTypes.class). + clusters(".*"). + enableModules(".*"); + return conf.suite(); + } + + private final IntraCaseUtils utils; + + public IngestedWithNoFileTypes(String name) { + super(name); + + this.utils = new IntraCaseUtils(this, "IngestedWithNoFileTypes"); + } + + @Override + public void setUp() { + this.utils.setUp(); + + IngestModuleTemplate hashLookupTemplate = getIngestModuleTemplate(new HashLookupModuleFactory()); + + ArrayList templates = new ArrayList<>(); + templates.add(hashLookupTemplate); + + IngestJobSettings ingestJobSettings = new IngestJobSettings(IngestedWithHashAndFileType.class.getCanonicalName(), IngestJobSettings.IngestType.FILES_ONLY, templates); + + try { + IngestUtils.runIngestJob(Case.getCurrentCaseThrows().getDataSources(), ingestJobSettings); + } catch (NoCurrentCaseException | TskCoreException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex); + } + } + + @Override + public void tearDown(){ + this.utils.tearDown(); + } + + /** + * Search using all data sources and filtering for media types. We should + * find nothing and no errors should arise. + */ + public void testOne() { + try { + Map dataSources = this.utils.getDataSourceMap(); + + CommonFilesMetadataBuilder allSourcesBuilder = new AllDataSourcesCommonFilesAlgorithm(dataSources, true, false); + CommonFilesMetadata metadata = allSourcesBuilder.findCommonFiles(); + + Map objectIdToDataSource = IntraCaseUtils.mapFileInstancesToDataSources(metadata); + + List files = IntraCaseUtils.getFiles(objectIdToDataSource.keySet()); + + assertTrue(files.isEmpty()); + + } catch (NoCurrentCaseException | TskCoreException | SQLException ex) { + Exceptions.printStackTrace(ex); + } + } + + /** + * Search using single data source and filtering for doc types. Observe that + * nothing is found and that nothing blows up. + */ + public void testTwo() { + try { + Map dataSources = this.utils.getDataSourceMap(); + Long third = IntraCaseUtils.getDataSourceIdByName(IntraCaseUtils.SET3, dataSources); + + CommonFilesMetadataBuilder singleSourceBuilder = new SingleDataSource(third, dataSources, true, false); + CommonFilesMetadata metadata = singleSourceBuilder.findCommonFiles(); + + Map objectIdToDataSource = IntraCaseUtils.mapFileInstancesToDataSources(metadata); + + List files = IntraCaseUtils.getFiles(objectIdToDataSource.keySet()); + + assertTrue(files.isEmpty()); + + } catch (NoCurrentCaseException | TskCoreException | SQLException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex); + } + } +} diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IntraCaseUtils.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IntraCaseUtils.java new file mode 100644 index 0000000000..66a68f3a38 --- /dev/null +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IntraCaseUtils.java @@ -0,0 +1,220 @@ +/* + * + * Autopsy Forensic Browser + * + * Copyright 2018 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.commonfilessearch; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.netbeans.junit.NbTestCase; +import org.openide.util.Exceptions; +import org.python.icu.impl.Assert; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.ImageDSProcessor; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.commonfilesearch.CommonFilesMetadata; +import org.sleuthkit.autopsy.commonfilesearch.DataSourceLoader; +import org.sleuthkit.autopsy.commonfilesearch.FileInstanceMetadata; +import org.sleuthkit.autopsy.commonfilesearch.Md5Metadata; +import org.sleuthkit.autopsy.testutils.CaseUtils; +import org.sleuthkit.autopsy.testutils.IngestUtils; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * + * Provides setup and utility for testing presence of files in different data + * sets discoverable by Common Files Features. + * + * Data set definitions: + * + * set 1 + * + file1 + * - IMG_6175.jpg + * + file2 + * - IMG_6175.jpg + * + file3 + * - BasicStyleGuide.doc + * + * set 2 + * - adsf.pdf + * - IMG_6175.jpg + * + * set 3 + * - BasicStyleGuide.doc + * - IMG_6175.jpg + * + * set 4 + * - file.dat (empty file) + */ +class IntraCaseUtils { + + private static final String CASE_NAME = "IntraCaseCommonFilesSearchTest"; + static final Path CASE_DIRECTORY_PATH = Paths.get(System.getProperty("java.io.tmpdir"), CASE_NAME); + + private final Path imagePath1; + private final Path imagePath2; + private final Path imagePath3; + private final Path imagePath4; + + static final String IMG = "IMG_6175.jpg"; + static final String DOC = "BasicStyleGuide.doc"; + static final String PDF = "adsf.pdf"; //not a typo - it appears this way in the test image + static final String EMPTY = "file.dat"; + + static final String SET1 = "commonfiles_image1_v1.vhd"; + static final String SET2 = "commonfiles_image2_v1.vhd"; + static final String SET3 = "commonfiles_image3_v1.vhd"; + static final String SET4 = "commonfiles_image4_v1.vhd"; + + private final DataSourceLoader dataSourceLoader; + + private final String caseName; + + IntraCaseUtils(NbTestCase nbTestCase, String caseName){ + imagePath1 = Paths.get(nbTestCase.getDataDir().toString(), "commonfiles_image1_v1.vhd"); + imagePath2 = Paths.get(nbTestCase.getDataDir().toString(), "commonfiles_image2_v1.vhd"); + imagePath3 = Paths.get(nbTestCase.getDataDir().toString(), "commonfiles_image3_v1.vhd"); + imagePath4 = Paths.get(nbTestCase.getDataDir().toString(), "commonfiles_image4_v1.vhd"); + + this.dataSourceLoader = new DataSourceLoader(); + + this.caseName = caseName; + } + + void setUp(){ + CaseUtils.createAsCurrentCase(this.caseName); + + final ImageDSProcessor imageDSProcessor = new ImageDSProcessor(); + + IngestUtils.addDataSource(imageDSProcessor, imagePath1); + IngestUtils.addDataSource(imageDSProcessor, imagePath2); + IngestUtils.addDataSource(imageDSProcessor, imagePath3); + IngestUtils.addDataSource(imageDSProcessor, imagePath4); + } + + Map getDataSourceMap() throws NoCurrentCaseException, TskCoreException, SQLException{ + return this.dataSourceLoader.getDataSourceMap(); + } + + void tearDown(){ + CaseUtils.closeCurrentCase(false); + try { + CaseUtils.deleteCaseDir(CASE_DIRECTORY_PATH.toFile()); + } catch (IOException ex) { + Exceptions.printStackTrace(ex); + //does not represent a failure in the common files search feature + } + } + + /** + * Verify that the given file appears a precise number times in the given + * data source. + * + * @param files search domain + * @param objectIdToDataSource mapping of file ids to data source names + * @param name name of file to search for + * @param dataSource name of data source where file should appear + * @param count number of appearances of the given file + * @return true if a file with the given name exists the specified number + * of times in the given data source + */ + static boolean verifyFileExistanceAndCount(List files, Map objectIdToDataSource, String name, String dataSource, int count) { + + int tally = 0; + + for (AbstractFile file : files) { + + Long objectId = file.getId(); + + String fileName = file.getName(); + + String dataSourceName = objectIdToDataSource.get(objectId); + + if (fileName.equals(name) && dataSourceName.equals(dataSource)) { + tally++; + } + } + + return tally == count; + } + + /** + * Convenience method which verifies that a file exists within a given data + * source exactly once. + * + * @param files search domain + * @param objectIdToDataSource mapping of file ids to data source names + * @param name name of file to search for + * @param dataSource name of data source where file should appear + * @return true if a file with the given name exists once in the given data + * source + */ + static boolean verifySingularFileExistance(List files, Map objectIdToDataSource, String name, String dataSource) { + return verifyFileExistanceAndCount(files, objectIdToDataSource, name, dataSource, 1); + } + + static Map mapFileInstancesToDataSources(CommonFilesMetadata metadata) { + Map instanceIdToDataSource = new HashMap<>(); + + for (Map.Entry entry : metadata.getMetadata().entrySet()) { + for (FileInstanceMetadata md : entry.getValue().getMetadata()) { + instanceIdToDataSource.put(md.getObjectId(), md.getDataSourceName()); + } + } + + return instanceIdToDataSource; + } + + static List getFiles(Set objectIds) { + List files = new ArrayList<>(objectIds.size()); + + for (Long id : objectIds) { + try { + AbstractFile file = Case.getCurrentCaseThrows().getSleuthkitCase().getAbstractFileById(id); + files.add(file); + } catch (NoCurrentCaseException | TskCoreException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex); + } + } + + return files; + } + + static Long getDataSourceIdByName(String name, Map dataSources){ + + if(dataSources.containsValue(name)){ + for(Map.Entry dataSource : dataSources.entrySet()){ + if(dataSource.getValue().equals(name)){ + return dataSource.getKey(); + } + } + } else { + throw new IndexOutOfBoundsException(String.format("Name should be one of: {0}", String.join(",", dataSources.values()))); + } + return null; + } +} diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/UningestedCases.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/UningestedCases.java new file mode 100644 index 0000000000..9bd7a02bae --- /dev/null +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/UningestedCases.java @@ -0,0 +1,114 @@ +/* + * + * Autopsy Forensic Browser + * + * Copyright 2018 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.commonfilessearch; + +import java.sql.SQLException; +import java.util.Map; +import static junit.framework.Assert.assertEquals; +import junit.framework.Test; +import org.netbeans.junit.NbModuleSuite; +import org.netbeans.junit.NbTestCase; +import org.openide.util.Exceptions; +import org.python.icu.impl.Assert; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.commonfilesearch.AllDataSourcesCommonFilesAlgorithm; +import org.sleuthkit.autopsy.commonfilesearch.CommonFilesMetadata; +import org.sleuthkit.autopsy.commonfilesearch.CommonFilesMetadataBuilder; +import org.sleuthkit.autopsy.commonfilesearch.SingleDataSource; +import static org.sleuthkit.autopsy.commonfilessearch.IntraCaseUtils.SET1; +import static org.sleuthkit.autopsy.commonfilessearch.IntraCaseUtils.getDataSourceIdByName; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Test that cases which are created but have not run any ingest modules turn up + * no results. + * + * Setup: + * + * Add images set 1, set 2, set 3, and set 4 to case. Do not ingest. + * + */ +public class UningestedCases extends NbTestCase { + + public static Test suite() { + NbModuleSuite.Configuration conf = NbModuleSuite.createConfiguration(UningestedCases.class). + clusters(".*"). + enableModules(".*"); + return conf.suite(); + } + + private final IntraCaseUtils utils; + + public UningestedCases(String name) { + super(name); + + this.utils = new IntraCaseUtils(this, "UningestedCasesTests"); + } + + @Override + public void setUp(){ + this.utils.setUp(); + } + + @Override + public void tearDown(){ + this.utils.tearDown(); + } + + /** + * Find all matches & all file types. Confirm no matches are found (since + * there are no hashes to match). + */ + public void testOne() { + try { + Map dataSources = this.utils.getDataSourceMap(); + + CommonFilesMetadataBuilder allSourcesBuilder = new AllDataSourcesCommonFilesAlgorithm(dataSources, false, false); + CommonFilesMetadata metadata = allSourcesBuilder.findCommonFiles(); + + int resultCount = metadata.size(); + assertEquals(resultCount, 0); + + } catch (NoCurrentCaseException | TskCoreException | SQLException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex); + } + } + + /** + * Find all matches on image #1 & all file types. Confirm no matches. + */ + public void testTwo() { + try { + Map dataSources = this.utils.getDataSourceMap(); + Long first = getDataSourceIdByName(SET1, dataSources); + + CommonFilesMetadataBuilder singleSourceBuilder = new SingleDataSource(first, dataSources, false, false); + CommonFilesMetadata metadata = singleSourceBuilder.findCommonFiles(); + + int resultCount = metadata.size(); + assertEquals(resultCount, 0); + + } catch (NoCurrentCaseException | TskCoreException | SQLException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex); + } + } +} diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/testutils/CaseUtils.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/testutils/CaseUtils.java index a1bbd0ae24..224f2c9dd6 100755 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/testutils/CaseUtils.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/testutils/CaseUtils.java @@ -125,5 +125,4 @@ public final class CaseUtils { */ private CaseUtils() { } - } diff --git a/CoreLibs/build.xml b/CoreLibs/build.xml index f4c8cf0844..0e4a3701f8 100644 --- a/CoreLibs/build.xml +++ b/CoreLibs/build.xml @@ -20,8 +20,16 @@ - - + + + + + + + + + + diff --git a/CoreLibs/nbproject/project.properties b/CoreLibs/nbproject/project.properties index 4031d14fc3..77a432d10c 100644 --- a/CoreLibs/nbproject/project.properties +++ b/CoreLibs/nbproject/project.properties @@ -56,6 +56,7 @@ file.reference.LGoodDatePicker-10.3.1.jar=release/modules/ext/LGoodDatePicker-10 file.reference.log4j-1.2.17.jar=release/modules/ext/log4j-1.2.17.jar file.reference.logkit-1.0.1.jar=release/modules/ext/logkit-1.0.1.jar file.reference.mail-1.4.3.jar=release/modules/ext/mail-1.4.3.jar +file.reference.opencv-2413.jar=release/modules/ext/opencv-2413.jar file.reference.openjfx-dialogs-1.0.2.jar=release/modules/ext/openjfx-dialogs-1.0.3.jar file.reference.platform-3.4.0.jar=release/modules/ext/platform-3.4.0.jar file.reference.poi-3.17.jar=release/modules/ext/poi-3.17.jar diff --git a/CoreLibs/nbproject/project.xml b/CoreLibs/nbproject/project.xml index ca89b8c1c2..8ed00c1826 100644 --- a/CoreLibs/nbproject/project.xml +++ b/CoreLibs/nbproject/project.xml @@ -619,6 +619,18 @@ org.joda.time.field org.joda.time.format org.joda.time.tz + org.opencv.calib3d + org.opencv.contrib + org.opencv.core + org.opencv.features2d + org.opencv.gpu + org.opencv.highgui + org.opencv.imgproc + org.opencv.ml + org.opencv.objdetect + org.opencv.photo + org.opencv.utils + org.opencv.video org.openxmlformats.schemas.drawingml.x2006.chart org.openxmlformats.schemas.drawingml.x2006.chart.impl org.openxmlformats.schemas.drawingml.x2006.main @@ -733,6 +745,10 @@ ext/jfxtras-common-8.0-r4.jar release/modules/ext/jfxtras-common-8.0-r4.jar + + ext/opencv-2413.jar + release/modules/ext/opencv-2413.jar + ext/jsr305-1.3.9.jar release/modules/ext/jsr305-1.3.9.jar diff --git a/CoreLibs/src/org/sleuthkit/autopsy/corelibs/OpenCvLoader.java b/CoreLibs/src/org/sleuthkit/autopsy/corelibs/OpenCvLoader.java new file mode 100644 index 0000000000..7cfad05bc8 --- /dev/null +++ b/CoreLibs/src/org/sleuthkit/autopsy/corelibs/OpenCvLoader.java @@ -0,0 +1,57 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2018 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.corelibs; + +import org.opencv.core.Core; + +public final class OpenCvLoader { + + private static final boolean OPEN_CV_LOADED; + private static UnsatisfiedLinkError exception = null; + + static { + boolean tempOpenCvLoaded = false; + try { + System.loadLibrary(Core.NATIVE_LIBRARY_NAME); + tempOpenCvLoaded = true; + } catch (UnsatisfiedLinkError e) { + tempOpenCvLoaded = false; + exception = e; //save relevant error for throwing at appropriate time + } + OPEN_CV_LOADED = tempOpenCvLoaded; + } + + /** + * Return whether or not the OpenCV library has been loaded. + * + * @return - true if the opencv library is loaded or false if it is not + */ + public static boolean isOpenCvLoaded() throws UnsatisfiedLinkError { + if (!OPEN_CV_LOADED) { + //exception should never be null if the open cv isn't loaded but just in case + if (exception != null) { + throw exception; + } else { + throw new UnsatisfiedLinkError("OpenCV native library failed to load"); + } + + } + return OPEN_CV_LOADED; + } +} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AinStatusDashboard.form b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AinStatusDashboard.form index 9f0ddc84c8..9bd8b349dd 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AinStatusDashboard.form +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AinStatusDashboard.form @@ -26,7 +26,9 @@ - + + + @@ -45,6 +47,7 @@ + @@ -89,5 +92,24 @@ + + + + + + + + + + + + + + + + + + + diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AinStatusDashboard.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AinStatusDashboard.java index 9a80706699..3cfe48da5d 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AinStatusDashboard.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AinStatusDashboard.java @@ -23,10 +23,12 @@ import java.awt.EventQueue; import java.util.Observable; import java.util.Observer; import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestMonitor.AutoIngestNodeState; +import org.sleuthkit.autopsy.healthmonitor.HealthMonitorDashboard; /** * A dashboard for monitoring the existing AutoIngestNodes and their status. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class AinStatusDashboard extends javax.swing.JPanel implements Observer { private final AutoIngestMonitor autoIngestMonitor; @@ -73,6 +75,7 @@ final class AinStatusDashboard extends javax.swing.JPanel implements Observer { clusterMetricsButton = new javax.swing.JButton(); nodeStatusScrollPane = new javax.swing.JScrollPane(); nodeStatusTableTitle = new javax.swing.JLabel(); + healthMonitorButton = new javax.swing.JButton(); org.openide.awt.Mnemonics.setLocalizedText(refreshButton, org.openide.util.NbBundle.getMessage(AinStatusDashboard.class, "AinStatusDashboard.refreshButton.text")); // NOI18N refreshButton.setToolTipText(org.openide.util.NbBundle.getMessage(AinStatusDashboard.class, "AinStatusDashboard.refreshButton.toolTipText")); // NOI18N @@ -92,6 +95,16 @@ final class AinStatusDashboard extends javax.swing.JPanel implements Observer { nodeStatusTableTitle.setFont(new java.awt.Font("Tahoma", 0, 14)); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(nodeStatusTableTitle, org.openide.util.NbBundle.getMessage(AinStatusDashboard.class, "AinStatusDashboard.nodeStatusTableTitle.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(healthMonitorButton, org.openide.util.NbBundle.getMessage(AinStatusDashboard.class, "AinStatusDashboard.healthMonitorButton.text")); // NOI18N + healthMonitorButton.setMaximumSize(new java.awt.Dimension(133, 23)); + healthMonitorButton.setMinimumSize(new java.awt.Dimension(133, 23)); + healthMonitorButton.setPreferredSize(new java.awt.Dimension(133, 23)); + healthMonitorButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + healthMonitorButtonActionPerformed(evt); + } + }); + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( @@ -105,7 +118,9 @@ final class AinStatusDashboard extends javax.swing.JPanel implements Observer { .addGap(0, 0, Short.MAX_VALUE)) .addGroup(layout.createSequentialGroup() .addComponent(refreshButton, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 715, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 576, Short.MAX_VALUE) + .addComponent(healthMonitorButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(clusterMetricsButton))) .addContainerGap()) ); @@ -122,7 +137,8 @@ final class AinStatusDashboard extends javax.swing.JPanel implements Observer { .addGap(382, 382, 382) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(refreshButton) - .addComponent(clusterMetricsButton)) + .addComponent(clusterMetricsButton) + .addComponent(healthMonitorButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addContainerGap()) ); }// //GEN-END:initComponents @@ -141,9 +157,14 @@ final class AinStatusDashboard extends javax.swing.JPanel implements Observer { new AutoIngestMetricsDialog(this.getTopLevelAncestor()); }//GEN-LAST:event_clusterMetricsButtonActionPerformed + private void healthMonitorButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_healthMonitorButtonActionPerformed + new HealthMonitorDashboard(this.getTopLevelAncestor()).display(); + }//GEN-LAST:event_healthMonitorButtonActionPerformed + // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton clusterMetricsButton; + private javax.swing.JButton healthMonitorButton; private javax.swing.JScrollPane nodeStatusScrollPane; private javax.swing.JLabel nodeStatusTableTitle; private javax.swing.JButton refreshButton; diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AinStatusDashboardTopComponent.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AinStatusDashboardTopComponent.java index 6ea478699e..090932e154 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AinStatusDashboardTopComponent.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AinStatusDashboardTopComponent.java @@ -39,6 +39,7 @@ import org.sleuthkit.autopsy.coreutils.Logger; @Messages({ "CTL_AinStatusDashboardAction=Auto Ingest Nodes", "CTL_AinStatusDashboardTopComponent=Auto Ingest Nodes"}) +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class AinStatusDashboardTopComponent extends TopComponent { private static final long serialVersionUID = 1L; diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AinStatusPanel.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AinStatusPanel.java index 8516151027..1cc10fab76 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AinStatusPanel.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AinStatusPanel.java @@ -32,6 +32,7 @@ import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestJobsNode.JobNode; * A panel which displays an outline view with all auto ingest nodes and their * status. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class AinStatusPanel extends javax.swing.JPanel implements ExplorerManager.Provider { private static final long serialVersionUID = 1L; diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/ArchiveFilePanel.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/ArchiveFilePanel.java index e0a5378444..3a8e844812 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/ArchiveFilePanel.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/ArchiveFilePanel.java @@ -43,6 +43,7 @@ import org.sleuthkit.autopsy.coreutils.PathValidator; * "zip", "rar", "arj", "7z", "7zip", "gzip, etc). Allows the user to select a * file. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class ArchiveFilePanel extends JPanel implements DocumentListener { private static final Logger logger = Logger.getLogger(ArchiveFilePanel.class.getName()); diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestControlPanel.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestControlPanel.java index 341632a083..402bae91a9 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestControlPanel.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestControlPanel.java @@ -113,6 +113,7 @@ import org.sleuthkit.autopsy.ingest.IngestProgressSnapshotDialog; "AutoIngestControlPanel.ConfigLockedTitle=Configuration directory locked", "AutoIngestControlPanel.PauseDueToSystemError=Paused due to system error, please consult the auto ingest system log" }) +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public final class AutoIngestControlPanel extends JPanel implements Observer { private static final long serialVersionUID = 1L; diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestDashboard.form b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestDashboard.form index 5e4da63ab8..103160060a 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestDashboard.form +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestDashboard.form @@ -34,7 +34,7 @@ - + @@ -45,11 +45,8 @@ - + - - - @@ -77,11 +74,7 @@ - - - - - + @@ -184,34 +177,5 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestDashboard.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestDashboard.java index f191823dc2..49e9cdbd17 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestDashboard.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestDashboard.java @@ -38,13 +38,13 @@ import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.core.ServicesMonitor; import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.healthmonitor.HealthMonitorDashboard; import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestMonitor.JobsSnapshot; import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestNodeRefreshEvents.RefreshChildrenEvent; /** * A dashboard for monitoring an automated ingest cluster. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class AutoIngestDashboard extends JPanel implements Observer { private final static String ADMIN_ACCESS_FILE_NAME = "adminAccess"; @@ -331,8 +331,6 @@ final class AutoIngestDashboard extends JPanel implements Observer { refreshButton = new javax.swing.JButton(); lbServicesStatus = new javax.swing.JLabel(); tbServicesStatusMessage = new javax.swing.JTextField(); - clusterMetricsButton = new javax.swing.JButton(); - healthMonitorButton = new javax.swing.JButton(); org.openide.awt.Mnemonics.setLocalizedText(jButton1, org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.jButton1.text")); // NOI18N @@ -366,23 +364,6 @@ final class AutoIngestDashboard extends JPanel implements Observer { tbServicesStatusMessage.setText(org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.tbServicesStatusMessage.text")); // NOI18N tbServicesStatusMessage.setBorder(null); - org.openide.awt.Mnemonics.setLocalizedText(clusterMetricsButton, org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.clusterMetricsButton.text")); // NOI18N - clusterMetricsButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - clusterMetricsButtonActionPerformed(evt); - } - }); - - org.openide.awt.Mnemonics.setLocalizedText(healthMonitorButton, org.openide.util.NbBundle.getMessage(AutoIngestDashboard.class, "AutoIngestDashboard.healthMonitorButton.text")); // NOI18N - healthMonitorButton.setMaximumSize(new java.awt.Dimension(133, 23)); - healthMonitorButton.setMinimumSize(new java.awt.Dimension(133, 23)); - healthMonitorButton.setPreferredSize(new java.awt.Dimension(133, 23)); - healthMonitorButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - healthMonitorButtonActionPerformed(evt); - } - }); - javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( @@ -396,7 +377,7 @@ final class AutoIngestDashboard extends JPanel implements Observer { .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup() .addComponent(lbServicesStatus) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(tbServicesStatusMessage, javax.swing.GroupLayout.DEFAULT_SIZE, 861, Short.MAX_VALUE)) + .addComponent(tbServicesStatusMessage, javax.swing.GroupLayout.DEFAULT_SIZE, 871, Short.MAX_VALUE)) .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) .addComponent(lbPending, javax.swing.GroupLayout.Alignment.LEADING) @@ -405,15 +386,9 @@ final class AutoIngestDashboard extends JPanel implements Observer { .addGap(0, 0, Short.MAX_VALUE)) .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup() .addComponent(refreshButton, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(healthMonitorButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(clusterMetricsButton))) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))) .addContainerGap()) ); - - layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {clusterMetricsButton, refreshButton}); - layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() @@ -434,10 +409,7 @@ final class AutoIngestDashboard extends JPanel implements Observer { .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(completedScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 179, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(refreshButton) - .addComponent(clusterMetricsButton) - .addComponent(healthMonitorButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(refreshButton) .addContainerGap()) ); }// //GEN-END:initComponents @@ -455,18 +427,8 @@ final class AutoIngestDashboard extends JPanel implements Observer { setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); }//GEN-LAST:event_refreshButtonActionPerformed - private void clusterMetricsButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_clusterMetricsButtonActionPerformed - new AutoIngestMetricsDialog(this.getTopLevelAncestor()); - }//GEN-LAST:event_clusterMetricsButtonActionPerformed - - private void healthMonitorButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_healthMonitorButtonActionPerformed - new HealthMonitorDashboard(this.getTopLevelAncestor()).display(); - }//GEN-LAST:event_healthMonitorButtonActionPerformed - // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JButton clusterMetricsButton; private javax.swing.JScrollPane completedScrollPane; - private javax.swing.JButton healthMonitorButton; private javax.swing.JButton jButton1; private javax.swing.JLabel lbCompleted; private javax.swing.JLabel lbPending; diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestDashboardTopComponent.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestDashboardTopComponent.java index 195df4e4e7..4ce309b6ab 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestDashboardTopComponent.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestDashboardTopComponent.java @@ -42,6 +42,7 @@ import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; @Messages({ "CTL_AutoIngestDashboardAction=Auto Ingest Jobs", "CTL_AutoIngestDashboardTopComponent=Auto Ingest Jobs"}) +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public final class AutoIngestDashboardTopComponent extends TopComponent { private static final long serialVersionUID = 1L; diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJob.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJob.java index 25a3b3d495..4083d448f7 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJob.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJob.java @@ -87,7 +87,7 @@ final class AutoIngestJob implements Comparable, IngestProgressSn private int numberOfCrashes; @GuardedBy("this") private StageDetails stageDetails; - + /* * Version 2 fields. */ @@ -100,7 +100,7 @@ final class AutoIngestJob implements Comparable, IngestProgressSn private List ingestThreadsSnapshot; private List ingestJobsSnapshot; private Map moduleRunTimesSnapshot; - + /** * Constructs a new automated ingest job. All job state not specified in the * job manifest is set to the default state for a new job. @@ -132,14 +132,14 @@ final class AutoIngestJob implements Comparable, IngestProgressSn this.processingStatus = ProcessingStatus.PENDING; this.numberOfCrashes = 0; this.stageDetails = this.getProcessingStageDetails(); - + /* * Version 2 fields. */ this.dataSourceSize = 0; - + /* - * Version 3 fields. + * Version 3 fields. */ this.ingestThreadsSnapshot = Collections.emptyList(); this.ingestJobsSnapshot = Collections.emptyList(); @@ -181,12 +181,12 @@ final class AutoIngestJob implements Comparable, IngestProgressSn this.processingStatus = nodeData.getProcessingStatus(); this.numberOfCrashes = nodeData.getNumberOfCrashes(); this.stageDetails = this.getProcessingStageDetails(); - + /* * Version 2 fields. */ this.dataSourceSize = nodeData.getDataSourceSize(); - + /* * Version 3 fields */ @@ -257,7 +257,7 @@ final class AutoIngestJob implements Comparable, IngestProgressSn * Sets the processing stage of the job. The start date/time for the stage * is set when the stage is set. * - * @param newStage The processing stage. + * @param newStage The processing stage. * @param stageStartDate The date and time this stage started. */ synchronized void setProcessingStage(Stage newStage, Date stageStartDate) { @@ -377,20 +377,22 @@ final class AutoIngestJob implements Comparable, IngestProgressSn /** * Sets the ingest job snapshot for the auto ingest job. - * @param snapshot + * + * @param snapshot */ synchronized void setIngestJobsSnapshot(List snapshot) { this.ingestJobsSnapshot = snapshot; } - + /** * Sets the module run times snapshot for the auto ingest job. - * @param snapshot + * + * @param snapshot */ synchronized void setModuleRuntimesSnapshot(Map snapshot) { this.moduleRunTimesSnapshot = snapshot; } - + /** * Cancels the job. */ @@ -589,7 +591,21 @@ final class AutoIngestJob implements Comparable, IngestProgressSn */ @Override public int compareTo(AutoIngestJob otherJob) { - return -this.getManifest().getDateFileCreated().compareTo(otherJob.getManifest().getDateFileCreated()); + int comparisonResult = -(this.getPriority().compareTo(otherJob.getPriority())); + if (comparisonResult == 0) { + //if the priority is the same compare with the jobs manifest creation date + comparisonResult = this.getManifest().getDateFileCreated().compareTo(otherJob.getManifest().getDateFileCreated()); + if (comparisonResult == 0) { + //if the manifest files were created at the same time compare with the jobs case name + comparisonResult = -this.getManifest().getCaseName().compareTo(otherJob.getManifest().getCaseName()); + if (comparisonResult == 0) { + //if the case name is the same compare with the jobs datasource file name + comparisonResult = -this.getManifest().getDataSourcePath().getFileName().toString().compareTo(otherJob.getManifest().getDataSourcePath().getFileName().toString()); + //if they are still the same at this point they may be ordered inconsistently + } + } + } + return comparisonResult; } @Override @@ -607,31 +623,6 @@ final class AutoIngestJob implements Comparable, IngestProgressSn return this.moduleRunTimesSnapshot; } - /** - * Comparator that supports doing a descending sort of jobs based on job - * completion date. - */ - static class CompletedDateDescendingComparator implements Comparator { - - @Override - public int compare(AutoIngestJob o1, AutoIngestJob o2) { - return -o1.getCompletedDate().compareTo(o2.getCompletedDate()); - } - - } - - /** - * Comparator that orders jobs in descending order by job priority. - */ - public static class PriorityComparator implements Comparator { - - @Override - public int compare(AutoIngestJob job, AutoIngestJob anotherJob) { - return -(job.getPriority().compareTo(anotherJob.getPriority())); - } - - } - /** * Comparator that orders jobs such that those running on the local host * appear first, then the remaining jobs are sorted alphabetically by case @@ -653,18 +644,6 @@ final class AutoIngestJob implements Comparable, IngestProgressSn } - /** - * Comparator that orders jobs by data source name. - */ - static class DataSourceFileNameComparator implements Comparator { - - @Override - public int compare(AutoIngestJob aJob, AutoIngestJob anotherJob) { - return aJob.getManifest().getDataSourceFileName().compareToIgnoreCase(anotherJob.getManifest().getDataSourceFileName()); - } - - } - /** * Processing statuses for an auto ingest job. */ @@ -730,7 +709,7 @@ final class AutoIngestJob implements Comparable, IngestProgressSn } } - + /** * Exception thrown when there is a problem creating auto ingest job. */ diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJobsNode.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJobsNode.java index 15913dfba7..a7b2ad6116 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJobsNode.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJobsNode.java @@ -23,6 +23,7 @@ import com.google.common.eventbus.Subscribe; import javax.swing.Action; import java.time.Instant; import java.util.ArrayList; +import java.util.Collections; import java.util.Date; import java.util.List; import org.openide.nodes.AbstractNode; @@ -41,7 +42,7 @@ import org.sleuthkit.autopsy.guiutils.StatusIconCellRenderer; * Each job with the specified status will have a child node representing it. */ final class AutoIngestJobsNode extends AbstractNode { - + //Event bus is non static so that each instance of this will only listen to events sent to that instance private final EventBus refreshChildrenEventBus; @@ -59,9 +60,14 @@ final class AutoIngestJobsNode extends AbstractNode { /** * Construct a new AutoIngestJobsNode. + * + * @param snapshot the snapshot which contains the AutoIngestJobs + * @param status the status of the jobs being displayed + * @param eventBus the event bus which will be used to send and receive + * refresh events */ - AutoIngestJobsNode(AutoIngestJobStatus status, EventBus eventBus) { - super(Children.create(new AutoIngestNodeChildren(status, eventBus), false)); + AutoIngestJobsNode(JobsSnapshot jobsSnapshot, AutoIngestJobStatus status, EventBus eventBus) { + super(Children.create(new AutoIngestNodeChildren(jobsSnapshot, status, eventBus), false)); refreshChildrenEventBus = eventBus; } @@ -88,9 +94,11 @@ final class AutoIngestJobsNode extends AbstractNode { * * @param snapshot the snapshot which contains the AutoIngestJobs * @param status the status of the jobs being displayed + * @param eventBus the event bus which the class registers to for + * refresh events */ - AutoIngestNodeChildren(AutoIngestJobStatus status, EventBus eventBus) { - jobsSnapshot = new JobsSnapshot(); + AutoIngestNodeChildren(JobsSnapshot snapshot, AutoIngestJobStatus status, EventBus eventBus) { + jobsSnapshot = snapshot; autoIngestJobStatus = status; refreshEventBus = eventBus; refreshChildrenSubscriber.register(refreshEventBus); @@ -102,7 +110,7 @@ final class AutoIngestJobsNode extends AbstractNode { switch (autoIngestJobStatus) { case PENDING_JOB: jobs = jobsSnapshot.getPendingJobs(); - jobs.sort(new AutoIngestJob.PriorityComparator()); + Collections.sort(jobs); break; case RUNNING_JOB: jobs = jobsSnapshot.getRunningJobs(); diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJobsPanel.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJobsPanel.java index 7818dd8d68..a3cdc1c51d 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJobsPanel.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJobsPanel.java @@ -30,12 +30,12 @@ import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.datamodel.EmptyNode; import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestJobsNode.AutoIngestJobStatus; import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestJobsNode.JobNode; -import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestMonitor.JobsSnapshot; import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestNodeRefreshEvents.AutoIngestRefreshEvent; /** * A panel which displays an outline view with all jobs for a specified status. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class AutoIngestJobsPanel extends javax.swing.JPanel implements ExplorerManager.Provider { private static final long serialVersionUID = 1L; @@ -171,7 +171,7 @@ final class AutoIngestJobsPanel extends javax.swing.JPanel implements ExplorerMa ((AutoIngestJobsNode) explorerManager.getRootContext()).refresh(refreshEvent); } else { //Make a new AutoIngestJobsNode with it's own EventBus and set it as the root context - explorerManager.setRootContext(new AutoIngestJobsNode(status, new EventBus("AutoIngestJobsNodeEventBus"))); + explorerManager.setRootContext(new AutoIngestJobsNode(refreshEvent.getJobsSnapshot(), status, new EventBus("AutoIngestJobsNodeEventBus"))); } outline.setRowSelectionAllowed(true); outline.setFocusable(true); diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java index 9040c63709..e30c795013 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java @@ -408,7 +408,7 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen completedJobs.remove(job); // Add to pending jobs table and re-sort. pendingJobs.add(job); - Collections.sort(pendingJobs, new AutoIngestJob.PriorityComparator()); + Collections.sort(pendingJobs); setChanged(); notifyObservers(Event.REPROCESS_JOB); @@ -454,7 +454,7 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen private void handleRemoteNodeControlEvent(AutoIngestNodeControlEvent event) { if (event.getTargetNodeName().compareToIgnoreCase(LOCAL_HOST_NAME) == 0) { - sysLogger.log(Level.INFO, "Received {0} event from user {1} on machine {2}", new Object[] {event.getControlEventType().toString(), event.getUserName(), event.getOriginatingNodeName()}); + sysLogger.log(Level.INFO, "Received {0} event from user {1} on machine {2}", new Object[]{event.getControlEventType().toString(), event.getUserName(), event.getOriginatingNodeName()}); switch (event.getControlEventType()) { case PAUSE: pause(); @@ -656,7 +656,7 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen } } - Collections.sort(pendingJobs, new AutoIngestJob.PriorityComparator()); + Collections.sort(pendingJobs); } if (!jobsToDeprioritize.isEmpty()) { @@ -705,7 +705,7 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen } } - Collections.sort(pendingJobs, new AutoIngestJob.PriorityComparator()); + Collections.sort(pendingJobs); } if (!jobsToPrioritize.isEmpty()) { @@ -755,7 +755,7 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen } } - Collections.sort(pendingJobs, new AutoIngestJob.PriorityComparator()); + Collections.sort(pendingJobs); } if (null != jobToDeprioritize) { @@ -811,7 +811,7 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen } } - Collections.sort(pendingJobs, new AutoIngestJob.PriorityComparator()); + Collections.sort(pendingJobs); } if (null != jobToPrioritize) { @@ -868,7 +868,7 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen } } - Collections.sort(pendingJobs, new AutoIngestJob.PriorityComparator()); + Collections.sort(pendingJobs); } } @@ -1168,7 +1168,7 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen newPendingJobsList.clear(); newCompletedJobsList.clear(); Files.walkFileTree(rootInputDirectory, EnumSet.of(FOLLOW_LINKS), Integer.MAX_VALUE, this); - Collections.sort(newPendingJobsList, new AutoIngestJob.PriorityComparator()); + Collections.sort(newPendingJobsList); AutoIngestManager.this.pendingJobs = newPendingJobsList; AutoIngestManager.this.completedJobs = newCompletedJobsList; @@ -2028,21 +2028,14 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen * while reading the lock data */ private Lock dequeueAndLockNextJob() throws CoordinationServiceException, InterruptedException { - sysLogger.log(Level.INFO, "Checking pending jobs queue for ready job, enforcing max jobs per case"); + sysLogger.log(Level.INFO, "Checking pending jobs queue for ready job"); Lock manifestLock; synchronized (jobsLock) { - manifestLock = dequeueAndLockNextJob(true); + manifestLock = dequeueAndLockNextJobHelper(); if (null != manifestLock) { sysLogger.log(Level.INFO, "Dequeued job for {0}", currentJob.getManifest().getFilePath()); } else { sysLogger.log(Level.INFO, "No ready job"); - sysLogger.log(Level.INFO, "Checking pending jobs queue for ready job, not enforcing max jobs per case"); - manifestLock = dequeueAndLockNextJob(false); - if (null != manifestLock) { - sysLogger.log(Level.INFO, "Dequeued job for {0}", currentJob.getManifest().getFilePath()); - } else { - sysLogger.log(Level.INFO, "No ready job"); - } } } return manifestLock; @@ -2054,8 +2047,6 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen * queue, made the current job, and a coordination service lock on the * manifest for the job is returned. * - * @param enforceMaxJobsPerCase Whether or not to enforce the maximum - * concurrent jobs per case setting. * * @return A manifest file lock if a ready job was found, null * otherwise. @@ -2066,7 +2057,7 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen * @throws InterruptedException if the thread is interrupted * while reading the lock data */ - private Lock dequeueAndLockNextJob(boolean enforceMaxJobsPerCase) throws CoordinationServiceException, InterruptedException { + private Lock dequeueAndLockNextJobHelper() throws CoordinationServiceException, InterruptedException { Lock manifestLock = null; synchronized (jobsLock) { Iterator iterator = pendingJobs.iterator(); @@ -2096,19 +2087,6 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen continue; } - if (enforceMaxJobsPerCase) { - int currentJobsForCase = 0; - for (AutoIngestJob runningJob : hostNamesToRunningJobs.values()) { - if (0 == job.getManifest().getCaseName().compareTo(runningJob.getManifest().getCaseName())) { - ++currentJobsForCase; - } - } - if (currentJobsForCase >= AutoIngestUserPreferences.getMaxConcurrentJobsForOneCase()) { - manifestLock.release(); - manifestLock = null; - continue; - } - } iterator.remove(); currentJob = job; break; @@ -2412,7 +2390,7 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen throw new CaseManagementException(String.format("Error creating solr settings file for case %s for %s", caseName, manifest.getFilePath()), ex); } catch (CaseActionException ex) { throw new CaseManagementException(String.format("Error creating or opening case %s for %s", caseName, manifest.getFilePath()), ex); - } + } } else { throw new CaseManagementException(String.format("Timed out acquiring case name lock for %s for %s", caseName, manifest.getFilePath())); } diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestMetricsDialog.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestMetricsDialog.java index de90616481..dcd6fd4982 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestMetricsDialog.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestMetricsDialog.java @@ -33,6 +33,7 @@ import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestMetricsCollector. /** * Displays auto ingest metrics for a cluster. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class AutoIngestMetricsDialog extends javax.swing.JDialog { private static final int GIGABYTE_SIZE = 1073741824; diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestRowSorter.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestRowSorter.java index 31ecd5f4ca..4f90a12f17 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestRowSorter.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestRowSorter.java @@ -37,21 +37,25 @@ class AutoIngestRowSorter extends TableRowSorter @Override public void toggleSortOrder(int column) { - if (!this.getModel().getColumnClass(column).equals(Date.class) && !this.getModel().getColumnClass(column).equals(Integer.class)) { - //currently the only Integer column this sorter is being applied to is the Priority column - super.toggleSortOrder(column); //if it isn't a date or Integer column perform the regular sorting - } else { - ArrayList sortKeys = new ArrayList<>(getSortKeys()); - if (sortKeys.isEmpty() || sortKeys.get(0).getColumn() != column) { //sort descending - sortKeys.add(0, new RowSorter.SortKey(column, SortOrder.DESCENDING)); - } else if (sortKeys.get(0).getSortOrder() == SortOrder.ASCENDING) { - sortKeys.removeIf(key -> key.getColumn() == column); - sortKeys.add(0, new RowSorter.SortKey(column, SortOrder.DESCENDING)); - } else { - sortKeys.removeIf(key -> key.getColumn() == column); - sortKeys.add(0, new RowSorter.SortKey(column, SortOrder.ASCENDING)); - } - setSortKeys(sortKeys); + SortOrder firstSort = SortOrder.ASCENDING; + SortOrder secondSort = SortOrder.DESCENDING; + + if (this.getModel().getColumnClass(column).equals(Date.class) || this.getModel().getColumnClass(column).equals(Integer.class)) { + firstSort = SortOrder.DESCENDING; + secondSort = SortOrder.ASCENDING; } + + ArrayList sortKeys = new ArrayList<>(getSortKeys()); + if (sortKeys.isEmpty() || sortKeys.get(0).getSortOrder() == SortOrder.UNSORTED) { + sortKeys.removeIf(key -> key.getColumn() == column); + sortKeys.add(0, new RowSorter.SortKey(column, firstSort)); + } else if (sortKeys.get(0).getSortOrder() == firstSort) { + sortKeys.removeIf(key -> key.getColumn() == column); + sortKeys.add(0, new RowSorter.SortKey(column, secondSort)); + } else { + sortKeys.removeIf(key -> key.getColumn() == column); + sortKeys.add(0, new RowSorter.SortKey(column, SortOrder.UNSORTED)); + } + setSortKeys(sortKeys); } } diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties index 8e2853adf4..d53f52c0cc 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties @@ -220,7 +220,6 @@ AutoIngestDashboard.refreshButton.toolTipText=Refresh displayed tables AutoIngestDashboard.refreshButton.text=&Refresh AutoIngestDashboard.jButton1.text=jButton1 AutoIngestMetricsDialog.reportTextArea.text= -AutoIngestDashboard.clusterMetricsButton.text=Auto Ingest &Metrics AutoIngestMetricsDialog.metricsButton.text=Generate Metrics Report AutoIngestMetricsDialog.closeButton.text=Close AutoIngestMetricsDialog.datePicker.toolTipText=Choose a date @@ -258,8 +257,8 @@ AutoIngestControlPanel.bnPrioritizeJob.toolTipText=Move this folder to the top o AutoIngestControlPanel.bnPrioritizeCase.toolTipText=Move all images associated with a case to top of Pending queue. AutoIngestControlPanel.bnPrioritizeJob.actionCommand=Prioritize Job AutoIngestControlPanel.bnDeprioritizeJob.actionCommand=Deprioritize Job -AutoIngestDashboard.healthMonitorButton.text=Health Monitor AinStatusDashboard.refreshButton.toolTipText=Refresh displayed tables AinStatusDashboard.refreshButton.text=&Refresh AinStatusDashboard.clusterMetricsButton.text=Auto Ingest &Metrics AinStatusDashboard.nodeStatusTableTitle.text=Auto Ingest Nodes +AinStatusDashboard.healthMonitorButton.text=Health Monitor diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/CaseImportPanel.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/CaseImportPanel.java index 2e4866b010..55ad830fb4 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/CaseImportPanel.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/CaseImportPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2015 Basis Technology Corp. + * Copyright 2015-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,7 +19,6 @@ package org.sleuthkit.autopsy.experimental.autoingest; import java.awt.Color; -import java.awt.Cursor; import java.awt.Desktop; import java.awt.EventQueue; import java.awt.Font; @@ -43,8 +42,8 @@ import org.sleuthkit.autopsy.experimental.configuration.AutoIngestUserPreference /** * This panel shows up in a tab pane next to the copy files panel for the * automated ingest copy node. - * */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public class CaseImportPanel extends javax.swing.JPanel implements ImportDoneCallback { private final CaseImportPanelController controller; diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/FileExporterSettingsPanel.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/FileExporterSettingsPanel.java index cea7338f51..517c1e46a6 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/FileExporterSettingsPanel.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/FileExporterSettingsPanel.java @@ -88,6 +88,7 @@ import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; * Global settings panel for data-source-level ingest modules that export and * catalog files based on user-defined export rules. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public final class FileExporterSettingsPanel extends JPanel { private static final long serialVersionUID = 1L; diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/AdvancedAutoIngestSettingsPanel.form b/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/AdvancedAutoIngestSettingsPanel.form index 5e1c5c0a41..60674ab755 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/AdvancedAutoIngestSettingsPanel.form +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/AdvancedAutoIngestSettingsPanel.form @@ -37,8 +37,8 @@ - - + + @@ -82,103 +82,95 @@ - + - - - - - - - - - - - - + + + + + + + - + + + + + + + + + + + - + - + + + + + + - - - - - - - - - - + + + + + + + - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + @@ -237,16 +229,6 @@ - - - - - - - - - - @@ -287,16 +269,6 @@ - - - - - - - - - - diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/AdvancedAutoIngestSettingsPanel.java b/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/AdvancedAutoIngestSettingsPanel.java index 59e135c867..042ad04f41 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/AdvancedAutoIngestSettingsPanel.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/AdvancedAutoIngestSettingsPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2015 Basis Technology Corp. + * Copyright 2015-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,13 +18,14 @@ */ package org.sleuthkit.autopsy.experimental.configuration; -import java.util.ArrayList; -import java.util.Collection; import javax.swing.DefaultComboBoxModel; -import javax.swing.JComponent; -import org.openide.util.NbBundle; import org.sleuthkit.autopsy.core.UserPreferences; +/** + * Configuration panel for advanced settings, such as number of concurrent jobs, + * number of retries, etc. + */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class AdvancedAutoIngestSettingsPanel extends javax.swing.JPanel { private static final long serialVersionUID = 1L; @@ -62,8 +63,6 @@ class AdvancedAutoIngestSettingsPanel extends javax.swing.JPanel { initThreadCount(); spSecondsBetweenJobs.setValue(AutoIngestUserPreferences.getSecondsToSleepBetweenCases()); spMaximumRetryAttempts.setValue(AutoIngestUserPreferences.getMaxNumTimesToProcessImage()); - int maxJobsPerCase = AutoIngestUserPreferences.getMaxConcurrentJobsForOneCase(); - spConcurrentJobsPerCase.setValue(maxJobsPerCase); spInputScanInterval.setValue(AutoIngestUserPreferences.getMinutesOfInputScanInterval()); spInputScanInterval.setEnabled(mode == AutoIngestSettingsPanel.OptionsUiMode.AIM); spSecondsBetweenJobs.setEnabled(mode == AutoIngestSettingsPanel.OptionsUiMode.AIM); @@ -82,7 +81,6 @@ class AdvancedAutoIngestSettingsPanel extends javax.swing.JPanel { void store() { AutoIngestUserPreferences.setSecondsToSleepBetweenCases((int) spSecondsBetweenJobs.getValue()); AutoIngestUserPreferences.setMaxNumTimesToProcessImage((int) spMaximumRetryAttempts.getValue()); - AutoIngestUserPreferences.setMaxConcurrentIngestNodesForOneCase((int) spConcurrentJobsPerCase.getValue()); AutoIngestUserPreferences.setMinutesOfInputScanInterval((int) spInputScanInterval.getValue()); UserPreferences.setNumberOfFileIngestThreads((Integer) numberOfFileIngestThreadsComboBox.getSelectedItem()); boolean isChecked = cbTimeoutEnabled.isSelected(); @@ -122,11 +120,9 @@ class AdvancedAutoIngestSettingsPanel extends javax.swing.JPanel { lbInputScanInterval = new javax.swing.JLabel(); lbRetriesAllowed = new javax.swing.JLabel(); lbNumberOfThreads = new javax.swing.JLabel(); - lbConcurrentJobsPerCase = new javax.swing.JLabel(); cbTimeoutEnabled = new javax.swing.JCheckBox(); numberOfFileIngestThreadsComboBox = new javax.swing.JComboBox<>(); lbRestartRequired = new javax.swing.JLabel(); - spConcurrentJobsPerCase = new javax.swing.JSpinner(); spMaximumRetryAttempts = new javax.swing.JSpinner(); spInputScanInterval = new javax.swing.JSpinner(); spTimeoutHours = new javax.swing.JSpinner(); @@ -161,9 +157,6 @@ class AdvancedAutoIngestSettingsPanel extends javax.swing.JPanel { org.openide.awt.Mnemonics.setLocalizedText(lbNumberOfThreads, org.openide.util.NbBundle.getMessage(AdvancedAutoIngestSettingsPanel.class, "AdvancedAutoIngestSettingsPanel.lbNumberOfThreads.text")); // NOI18N lbNumberOfThreads.setToolTipText(org.openide.util.NbBundle.getMessage(AdvancedAutoIngestSettingsPanel.class, "AdvancedAutoIngestSettingsPanel.lbNumberOfThreads.toolTipText_1")); // NOI18N - org.openide.awt.Mnemonics.setLocalizedText(lbConcurrentJobsPerCase, org.openide.util.NbBundle.getMessage(AdvancedAutoIngestSettingsPanel.class, "AdvancedAutoIngestSettingsPanel.lbConcurrentJobsPerCase.text")); // NOI18N - lbConcurrentJobsPerCase.setToolTipText(org.openide.util.NbBundle.getMessage(AdvancedAutoIngestSettingsPanel.class, "AdvancedAutoIngestSettingsPanel.lbConcurrentJobsPerCase.toolTipText_1")); // NOI18N - org.openide.awt.Mnemonics.setLocalizedText(cbTimeoutEnabled, org.openide.util.NbBundle.getMessage(AdvancedAutoIngestSettingsPanel.class, "AdvancedAutoIngestSettingsPanel.cbTimeoutEnabled.text")); // NOI18N cbTimeoutEnabled.setToolTipText(org.openide.util.NbBundle.getMessage(AdvancedAutoIngestSettingsPanel.class, "AdvancedAutoIngestSettingsPanel.cbTimeoutEnabled.toolTipText")); // NOI18N cbTimeoutEnabled.addItemListener(new java.awt.event.ItemListener() { @@ -187,9 +180,6 @@ class AdvancedAutoIngestSettingsPanel extends javax.swing.JPanel { lbRestartRequired.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/warning16.png"))); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(lbRestartRequired, org.openide.util.NbBundle.getMessage(AdvancedAutoIngestSettingsPanel.class, "AdvancedAutoIngestSettingsPanel.lbRestartRequired.text")); // NOI18N - spConcurrentJobsPerCase.setModel(new javax.swing.SpinnerNumberModel(3, 1, 100, 1)); - spConcurrentJobsPerCase.setToolTipText(org.openide.util.NbBundle.getMessage(AdvancedAutoIngestSettingsPanel.class, "AdvancedAutoIngestSettingsPanel.lbConcurrentJobsPerCase.toolTipText")); // NOI18N - spMaximumRetryAttempts.setModel(new javax.swing.SpinnerNumberModel(2, 0, 9999999, 1)); spMaximumRetryAttempts.setToolTipText(org.openide.util.NbBundle.getMessage(AdvancedAutoIngestSettingsPanel.class, "AdvancedAutoIngestSettingsPanel.lbRetriesAllowed.toolTipText_2")); // NOI18N @@ -220,76 +210,70 @@ class AdvancedAutoIngestSettingsPanel extends javax.swing.JPanel { .addGap(5, 5, 5) .addGroup(jPanelAutoIngestJobSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(jPanelAutoIngestJobSettingsLayout.createSequentialGroup() - .addGroup(jPanelAutoIngestJobSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) - .addGroup(jPanelAutoIngestJobSettingsLayout.createSequentialGroup() - .addComponent(lbInputScanInterval) - .addGap(49, 49, 49)) - .addGroup(jPanelAutoIngestJobSettingsLayout.createSequentialGroup() - .addComponent(lbRetriesAllowed) - .addGap(54, 54, 54)) - .addComponent(lbConcurrentJobsPerCase, javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(lbNumberOfThreads, javax.swing.GroupLayout.Alignment.LEADING)) + .addComponent(lbNumberOfThreads) + .addComponent(numberOfFileIngestThreadsComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 91, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(lbRestartRequired) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addGroup(jPanelAutoIngestJobSettingsLayout.createSequentialGroup() .addGroup(jPanelAutoIngestJobSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(jPanelAutoIngestJobSettingsLayout.createSequentialGroup() - .addGap(0, 41, Short.MAX_VALUE) + .addGroup(jPanelAutoIngestJobSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) + .addGroup(jPanelAutoIngestJobSettingsLayout.createSequentialGroup() + .addComponent(lbInputScanInterval) + .addGap(49, 49, 49)) + .addGroup(jPanelAutoIngestJobSettingsLayout.createSequentialGroup() + .addComponent(lbRetriesAllowed) + .addGap(54, 54, 54))) + .addGap(0, 21, Short.MAX_VALUE) .addGroup(jPanelAutoIngestJobSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) .addComponent(spInputScanInterval, javax.swing.GroupLayout.PREFERRED_SIZE, 90, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(spMaximumRetryAttempts, javax.swing.GroupLayout.PREFERRED_SIZE, 90, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(spConcurrentJobsPerCase, javax.swing.GroupLayout.PREFERRED_SIZE, 90, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addComponent(spMaximumRetryAttempts, javax.swing.GroupLayout.PREFERRED_SIZE, 90, javax.swing.GroupLayout.PREFERRED_SIZE))) .addGroup(jPanelAutoIngestJobSettingsLayout.createSequentialGroup() + .addComponent(lbSecondsBetweenJobs) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(numberOfFileIngestThreadsComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 91, javax.swing.GroupLayout.PREFERRED_SIZE)))) - .addGroup(jPanelAutoIngestJobSettingsLayout.createSequentialGroup() - .addComponent(lbSecondsBetweenJobs) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(spSecondsBetweenJobs, javax.swing.GroupLayout.PREFERRED_SIZE, 90, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanelAutoIngestJobSettingsLayout.createSequentialGroup() - .addComponent(lbTimeoutText) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(spTimeoutHours, javax.swing.GroupLayout.PREFERRED_SIZE, 90, javax.swing.GroupLayout.PREFERRED_SIZE))) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(jPanelAutoIngestJobSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(lbRestartRequired) - .addComponent(lbSecondsBetweenJobsSeconds) - .addComponent(lbTimeoutHours) - .addComponent(lbInputScanIntervalMinutes)) - .addContainerGap(50, Short.MAX_VALUE)) + .addComponent(spSecondsBetweenJobs, javax.swing.GroupLayout.PREFERRED_SIZE, 90, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanelAutoIngestJobSettingsLayout.createSequentialGroup() + .addComponent(lbTimeoutText) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(spTimeoutHours, javax.swing.GroupLayout.PREFERRED_SIZE, 90, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanelAutoIngestJobSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(lbSecondsBetweenJobsSeconds) + .addComponent(lbTimeoutHours) + .addComponent(lbInputScanIntervalMinutes)) + .addContainerGap(255, Short.MAX_VALUE)))) ); jPanelAutoIngestJobSettingsLayout.setVerticalGroup( jPanelAutoIngestJobSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(jPanelAutoIngestJobSettingsLayout.createSequentialGroup() .addContainerGap() + .addGroup(jPanelAutoIngestJobSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(lbSecondsBetweenJobs) + .addComponent(spSecondsBetweenJobs, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(lbSecondsBetweenJobsSeconds)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanelAutoIngestJobSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanelAutoIngestJobSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(lbTimeoutText) + .addComponent(spTimeoutHours, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(lbTimeoutHours)) + .addComponent(cbTimeoutEnabled)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanelAutoIngestJobSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(lbInputScanInterval) + .addComponent(spInputScanInterval, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(lbInputScanIntervalMinutes)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanelAutoIngestJobSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(lbRetriesAllowed) + .addComponent(spMaximumRetryAttempts, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(jPanelAutoIngestJobSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) .addComponent(lbRestartRequired) - .addGroup(jPanelAutoIngestJobSettingsLayout.createSequentialGroup() - .addGroup(jPanelAutoIngestJobSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(lbSecondsBetweenJobs) - .addComponent(spSecondsBetweenJobs, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(lbSecondsBetweenJobsSeconds)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(jPanelAutoIngestJobSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(jPanelAutoIngestJobSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(lbTimeoutText) - .addComponent(spTimeoutHours, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(lbTimeoutHours)) - .addComponent(cbTimeoutEnabled)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(jPanelAutoIngestJobSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(lbInputScanInterval) - .addComponent(spInputScanInterval, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(lbInputScanIntervalMinutes)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(jPanelAutoIngestJobSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(lbRetriesAllowed) - .addComponent(spMaximumRetryAttempts, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(jPanelAutoIngestJobSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(lbConcurrentJobsPerCase) - .addComponent(spConcurrentJobsPerCase, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(jPanelAutoIngestJobSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(lbNumberOfThreads) - .addComponent(numberOfFileIngestThreadsComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))) + .addGroup(jPanelAutoIngestJobSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(lbNumberOfThreads) + .addComponent(numberOfFileIngestThreadsComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); @@ -315,8 +299,8 @@ class AdvancedAutoIngestSettingsPanel extends javax.swing.JPanel { .addGap(20, 20, 20) .addComponent(spMainScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 106, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jPanelAutoIngestJobSettings, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addGap(26, 26, 26)) + .addComponent(jPanelAutoIngestJobSettings, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); }// //GEN-END:initComponents @@ -335,7 +319,6 @@ class AdvancedAutoIngestSettingsPanel extends javax.swing.JPanel { // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JCheckBox cbTimeoutEnabled; private javax.swing.JPanel jPanelAutoIngestJobSettings; - private javax.swing.JLabel lbConcurrentJobsPerCase; private javax.swing.JLabel lbInputScanInterval; private javax.swing.JLabel lbInputScanIntervalMinutes; private javax.swing.JLabel lbNumberOfThreads; @@ -346,7 +329,6 @@ class AdvancedAutoIngestSettingsPanel extends javax.swing.JPanel { private javax.swing.JLabel lbTimeoutHours; private javax.swing.JLabel lbTimeoutText; private javax.swing.JComboBox numberOfFileIngestThreadsComboBox; - private javax.swing.JSpinner spConcurrentJobsPerCase; private javax.swing.JSpinner spInputScanInterval; private javax.swing.JScrollPane spMainScrollPane; private javax.swing.JSpinner spMaximumRetryAttempts; diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/AutoIngestSettingsPanel.java b/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/AutoIngestSettingsPanel.java index 85737799e9..d88ece8ddd 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/AutoIngestSettingsPanel.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/AutoIngestSettingsPanel.java @@ -45,17 +45,18 @@ import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.experimental.autoingest.FileExporterSettingsPanel; /** - * + * Configuration panel for auto ingest settings. */ @Messages({"AutoIngestSettingsPanel.examinerModeRadioButton.text=Examiner mode", "AutoIngestSettingsPanel.autoIngestModeRadioButton.text=Auto Ingest mode"}) +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public class AutoIngestSettingsPanel extends javax.swing.JPanel { private final AutoIngestSettingsPanelController controller; private final JFileChooser fc = new JFileChooser(); private static final long serialVersionUID = 1L; private static final Logger logger = Logger.getLogger(AutoIngestSettingsPanel.class.getName()); - private Integer oldIngestThreads; + private final Integer oldIngestThreads; private static final String MULTI_USER_SETTINGS_MUST_BE_ENABLED = NbBundle.getMessage(AutoIngestSettingsPanel.class, "AutoIngestSettingsPanel.validationErrMsg.MUdisabled"); enum OptionsUiMode { diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/AutoIngestUserPreferences.java b/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/AutoIngestUserPreferences.java index 8cd99b89c4..e532b875cc 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/AutoIngestUserPreferences.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/AutoIngestUserPreferences.java @@ -267,30 +267,7 @@ public final class AutoIngestUserPreferences { ModuleSettings.setConfigSetting(UserPreferences.SETTINGS_PROPERTIES, MAX_NUM_TIMES_TO_PROCESS_IMAGE, Integer.toString(retries)); } - /** - * Get maximum number of concurrent ingest nodes allowable for one case at a - * time. - * - * @return maximum number of concurrent nodes for one case. Default is 3. - */ - public static int getMaxConcurrentJobsForOneCase() { - if (ModuleSettings.settingExists(UserPreferences.SETTINGS_PROPERTIES, MAX_CONCURRENT_NODES_FOR_ONE_CASE)) { - return Integer.parseInt(ModuleSettings.getConfigSetting(UserPreferences.SETTINGS_PROPERTIES, MAX_CONCURRENT_NODES_FOR_ONE_CASE)); - } - return 3; - } - - /** - * Get maximum number of concurrent ingest nodes allowable for one case at a - * time. - * - * @param numberOfNodes the number of concurrent nodes to allow for one case - */ - public static void setMaxConcurrentIngestNodesForOneCase(int numberOfNodes) { - ModuleSettings.setConfigSetting(UserPreferences.SETTINGS_PROPERTIES, MAX_CONCURRENT_NODES_FOR_ONE_CASE, Integer.toString(numberOfNodes)); - } - - /** + /** * Get status database logging checkbox state for automated ingest mode from * persistent storage. * diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/Bundle.properties b/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/Bundle.properties index 97a82988f2..eaae72eb66 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/Bundle.properties +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/Bundle.properties @@ -89,7 +89,6 @@ AdvancedAutoIngestSettingsPanel.spMaximumRetryAttempts.toolTipText=The maximum n AdvancedAutoIngestSettingsPanel.lbRestartRequired.text=Application restart required to take effect. AdvancedAutoIngestSettingsPanel.cbTimeoutEnabled.toolTipText=Components that spawn potentially long-running processes optionally terminate those processes if the specified time out period has elapsed. AdvancedAutoIngestSettingsPanel.cbTimeoutEnabled.text= -AdvancedAutoIngestSettingsPanel.lbConcurrentJobsPerCase.text=Target concurrent jobs per case: AdvancedAutoIngestSettingsPanel.lbNumberOfThreads.text=Number of threads to use for file ingest: AdvancedAutoIngestSettingsPanel.lbRetriesAllowed.text=Maximum job retries allowed: AdvancedAutoIngestSettingsPanel.lbInputScanInterval.text=Interval between input scans: @@ -103,7 +102,6 @@ AdvancedAutoIngestSettingsPanel.lbInputScanIntervalMinutes.toolTipText= AdvancedAutoIngestSettingsPanel.lbTimeoutHours.toolTipText= AdvancedAutoIngestSettingsPanel.lbRetriesAllowed.toolTipText_1=The maximum number of retries for crashed jobs. AdvancedAutoIngestSettingsPanel.lbRetriesAllowed.toolTipText_2=The maximum number of retries for crashed jobs. -AdvancedAutoIngestSettingsPanel.lbConcurrentJobsPerCase.toolTipText_1=A soft limit on the number of concurrent jobs per case when multiple cases are processed simultaneously. AdvancedAutoIngestSettingsPanel.lbNumberOfThreads.toolTipText_1=The number of threads running file level ingest modules. AdvancedAutoIngestSettingsPanel.numberOfFileIngestThreadsComboBox.toolTipText=The number of threads running file level ingest modules. NodeStatusLogPanel.tbDbName.toolTipText_1=Database name diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/NodeStatusLogPanel.java b/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/NodeStatusLogPanel.java index e17009feda..88163b67df 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/NodeStatusLogPanel.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/NodeStatusLogPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2015 Basis Technology Corp. + * Copyright 2015-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -39,8 +39,9 @@ import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.experimental.autoingest.StatusDatabaseLogger; /** - * + * Node status log panel. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public class NodeStatusLogPanel extends javax.swing.JPanel { private static final String HOST_NAME_OR_IP_PROMPT = NbBundle.getMessage(NodeStatusLogPanel.class, "NodeStatusLogPanel.tbDbHostname.toolTipText"); @@ -57,6 +58,7 @@ public class NodeStatusLogPanel extends javax.swing.JPanel { /** * Creates new form DatabaseLogPanell + * @param jDialog */ public NodeStatusLogPanel(JDialog jDialog) { initComponents(); diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/objectdetection/ObjectDetectectionFileIngestModule.java b/Experimental/src/org/sleuthkit/autopsy/experimental/objectdetection/ObjectDetectectionFileIngestModule.java new file mode 100644 index 0000000000..fe86fb9f0b --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/objectdetection/ObjectDetectectionFileIngestModule.java @@ -0,0 +1,164 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2018 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.experimental.objectdetection; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.io.IOUtils; +import org.opencv.core.CvException; +import org.opencv.core.Mat; +import org.opencv.core.MatOfByte; +import org.opencv.core.MatOfRect; +import org.opencv.highgui.Highgui; +import org.opencv.objdetect.CascadeClassifier; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.casemodule.services.Blackboard; +import org.sleuthkit.autopsy.corelibs.OpenCvLoader; +import org.sleuthkit.autopsy.coreutils.ImageUtils; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.PlatformUtil; +import org.sleuthkit.autopsy.ingest.FileIngestModuleAdapter; +import org.sleuthkit.autopsy.ingest.IngestJobContext; +import org.sleuthkit.autopsy.ingest.IngestMessage; +import org.sleuthkit.autopsy.ingest.IngestModule; +import org.sleuthkit.autopsy.ingest.IngestModuleReferenceCounter; +import org.sleuthkit.autopsy.ingest.IngestServices; +import org.sleuthkit.autopsy.ingest.ModuleDataEvent; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.BlackboardArtifact; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_OBJECT_DETECTED; +import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.ReadContentInputStream; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Data source module to detect objects in images. + */ +public class ObjectDetectectionFileIngestModule extends FileIngestModuleAdapter { + + private final static Logger logger = Logger.getLogger(ObjectDetectectionFileIngestModule.class.getName()); + private static final IngestModuleReferenceCounter refCounter = new IngestModuleReferenceCounter(); + private long jobId; + private Map classifiers; + private final IngestServices services = IngestServices.getInstance(); + private Blackboard blackboard; + + @Messages({"ObjectDetectionFileIngestModule.noClassifiersFound.subject=No classifiers found.", + "# {0} - classifierDir", "ObjectDetectionFileIngestModule.noClassifiersFound.message=No classifiers were found in {0}, object detection will not be executed."}) + @Override + public void startUp(IngestJobContext context) throws IngestModule.IngestModuleException { + jobId = context.getJobId(); + File classifierDir = new File(PlatformUtil.getObjectDetectionClassifierPath()); + classifiers = new HashMap<>(); + //Load all classifiers found in PlatformUtil.getObjectDetectionClassifierPath() + if (OpenCvLoader.isOpenCvLoaded() && classifierDir.exists() && classifierDir.isDirectory()) { + for (File classifier : classifierDir.listFiles()) { + if (classifier.isFile() && FilenameUtils.getExtension(classifier.getName()).equalsIgnoreCase("xml")) { + classifiers.put(classifier.getName(), new CascadeClassifier(classifier.getAbsolutePath())); + } + } + } else { + throw new IngestModule.IngestModuleException("Unable to load classifiers for object detection module."); + } + if (refCounter.incrementAndGet(jobId) == 1 && classifiers.isEmpty()) { + services.postMessage(IngestMessage.createWarningMessage(ObjectDetectionModuleFactory.getModuleName(), + Bundle.ObjectDetectionFileIngestModule_noClassifiersFound_subject(), + Bundle.ObjectDetectionFileIngestModule_noClassifiersFound_message(PlatformUtil.getObjectDetectionClassifierPath()))); + } + try { + blackboard = Case.getCurrentCaseThrows().getServices().getBlackboard(); + } catch (NoCurrentCaseException ex) { + throw new IngestModule.IngestModuleException("Exception while getting open case.", ex); + } + } + + @Messages({"# {0} - detectionCount", "ObjectDetectionFileIngestModule.classifierDetection.text=Classifier detected {0} object(s)"}) + @Override + public ProcessResult process(AbstractFile file) { + if (!classifiers.isEmpty() && ImageUtils.isImageThumbnailSupported(file)) { + //Any image we can create a thumbnail for is one we should apply the classifiers to + InputStream inputStream = new ReadContentInputStream(file); + byte[] imageInMemory; + try { + imageInMemory = IOUtils.toByteArray(inputStream); + } catch (IOException ex) { + logger.log(Level.WARNING, "Unable to perform object detection on " + file.getName(), ex); + return IngestModule.ProcessResult.ERROR; + } + Mat originalImage = Highgui.imdecode(new MatOfByte(imageInMemory), Highgui.IMREAD_GRAYSCALE); + MatOfRect detectionRectangles = new MatOfRect(); //the rectangles which reprent the coordinates on the image for where objects were detected + for (String classifierKey : classifiers.keySet()) { + //apply each classifier to the file + try { + classifiers.get(classifierKey).detectMultiScale(originalImage, detectionRectangles); + } catch (CvException ignored) { + //The image was likely an image which we are unable to generate a thumbnail for, and the classifier was likely one where that is not acceptable + logger.log(Level.INFO, String.format("Classifier '%s' could not be applied to file '%s'.", classifierKey, file.getParentPath() + file.getName())); //NON-NLS + continue; + } + + if (!detectionRectangles.empty()) { + //if any detections occurred create an artifact for this classifier and file combination + try { + BlackboardArtifact artifact = file.newArtifact(TSK_OBJECT_DETECTED); + artifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION, + ObjectDetectionModuleFactory.getModuleName(), + classifierKey)); + artifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT, + ObjectDetectionModuleFactory.getModuleName(), + Bundle.ObjectDetectionFileIngestModule_classifierDetection_text((int) detectionRectangles.size().height))); + + try { + /* + * Index the artifact for keyword search. + */ + blackboard.indexArtifact(artifact); + } catch (Blackboard.BlackboardException ex) { + logger.log(Level.SEVERE, "Unable to index blackboard artifact " + artifact.getArtifactID(), ex); //NON-NLS + } + + /* + * Send an event to update the view with the new result. + */ + services.fireModuleDataEvent(new ModuleDataEvent(ObjectDetectionModuleFactory.getModuleName(), TSK_OBJECT_DETECTED, Collections.singletonList(artifact))); + + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, String.format("Failed to create blackboard artifact for '%s'.", file.getParentPath() + file.getName()), ex); //NON-NLS + return IngestModule.ProcessResult.ERROR; + } + } + } + } + + return IngestModule.ProcessResult.OK; + } + + @Override + public void shutDown() { + refCounter.decrementAndGet(jobId); + } +} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/objectdetection/ObjectDetectionModuleFactory.java b/Experimental/src/org/sleuthkit/autopsy/experimental/objectdetection/ObjectDetectionModuleFactory.java new file mode 100644 index 0000000000..829b4e5c02 --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/objectdetection/ObjectDetectionModuleFactory.java @@ -0,0 +1,73 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2018 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.experimental.objectdetection; + +import org.openide.util.NbBundle.Messages; +import org.openide.util.lookup.ServiceProvider; +import org.sleuthkit.autopsy.coreutils.Version; +import org.sleuthkit.autopsy.ingest.FileIngestModule; +import org.sleuthkit.autopsy.ingest.IngestModuleFactory; +import org.sleuthkit.autopsy.ingest.IngestModuleFactoryAdapter; +import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings; + +/** + * A factory that creates ingest modules which uses classifiers to detect + * objects in pictures. + */ +@ServiceProvider(service = IngestModuleFactory.class) +public class ObjectDetectionModuleFactory extends IngestModuleFactoryAdapter { + + + /** + * Get the name of the Object Detection module + * + * @return the name of the Object Detection module + */ + static String getModuleName() { + return Bundle.ObjectDetectionModuleFactory_moduleName_text(); + } + + @Messages({"ObjectDetectionModuleFactory.moduleName.text=Object Detection"}) + @Override + public String getModuleDisplayName() { + return getModuleName(); + } + + @Messages({"ObjectDetectionModuleFactory.moduleDescription.text=Use object classifiers to identify objects in pictures."}) + @Override + public String getModuleDescription() { + return Bundle.ObjectDetectionModuleFactory_moduleDescription_text(); + } + + @Override + public String getModuleVersionNumber() { + return Version.getVersion(); + } + + @Override + public boolean isFileIngestModuleFactory() { + return true; + } + + @Override + public FileIngestModule createFileIngestModule(IngestModuleIngestJobSettings settings) { + return new ObjectDetectectionFileIngestModule(); + } + +} diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryOptionsPanel.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryOptionsPanel.java index beb2bc0a3d..2b3fc1692f 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryOptionsPanel.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryOptionsPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013-16 Basis Technology Corp. + * Copyright 2013-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -33,6 +33,7 @@ import org.sleuthkit.autopsy.coreutils.Logger; * Uses {@link ImageGalleryPreferences} and {@link PerCaseProperties} to persist * settings */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class ImageGalleryOptionsPanel extends javax.swing.JPanel { ImageGalleryOptionsPanel(ImageGalleryOptionsPanelController controller) { diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryTopComponent.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryTopComponent.java index ec5b511b7f..a6ce70c3ac 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryTopComponent.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryTopComponent.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-17 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -65,6 +65,7 @@ import org.sleuthkit.autopsy.imagegallery.gui.navpanel.HashHitGroupList; "CTL_ImageGalleryAction=Image/Video Gallery", "CTL_ImageGalleryTopComponent=Image/Video Gallery" }) +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public final class ImageGalleryTopComponent extends TopComponent implements ExplorerManager.Provider, Lookup.Provider { public final static String PREFERRED_ID = "ImageGalleryTopComponent"; // NON-NLS diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/AdHocSearchDelegator.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/AdHocSearchDelegator.java index 9afb18e100..c085af7a30 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/AdHocSearchDelegator.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/AdHocSearchDelegator.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -23,6 +23,7 @@ import java.util.Collection; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.logging.Level; import org.openide.nodes.AbstractNode; import org.openide.nodes.Children; @@ -41,11 +42,13 @@ class AdHocSearchDelegator { private final List keywordLists; private List queryDelegates; + private final Set dataSourceIds; private static int resultWindowCount = 0; //keep track of unique window ids to display private static final Logger logger = Logger.getLogger(AdHocSearchDelegator.class.getName()); - public AdHocSearchDelegator(List keywordLists) { + public AdHocSearchDelegator(List keywordLists, Set dataSourceIds) { this.keywordLists = keywordLists; + this.dataSourceIds = dataSourceIds; init(); } @@ -56,6 +59,13 @@ class AdHocSearchDelegator { for (KeywordList keywordList : keywordLists) { for (Keyword keyword : keywordList.getKeywords()) { KeywordSearchQuery query = KeywordSearchUtil.getQueryForKeyword(keyword, keywordList); + + //Limit search to a set of data sources + if (dataSourceIds != null && !dataSourceIds.isEmpty()) { + final KeywordQueryFilter dataSourceFilter = new KeywordQueryFilter(KeywordQueryFilter.FilterType.DATA_SOURCE, dataSourceIds); + query.addFilter(dataSourceFilter); + } + queryDelegates.add(query); } } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/AdHocSearchPanel.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/AdHocSearchPanel.java index 13798614c5..c06394bf23 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/AdHocSearchPanel.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/AdHocSearchPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2013 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,20 +20,32 @@ package org.sleuthkit.autopsy.keywordsearch; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Set; import org.sleuthkit.autopsy.ingest.IngestManager; import org.openide.util.NbBundle; - +import org.sleuthkit.datamodel.DataSource; +import javax.swing.DefaultListModel; /** * Common functionality among keyword search widgets / panels. This is extended * by the various panels and interfaces that perform the keyword searches. This * class and extended classes model the user's intentions, not necessarily how * the search manager and 3rd party tools actually perform the search. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives abstract class AdHocSearchPanel extends javax.swing.JPanel { private final String keywordSearchErrorDialogHeader = org.openide.util.NbBundle.getMessage(this.getClass(), "AbstractKeywordSearchPerformer.search.dialogErrorHeader"); protected int filesIndexed; + private final Map dataSourceMap = new HashMap<>(); + private final List toolTipList = new ArrayList<>(); + private List dataSources = new ArrayList<>(); + private final DefaultListModel dataSourceListModel = new DefaultListModel<>(); AdHocSearchPanel() { initListeners(); @@ -48,7 +60,7 @@ abstract class AdHocSearchPanel extends javax.swing.JPanel { Object newValue = evt.getNewValue(); if (changed.equals(KeywordSearch.NUM_FILES_CHANGE_EVT)) { - int newFilesIndexed = ((Integer) newValue).intValue(); + int newFilesIndexed = ((Integer) newValue); filesIndexed = newFilesIndexed; postFilesIndexedChange(); } @@ -68,6 +80,13 @@ abstract class AdHocSearchPanel extends javax.swing.JPanel { */ abstract List getKeywordLists(); + /** + * Get a set of data source object ids that are selected. + * + * @return A set of selected object ids. + */ + abstract Set getDataSourcesSelected(); + /** * Set the number of files that have been indexed * @@ -110,8 +129,6 @@ abstract class AdHocSearchPanel extends javax.swing.JPanel { } } - AdHocSearchDelegator man = null; - final List keywordLists = getKeywordLists(); if (keywordLists.isEmpty()) { KeywordSearchUtil.displayDialog(keywordSearchErrorDialogHeader, NbBundle.getMessage(this.getClass(), @@ -119,8 +136,8 @@ abstract class AdHocSearchPanel extends javax.swing.JPanel { KeywordSearchUtil.DIALOG_MESSAGE_TYPE.ERROR); return; } - man = new AdHocSearchDelegator(keywordLists); - + + AdHocSearchDelegator man = new AdHocSearchDelegator(keywordLists, getDataSourcesSelected()); if (man.validate()) { man.execute(); } else { @@ -128,4 +145,58 @@ abstract class AdHocSearchPanel extends javax.swing.JPanel { "AbstractKeywordSearchPerformer.search.invalidSyntaxHeader"), KeywordSearchUtil.DIALOG_MESSAGE_TYPE.ERROR); } } + + /** + * Get a list of data source display name. + * + * @return The list of data source name + */ + synchronized List getDataSourceArray() { + List dsList = new ArrayList<>(); + toolTipList.clear(); + Collections.sort(this.dataSources, (DataSource ds1, DataSource ds2) -> ds1.getName().compareTo(ds2.getName())); + for (DataSource ds : dataSources) { + String dsName = ds.getName(); + File dataSourceFullName = new File(dsName); + String displayName = dataSourceFullName.getName(); + dataSourceMap.put(ds.getId(), displayName); + toolTipList.add(dsName); + dsList.add(displayName); + } + return dsList; + } + + /** + * Set dataSources + * @param dataSources A list of DataSource + */ + synchronized void setDataSources(List dataSources) { + this.dataSources = dataSources; + } + + /** + * Get dataSourceMap with object id and data source display name. Add the + * data source full name to toolTipList + * + * @return The list of data source name + */ + Map getDataSourceMap() { + return dataSourceMap; + } + + /** + * Get a list of tooltip text for data source + * @return A list of tool tips + */ + List getDataSourceToolTipList() { + return toolTipList; + } + + /** + * Get a list of DataSourceListModel + * @return A list of DataSourceListModel + */ + final DefaultListModel getDataSourceListModel() { + return dataSourceListModel; + } } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/AddKeywordsDialog.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/AddKeywordsDialog.java index 5e42e5d468..d877c3ece6 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/AddKeywordsDialog.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/AddKeywordsDialog.java @@ -35,6 +35,7 @@ import org.openide.windows.WindowManager; /** * Dialog to add one or more keywords to a list */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class AddKeywordsDialog extends javax.swing.JDialog { List newKeywords = new ArrayList<>(); diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties index 9774106659..ee49ae2c2b 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties @@ -310,4 +310,6 @@ ExtractedContentPanel.pageOfLabel.text=of ExtractedContentPanel.pageTotalLabel.text=- ExtractedContentPanel.pageButtonsLabel.text=Page ExtractedContentPanel.pagesLabel.text=Page: - +DropdownSingleTermSearchPanel.dataSourceCheckBox.text=Restrict search to the selected data sources: +DropdownListSearchPanel.dataSourceCheckBox.text=Restrict search to the selected data sources: +DropdownSingleTermSearchPanel.ingestIndexLabel.text=Files Indexed: diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle_ja.properties b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle_ja.properties index 2442e5bbc4..201e85bde2 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle_ja.properties +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle_ja.properties @@ -278,3 +278,4 @@ ExtractedContentPanel.pageOfLabel.text=of ExtractedContentPanel.pageTotalLabel.text=- ExtractedContentPanel.pageButtonsLabel.text=\u30da\u30fc\u30b8 ExtractedContentPanel.pagesLabel.text=\u30da\u30fc\u30b8\uff1a +DropdownSingleTermSearchPanel.ingestIndexLabel.text=\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u5316\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb\uff1a diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/DropdownListSearchPanel.form b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/DropdownListSearchPanel.form index bfb20406e3..873824840b 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/DropdownListSearchPanel.form +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/DropdownListSearchPanel.form @@ -25,32 +25,34 @@ + + - - - - - - - - - - - - + + + + + + + + + - - - + + + + + + @@ -171,6 +173,9 @@ + + + @@ -191,5 +196,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/DropdownListSearchPanel.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/DropdownListSearchPanel.java index 89e59cd2eb..72a1cbab5a 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/DropdownListSearchPanel.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/DropdownListSearchPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2015 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,13 +21,18 @@ package org.sleuthkit.autopsy.keywordsearch; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; +import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Set; import java.util.logging.Level; import javax.swing.JCheckBox; +import javax.swing.JList; import javax.swing.JTable; import javax.swing.ListSelectionModel; import javax.swing.event.ListSelectionEvent; @@ -63,6 +68,27 @@ class DropdownListSearchPanel extends AdHocSearchPanel { keywordsTableModel = new KeywordsTableModel(); initComponents(); customizeComponents(); + dataSourceList.setModel(getDataSourceListModel()); + + dataSourceList.addListSelectionListener((ListSelectionEvent evt) -> { + firePropertyChange(Bundle.DropdownSingleTermSearchPanel_selected(), null, null); + }); + dataSourceList.addMouseMotionListener(new MouseMotionListener() { + + @Override + public void mouseDragged(MouseEvent evt) { + //Unused by now + } + + @Override + public void mouseMoved(MouseEvent evt) { + JList dsList = (JList) evt.getSource(); + int index = dsList.locationToIndex(evt.getPoint()); + if (index > -1) { + dsList.setToolTipText(getDataSourceToolTipList().get(index)); + } + } + }); } static synchronized DropdownListSearchPanel getDefault() { @@ -194,6 +220,9 @@ class DropdownListSearchPanel extends AdHocSearchPanel { manageListsButton = new javax.swing.JButton(); searchAddButton = new javax.swing.JButton(); ingestIndexLabel = new javax.swing.JLabel(); + dataSourceCheckBox = new javax.swing.JCheckBox(); + jScrollPane1 = new javax.swing.JScrollPane(); + dataSourceList = new javax.swing.JList<>(); setFont(getFont().deriveFont(getFont().getStyle() & ~java.awt.Font.BOLD, 11)); @@ -232,6 +261,7 @@ class DropdownListSearchPanel extends AdHocSearchPanel { }); searchAddButton.setFont(searchAddButton.getFont().deriveFont(searchAddButton.getFont().getStyle() & ~java.awt.Font.BOLD, 11)); + searchAddButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/keywordsearch/search-icon.png"))); // NOI18N searchAddButton.setText(org.openide.util.NbBundle.getMessage(DropdownListSearchPanel.class, "KeywordSearchListsViewerPanel.searchAddButton.text")); // NOI18N searchAddButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { @@ -242,33 +272,50 @@ class DropdownListSearchPanel extends AdHocSearchPanel { ingestIndexLabel.setFont(ingestIndexLabel.getFont().deriveFont(ingestIndexLabel.getFont().getStyle() & ~java.awt.Font.BOLD, 10)); ingestIndexLabel.setText(org.openide.util.NbBundle.getMessage(DropdownListSearchPanel.class, "KeywordSearchListsViewerPanel.ingestIndexLabel.text")); // NOI18N + dataSourceCheckBox.setText(org.openide.util.NbBundle.getMessage(DropdownListSearchPanel.class, "DropdownListSearchPanel.dataSourceCheckBox.text")); // NOI18N + dataSourceCheckBox.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + dataSourceCheckBoxActionPerformed(evt); + } + }); + + dataSourceList.setMinimumSize(new java.awt.Dimension(0, 200)); + jScrollPane1.setViewportView(dataSourceList); + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(jSplitPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE) .addGroup(layout.createSequentialGroup() - .addContainerGap() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addComponent(searchAddButton) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 220, Short.MAX_VALUE) - .addComponent(manageListsButton)) - .addGroup(layout.createSequentialGroup() - .addComponent(ingestIndexLabel) - .addGap(0, 317, Short.MAX_VALUE))) - .addContainerGap()) + .addGap(4, 4, 4) + .addComponent(searchAddButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(manageListsButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(ingestIndexLabel) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addGroup(layout.createSequentialGroup() + .addComponent(dataSourceCheckBox) + .addGap(0, 0, Short.MAX_VALUE)) + .addComponent(jScrollPane1) ); + + layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {manageListsButton, searchAddButton}); + layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() - .addComponent(jSplitPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 268, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 7, Short.MAX_VALUE) - .addComponent(ingestIndexLabel) + .addComponent(jSplitPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 183, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(dataSourceCheckBox) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 65, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(manageListsButton) - .addComponent(searchAddButton)) + .addComponent(searchAddButton) + .addComponent(ingestIndexLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 13, javax.swing.GroupLayout.PREFERRED_SIZE)) .addContainerGap()) ); }// //GEN-END:initComponents @@ -277,12 +324,19 @@ class DropdownListSearchPanel extends AdHocSearchPanel { SystemAction.get(KeywordSearchConfigurationAction.class).performAction(); }//GEN-LAST:event_manageListsButtonActionPerformed + private void dataSourceCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_dataSourceCheckBoxActionPerformed + updateDataSourceListModel(); + }//GEN-LAST:event_dataSourceCheckBoxActionPerformed + private void searchAddButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_searchAddButtonActionPerformed // TODO add your handling code here: }//GEN-LAST:event_searchAddButtonActionPerformed // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JCheckBox dataSourceCheckBox; + private javax.swing.JList dataSourceList; private javax.swing.JLabel ingestIndexLabel; + private javax.swing.JScrollPane jScrollPane1; private javax.swing.JSplitPane jSplitPane1; private javax.swing.JTable keywordsTable; private javax.swing.JScrollPane leftPane; @@ -311,6 +365,24 @@ class DropdownListSearchPanel extends AdHocSearchPanel { searchAddButton.addActionListener(al); } + /** + * Get a set of data source object ids that are selected. + * @return A set of selected object ids. + */ + @Override + Set getDataSourcesSelected() { + Set dataSourceObjIdSet = new HashSet<>(); + for (Long key : getDataSourceMap().keySet()) { + String value = getDataSourceMap().get(key); + for (String dataSource : this.dataSourceList.getSelectedValuesList()) { + if (value.equals(dataSource)) { + dataSourceObjIdSet.add(key); + } + } + } + return dataSourceObjIdSet; + } + private class KeywordListsTableModel extends AbstractTableModel { //data @@ -592,4 +664,32 @@ class DropdownListSearchPanel extends AdHocSearchPanel { return this; } } + + /** + * Update the dataSourceListModel + */ + @NbBundle.Messages({"DropdownListSearchPanel.selected=Ad Hoc Search data source filter is selected"}) + void updateDataSourceListModel() { + getDataSourceListModel().removeAllElements(); + for (String dsName : getDataSourceArray()) { + getDataSourceListModel().addElement(dsName); + } + setComponentsEnabled(); + firePropertyChange(Bundle.DropdownListSearchPanel_selected(), null, null); + + } + + /** + * Set the dataSourceList enabled if the dataSourceCheckBox is selected + */ + private void setComponentsEnabled() { + boolean enabled = this.dataSourceCheckBox.isSelected(); + this.dataSourceList.setEnabled(enabled); + if (enabled) { + this.dataSourceList.setSelectionInterval(0, this.dataSourceList.getModel().getSize()-1); + } else { + this.dataSourceList.setSelectedIndices(new int[0]); + } + } + } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/DropdownSingleTermSearchPanel.form b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/DropdownSingleTermSearchPanel.form index 54e3cdee7f..19fb132f0d 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/DropdownSingleTermSearchPanel.form +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/DropdownSingleTermSearchPanel.form @@ -59,21 +59,23 @@ - - - - - - + + + + + + + + - + @@ -81,17 +83,23 @@ - - - - + - + + + + + + + + + + @@ -142,7 +150,6 @@ - @@ -168,5 +175,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/DropdownSingleTermSearchPanel.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/DropdownSingleTermSearchPanel.java index 8dd9a20b0b..b859df85ef 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/DropdownSingleTermSearchPanel.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/DropdownSingleTermSearchPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2016 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,16 +18,26 @@ */ package org.sleuthkit.autopsy.keywordsearch; +import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; +import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionListener; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.logging.Level; +import javax.swing.JList; import javax.swing.JMenuItem; +import javax.swing.event.ListSelectionEvent; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.ingest.IngestManager; /** * A dropdown panel that provides GUI components that allow a user to do three @@ -49,11 +59,14 @@ public class DropdownSingleTermSearchPanel extends AdHocSearchPanel { private static final long serialVersionUID = 1L; private static final Logger LOGGER = Logger.getLogger(DropdownSingleTermSearchPanel.class.getName()); private static DropdownSingleTermSearchPanel defaultInstance = null; + private boolean ingestRunning; + /** * Gets the default instance of a dropdown panel that provides GUI * components that allow a user to do three types of ad hoc single keyword * searches. + * * @return the default instance of DropdownSingleKeywordSearchPanel */ public static synchronized DropdownSingleTermSearchPanel getDefault() { @@ -67,9 +80,30 @@ public class DropdownSingleTermSearchPanel extends AdHocSearchPanel { * Constructs a dropdown panel that provides GUI components that allow a * user to do three types of ad hoc single keyword searches. */ - public DropdownSingleTermSearchPanel() { + @NbBundle.Messages({"DropdownSingleTermSearchPanel.selected=Ad Hoc Search data source filter is selected"}) + private DropdownSingleTermSearchPanel() { initComponents(); customizeComponents(); + dataSourceList.setModel(getDataSourceListModel()); + this.dataSourceList.addListSelectionListener((ListSelectionEvent evt) -> { + firePropertyChange(Bundle.DropdownSingleTermSearchPanel_selected(), null, null); + }); + this.dataSourceList.addMouseMotionListener(new MouseMotionListener() { + + @Override + public void mouseDragged(MouseEvent evt) { + //Unused by now + } + + @Override + public void mouseMoved(MouseEvent evt) { + JList DsList = (JList) evt.getSource(); + int index = DsList.locationToIndex(evt.getPoint()); + if (index > -1) { + DsList.setToolTipText(getDataSourceToolTipList().get(index)); + } + } + }); } /** @@ -103,6 +137,22 @@ public class DropdownSingleTermSearchPanel extends AdHocSearchPanel { keywordTextField.selectAll(); } }; + ingestRunning = IngestManager.getInstance().isIngestRunning(); + updateIngestIndexLabel(); + + IngestManager.getInstance().addIngestJobEventListener(new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + Object source = evt.getSource(); + if (source instanceof String && ((String) source).equals("LOCAL")) { //NON-NLS + EventQueue.invokeLater(() -> { + ingestRunning = IngestManager.getInstance().isIngestRunning(); + updateIngestIndexLabel(); + }); + } + } + }); + cutMenuItem.addActionListener(actList); copyMenuItem.addActionListener(actList); pasteMenuItem.addActionListener(actList); @@ -130,7 +180,7 @@ public class DropdownSingleTermSearchPanel extends AdHocSearchPanel { exactRadioButton.setSelected(true); regexRadioButton.setEnabled(enabled); } - + /** * Gets a single keyword list consisting of a single keyword encapsulating * the input term(s)/phrase/substring/regex. @@ -138,10 +188,9 @@ public class DropdownSingleTermSearchPanel extends AdHocSearchPanel { * @return The keyword list. */ @NbBundle.Messages({"DropdownSingleTermSearchPanel.warning.title=Warning", - "DropdownSingleTermSearchPanel.warning.text=Boundary characters ^ and $ do not match word boundaries. Consider\nreplacing with an explicit list of boundary characters, such as [ \\.,]"}) + "DropdownSingleTermSearchPanel.warning.text=Boundary characters ^ and $ do not match word boundaries. Consider\nreplacing with an explicit list of boundary characters, such as [ \\.,]"}) @Override List getKeywordLists() { - if (regexRadioButton.isSelected()) { if((keywordTextField.getText() != null) && (keywordTextField.getText().startsWith("^") || @@ -152,7 +201,6 @@ public class DropdownSingleTermSearchPanel extends AdHocSearchPanel { KeywordSearchUtil.DIALOG_MESSAGE_TYPE.WARN); } } - List keywords = new ArrayList<>(); keywords.add(new Keyword(keywordTextField.getText(), !regexRadioButton.isSelected(), exactRadioButton.isSelected())); List keywordLists = new ArrayList<>(); @@ -165,6 +213,7 @@ public class DropdownSingleTermSearchPanel extends AdHocSearchPanel { */ @Override protected void postFilesIndexedChange() { + updateIngestIndexLabel(); } /** @@ -187,6 +236,10 @@ public class DropdownSingleTermSearchPanel extends AdHocSearchPanel { exactRadioButton = new javax.swing.JRadioButton(); substringRadioButton = new javax.swing.JRadioButton(); regexRadioButton = new javax.swing.JRadioButton(); + dataSourceCheckBox = new javax.swing.JCheckBox(); + jScrollPane1 = new javax.swing.JScrollPane(); + dataSourceList = new javax.swing.JList<>(); + ingestIndexLabel = new javax.swing.JLabel(); org.openide.awt.Mnemonics.setLocalizedText(cutMenuItem, org.openide.util.NbBundle.getMessage(DropdownSingleTermSearchPanel.class, "DropdownSearchPanel.cutMenuItem.text")); // NOI18N rightClickMenu.add(cutMenuItem); @@ -225,7 +278,6 @@ public class DropdownSingleTermSearchPanel extends AdHocSearchPanel { }); queryTypeButtonGroup.add(exactRadioButton); - exactRadioButton.setSelected(true); org.openide.awt.Mnemonics.setLocalizedText(exactRadioButton, org.openide.util.NbBundle.getMessage(DropdownSingleTermSearchPanel.class, "DropdownSearchPanel.exactRadioButton.text")); // NOI18N queryTypeButtonGroup.add(substringRadioButton); @@ -234,6 +286,19 @@ public class DropdownSingleTermSearchPanel extends AdHocSearchPanel { queryTypeButtonGroup.add(regexRadioButton); org.openide.awt.Mnemonics.setLocalizedText(regexRadioButton, org.openide.util.NbBundle.getMessage(DropdownSingleTermSearchPanel.class, "DropdownSearchPanel.regexRadioButton.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(dataSourceCheckBox, org.openide.util.NbBundle.getMessage(DropdownSingleTermSearchPanel.class, "DropdownSingleTermSearchPanel.dataSourceCheckBox.text")); // NOI18N + dataSourceCheckBox.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + dataSourceCheckBoxActionPerformed(evt); + } + }); + + dataSourceList.setMinimumSize(new java.awt.Dimension(0, 200)); + jScrollPane1.setViewportView(dataSourceList); + + ingestIndexLabel.setFont(ingestIndexLabel.getFont().deriveFont(ingestIndexLabel.getFont().getStyle() & ~java.awt.Font.BOLD, 10)); + org.openide.awt.Mnemonics.setLocalizedText(ingestIndexLabel, org.openide.util.NbBundle.getMessage(DropdownSingleTermSearchPanel.class, "DropdownSingleTermSearchPanel.ingestIndexLabel.text")); // NOI18N + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( @@ -241,31 +306,39 @@ public class DropdownSingleTermSearchPanel extends AdHocSearchPanel { .addGroup(layout.createSequentialGroup() .addGap(5, 5, 5) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(keywordTextField, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(searchButton)) .addGroup(layout.createSequentialGroup() .addComponent(exactRadioButton) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(substringRadioButton) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(regexRadioButton) - .addGap(0, 27, Short.MAX_VALUE))) - .addGap(5, 5, 5)) + .addComponent(regexRadioButton)) + .addComponent(dataSourceCheckBox) + .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 297, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGroup(layout.createSequentialGroup() + .addComponent(searchButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(ingestIndexLabel)) + .addComponent(keywordTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 296, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(keywordTextField, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(searchButton, javax.swing.GroupLayout.DEFAULT_SIZE, 26, Short.MAX_VALUE)) + .addComponent(keywordTextField, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(exactRadioButton) .addComponent(substringRadioButton) .addComponent(regexRadioButton)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(dataSourceCheckBox) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 61, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(searchButton) + .addComponent(ingestIndexLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 13, javax.swing.GroupLayout.PREFERRED_SIZE)) .addContainerGap()) ); }// //GEN-END:initComponents @@ -303,10 +376,73 @@ public class DropdownSingleTermSearchPanel extends AdHocSearchPanel { } }//GEN-LAST:event_keywordTextFieldMouseClicked + private void dataSourceCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_dataSourceCheckBoxActionPerformed + updateDataSourceListModel(); + }//GEN-LAST:event_dataSourceCheckBoxActionPerformed + + /** + * Update the dataSourceListModel + */ + void updateDataSourceListModel() { + getDataSourceListModel().removeAllElements(); + for (String dsName : getDataSourceArray()) { + getDataSourceListModel().addElement(dsName); + } + setComponentsEnabled(); + firePropertyChange(Bundle.DropdownSingleTermSearchPanel_selected(), null, null); + + } + + /** + * Set the dataSourceList enabled if the dataSourceCheckBox is selected + */ + private void setComponentsEnabled() { + boolean enabled = this.dataSourceCheckBox.isSelected(); + this.dataSourceList.setEnabled(enabled); + if (enabled) { + this.dataSourceList.setSelectionInterval(0, this.dataSourceList.getModel().getSize()-1); + } else { + this.dataSourceList.setSelectedIndices(new int[0]); + } + } + + /** + * Get a set of data source object ids that are selected. + * @return A set of selected object ids. + */ + @Override + Set getDataSourcesSelected() { + Set dataSourceObjIdSet = new HashSet<>(); + for (Long key : getDataSourceMap().keySet()) { + String value = getDataSourceMap().get(key); + for (String dataSource : this.dataSourceList.getSelectedValuesList()) { + if (value.equals(dataSource)) { + dataSourceObjIdSet.add(key); + } + } + } + return dataSourceObjIdSet; + } + + /** + * Update ingestIndexLabel + */ + private void updateIngestIndexLabel() { + if (ingestRunning) { + ingestIndexLabel.setText(NbBundle.getMessage(this.getClass(), "KeywordSearchListsViewerPanel.initIngest.ongoingIngestMsg", filesIndexed)); + } else { + ingestIndexLabel.setText(NbBundle.getMessage(this.getClass(), "KeywordSearchListsViewerPanel.initIngest.fileIndexCtMsg", filesIndexed)); + } + } + // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JMenuItem copyMenuItem; private javax.swing.JMenuItem cutMenuItem; + private javax.swing.JCheckBox dataSourceCheckBox; + private javax.swing.JList dataSourceList; private javax.swing.JRadioButton exactRadioButton; + private javax.swing.JLabel ingestIndexLabel; + private javax.swing.JScrollPane jScrollPane1; private javax.swing.JTextField keywordTextField; private javax.swing.JMenuItem pasteMenuItem; private javax.swing.ButtonGroup queryTypeButtonGroup; diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/DropdownToolbar.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/DropdownToolbar.java index 11452e917b..03c9f87fe1 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/DropdownToolbar.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/DropdownToolbar.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -23,20 +23,26 @@ import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.util.ArrayList; import java.util.EnumSet; +import java.util.List; import java.util.logging.Level; import javax.swing.SwingUtilities; import javax.swing.event.PopupMenuEvent; import javax.swing.event.PopupMenuListener; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.core.RuntimeProperties; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.DataSource; +import org.sleuthkit.datamodel.TskCoreException; /** * A panel that provides a toolbar button for the dropdown keyword list search * panel and dropdown single keyword search panel. Displayed in the upper right * hand corner of the application by default. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class DropdownToolbar extends javax.swing.JPanel { private static final long serialVersionUID = 1L; @@ -45,7 +51,8 @@ class DropdownToolbar extends javax.swing.JPanel { private SearchSettingsChangeListener searchSettingsChangeListener; private boolean active = false; private DropdownSingleTermSearchPanel dropPanel = null; - + private DropdownListSearchPanel listsPanel = null; + private List dataSources = new ArrayList<>(); /** * Gets the singleton panel that provides a toolbar button for the dropdown * keyword list search panel and dropdown single keyword search panel. @@ -77,13 +84,13 @@ class DropdownToolbar extends javax.swing.JPanel { private void customizeComponents() { searchSettingsChangeListener = new SearchSettingsChangeListener(); KeywordSearch.getServer().addServerActionListener(searchSettingsChangeListener); - Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), searchSettingsChangeListener); + Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE, Case.Events.DATA_SOURCE_ADDED), searchSettingsChangeListener); - DropdownListSearchPanel listsPanel = DropdownListSearchPanel.getDefault(); + listsPanel = DropdownListSearchPanel.getDefault(); listsPanel.addSearchButtonActionListener((ActionEvent e) -> { listsMenu.setVisible(false); }); - + listsPanel.addPropertyChangeListener(searchSettingsChangeListener); // Adding border of six to account for menu border listsMenu.setSize(listsPanel.getPreferredSize().width + 6, listsPanel.getPreferredSize().height + 6); listsMenu.add(listsPanel); @@ -105,6 +112,7 @@ class DropdownToolbar extends javax.swing.JPanel { }); dropPanel = DropdownSingleTermSearchPanel.getDefault(); + dropPanel.addPropertyChangeListener(searchSettingsChangeListener); dropPanel.addSearchButtonActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -139,6 +147,8 @@ class DropdownToolbar extends javax.swing.JPanel { if (evt != null && !SwingUtilities.isLeftMouseButton(evt)) { return; } + listsPanel.setDataSources(dataSources); + listsPanel.updateDataSourceListModel(); listsMenu.show(listsButton, listsButton.getWidth() - listsMenu.getWidth(), listsButton.getHeight() - 1); } @@ -149,6 +159,8 @@ class DropdownToolbar extends javax.swing.JPanel { if (evt != null && !SwingUtilities.isLeftMouseButton(evt)) { return; } + dropPanel.setDataSources(dataSources); + dropPanel.updateDataSourceListModel(); searchMenu.show(searchDropButton, searchDropButton.getWidth() - searchMenu.getWidth(), searchDropButton.getHeight() - 1); } @@ -198,6 +210,16 @@ class DropdownToolbar extends javax.swing.JPanel { disableSearch = true; } + //set the data source list + try { + dataSources = getDataSourceList(); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error getting text index info", ex); //NON-NLS + disableSearch = true; + } catch (NoCurrentCaseException ex) { + logger.log(Level.SEVERE, "Exception while getting current case.", ex); //NON-NLS + disableSearch = true; + } if (disableSearch) { searchDropButton.setEnabled(false); listsButton.setEnabled(false); @@ -228,11 +250,24 @@ class DropdownToolbar extends javax.swing.JPanel { break; default: } + } else if (changed.equals(Case.Events.DATA_SOURCE_ADDED.toString())) { + DataSource newDataSource = (DataSource) evt.getNewValue(); + dataSources.add(newDataSource); } } } } - + + /** + * Get a list of DataSource from case database + * @return A list of DataSource + * @throws NoCurrentCaseException + * @throws TskCoreException + */ + private synchronized List getDataSourceList() throws NoCurrentCaseException, TskCoreException { + Case openCase = Case.getCurrentCaseThrows(); + return openCase.getSleuthkitCase().getDataSources(); + } /** * 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 diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/ExtractedContentPanel.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/ExtractedContentPanel.java index f7463bbccb..5061a2e2f0 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/ExtractedContentPanel.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/ExtractedContentPanel.java @@ -27,7 +27,6 @@ import java.util.List; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.logging.Level; -import javax.swing.JTextPane; import javax.swing.SizeRequirements; import javax.swing.SwingWorker; import javax.swing.text.Element; @@ -48,6 +47,7 @@ import org.sleuthkit.autopsy.coreutils.TextUtil; * Panel displays HTML content sent to ExtractedContentViewer, and provides a * combo-box to select between multiple sources. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class ExtractedContentPanel extends javax.swing.JPanel { private static final Logger logger = Logger.getLogger(ExtractedContentPanel.class.getName()); diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalEditListPanel.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalEditListPanel.java index da7cf3c459..cf990e9674 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalEditListPanel.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalEditListPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -41,6 +41,7 @@ import org.sleuthkit.autopsy.ingest.IngestManager; /** * GlobalEditListPanel widget to manage keywords in lists */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class GlobalEditListPanel extends javax.swing.JPanel implements ListSelectionListener, OptionsPanel { private static final Logger logger = Logger.getLogger(GlobalEditListPanel.class.getName()); diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalListSettingsPanel.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalListSettingsPanel.java index 2a362dc945..db3a1d3cf0 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalListSettingsPanel.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalListSettingsPanel.java @@ -27,6 +27,10 @@ import javax.swing.JOptionPane; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.corecomponents.OptionsPanel; +/** + * Configuration for keyword lists. + */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class GlobalListSettingsPanel extends javax.swing.JPanel implements OptionsPanel { private static final long serialVersionUID = 1L; diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalListsManagementPanel.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalListsManagementPanel.java index 1d0dce9189..56c1fcd4c5 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalListsManagementPanel.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalListsManagementPanel.java @@ -43,6 +43,7 @@ import org.sleuthkit.autopsy.ingest.IngestManager; /** * A panel to manage all keyword lists created/imported in Autopsy. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class GlobalListsManagementPanel extends javax.swing.JPanel implements OptionsPanel { private static final long serialVersionUID = 1L; diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchGlobalLanguageSettingsPanel.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchGlobalLanguageSettingsPanel.java index 8342587833..2cc6f6365b 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchGlobalLanguageSettingsPanel.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchGlobalLanguageSettingsPanel.java @@ -36,6 +36,7 @@ import org.sleuthkit.autopsy.ingest.IngestManager; /** * Child panel of the global settings panel (Languages tab). */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class KeywordSearchGlobalLanguageSettingsPanel extends javax.swing.JPanel implements OptionsPanel { private final Map scripts = new HashMap<>(); diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchGlobalSearchSettingsPanel.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchGlobalSearchSettingsPanel.java index e6c8a98662..d2a52a3e1c 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchGlobalSearchSettingsPanel.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchGlobalSearchSettingsPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2012-2014 Basis Technology Corp. + * Copyright 2012-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -30,6 +30,7 @@ import org.sleuthkit.autopsy.keywordsearch.KeywordSearchIngestModule.UpdateFrequ /** * General, not per list, keyword search configuration and status display widget */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class KeywordSearchGlobalSearchSettingsPanel extends javax.swing.JPanel implements OptionsPanel { private final Logger logger = Logger.getLogger(KeywordSearchGlobalSearchSettingsPanel.class.getName()); @@ -360,7 +361,7 @@ class KeywordSearchGlobalSearchSettingsPanel extends javax.swing.JPanel implemen Object newValue = evt.getNewValue(); if (changed.equals(KeywordSearch.NUM_FILES_CHANGE_EVT)) { - int newFilesIndexed = ((Integer) newValue).intValue(); + int newFilesIndexed = ((Integer) newValue); filesIndexedValue.setText(Integer.toString(newFilesIndexed)); try { chunksValLabel.setText(Integer.toString(KeywordSearch.getServer().queryNumIndexedChunks())); diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchGlobalSettingsPanel.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchGlobalSettingsPanel.java index c34e672a1c..156bbd0220 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchGlobalSettingsPanel.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchGlobalSettingsPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -26,6 +26,7 @@ import org.sleuthkit.autopsy.ingest.IngestModuleGlobalSettingsPanel; /** * Global options panel for keyword searching. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class KeywordSearchGlobalSettingsPanel extends IngestModuleGlobalSettingsPanel implements OptionsPanel { private static final long serialVersionUID = 1L; diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchJobSettingsPanel.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchJobSettingsPanel.java index 5515b10115..c80e66d947 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchJobSettingsPanel.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchJobSettingsPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2014 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -35,6 +35,7 @@ import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettingsPanel; /** * Ingest job settings panel for keyword search file ingest modules. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public final class KeywordSearchJobSettingsPanel extends IngestModuleIngestJobSettingsPanel implements PropertyChangeListener { private final KeywordListsTableModel tableModel = new KeywordListsTableModel(); diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/NewKeywordPanel.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/NewKeywordPanel.java index f96738be67..d62e4442af 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/NewKeywordPanel.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/NewKeywordPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2016 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -24,6 +24,7 @@ package org.sleuthkit.autopsy.keywordsearch; * indicate whether they want the keyword to be an exact match, a substring, or * a regular expression. */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives class NewKeywordPanel extends javax.swing.JPanel { /** diff --git a/pythonExamples/Aug2015DataSourceTutorial/RunExe.py b/pythonExamples/Aug2015DataSourceTutorial/RunExe.py index 14477f06df..6b43853f30 100644 --- a/pythonExamples/Aug2015DataSourceTutorial/RunExe.py +++ b/pythonExamples/Aug2015DataSourceTutorial/RunExe.py @@ -36,9 +36,11 @@ import jarray import inspect import os -import subprocess +import java.util.ArrayList as ArrayList from java.lang import Class from java.lang import System +from java.lang import ProcessBuilder +from java.io import File from java.util.logging import Level from org.sleuthkit.datamodel import SleuthkitCase from org.sleuthkit.datamodel import AbstractFile @@ -47,8 +49,10 @@ from org.sleuthkit.datamodel import BlackboardArtifact from org.sleuthkit.datamodel import BlackboardAttribute from org.sleuthkit.datamodel import Image from org.sleuthkit.autopsy.ingest import IngestModule +from org.sleuthkit.autopsy.ingest import IngestJobContext from org.sleuthkit.autopsy.ingest.IngestModule import IngestModuleException from org.sleuthkit.autopsy.ingest import DataSourceIngestModule +from org.sleuthkit.autopsy.ingest import DataSourceIngestModuleProcessTerminator from org.sleuthkit.autopsy.ingest import IngestModuleFactoryAdapter from org.sleuthkit.autopsy.ingest import IngestMessage from org.sleuthkit.autopsy.ingest import IngestServices @@ -58,6 +62,7 @@ from org.sleuthkit.autopsy.coreutils import PlatformUtil from org.sleuthkit.autopsy.casemodule import Case from org.sleuthkit.autopsy.casemodule.services import Services from org.sleuthkit.autopsy.datamodel import ContentUtils +from org.sleuthkit.autopsy.coreutils import ExecUtil # Factory that defines the name and details of the module and allows Autopsy @@ -102,10 +107,10 @@ class RunExeIngestModule(DataSourceIngestModule): # Get path to EXE based on where this script is run from. # Assumes EXE is in same folder as script # Verify it is there before any ingest starts - self.path_to_exe = os.path.join(os.path.dirname(os.path.abspath(__file__)), "img_stat.exe") - if not os.path.exists(self.path_to_exe): + exe_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "img_stat.exe") + self.pathToEXE = File(exe_path) + if not self.pathToEXE.exists(): raise IngestModuleException("EXE was not found in module folder") - # Where the analysis is done. # The 'dataSource' object being passed in is of type org.sleuthkit.datamodel.Content. # See: http://www.sleuthkit.org/sleuthkit/docs/jni-docs/4.4/interfaceorg_1_1sleuthkit_1_1datamodel_1_1_content.html @@ -115,7 +120,6 @@ class RunExeIngestModule(DataSourceIngestModule): # we don't know how much work there will be progressBar.switchToIndeterminate() - # Example has only a Windows EXE, so bail if we aren't on Windows if not PlatformUtil.isWindowsOS(): self.log(Level.INFO, "Ignoring data source. Not running on Windows") @@ -130,17 +134,27 @@ class RunExeIngestModule(DataSourceIngestModule): imagePaths = dataSource.getPaths() # We'll save our output to a file in the reports folder, named based on EXE and data source ID - reportPath = os.path.join(Case.getCurrentCase().getCaseDirectory(), "Reports", "img_stat-" + str(dataSource.getId()) + ".txt") - reportHandle = open(reportPath, 'w') - + reportFile = File(Case.getCurrentCase().getCaseDirectory() + "\\Reports" + "\\img_stat-" + str(dataSource.getId()) + ".txt") # Run the EXE, saving output to the report - # NOTE: we should really be checking for if the module has been - # cancelled and then killing the process. + # Check if the ingest is terminated and delete the incomplete report file + # Do not add report to the case tree if the ingest is cancelled before finish. + # This can be done by using IngestJobContext.dataSourceIngestIsCancelled + # See: http://sleuthkit.org/autopsy/docs/api-docs/4.7.0/_ingest_job_context_8java.html self.log(Level.INFO, "Running program on data source") - subprocess.Popen([self.path_to_exe, imagePaths[0]], stdout=reportHandle).communicate()[0] - reportHandle.close() + cmd = ArrayList() + cmd.add(self.pathToEXE.toString()) + cmd.add(imagePaths[0]) + + processBuilder = ProcessBuilder(cmd); + processBuilder.redirectOutput(reportFile) + ExecUtil.execute(processBuilder,DataSourceIngestModuleProcessTerminator(self.context)) # Add the report to the case, so it shows up in the tree - Case.getCurrentCase().addReport(reportPath, "Run EXE", "img_stat output") - + if not self.context.dataSourceIngestIsCancelled(): + Case.getCurrentCase().addReport(reportFile.toString(), "Run EXE", "img_stat output") + else: + if reportFile.exists(): + if not reportFile.delete(): + self.log(LEVEL.warning,"Error deleting the incomplete report file") + return IngestModule.ProcessResult.OK diff --git a/thirdparty/opencv/ext/opencv-2413.jar b/thirdparty/opencv/ext/opencv-2413.jar new file mode 100644 index 0000000000..dccffb96a9 Binary files /dev/null and b/thirdparty/opencv/ext/opencv-2413.jar differ diff --git a/thirdparty/opencv/ext/opencv-248.jar b/thirdparty/opencv/ext/opencv-248.jar deleted file mode 100755 index f09b29077d..0000000000 Binary files a/thirdparty/opencv/ext/opencv-248.jar and /dev/null differ diff --git a/thirdparty/opencv/lib/amd64/opencv_ffmpeg248_64.dll b/thirdparty/opencv/lib/amd64/opencv_ffmpeg2413_64.dll old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/opencv/lib/amd64/opencv_ffmpeg248_64.dll rename to thirdparty/opencv/lib/amd64/opencv_ffmpeg2413_64.dll diff --git a/thirdparty/opencv/lib/amd64/opencv_java2413.dll b/thirdparty/opencv/lib/amd64/opencv_java2413.dll new file mode 100644 index 0000000000..e1aeca4ba5 Binary files /dev/null and b/thirdparty/opencv/lib/amd64/opencv_java2413.dll differ diff --git a/thirdparty/opencv/lib/i386/opencv_ffmpeg248.dll b/thirdparty/opencv/lib/i386/opencv_ffmpeg2413.dll old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/opencv/lib/i386/opencv_ffmpeg248.dll rename to thirdparty/opencv/lib/i386/opencv_ffmpeg2413.dll diff --git a/thirdparty/opencv/lib/x86_64/opencv_java248.dll b/thirdparty/opencv/lib/i386/opencv_java2413.dll old mode 100755 new mode 100644 similarity index 50% rename from thirdparty/opencv/lib/x86_64/opencv_java248.dll rename to thirdparty/opencv/lib/i386/opencv_java2413.dll index ab989a6b11..c25b6209f8 Binary files a/thirdparty/opencv/lib/x86_64/opencv_java248.dll and b/thirdparty/opencv/lib/i386/opencv_java2413.dll differ diff --git a/thirdparty/opencv/lib/i386/opencv_java248.dll b/thirdparty/opencv/lib/i386/opencv_java248.dll deleted file mode 100755 index f1c1bc9a43..0000000000 Binary files a/thirdparty/opencv/lib/i386/opencv_java248.dll and /dev/null differ diff --git a/thirdparty/opencv/lib/x86_64/opencv_ffmpeg248_64.dll b/thirdparty/opencv/lib/i586/opencv_ffmpeg2413_64.dll old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/opencv/lib/x86_64/opencv_ffmpeg248_64.dll rename to thirdparty/opencv/lib/i586/opencv_ffmpeg2413_64.dll diff --git a/thirdparty/opencv/lib/i586/opencv_java2413.dll b/thirdparty/opencv/lib/i586/opencv_java2413.dll new file mode 100644 index 0000000000..e1aeca4ba5 Binary files /dev/null and b/thirdparty/opencv/lib/i586/opencv_java2413.dll differ diff --git a/thirdparty/opencv/lib/i586/opencv_java248.dll b/thirdparty/opencv/lib/i586/opencv_java248.dll deleted file mode 100755 index f1c1bc9a43..0000000000 Binary files a/thirdparty/opencv/lib/i586/opencv_java248.dll and /dev/null differ diff --git a/thirdparty/opencv/lib/x86/opencv_ffmpeg248.dll b/thirdparty/opencv/lib/i686/opencv_ffmpeg2413_64.dll old mode 100755 new mode 100644 similarity index 53% rename from thirdparty/opencv/lib/x86/opencv_ffmpeg248.dll rename to thirdparty/opencv/lib/i686/opencv_ffmpeg2413_64.dll index b1e70df6a3..37236e5424 Binary files a/thirdparty/opencv/lib/x86/opencv_ffmpeg248.dll and b/thirdparty/opencv/lib/i686/opencv_ffmpeg2413_64.dll differ diff --git a/thirdparty/opencv/lib/i686/opencv_java2413.dll b/thirdparty/opencv/lib/i686/opencv_java2413.dll new file mode 100644 index 0000000000..e1aeca4ba5 Binary files /dev/null and b/thirdparty/opencv/lib/i686/opencv_java2413.dll differ diff --git a/thirdparty/opencv/lib/i686/opencv_java248.dll b/thirdparty/opencv/lib/i686/opencv_java248.dll deleted file mode 100755 index f1c1bc9a43..0000000000 Binary files a/thirdparty/opencv/lib/i686/opencv_java248.dll and /dev/null differ diff --git a/thirdparty/opencv/lib/i586/opencv_ffmpeg248_64.dll b/thirdparty/opencv/lib/x86/opencv_ffmpeg2413.dll old mode 100755 new mode 100644 similarity index 100% rename from thirdparty/opencv/lib/i586/opencv_ffmpeg248_64.dll rename to thirdparty/opencv/lib/x86/opencv_ffmpeg2413.dll diff --git a/thirdparty/opencv/lib/amd64/opencv_java248.dll b/thirdparty/opencv/lib/x86/opencv_java2413.dll old mode 100755 new mode 100644 similarity index 50% rename from thirdparty/opencv/lib/amd64/opencv_java248.dll rename to thirdparty/opencv/lib/x86/opencv_java2413.dll index ab989a6b11..c25b6209f8 Binary files a/thirdparty/opencv/lib/amd64/opencv_java248.dll and b/thirdparty/opencv/lib/x86/opencv_java2413.dll differ diff --git a/thirdparty/opencv/lib/x86/opencv_java248.dll b/thirdparty/opencv/lib/x86/opencv_java248.dll deleted file mode 100755 index f1c1bc9a43..0000000000 Binary files a/thirdparty/opencv/lib/x86/opencv_java248.dll and /dev/null differ diff --git a/thirdparty/opencv/lib/i686/opencv_ffmpeg248_64.dll b/thirdparty/opencv/lib/x86_64/opencv_ffmpeg2413_64.dll old mode 100755 new mode 100644 similarity index 53% rename from thirdparty/opencv/lib/i686/opencv_ffmpeg248_64.dll rename to thirdparty/opencv/lib/x86_64/opencv_ffmpeg2413_64.dll index b1e70df6a3..37236e5424 Binary files a/thirdparty/opencv/lib/i686/opencv_ffmpeg248_64.dll and b/thirdparty/opencv/lib/x86_64/opencv_ffmpeg2413_64.dll differ diff --git a/thirdparty/opencv/lib/x86_64/opencv_java2413.dll b/thirdparty/opencv/lib/x86_64/opencv_java2413.dll new file mode 100644 index 0000000000..e1aeca4ba5 Binary files /dev/null and b/thirdparty/opencv/lib/x86_64/opencv_java2413.dll differ