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/DataContentViewerOtherCases.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java index 4a60696186..e71e7c616a 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java @@ -18,7 +18,9 @@ */ package org.sleuthkit.autopsy.centralrepository.contentviewer; +import java.awt.Color; import java.awt.Component; +import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.BufferedWriter; @@ -31,19 +33,30 @@ 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; +import javax.swing.GroupLayout; import javax.swing.JFileChooser; +import javax.swing.JLabel; import javax.swing.JMenuItem; 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.JPopupMenu; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.LayoutStyle; +import javax.swing.ListSelectionModel; import javax.swing.filechooser.FileNameExtensionFilter; import javax.swing.table.TableCellRenderer; import javax.swing.table.TableColumn; +import org.openide.awt.Mnemonics; import org.openide.nodes.Node; +import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; import org.openide.util.lookup.ServiceProvider; import org.sleuthkit.autopsy.casemodule.Case; @@ -65,14 +78,16 @@ import org.sleuthkit.datamodel.TskException; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb; 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 static final long serialVersionUID = -1L; @@ -453,12 +468,12 @@ public class DataContentViewerOtherCases extends javax.swing.JPanel implements D * * @return A collection of correlated artifact instances from other cases */ - 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<>(); + HashMap artifactInstances = new HashMap<>(); if (EamDb.isEnabled()) { EamDb dbManager = EamDb.getInstance(); @@ -466,8 +481,7 @@ public class DataContentViewerOtherCases extends javax.swing.JPanel implements D .filter(artifactInstance -> !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))); } @@ -509,29 +523,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: " + 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); } } @@ -590,7 +634,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)); @@ -610,6 +654,7 @@ public class DataContentViewerOtherCases extends javax.swing.JPanel implements D } 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()); @@ -647,131 +692,170 @@ public class DataContentViewerOtherCases extends javax.swing.JPanel implements D // //GEN-BEGIN:initComponents private void initComponents() { - rightClickPopupMenu = new javax.swing.JPopupMenu(); - selectAllMenuItem = new javax.swing.JMenuItem(); - exportToCSVMenuItem = new javax.swing.JMenuItem(); - showCaseDetailsMenuItem = new javax.swing.JMenuItem(); - showCommonalityMenuItem = new javax.swing.JMenuItem(); - CSVFileChooser = new javax.swing.JFileChooser(); - otherCasesPanel = new javax.swing.JPanel(); - tableContainerPanel = new javax.swing.JPanel(); - tableScrollPane = new javax.swing.JScrollPane(); - otherCasesTable = new javax.swing.JTable(); - tableStatusPanel = new javax.swing.JPanel(); - tableStatusPanelLabel = new javax.swing.JLabel(); + rightClickPopupMenu = new JPopupMenu(); + selectAllMenuItem = new JMenuItem(); + exportToCSVMenuItem = new JMenuItem(); + showCaseDetailsMenuItem = new JMenuItem(); + showCommonalityMenuItem = new JMenuItem(); + CSVFileChooser = new JFileChooser(); + otherCasesPanel = new JPanel(); + tableContainerPanel = new JPanel(); + tableScrollPane = new JScrollPane(); + otherCasesTable = new JTable(); + tableStatusPanel = new JPanel(); + tableStatusPanelLabel = new JLabel(); - org.openide.awt.Mnemonics.setLocalizedText(selectAllMenuItem, org.openide.util.NbBundle.getMessage(DataContentViewerOtherCases.class, "DataContentViewerOtherCases.selectAllMenuItem.text")); // NOI18N + Mnemonics.setLocalizedText(selectAllMenuItem, NbBundle.getMessage(DataContentViewerOtherCases.class, "DataContentViewerOtherCases.selectAllMenuItem.text")); // NOI18N rightClickPopupMenu.add(selectAllMenuItem); - org.openide.awt.Mnemonics.setLocalizedText(exportToCSVMenuItem, org.openide.util.NbBundle.getMessage(DataContentViewerOtherCases.class, "DataContentViewerOtherCases.exportToCSVMenuItem.text")); // NOI18N + Mnemonics.setLocalizedText(exportToCSVMenuItem, NbBundle.getMessage(DataContentViewerOtherCases.class, "DataContentViewerOtherCases.exportToCSVMenuItem.text")); // NOI18N rightClickPopupMenu.add(exportToCSVMenuItem); - org.openide.awt.Mnemonics.setLocalizedText(showCaseDetailsMenuItem, org.openide.util.NbBundle.getMessage(DataContentViewerOtherCases.class, "DataContentViewerOtherCases.showCaseDetailsMenuItem.text")); // NOI18N + Mnemonics.setLocalizedText(showCaseDetailsMenuItem, NbBundle.getMessage(DataContentViewerOtherCases.class, "DataContentViewerOtherCases.showCaseDetailsMenuItem.text")); // NOI18N rightClickPopupMenu.add(showCaseDetailsMenuItem); - org.openide.awt.Mnemonics.setLocalizedText(showCommonalityMenuItem, org.openide.util.NbBundle.getMessage(DataContentViewerOtherCases.class, "DataContentViewerOtherCases.showCommonalityMenuItem.text")); // NOI18N + Mnemonics.setLocalizedText(showCommonalityMenuItem, NbBundle.getMessage(DataContentViewerOtherCases.class, "DataContentViewerOtherCases.showCommonalityMenuItem.text")); // NOI18N rightClickPopupMenu.add(showCommonalityMenuItem); - setMinimumSize(new java.awt.Dimension(1500, 10)); + setMinimumSize(new Dimension(1500, 10)); setOpaque(false); - setPreferredSize(new java.awt.Dimension(1500, 44)); + setPreferredSize(new Dimension(1500, 44)); - otherCasesPanel.setPreferredSize(new java.awt.Dimension(1500, 144)); + otherCasesPanel.setPreferredSize(new Dimension(1500, 144)); - tableContainerPanel.setPreferredSize(new java.awt.Dimension(1500, 63)); + tableContainerPanel.setPreferredSize(new Dimension(1500, 63)); - tableScrollPane.setPreferredSize(new java.awt.Dimension(1500, 30)); + tableScrollPane.setPreferredSize(new Dimension(1500, 30)); otherCasesTable.setAutoCreateRowSorter(true); otherCasesTable.setModel(tableModel); - otherCasesTable.setToolTipText(org.openide.util.NbBundle.getMessage(DataContentViewerOtherCases.class, "DataContentViewerOtherCases.table.toolTip.text")); // NOI18N + otherCasesTable.setToolTipText(NbBundle.getMessage(DataContentViewerOtherCases.class, "DataContentViewerOtherCases.table.toolTip.text")); // NOI18N otherCasesTable.setComponentPopupMenu(rightClickPopupMenu); - otherCasesTable.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_INTERVAL_SELECTION); + otherCasesTable.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION); tableScrollPane.setViewportView(otherCasesTable); - tableStatusPanel.setPreferredSize(new java.awt.Dimension(1500, 16)); + tableStatusPanel.setPreferredSize(new Dimension(1500, 16)); - tableStatusPanelLabel.setForeground(new java.awt.Color(255, 0, 51)); + tableStatusPanelLabel.setForeground(new Color(255, 0, 51)); - javax.swing.GroupLayout tableStatusPanelLayout = new javax.swing.GroupLayout(tableStatusPanel); + GroupLayout tableStatusPanelLayout = new GroupLayout(tableStatusPanel); tableStatusPanel.setLayout(tableStatusPanelLayout); - tableStatusPanelLayout.setHorizontalGroup( - tableStatusPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + tableStatusPanelLayout.setHorizontalGroup(tableStatusPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING) .addGap(0, 0, Short.MAX_VALUE) - .addGroup(tableStatusPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(tableStatusPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING) .addGroup(tableStatusPanelLayout.createSequentialGroup() .addContainerGap() - .addComponent(tableStatusPanelLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 780, Short.MAX_VALUE) + .addComponent(tableStatusPanelLabel, GroupLayout.DEFAULT_SIZE, 780, Short.MAX_VALUE) .addContainerGap())) ); - tableStatusPanelLayout.setVerticalGroup( - tableStatusPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + tableStatusPanelLayout.setVerticalGroup(tableStatusPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING) .addGap(0, 16, Short.MAX_VALUE) - .addGroup(tableStatusPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(tableStatusPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING) .addGroup(tableStatusPanelLayout.createSequentialGroup() - .addComponent(tableStatusPanelLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(tableStatusPanelLabel, GroupLayout.PREFERRED_SIZE, 16, GroupLayout.PREFERRED_SIZE) .addGap(0, 0, Short.MAX_VALUE))) ); - javax.swing.GroupLayout tableContainerPanelLayout = new javax.swing.GroupLayout(tableContainerPanel); + GroupLayout tableContainerPanelLayout = new GroupLayout(tableContainerPanel); tableContainerPanel.setLayout(tableContainerPanelLayout); - tableContainerPanelLayout.setHorizontalGroup( - tableContainerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(tableScrollPane, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(tableStatusPanel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + tableContainerPanelLayout.setHorizontalGroup(tableContainerPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING) + .addComponent(tableScrollPane, GroupLayout.Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(tableStatusPanel, GroupLayout.Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) ); - tableContainerPanelLayout.setVerticalGroup( - tableContainerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + tableContainerPanelLayout.setVerticalGroup(tableContainerPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING) .addGroup(tableContainerPanelLayout.createSequentialGroup() - .addComponent(tableScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(tableStatusPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(tableScrollPane, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) + .addComponent(tableStatusPanel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) .addContainerGap()) ); - javax.swing.GroupLayout otherCasesPanelLayout = new javax.swing.GroupLayout(otherCasesPanel); + GroupLayout otherCasesPanelLayout = new GroupLayout(otherCasesPanel); otherCasesPanel.setLayout(otherCasesPanelLayout); - otherCasesPanelLayout.setHorizontalGroup( - otherCasesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + otherCasesPanelLayout.setHorizontalGroup(otherCasesPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING) .addGap(0, 1500, Short.MAX_VALUE) - .addGroup(otherCasesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(tableContainerPanel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addGroup(otherCasesPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING) + .addComponent(tableContainerPanel, GroupLayout.Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); - otherCasesPanelLayout.setVerticalGroup( - otherCasesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + otherCasesPanelLayout.setVerticalGroup(otherCasesPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING) .addGap(0, 60, Short.MAX_VALUE) - .addGroup(otherCasesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(otherCasesPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING) .addGroup(otherCasesPanelLayout.createSequentialGroup() - .addComponent(tableContainerPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 60, Short.MAX_VALUE) + .addComponent(tableContainerPanel, GroupLayout.DEFAULT_SIZE, 60, Short.MAX_VALUE) .addGap(0, 0, 0))) ); - javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + GroupLayout layout = new GroupLayout(this); this.setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(otherCasesPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + layout.setHorizontalGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) + .addComponent(otherCasesPanel, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) ); - layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(otherCasesPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 60, Short.MAX_VALUE) + layout.setVerticalGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) + .addComponent(otherCasesPanel, GroupLayout.DEFAULT_SIZE, 60, Short.MAX_VALUE) ); }// //GEN-END:initComponents // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JFileChooser CSVFileChooser; - private javax.swing.JMenuItem exportToCSVMenuItem; - private javax.swing.JPanel otherCasesPanel; - private javax.swing.JTable otherCasesTable; - private javax.swing.JPopupMenu rightClickPopupMenu; - private javax.swing.JMenuItem selectAllMenuItem; - private javax.swing.JMenuItem showCaseDetailsMenuItem; - private javax.swing.JMenuItem showCommonalityMenuItem; - private javax.swing.JPanel tableContainerPanel; - private javax.swing.JScrollPane tableScrollPane; - private javax.swing.JPanel tableStatusPanel; - private javax.swing.JLabel tableStatusPanelLabel; + private JFileChooser CSVFileChooser; + private JMenuItem exportToCSVMenuItem; + private JPanel otherCasesPanel; + private JTable otherCasesTable; + private JPopupMenu rightClickPopupMenu; + private JMenuItem selectAllMenuItem; + private JMenuItem showCaseDetailsMenuItem; + private JMenuItem showCommonalityMenuItem; + private JPanel tableContainerPanel; + private JScrollPane tableScrollPane; + private JPanel tableStatusPanel; + private 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/commonfilesearch/CommonFilesPanel.form b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesPanel.form index 11d5ad88d4..e51aedbef3 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesPanel.form +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesPanel.form @@ -32,10 +32,7 @@ - - - - + diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesPanel.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesPanel.java index 07464f138d..c957548821 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesPanel.java @@ -115,7 +115,7 @@ public final class CommonFilesPanel extends javax.swing.JPanel { private void search() { String pathText = Bundle.CommonFilesPanel_search_results_pathText(); - new SwingWorker() { + new SwingWorker() { private String tabTitle; private ProgressHandle progress; @@ -133,7 +133,7 @@ public final class CommonFilesPanel extends javax.swing.JPanel { @Override @SuppressWarnings({"BoxedValueEquality", "NumberEquality"}) - protected CommonFilesMetadata doInBackground() throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException, Exception { + protected TableFilterNode doInBackground() throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException, Exception { progress = ProgressHandle.createHandle(Bundle.CommonFilesPanel_search_done_searchProgress1()); progress.start(); progress.switchToIndeterminate(); @@ -180,7 +180,13 @@ public final class CommonFilesPanel extends javax.swing.JPanel { this.tabTitle = builder.buildTabTitle(); - return metadata; + CommonFilesNode commonFilesNode = new CommonFilesNode(metadata); + + DataResultFilterNode dataResultFilterNode = new DataResultFilterNode(commonFilesNode, ExplorerManager.find(CommonFilesPanel.this)); + + TableFilterNode tableFilterWithDescendantsNode = new TableFilterNode(dataResultFilterNode); + + return tableFilterWithDescendantsNode; } @Override @@ -188,20 +194,15 @@ public final class CommonFilesPanel extends javax.swing.JPanel { try { super.done(); progress.setDisplayName(Bundle.CommonFilesPanel_search_done_searchProgress2()); - CommonFilesMetadata metadata = get(); + TableFilterNode tableFilterWithDescendantsNode = get(); - CommonFilesNode commonFilesNode = new CommonFilesNode(metadata); - - DataResultFilterNode dataResultFilterNode = new DataResultFilterNode(commonFilesNode, ExplorerManager.find(CommonFilesPanel.this)); - - TableFilterNode tableFilterWithDescendantsNode = new TableFilterNode(dataResultFilterNode); DataResultViewerTable table = new DataResultViewerTable(); Collection viewers = new ArrayList<>(1); viewers.add(table); progress.setDisplayName(Bundle.CommonFilesPanel_search_done_searchProgress3()); - DataResultTopComponent.createInstance(tabTitle, pathText, tableFilterWithDescendantsNode, metadata.size(), viewers); + DataResultTopComponent.createInstance(tabTitle, pathText, tableFilterWithDescendantsNode, 0, viewers); progress.finish(); } catch (InterruptedException ex) { LOGGER.log(Level.SEVERE, "Interrupted while loading Common Files", ex); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Md5Node.java b/Core/src/org/sleuthkit/autopsy/datamodel/Md5Node.java index 1287fd061f..b171354fb4 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Md5Node.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Md5Node.java @@ -23,6 +23,7 @@ import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.Callable; import java.util.logging.Level; import org.openide.nodes.ChildFactory; import org.openide.nodes.Children; @@ -56,16 +57,33 @@ public class Md5Node extends DisplayableItemNode { private final String dataSources; public Md5Node(Md5Metadata data) { - super(Children.create( - new FileInstanceNodeFactory(data), true), - Lookups.singleton(data.getMd5())); - + super(Children.createLazy(new Md5ChildCallable(data)), Lookups.singleton(data.getMd5())); this.commonFileCount = data.size(); this.dataSources = String.join(", ", data.getDataSources()); this.md5Hash = data.getMd5(); this.setDisplayName(this.md5Hash); } + + private static class Md5ChildCallable implements Callable { + private final Md5Metadata key; + private Md5ChildCallable(Md5Metadata key) { + this.key = key; + } + @Override + public Children call() throws Exception { + //Check, somehow, that your key has children, + //e.g., create "hasChildren" on the object + //to look in the database to see whether + //the object has children; + //if it doesn't have children, return a leaf: + if (key.getMetadata().isEmpty()) { + return Children.LEAF; + } else { + return Children.create(new FileInstanceNodeFactory(key), true); + } + } + } int getCommonFileCount() { return this.commonFileCount; diff --git a/Core/src/org/sleuthkit/autopsy/healthmonitor/EnterpriseHealthMonitor.java b/Core/src/org/sleuthkit/autopsy/healthmonitor/EnterpriseHealthMonitor.java index d28ef1b475..8facd6cd3e 100644 --- a/Core/src/org/sleuthkit/autopsy/healthmonitor/EnterpriseHealthMonitor.java +++ b/Core/src/org/sleuthkit/autopsy/healthmonitor/EnterpriseHealthMonitor.java @@ -190,7 +190,7 @@ public final class EnterpriseHealthMonitor implements PropertyChangeListener { stopTimer(); healthMonitorOutputTimer = new ScheduledThreadPoolExecutor(1, new ThreadFactoryBuilder().setNameFormat("health_monitor_timer").build()); - healthMonitorOutputTimer.scheduleWithFixedDelay(new PeriodicHealthMonitorTask(), DATABASE_WRITE_INTERVAL, DATABASE_WRITE_INTERVAL, TimeUnit.MINUTES); + healthMonitorOutputTimer.scheduleWithFixedDelay(new PeriodicHealthMonitorTask(false), DATABASE_WRITE_INTERVAL, DATABASE_WRITE_INTERVAL, TimeUnit.MINUTES); } /** @@ -356,11 +356,16 @@ public final class EnterpriseHealthMonitor implements PropertyChangeListener { /** * Collect metrics at a scheduled time. + * @param caseIsClosing True if this was triggered from a case closed event * @throws HealthMonitorException */ - private void gatherTimerBasedMetrics() throws HealthMonitorException { - // Time a database query - performDatabaseQuery(); + private void gatherTimerBasedMetrics(boolean caseIsClosing) throws HealthMonitorException { + // Time a database query. If this was triggered from a case close event + // it will fail - since we're on a new thread the case database will + // be in the process of closing. In that case, skip collecting the metric. + if( ! caseIsClosing) { + performDatabaseQuery(); + } } /** @@ -806,17 +811,23 @@ public final class EnterpriseHealthMonitor implements PropertyChangeListener { */ static final class PeriodicHealthMonitorTask implements Runnable { + boolean caseIsClosing; + + PeriodicHealthMonitorTask(boolean caseIsClosing) { + this.caseIsClosing = caseIsClosing; + } + /** * Perform all periodic tasks: * - Check if monitoring has been enabled / disabled in the database - * - Gather any additional metrics + * - Calculate any final metrics * - Write current metric data to the database */ @Override public void run() { try { getInstance().updateFromGlobalEnabledStatus(); - getInstance().gatherTimerBasedMetrics(); + getInstance().gatherTimerBasedMetrics(caseIsClosing); getInstance().writeCurrentStateToDatabase(); } catch (HealthMonitorException ex) { logger.log(Level.SEVERE, "Error performing periodic task", ex); //NON-NLS @@ -832,7 +843,7 @@ 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()); + healthMonitorExecutor.submit(new EnterpriseHealthMonitor.PeriodicHealthMonitorTask(true)); } break; } diff --git a/Core/src/org/sleuthkit/autopsy/healthmonitor/TimingMetricGraphPanel.java b/Core/src/org/sleuthkit/autopsy/healthmonitor/TimingMetricGraphPanel.java index 6d995ed9ac..8d4a57b4f6 100644 --- a/Core/src/org/sleuthkit/autopsy/healthmonitor/TimingMetricGraphPanel.java +++ b/Core/src/org/sleuthkit/autopsy/healthmonitor/TimingMetricGraphPanel.java @@ -373,7 +373,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/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}.