From 1dc55ba7e8b65a3c86cbdcc6ac5e20dcbe9117f0 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dgrove" Date: Fri, 6 Jul 2018 10:59:38 -0400 Subject: [PATCH 001/150] Implemented message node to handle 'No results found' message. --- .../DataContentViewerOtherCases.form | 4 +- .../DataContentViewerOtherCases.java | 33 +-- ...DataContentViewerOtherCasesTableModel.java | 46 ++-- .../OtherOccurrenceNodeData.java | 213 +--------------- .../OtherOccurrenceNodeInstanceData.java | 233 ++++++++++++++++++ .../OtherOccurrenceNodeMessageData.java | 34 +++ 6 files changed, 319 insertions(+), 244 deletions(-) mode change 100644 => 100755 Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeData.java create mode 100644 Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeInstanceData.java create mode 100755 Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeMessageData.java diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.form b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.form index 60667bae46..7b9e47c236 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.form +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.form @@ -80,7 +80,7 @@ - + @@ -106,7 +106,7 @@ - + diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java index 8ead475d95..c3558a106b 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java @@ -126,7 +126,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi showCommonalityDetails(); } else if (jmi.equals(addCommentMenuItem)) { try { - OtherOccurrenceNodeData selectedNode = (OtherOccurrenceNodeData) tableModel.getRow(otherCasesTable.getSelectedRow()); + OtherOccurrenceNodeInstanceData selectedNode = (OtherOccurrenceNodeInstanceData) tableModel.getRow(otherCasesTable.getSelectedRow()); AddEditCentralRepoCommentAction action = AddEditCentralRepoCommentAction.createAddEditCommentAction(selectedNode.createCorrelationAttribute()); String currentComment = action.addEditCentralRepoComment(); selectedNode.updateComment(currentComment); @@ -205,7 +205,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi if (-1 != selectedRowViewIdx) { EamDb dbManager = EamDb.getInstance(); int selectedRowModelIdx = otherCasesTable.convertRowIndexToModel(selectedRowViewIdx); - OtherOccurrenceNodeData nodeData = (OtherOccurrenceNodeData) tableModel.getRow(selectedRowModelIdx); + OtherOccurrenceNodeInstanceData nodeData = (OtherOccurrenceNodeInstanceData) tableModel.getRow(selectedRowModelIdx); CorrelationCase eamCasePartial = nodeData.getCorrelationAttributeInstance().getCorrelationCase(); if (eamCasePartial == null) { JOptionPane.showConfirmDialog(showCaseDetailsMenuItem, @@ -504,13 +504,13 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi * * @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 nodeDataMap = new HashMap<>(); + HashMap nodeDataMap = new HashMap<>(); if (EamDb.isEnabled()) { List instances = EamDb.getInstance().getArtifactInstancesByTypeValue(corAttr.getCorrelationType(), corAttr.getCorrelationValue()); @@ -528,7 +528,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi || !artifactInstance.getCorrelationDataSource().getDeviceID().equals(deviceId) || !artifactInstance.getFilePath().equalsIgnoreCase(file.getParentPath() + file.getName())) { - OtherOccurrenceNodeData newNode = new OtherOccurrenceNodeData(artifactInstance, corAttr.getCorrelationType(), corAttr.getCorrelationValue()); + OtherOccurrenceNodeInstanceData newNode = new OtherOccurrenceNodeInstanceData(artifactInstance, corAttr.getCorrelationType(), corAttr.getCorrelationValue()); UniquePathKey uniquePathKey = new UniquePathKey(newNode); nodeDataMap.put(uniquePathKey, newNode); } @@ -592,9 +592,9 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi * @throws TskCoreException * @throws EamDbException */ - private void addOrUpdateNodeData(final Case autopsyCase, Map nodeDataMap, AbstractFile newFile) throws TskCoreException, EamDbException { + private void addOrUpdateNodeData(final Case autopsyCase, Map nodeDataMap, AbstractFile newFile) throws TskCoreException, EamDbException { - OtherOccurrenceNodeData newNode = new OtherOccurrenceNodeData(newFile, autopsyCase); + OtherOccurrenceNodeInstanceData newNode = new OtherOccurrenceNodeInstanceData(newFile, autopsyCase); // If the caseDB object has a notable tag associated with it, update // the known status to BAD @@ -617,7 +617,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi // Otherwise this is a new node so add the new node to the map. if (nodeDataMap.containsKey(uniquePathKey)) { if (newNode.getKnown() == TskData.FileKnown.BAD) { - OtherOccurrenceNodeData prevInstance = nodeDataMap.get(uniquePathKey); + OtherOccurrenceNodeInstanceData prevInstance = nodeDataMap.get(uniquePathKey); prevInstance.updateKnown(newNode.getKnown()); } } else { @@ -663,8 +663,10 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi * * @param node The node being viewed. */ - @Messages({"DataContentViewerOtherCases.table.isempty=There are no associated artifacts or files from other occurrences to display.", - "DataContentViewerOtherCases.table.noArtifacts=Correlation cannot be performed on the selected file."}) + @Messages({ + "DataContentViewerOtherCases.table.noResultsFound=No results found.", + "DataContentViewerOtherCases.table.noArtifacts=Correlation cannot be performed on the selected file." + }) private void populateTable(Node node) { String dataSourceName = ""; String deviceId = ""; @@ -682,7 +684,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi // get the attributes we can correlate on correlationAttributes.addAll(getCorrelationAttributesFromNode(node)); for (CorrelationAttribute corAttr : correlationAttributes) { - Map correlatedNodeDataMap = new HashMap<>(0); + Map correlatedNodeDataMap = new HashMap<>(0); // get correlation and reference set instances from DB correlatedNodeDataMap.putAll(getCorrelatedInstances(corAttr, dataSourceName, deviceId)); @@ -697,7 +699,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi // @@@ 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()); + tableModel.addNodeData(new OtherOccurrenceNodeMessageData(Bundle.DataContentViewerOtherCases_table_noResultsFound())); } else { clearMessageOnTableStatusPanel(); setColumnWidths(); @@ -877,8 +879,9 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi if (EamDbUtil.useCentralRepo() && otherCasesTable.getSelectedRowCount() == 1) { int rowIndex = otherCasesTable.getSelectedRow(); OtherOccurrenceNodeData selectedNode = (OtherOccurrenceNodeData) tableModel.getRow(rowIndex); - if (selectedNode.isCentralRepoNode()) { - enableCentralRepoActions = true; + if (selectedNode instanceof OtherOccurrenceNodeInstanceData) { + OtherOccurrenceNodeInstanceData instanceData = (OtherOccurrenceNodeInstanceData) selectedNode; + enableCentralRepoActions = instanceData.isCentralRepoNode(); } } @@ -915,7 +918,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi private final String filePath; private final String type; - UniquePathKey(OtherOccurrenceNodeData nodeData) { + UniquePathKey(OtherOccurrenceNodeInstanceData nodeData) { super(); dataSourceID = nodeData.getDeviceID(); if (nodeData.getFilePath() != null) { diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCasesTableModel.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCasesTableModel.java index 5febf88dc3..0d7018885c 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCasesTableModel.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCasesTableModel.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"); @@ -22,8 +22,6 @@ import java.util.ArrayList; import java.util.List; import javax.swing.table.AbstractTableModel; import org.openide.util.NbBundle.Messages; -import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute; -import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; /** * Model for cells in data content viewer table @@ -34,7 +32,7 @@ public class DataContentViewerOtherCasesTableModel extends AbstractTableModel { "DataContentViewerOtherCasesTableModel.device=Device", "DataContentViewerOtherCasesTableModel.dataSource=Data Source", "DataContentViewerOtherCasesTableModel.path=Path", - "DataContentViewerOtherCasesTableModel.type=Correlation Type", + "DataContentViewerOtherCasesTableModel.property=Correlation Property", "DataContentViewerOtherCasesTableModel.value=Correlation Value", "DataContentViewerOtherCasesTableModel.known=Known", "DataContentViewerOtherCasesTableModel.comment=Comment", @@ -44,7 +42,7 @@ public class DataContentViewerOtherCasesTableModel extends AbstractTableModel { // If order is changed, update the CellRenderer to ensure correct row coloring. CASE_NAME(Bundle.DataContentViewerOtherCasesTableModel_case(), 100), DATA_SOURCE(Bundle.DataContentViewerOtherCasesTableModel_dataSource(), 100), - TYPE(Bundle.DataContentViewerOtherCasesTableModel_type(), 100), + PROPERTY(Bundle.DataContentViewerOtherCasesTableModel_property(), 125), VALUE(Bundle.DataContentViewerOtherCasesTableModel_value(), 200), KNOWN(Bundle.DataContentViewerOtherCasesTableModel_known(), 50), FILE_PATH(Bundle.DataContentViewerOtherCasesTableModel_path(), 450), @@ -126,38 +124,49 @@ public class DataContentViewerOtherCasesTableModel extends AbstractTableModel { */ private Object mapValueById(int rowIdx, TableColumns colId) { OtherOccurrenceNodeData nodeData = nodeDataList.get(rowIdx); + + if (nodeData instanceof OtherOccurrenceNodeMessageData) { + if (colId == TableColumns.CASE_NAME) { + OtherOccurrenceNodeMessageData messageData = (OtherOccurrenceNodeMessageData) nodeData; + return messageData.getDisplayMessage(); + } else { + return ""; + } + } + + OtherOccurrenceNodeInstanceData instanceData = (OtherOccurrenceNodeInstanceData) nodeData; String value = Bundle.DataContentViewerOtherCasesTableModel_noData(); switch (colId) { case CASE_NAME: - if (null != nodeData.getCaseName()) { - value = nodeData.getCaseName(); + if (null != instanceData.getCaseName()) { + value = instanceData.getCaseName(); } break; case DEVICE: - if (null != nodeData.getDeviceID()) { - value = nodeData.getDeviceID(); + if (null != instanceData.getDeviceID()) { + value = instanceData.getDeviceID(); } break; case DATA_SOURCE: - if (null != nodeData.getDataSourceName()) { - value = nodeData.getDataSourceName(); + if (null != instanceData.getDataSourceName()) { + value = instanceData.getDataSourceName(); } break; case FILE_PATH: - value = nodeData.getFilePath(); + value = instanceData.getFilePath(); break; - case TYPE: - value = nodeData.getType(); + case PROPERTY: + value = instanceData.getType(); break; case VALUE: - value = nodeData.getValue(); + value = instanceData.getValue(); break; case KNOWN: - value = nodeData.getKnown().getName(); + value = instanceData.getKnown().getName(); break; case COMMENT: - value = nodeData.getComment(); + value = instanceData.getComment(); break; } return value; @@ -178,6 +187,9 @@ public class DataContentViewerOtherCasesTableModel extends AbstractTableModel { fireTableDataChanged(); } + /** + * Clear the node data table. + */ void clearTable() { nodeDataList.clear(); fireTableDataChanged(); diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeData.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeData.java old mode 100644 new mode 100755 index 958068fb14..c10f078313 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeData.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeData.java @@ -1,5 +1,5 @@ /* - * Central Repository + * Autopsy Forensic Browser * * Copyright 2018 Basis Technology Corp. * Contact: carrier sleuthkit org @@ -18,216 +18,9 @@ */ package org.sleuthkit.autopsy.centralrepository.contentviewer; -import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute; -import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; -import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; -import org.sleuthkit.datamodel.AbstractFile; -import org.sleuthkit.datamodel.DataSource; -import org.sleuthkit.datamodel.TskCoreException; -import org.sleuthkit.datamodel.TskData; -import org.sleuthkit.datamodel.TskDataException; - /** - * Class for populating the Other Occurrences tab + * Marker interface for Other Occurrences nodes. */ -class OtherOccurrenceNodeData { +interface OtherOccurrenceNodeData { - // For now hard code the string for the central repo files type, since - // getting it dynamically can fail. - private static final String FILE_TYPE_STR = "Files"; - - private final String caseName; - private String deviceID; - private String dataSourceName; - private final String filePath; - private final String typeStr; - private final CorrelationAttribute.Type type; - private final String value; - private TskData.FileKnown known; - private String comment; - - private AbstractFile originalAbstractFile = null; - private CorrelationAttributeInstance originalCorrelationInstance = null; - - /** - * Create a node from a central repo instance. - * @param instance The central repo instance - * @param type The type of the instance - * @param value The value of the instance - */ - OtherOccurrenceNodeData(CorrelationAttributeInstance instance, CorrelationAttribute.Type type, String value) { - caseName = instance.getCorrelationCase().getDisplayName(); - deviceID = instance.getCorrelationDataSource().getDeviceID(); - dataSourceName = instance.getCorrelationDataSource().getName(); - filePath = instance.getFilePath(); - this.typeStr = type.getDisplayName(); - this.type = type; - this.value = value; - known = instance.getKnownStatus(); - comment = instance.getComment(); - - originalCorrelationInstance = instance; - } - - /** - * Create a node from an abstract file. - * @param newFile The abstract file - * @param autopsyCase The current case - * @throws EamDbException - */ - OtherOccurrenceNodeData(AbstractFile newFile, Case autopsyCase) throws EamDbException { - caseName = autopsyCase.getDisplayName(); - try { - DataSource dataSource = autopsyCase.getSleuthkitCase().getDataSource(newFile.getDataSource().getId()); - deviceID = dataSource.getDeviceId(); - dataSourceName = dataSource.getName(); - } catch (TskDataException | TskCoreException ex) { - throw new EamDbException("Error loading data source for abstract file ID " + newFile.getId(), ex); - } - - filePath = newFile.getParentPath() + newFile.getName(); - typeStr = FILE_TYPE_STR; - this.type = null; - value = newFile.getMd5Hash(); - known = newFile.getKnown(); - comment = ""; - - originalAbstractFile = newFile; - } - - /** - * Check if this node is a "file" type - * @return true if it is a file type - */ - boolean isFileType() { - return FILE_TYPE_STR.equals(typeStr); - } - - /** - * Update the known status for this node - * @param newKnownStatus The new known status - */ - void updateKnown(TskData.FileKnown newKnownStatus) { - known = newKnownStatus; - } - - /** - * Update the comment for this node - * @param newComment The new comment - */ - void updateComment(String newComment) { - comment = newComment; - } - - /** - * Check if this is a central repo node. - * @return true if this node was created from a central repo instance, false otherwise - */ - boolean isCentralRepoNode() { - return (originalCorrelationInstance != null); - } - - /** - * Uses the saved instance plus type and value to make a new CorrelationAttribute. - * Should only be called if isCentralRepoNode() is true. - * @return the newly created CorrelationAttribute - */ - CorrelationAttribute createCorrelationAttribute() throws EamDbException { - if (! isCentralRepoNode() ) { - throw new EamDbException("Can not create CorrelationAttribute for non central repo node"); - } - CorrelationAttribute attr = new CorrelationAttribute(type, value); - attr.addInstance(originalCorrelationInstance); - return attr; - } - - /** - * Get the case name - * @return the case name - */ - String getCaseName() { - return caseName; - } - - /** - * Get the device ID - * @return the device ID - */ - String getDeviceID() { - return deviceID; - } - - /** - * Get the data source name - * @return the data source name - */ - String getDataSourceName() { - return dataSourceName; - } - - /** - * Get the file path - * @return the file path - */ - String getFilePath() { - return filePath; - } - - /** - * Get the type (as a string) - * @return the type - */ - String getType() { - return typeStr; - } - - /** - * Get the value (MD5 hash for files) - * @return the value - */ - String getValue() { - return value; - } - - /** - * Get the known status - * @return the known status - */ - TskData.FileKnown getKnown() { - return known; - } - - /** - * Get the comment - * @return the comment - */ - String getComment() { - return comment; - } - - /** - * Get the backing abstract file. - * Should only be called if isCentralRepoNode() is false - * @return the original abstract file - */ - AbstractFile getAbstractFile() throws EamDbException { - if (originalCorrelationInstance == null) { - throw new EamDbException("AbstractFile is null"); - } - return originalAbstractFile; - } - - /** - * Get the backing CorrelationAttributeInstance. - * Should only be called if isCentralRepoNode() is true - * @return the original CorrelationAttributeInstance - * @throws EamDbException - */ - CorrelationAttributeInstance getCorrelationAttributeInstance() throws EamDbException { - if (originalCorrelationInstance == null) { - throw new EamDbException("CorrelationAttributeInstance is null"); - } - return originalCorrelationInstance; - } } diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeInstanceData.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeInstanceData.java new file mode 100644 index 0000000000..7eb907aba8 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeInstanceData.java @@ -0,0 +1,233 @@ +/* + * Central Repository + * + * 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.contentviewer; + +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.DataSource; +import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.TskData; +import org.sleuthkit.datamodel.TskDataException; + +/** + * Class for populating the Other Occurrences tab + */ +class OtherOccurrenceNodeInstanceData implements OtherOccurrenceNodeData { + + // For now hard code the string for the central repo files type, since + // getting it dynamically can fail. + private static final String FILE_TYPE_STR = "Files"; + + private final String caseName; + private String deviceID; + private String dataSourceName; + private final String filePath; + private final String typeStr; + private final CorrelationAttribute.Type type; + private final String value; + private TskData.FileKnown known; + private String comment; + + private AbstractFile originalAbstractFile = null; + private CorrelationAttributeInstance originalCorrelationInstance = null; + + /** + * Create a node from a central repo instance. + * @param instance The central repo instance + * @param type The type of the instance + * @param value The value of the instance + */ + OtherOccurrenceNodeInstanceData(CorrelationAttributeInstance instance, CorrelationAttribute.Type type, String value) { + caseName = instance.getCorrelationCase().getDisplayName(); + deviceID = instance.getCorrelationDataSource().getDeviceID(); + dataSourceName = instance.getCorrelationDataSource().getName(); + filePath = instance.getFilePath(); + this.typeStr = type.getDisplayName(); + this.type = type; + this.value = value; + known = instance.getKnownStatus(); + comment = instance.getComment(); + + originalCorrelationInstance = instance; + } + + /** + * Create a node from an abstract file. + * @param newFile The abstract file + * @param autopsyCase The current case + * @throws EamDbException + */ + OtherOccurrenceNodeInstanceData(AbstractFile newFile, Case autopsyCase) throws EamDbException { + caseName = autopsyCase.getDisplayName(); + try { + DataSource dataSource = autopsyCase.getSleuthkitCase().getDataSource(newFile.getDataSource().getId()); + deviceID = dataSource.getDeviceId(); + dataSourceName = dataSource.getName(); + } catch (TskDataException | TskCoreException ex) { + throw new EamDbException("Error loading data source for abstract file ID " + newFile.getId(), ex); + } + + filePath = newFile.getParentPath() + newFile.getName(); + typeStr = FILE_TYPE_STR; + this.type = null; + value = newFile.getMd5Hash(); + known = newFile.getKnown(); + comment = ""; + + originalAbstractFile = newFile; + } + + /** + * Check if this node is a "file" type + * @return true if it is a file type + */ + boolean isFileType() { + return FILE_TYPE_STR.equals(typeStr); + } + + /** + * Update the known status for this node + * @param newKnownStatus The new known status + */ + void updateKnown(TskData.FileKnown newKnownStatus) { + known = newKnownStatus; + } + + /** + * Update the comment for this node + * @param newComment The new comment + */ + void updateComment(String newComment) { + comment = newComment; + } + + /** + * Check if this is a central repo node. + * @return true if this node was created from a central repo instance, false otherwise + */ + boolean isCentralRepoNode() { + return (originalCorrelationInstance != null); + } + + /** + * Uses the saved instance plus type and value to make a new CorrelationAttribute. + * Should only be called if isCentralRepoNode() is true. + * @return the newly created CorrelationAttribute + */ + CorrelationAttribute createCorrelationAttribute() throws EamDbException { + if (! isCentralRepoNode() ) { + throw new EamDbException("Can not create CorrelationAttribute for non central repo node"); + } + CorrelationAttribute attr = new CorrelationAttribute(type, value); + attr.addInstance(originalCorrelationInstance); + return attr; + } + + /** + * Get the case name + * @return the case name + */ + String getCaseName() { + return caseName; + } + + /** + * Get the device ID + * @return the device ID + */ + String getDeviceID() { + return deviceID; + } + + /** + * Get the data source name + * @return the data source name + */ + String getDataSourceName() { + return dataSourceName; + } + + /** + * Get the file path + * @return the file path + */ + String getFilePath() { + return filePath; + } + + /** + * Get the type (as a string) + * @return the type + */ + String getType() { + return typeStr; + } + + /** + * Get the value (MD5 hash for files) + * @return the value + */ + String getValue() { + return value; + } + + /** + * Get the known status + * @return the known status + */ + TskData.FileKnown getKnown() { + return known; + } + + /** + * Get the comment + * @return the comment + */ + String getComment() { + return comment; + } + + /** + * Get the backing abstract file. + * Should only be called if isCentralRepoNode() is false + * @return the original abstract file + */ + AbstractFile getAbstractFile() throws EamDbException { + if (originalCorrelationInstance == null) { + throw new EamDbException("AbstractFile is null"); + } + return originalAbstractFile; + } + + /** + * Get the backing CorrelationAttributeInstance. + * Should only be called if isCentralRepoNode() is true + * @return the original CorrelationAttributeInstance + * @throws EamDbException + */ + CorrelationAttributeInstance getCorrelationAttributeInstance() throws EamDbException { + if (originalCorrelationInstance == null) { + throw new EamDbException("CorrelationAttributeInstance is null"); + } + return originalCorrelationInstance; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeMessageData.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeMessageData.java new file mode 100755 index 0000000000..99e530349a --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeMessageData.java @@ -0,0 +1,34 @@ +/* + * 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.contentviewer; + +/** + * Class for populating the Other Occurrences tab with a single message. + */ +final class OtherOccurrenceNodeMessageData implements OtherOccurrenceNodeData { + private final String displayMessage; + + OtherOccurrenceNodeMessageData(String displayMessage) { + this.displayMessage = displayMessage; + } + + String getDisplayMessage() { + return displayMessage; + } +} From 5aec70fbaf03c09630ddba81185a59a5fa13c1fb Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dgrove" Date: Fri, 6 Jul 2018 16:43:56 -0400 Subject: [PATCH 002/150] Partial handling of 'noArtifacts' message. --- .../DataContentViewerOtherCases.java | 25 +++++++------------ ...DataContentViewerOtherCasesTableModel.java | 8 +++--- 2 files changed, 13 insertions(+), 20 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java index c3558a106b..ebc5b7c375 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java @@ -38,7 +38,6 @@ 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.JFileChooser; import javax.swing.JMenuItem; import javax.swing.JOptionPane; @@ -49,6 +48,7 @@ import javax.swing.JPanel; import javax.swing.filechooser.FileNameExtensionFilter; import javax.swing.table.TableCellRenderer; import javax.swing.table.TableColumn; +import javax.swing.table.TableColumnModel; import org.joda.time.DateTimeZone; import org.joda.time.LocalDateTime; import org.openide.nodes.Node; @@ -147,7 +147,6 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi // Set background of every nth row as light grey. TableCellRenderer renderer = new DataContentViewerOtherCasesTableCellRenderer(); otherCasesTable.setDefaultRenderer(Object.class, renderer); - tableStatusPanelLabel.setVisible(false); } @@ -664,8 +663,8 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi * @param node The node being viewed. */ @Messages({ - "DataContentViewerOtherCases.table.noResultsFound=No results found.", - "DataContentViewerOtherCases.table.noArtifacts=Correlation cannot be performed on the selected file." + "DataContentViewerOtherCases.table.noArtifacts=Item has no attributes with which to search.", + "DataContentViewerOtherCases.table.noResultsFound=No results found." }) private void populateTable(Node node) { String dataSourceName = ""; @@ -696,17 +695,20 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi } 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()); + tableModel.addNodeData(new OtherOccurrenceNodeMessageData(Bundle.DataContentViewerOtherCases_table_noArtifacts())); + //DLG: Removing columns causes problems. Look into resizing columns. } else if (0 == tableModel.getRowCount()) { tableModel.addNodeData(new OtherOccurrenceNodeMessageData(Bundle.DataContentViewerOtherCases_table_noResultsFound())); + //DLG: Removing columns causes problems. Look into resizing columns. } else { - clearMessageOnTableStatusPanel(); setColumnWidths(); } setEarliestCaseDate(); } + /** + * Adjust column widths to their preferred values. + */ private void setColumnWidths() { for (int idx = 0; idx < tableModel.getColumnCount(); idx++) { TableColumn column = otherCasesTable.getColumnModel().getColumn(idx); @@ -717,15 +719,6 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi } } - private void displayMessageOnTableStatusPanel(String message) { - tableStatusPanelLabel.setText(message); - tableStatusPanelLabel.setVisible(true); - } - - private void clearMessageOnTableStatusPanel() { - tableStatusPanelLabel.setVisible(false); - } - /** * 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/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCasesTableModel.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCasesTableModel.java index 0d7018885c..de528e2d42 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCasesTableModel.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCasesTableModel.java @@ -32,8 +32,8 @@ public class DataContentViewerOtherCasesTableModel extends AbstractTableModel { "DataContentViewerOtherCasesTableModel.device=Device", "DataContentViewerOtherCasesTableModel.dataSource=Data Source", "DataContentViewerOtherCasesTableModel.path=Path", - "DataContentViewerOtherCasesTableModel.property=Correlation Property", - "DataContentViewerOtherCasesTableModel.value=Correlation Value", + "DataContentViewerOtherCasesTableModel.attribute=Matched Attribute", + "DataContentViewerOtherCasesTableModel.value=Attribute Value", "DataContentViewerOtherCasesTableModel.known=Known", "DataContentViewerOtherCasesTableModel.comment=Comment", "DataContentViewerOtherCasesTableModel.noData=No Data.",}) @@ -42,7 +42,7 @@ public class DataContentViewerOtherCasesTableModel extends AbstractTableModel { // If order is changed, update the CellRenderer to ensure correct row coloring. CASE_NAME(Bundle.DataContentViewerOtherCasesTableModel_case(), 100), DATA_SOURCE(Bundle.DataContentViewerOtherCasesTableModel_dataSource(), 100), - PROPERTY(Bundle.DataContentViewerOtherCasesTableModel_property(), 125), + ATTRIBUTE(Bundle.DataContentViewerOtherCasesTableModel_attribute(), 125), VALUE(Bundle.DataContentViewerOtherCasesTableModel_value(), 200), KNOWN(Bundle.DataContentViewerOtherCasesTableModel_known(), 50), FILE_PATH(Bundle.DataContentViewerOtherCasesTableModel_path(), 450), @@ -156,7 +156,7 @@ public class DataContentViewerOtherCasesTableModel extends AbstractTableModel { case FILE_PATH: value = instanceData.getFilePath(); break; - case PROPERTY: + case ATTRIBUTE: value = instanceData.getType(); break; case VALUE: From 75fbb309b9ad390b5ce9261ec5e228d994a01be8 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dgrove" Date: Wed, 11 Jul 2018 01:35:06 -0400 Subject: [PATCH 003/150] Column width adjustments handled. --- .../DataContentViewerOtherCases.form | 24 +--- .../DataContentViewerOtherCases.java | 123 +++++++++-------- ...DataContentViewerOtherCasesTableModel.java | 126 ++++++++++-------- 3 files changed, 142 insertions(+), 131 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.form b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.form index 7b9e47c236..9c42be16a8 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.form +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.form @@ -133,23 +133,18 @@ - - - + - - - - - - - - + + + + + @@ -230,13 +225,6 @@ - - - - - - - diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java index ebc5b7c375..1c69671b05 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java @@ -19,6 +19,7 @@ package org.sleuthkit.autopsy.centralrepository.contentviewer; import java.awt.Component; +import java.awt.FontMetrics; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.BufferedWriter; @@ -86,7 +87,10 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi private static final long serialVersionUID = -1L; - private final static Logger logger = Logger.getLogger(DataContentViewerOtherCases.class.getName()); + private static final Logger logger = Logger.getLogger(DataContentViewerOtherCases.class.getName()); + + private static final int DEFAULT_MIN_CELL_WIDTH = 15; + private static final int CELL_TEXT_WIDTH_PADDING = 5; private final DataContentViewerOtherCasesTableModel tableModel; private final Collection correlationAttributes; @@ -459,12 +463,12 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi @Messages({"DataContentViewerOtherCases.earliestCaseNotAvailable= Not Enabled."}) /** - * Gets the list of Eam Cases and determines the earliest case creation date. - * Sets the label to display the earliest date string to the user. + * Gets the list of Eam Cases and determines the earliest case creation + * date. Sets the label to display the earliest date string to the user. */ - private void setEarliestCaseDate() { - String dateStringDisplay = Bundle.DataContentViewerOtherCases_earliestCaseNotAvailable(); - + private void setEarliestCaseDate() { + String dateStringDisplay = Bundle.DataContentViewerOtherCases_earliestCaseNotAvailable(); + if (EamDb.isEnabled()) { LocalDateTime earliestDate = LocalDateTime.now(DateTimeZone.UTC); DateFormat datetimeFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.US); @@ -472,15 +476,15 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi EamDb dbManager = EamDb.getInstance(); List cases = dbManager.getCases(); for (CorrelationCase aCase : cases) { - LocalDateTime caseDate = LocalDateTime.fromDateFields(datetimeFormat.parse(aCase.getCreationDate())); - - if (caseDate.isBefore(earliestDate)) { + LocalDateTime caseDate = LocalDateTime.fromDateFields(datetimeFormat.parse(aCase.getCreationDate())); + + if (caseDate.isBefore(earliestDate)) { earliestDate = caseDate; dateStringDisplay = aCase.getCreationDate(); - } + } } - + } catch (EamDbException ex) { logger.log(Level.SEVERE, "Error getting list of cases from database.", ex); // NON-NLS } catch (ParseException ex) { @@ -492,10 +496,10 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi } /** - * Query the central repo database (if enabled) and the case database to find all - * artifact instances correlated to the given central repository artifact. If the - * central repo is not enabled, this will only return files from the current case - * with matching MD5 hashes. + * Query the central repo database (if enabled) and the case database to + * find all artifact instances correlated to the given central repository + * artifact. If the central repo is not enabled, this will only return files + * from the current case with matching MD5 hashes. * * @param corAttr CorrelationAttribute to query for * @param dataSourceName Data source to filter results @@ -503,19 +507,19 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi * * @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 nodeDataMap = new HashMap<>(); + HashMap nodeDataMap = new HashMap<>(); if (EamDb.isEnabled()) { List instances = EamDb.getInstance().getArtifactInstancesByTypeValue(corAttr.getCorrelationType(), corAttr.getCorrelationValue()); - for (CorrelationAttributeInstance artifactInstance:instances) { - + for (CorrelationAttributeInstance artifactInstance : instances) { + // Only add the attribute if it isn't the object the user selected. // We consider it to be a different object if at least one of the following is true: // - the case UUID is different @@ -534,7 +538,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi } } - if (corAttr.getCorrelationType().getDisplayName().equals("Files")) { + if (corAttr.getCorrelationType().getDisplayName().equals("Files")) { List caseDbFiles = getCaseDbMatches(corAttr, openCase); for (AbstractFile caseDbFile : caseDbFiles) { @@ -557,13 +561,17 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi } /** - * Get all other abstract files in the current case with the same MD5 as the selected node. + * Get all other abstract files in the current case with the same MD5 as the + * selected node. + * * @param corAttr The CorrelationAttribute containing the MD5 to search for * @param openCase The current case + * * @return List of matching AbstractFile objects + * * @throws NoCurrentCaseException * @throws TskCoreException - * @throws EamDbException + * @throws EamDbException */ private List getCaseDbMatches(CorrelationAttribute corAttr, Case openCase) throws NoCurrentCaseException, TskCoreException, EamDbException { String md5 = corAttr.getCorrelationValue(); @@ -583,18 +591,18 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi /** * Adds the file to the nodeDataMap map if it does not already exist - * - * @param autopsyCase + * + * @param autopsyCase * @param nodeDataMap * @param newFile * * @throws TskCoreException * @throws EamDbException */ - private void addOrUpdateNodeData(final Case autopsyCase, Map nodeDataMap, AbstractFile newFile) throws TskCoreException, EamDbException { - + private void addOrUpdateNodeData(final Case autopsyCase, Map nodeDataMap, AbstractFile newFile) throws TskCoreException, EamDbException { + OtherOccurrenceNodeInstanceData newNode = new OtherOccurrenceNodeInstanceData(newFile, autopsyCase); - + // If the caseDB object has a notable tag associated with it, update // the known status to BAD if (newNode.getKnown() != TskData.FileKnown.BAD) { @@ -610,7 +618,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi // Make a key to see if the file is already in the map UniquePathKey uniquePathKey = new UniquePathKey(newNode); - + // If this node is already in the list, the only thing we need to do is // update the known status to BAD if the caseDB version had known status BAD. // Otherwise this is a new node so add the new node to the map. @@ -639,7 +647,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi } else { return this.file != null && this.file.getSize() > 0 - && ((this.file.getMd5Hash() != null) && ( ! this.file.getMd5Hash().isEmpty())); + && ((this.file.getMd5Hash() != null) && (!this.file.getMd5Hash().isEmpty())); } } @@ -683,7 +691,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi // get the attributes we can correlate on correlationAttributes.addAll(getCorrelationAttributesFromNode(node)); for (CorrelationAttribute corAttr : correlationAttributes) { - Map correlatedNodeDataMap = new HashMap<>(0); + Map correlatedNodeDataMap = new HashMap<>(0); // get correlation and reference set instances from DB correlatedNodeDataMap.putAll(getCorrelatedInstances(corAttr, dataSourceName, deviceId)); @@ -696,25 +704,40 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi if (correlationAttributes.isEmpty()) { tableModel.addNodeData(new OtherOccurrenceNodeMessageData(Bundle.DataContentViewerOtherCases_table_noArtifacts())); - //DLG: Removing columns causes problems. Look into resizing columns. + setColumnWidthToText(0, Bundle.DataContentViewerOtherCases_table_noArtifacts()); } else if (0 == tableModel.getRowCount()) { tableModel.addNodeData(new OtherOccurrenceNodeMessageData(Bundle.DataContentViewerOtherCases_table_noResultsFound())); - //DLG: Removing columns causes problems. Look into resizing columns. + setColumnWidthToText(0, Bundle.DataContentViewerOtherCases_table_noResultsFound()); } else { setColumnWidths(); } setEarliestCaseDate(); } + /** + * Adjust a given column for the text provided. + * + * @param columnIndex The index of the column to adjust. + * @param text The text whose length will be used to adjust the + * column width. + */ + private void setColumnWidthToText(int columnIndex, String text) { + TableColumn column = otherCasesTable.getColumnModel().getColumn(columnIndex); + FontMetrics fontMetrics = otherCasesTable.getFontMetrics(otherCasesTable.getFont()); + int stringWidth = fontMetrics.stringWidth(text); + column.setMinWidth(stringWidth + CELL_TEXT_WIDTH_PADDING); + } + /** * Adjust column widths to their preferred values. */ private void setColumnWidths() { for (int idx = 0; idx < tableModel.getColumnCount(); idx++) { TableColumn column = otherCasesTable.getColumnModel().getColumn(idx); - int colWidth = tableModel.getColumnPreferredWidth(idx); - if (0 < colWidth) { - column.setPreferredWidth(colWidth); + column.setMinWidth(DEFAULT_MIN_CELL_WIDTH); + int columnWidth = tableModel.getColumnPreferredWidth(idx); + if (columnWidth > 0) { + column.setPreferredWidth(columnWidth); } } } @@ -742,7 +765,6 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi earliestCaseLabel = new javax.swing.JLabel(); earliestCaseDate = new javax.swing.JLabel(); 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) { @@ -804,8 +826,6 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi .addGap(0, 16, Short.MAX_VALUE) ); - tableStatusPanelLabel.setForeground(new java.awt.Color(255, 0, 51)); - javax.swing.GroupLayout tableContainerPanelLayout = new javax.swing.GroupLayout(tableContainerPanel); tableContainerPanel.setLayout(tableContainerPanelLayout); tableContainerPanelLayout.setHorizontalGroup( @@ -818,20 +838,16 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi .addComponent(earliestCaseLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(earliestCaseDate) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(tableStatusPanelLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addContainerGap()) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); tableContainerPanelLayout.setVerticalGroup( tableContainerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, tableContainerPanelLayout.createSequentialGroup() - .addComponent(tableScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 176, Short.MAX_VALUE) - .addGap(0, 0, 0) - .addGroup(tableContainerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addGroup(tableContainerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(earliestCaseLabel) - .addComponent(earliestCaseDate)) - .addComponent(tableStatusPanelLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(tableScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 27, Short.MAX_VALUE) + .addGap(2, 2, 2) + .addGroup(tableContainerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(earliestCaseLabel) + .addComponent(earliestCaseDate)) .addGap(0, 0, 0) .addComponent(tableStatusPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(0, 0, 0)) @@ -850,7 +866,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi .addGap(0, 483, Short.MAX_VALUE) .addGroup(otherCasesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(otherCasesPanelLayout.createSequentialGroup() - .addComponent(tableContainerPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 483, Short.MAX_VALUE) + .addComponent(tableContainerPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 59, Short.MAX_VALUE) .addGap(0, 0, 0))) ); @@ -862,7 +878,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(otherCasesPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 483, Short.MAX_VALUE) + .addComponent(otherCasesPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 59, Short.MAX_VALUE) ); }// //GEN-END:initComponents @@ -898,7 +914,6 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi private javax.swing.JPanel tableContainerPanel; private javax.swing.JScrollPane tableScrollPane; private javax.swing.JPanel tableStatusPanel; - private javax.swing.JLabel tableStatusPanelLabel; // End of variables declaration//GEN-END:variables /** @@ -925,9 +940,9 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi @Override public boolean equals(Object other) { if (other instanceof UniquePathKey) { - UniquePathKey otherKey = (UniquePathKey)(other); - return ( Objects.equals(otherKey.dataSourceID, this.dataSourceID) - && Objects.equals(otherKey.filePath, this.filePath) + UniquePathKey otherKey = (UniquePathKey) (other); + return (Objects.equals(otherKey.dataSourceID, this.dataSourceID) + && Objects.equals(otherKey.filePath, this.filePath) && Objects.equals(otherKey.type, this.type)); } return false; diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCasesTableModel.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCasesTableModel.java index de528e2d42..9ae79259c2 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCasesTableModel.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCasesTableModel.java @@ -107,71 +107,79 @@ public class DataContentViewerOtherCasesTableModel extends AbstractTableModel { return Bundle.DataContentViewerOtherCasesTableModel_noData(); } - return mapValueById(rowIdx, TableColumns.values()[colIdx]); + OtherOccurrenceNodeData nodeData = nodeDataList.get(rowIdx); + TableColumns columnId = TableColumns.values()[colIdx]; + if (nodeData instanceof OtherOccurrenceNodeMessageData) { + return mapNodeMessageData((OtherOccurrenceNodeMessageData) nodeData, columnId); + } + return mapNodeInstanceData((OtherOccurrenceNodeInstanceData) nodeData, columnId); + } + + /** + * Map a column ID to the value in that cell for node message data. + * + * @param nodeData The node message data. + * @param columnId The ID of the cell column. + * + * @return The value in the cell. + */ + private Object mapNodeMessageData(OtherOccurrenceNodeMessageData nodeData, TableColumns columnId) { + if (columnId == TableColumns.CASE_NAME) { + return nodeData.getDisplayMessage(); + } + return ""; + } + + /** + * Map a column ID to the value in that cell for node instance data. + * + * @param nodeData The node instance data. + * @param columnId The ID of the cell column. + * + * @return The value in the cell. + */ + private Object mapNodeInstanceData(OtherOccurrenceNodeInstanceData nodeData, TableColumns columnId) { + String value = Bundle.DataContentViewerOtherCasesTableModel_noData(); + + switch (columnId) { + case CASE_NAME: + if (null != nodeData.getCaseName()) { + value = nodeData.getCaseName(); + } + break; + case DEVICE: + if (null != nodeData.getDeviceID()) { + value = nodeData.getDeviceID(); + } + break; + case DATA_SOURCE: + if (null != nodeData.getDataSourceName()) { + value = nodeData.getDataSourceName(); + } + break; + case FILE_PATH: + value = nodeData.getFilePath(); + break; + case ATTRIBUTE: + value = nodeData.getType(); + break; + case VALUE: + value = nodeData.getValue(); + break; + case KNOWN: + value = nodeData.getKnown().getName(); + break; + case COMMENT: + value = nodeData.getComment(); + break; + } + return value; } Object getRow(int rowIdx) { return nodeDataList.get(rowIdx); } - /** - * Map a rowIdx and colId to the value in that cell. - * - * @param rowIdx Index of row to search - * @param colId ID of column to search - * - * @return value in the cell - */ - private Object mapValueById(int rowIdx, TableColumns colId) { - OtherOccurrenceNodeData nodeData = nodeDataList.get(rowIdx); - - if (nodeData instanceof OtherOccurrenceNodeMessageData) { - if (colId == TableColumns.CASE_NAME) { - OtherOccurrenceNodeMessageData messageData = (OtherOccurrenceNodeMessageData) nodeData; - return messageData.getDisplayMessage(); - } else { - return ""; - } - } - - OtherOccurrenceNodeInstanceData instanceData = (OtherOccurrenceNodeInstanceData) nodeData; - String value = Bundle.DataContentViewerOtherCasesTableModel_noData(); - - switch (colId) { - case CASE_NAME: - if (null != instanceData.getCaseName()) { - value = instanceData.getCaseName(); - } - break; - case DEVICE: - if (null != instanceData.getDeviceID()) { - value = instanceData.getDeviceID(); - } - break; - case DATA_SOURCE: - if (null != instanceData.getDataSourceName()) { - value = instanceData.getDataSourceName(); - } - break; - case FILE_PATH: - value = instanceData.getFilePath(); - break; - case ATTRIBUTE: - value = instanceData.getType(); - break; - case VALUE: - value = instanceData.getValue(); - break; - case KNOWN: - value = instanceData.getKnown().getName(); - break; - case COMMENT: - value = instanceData.getComment(); - break; - } - return value; - } - @Override public Class getColumnClass(int colIdx) { return String.class; From 3b8788c952d55dbed81852167855aaa38199393d Mon Sep 17 00:00:00 2001 From: rishwanth1995 Date: Mon, 23 Jul 2018 11:58:59 -0400 Subject: [PATCH 004/150] live_usb warning --- .../casemodule/NewCaseVisualPanel1.java | 5 +++ .../autopsy/coreutils/PathValidator.java | 31 +++++++++++++++++-- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java b/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java index 6f365af86e..6959883de1 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java @@ -155,6 +155,11 @@ final class NewCaseVisualPanel1 extends JPanel implements DocumentListener { caseParentDirWarningLabel.setVisible(true); caseParentDirWarningLabel.setText(NbBundle.getMessage(this.getClass(), "NewCaseVisualPanel1.CaseFolderOnCDriveError.text")); } + + if(!PathValidator.isCasedataPersistable(parentDir)){ + caseParentDirWarningLabel.setVisible(true); + caseParentDirWarningLabel.setText("Please make the case directory in a mounted drive to save case progress"); + } /** * Enable the "Next" button for the wizard if there is text entered for diff --git a/Core/src/org/sleuthkit/autopsy/coreutils/PathValidator.java b/Core/src/org/sleuthkit/autopsy/coreutils/PathValidator.java index 41b5c19d62..41d557c42a 100644 --- a/Core/src/org/sleuthkit/autopsy/coreutils/PathValidator.java +++ b/Core/src/org/sleuthkit/autopsy/coreutils/PathValidator.java @@ -29,6 +29,7 @@ import org.sleuthkit.autopsy.casemodule.Case; public final class PathValidator { private static final Pattern driveLetterPattern = Pattern.compile("^[Cc]:.*$"); + private static final Pattern unixMediaDrivePattern = Pattern.compile("^\\/(media|mnt)\\/.*$"); public static boolean isValid(String path, Case.CaseType caseType) { @@ -39,11 +40,37 @@ public final class PathValidator { } } else { // single user case - no validation needed - } + } return true; } - + + public static boolean isCasedataPersistable(String path) { + if(checkForLiveAutopsy()) { + if(PlatformUtil.isWindowsOS()) { + if(pathOnCDrive(path)){ + return false; + } + }else if(System.getProperty("os.name").toLowerCase().contains("nux")){ + if(!pathIsMedia(path)){ + return false; + } + } + } + return true; + } + + private static boolean checkForLiveAutopsy() { + + String envValue = System.getenv("AUTOPSY_LIVE_TRIAGE"); + return "True".equals(envValue); + } + + private static boolean pathIsMedia(String filePath) { + Matcher m = unixMediaDrivePattern.matcher(filePath); + return m.find(); + } + /** * Checks whether a file path contains drive letter defined by pattern. * From 1198a38f5b1b0c4c11444e93a9e5a92defe4d797 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Wed, 1 Aug 2018 16:20:13 -0400 Subject: [PATCH 005/150] Refactored SQLite parsing functionality out of SQLiteViewer and into SQLiteExtractor --- .../autopsy/contentviewers/SQLiteViewer.java | 245 ++++------------- .../textextraction/SQLiteExtractor.java | 247 ++++++++++++++++++ 2 files changed, 301 insertions(+), 191 deletions(-) create mode 100755 Core/src/org/sleuthkit/autopsy/textextraction/SQLiteExtractor.java diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java index 40a667e3d7..6ace716240 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java @@ -22,22 +22,14 @@ 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; -import java.sql.ResultSet; -import java.sql.ResultSetMetaData; import java.sql.SQLException; -import java.sql.Statement; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.TreeMap; import java.util.logging.Level; import javax.swing.JComboBox; import javax.swing.JFileChooser; @@ -48,14 +40,11 @@ import org.openide.util.NbBundle; import org.openide.windows.WindowManager; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; -import org.sleuthkit.autopsy.casemodule.services.FileManager; -import org.sleuthkit.autopsy.casemodule.services.Services; import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.datamodel.ContentUtils; 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.textextraction.SQLiteExtractor; /** * A file content viewer for SQLite database files. @@ -370,41 +359,12 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { "SQLiteViewer.errorMessage.failedToQueryDatabase=The database tables in the file could not be read.", "SQLiteViewer.errorMessage.failedToinitJDBCDriver=The JDBC driver for SQLite could not be loaded.", "# {0} - exception message", "SQLiteViewer.errorMessage.unexpectedError=An unexpected error occurred:\n{0).",}) - private void processSQLiteFile() { - + private void processSQLiteFile() { tablesDropdownList.removeAllItems(); - // Copy the file to temp folder - String tmpDBPathName; try { - tmpDBPathName = Case.getCurrentCaseThrows().getTempDirectory() + File.separator + sqliteDbFile.getName(); - } catch (NoCurrentCaseException ex) { - logger.log(Level.SEVERE, "Current case has been closed", ex); //NON-NLS - MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_errorMessage_noCurrentCase()); - return; - } - - tmpDbFile = new File(tmpDBPathName); - if (! tmpDbFile.exists()) { - try { - ContentUtils.writeToFile(sqliteDbFile, tmpDbFile); - - // Look for any meta files associated with this DB - WAL, SHM, etc. - findAndCopySQLiteMetaFile(sqliteDbFile, sqliteDbFile.getName() + "-wal"); - findAndCopySQLiteMetaFile(sqliteDbFile, sqliteDbFile.getName() + "-shm"); - } catch (IOException | NoCurrentCaseException | TskCoreException ex) { - logger.log(Level.SEVERE, String.format("Failed to create temp copy of DB file '%s' (objId=%d)", sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS - MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_errorMessage_failedToExtractFile()); - return; - } - } - - try { - // Load the SQLite JDBC driver, if necessary. - Class.forName("org.sqlite.JDBC"); //NON-NLS - connection = DriverManager.getConnection("jdbc:sqlite:" + tmpDBPathName); //NON-NLS - - Map dbTablesMap = getTables(); + connection = SQLiteExtractor.getDatabaseConnection(sqliteDbFile); + Map dbTablesMap = SQLiteExtractor.getTableNameAndSchemaPairs(connection); if (dbTablesMap.isEmpty()) { tablesDropdownList.addItem(Bundle.SQLiteViewer_comboBox_noTableEntry()); tablesDropdownList.setEnabled(false); @@ -413,78 +373,36 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { tablesDropdownList.addItem(tableName); }); } + } catch (NoCurrentCaseException ex) { + logger.log(Level.SEVERE, "Current case has been closed", ex); //NON-NLS + MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_errorMessage_noCurrentCase()); + } catch (IOException | TskCoreException ex) { + logger.log(Level.SEVERE, String.format( + "Failed to create temp copy of DB file '%s' (objId=%d)", //NON-NLS + sqliteDbFile.getName(), sqliteDbFile.getId()), ex); + MessageNotifyUtil.Message.error( + Bundle.SQLiteViewer_errorMessage_failedToExtractFile()); } catch (ClassNotFoundException ex) { - logger.log(Level.SEVERE, String.format("Failed to initialize JDBC SQLite '%s' (objId=%d)", sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS - MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_errorMessage_failedToinitJDBCDriver()); + logger.log(Level.SEVERE, String.format( + "Failed to initialize JDBC SQLite '%s' (objId=%d)", //NON-NLS + sqliteDbFile.getName(), sqliteDbFile.getId()), ex); + MessageNotifyUtil.Message.error( + Bundle.SQLiteViewer_errorMessage_failedToinitJDBCDriver()); } catch (SQLException ex) { - logger.log(Level.SEVERE, String.format("Failed to get tables from DB file '%s' (objId=%d)", sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS - MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_errorMessage_failedToQueryDatabase()); + logger.log(Level.SEVERE, String.format( + "Failed to get tables from DB file '%s' (objId=%d)", //NON-NLS + sqliteDbFile.getName(), sqliteDbFile.getId()), ex); + MessageNotifyUtil.Message.error( + Bundle.SQLiteViewer_errorMessage_failedToQueryDatabase()); } } - /** - * Searches for a meta file associated with the give SQLite db If found, - * copies the file to the temp folder - * - * @param sqliteFile - SQLIte db file being processed - * @param metaFileName name of meta file to look for - */ - private void findAndCopySQLiteMetaFile(AbstractFile sqliteFile, String metaFileName) throws NoCurrentCaseException, TskCoreException, IOException { - Case openCase = Case.getCurrentCaseThrows(); - SleuthkitCase sleuthkitCase = openCase.getSleuthkitCase(); - Services services = new Services(sleuthkitCase); - FileManager fileManager = services.getFileManager(); - List metaFiles = fileManager.findFiles(sqliteFile.getDataSource(), metaFileName, sqliteFile.getParent().getName()); - if (metaFiles != null) { - for (AbstractFile metaFile : metaFiles) { - String tmpMetafilePathName = openCase.getTempDirectory() + File.separator + metaFile.getName(); - File tmpMetafile = new File(tmpMetafilePathName); - ContentUtils.writeToFile(metaFile, tmpMetafile); - } - } - } - - /** - * Gets the table names and schemas from the SQLite database file. - * - * @return A mapping of table names to SQL CREATE TABLE statements. - */ - private Map getTables() throws SQLException { - Map dbTablesMap = new TreeMap<>(); - Statement statement = null; - ResultSet resultSet = null; - try { - statement = connection.createStatement(); - resultSet = statement.executeQuery( - "SELECT name, sql FROM sqlite_master " - + " WHERE type= 'table' " - + " ORDER BY name;"); //NON-NLS - while (resultSet.next()) { - String tableName = resultSet.getString("name"); //NON-NLS - String tableSQL = resultSet.getString("sql"); //NON-NLS - dbTablesMap.put(tableName, tableSQL); - } - } finally { - if (null != resultSet) { - resultSet.close(); - } - if (null != statement) { - statement.close(); - } - } - return dbTablesMap; - } - @NbBundle.Messages({"# {0} - tableName", "SQLiteViewer.selectTable.errorText=Error getting row count for table: {0}" }) private void selectTable(String tableName) { - - try (Statement statement = connection.createStatement(); - ResultSet resultSet = statement.executeQuery( - "SELECT count (*) as count FROM " + tableName)) { //NON-NLS{ - - numRows = resultSet.getInt("count"); + try { + numRows = SQLiteExtractor.getTableRowCount(connection, tableName); numEntriesField.setText(numRows + " entries"); currPage = 1; @@ -504,8 +422,11 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { } } catch (SQLException ex) { - logger.log(Level.SEVERE, String.format("Failed to load table %s from DB file '%s' (objId=%d)", tableName, sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS - MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_selectTable_errorText(tableName)); + logger.log(Level.SEVERE, String.format( + "Failed to load table %s from DB file '%s' (objId=%d)", tableName, //NON-NLS + sqliteDbFile.getName(), sqliteDbFile.getId()), ex); + MessageNotifyUtil.Message.error( + Bundle.SQLiteViewer_selectTable_errorText(tableName)); } } @@ -513,48 +434,22 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { "SQLiteViewer.readTable.errorText=Error getting rows for table: {0}"}) private void readTable(String tableName, int startRow, int numRowsToRead) { - try ( - Statement statement = connection.createStatement(); - ResultSet resultSet = statement.executeQuery( - "SELECT * FROM " + tableName - + " LIMIT " + Integer.toString(numRowsToRead) - + " OFFSET " + Integer.toString(startRow - 1))) { - - ArrayList> rows = resultSetToArrayList(resultSet); + try { + List> rows = SQLiteExtractor.getRowsFromTable( + connection, tableName, startRow, numRowsToRead); if (Objects.nonNull(rows)) { selectedTableView.setupTable(rows); } else { selectedTableView.setupTable(Collections.emptyList()); } } 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)); + logger.log(Level.SEVERE, String.format( + "Failed to read table %s from DB file '%s' (objId=%d)", tableName, //NON-NLS + sqliteDbFile.getName(), sqliteDbFile.getId()), ex); + MessageNotifyUtil.Message.error( + Bundle.SQLiteViewer_readTable_errorText(tableName)); } } - - @NbBundle.Messages("SQLiteViewer.BlobNotShown.message=BLOB Data not shown") - private ArrayList> resultSetToArrayList(ResultSet rs) throws SQLException { - ResultSetMetaData metaData = rs.getMetaData(); - int columns = metaData.getColumnCount(); - ArrayList> rowlist = new ArrayList<>(); - while (rs.next()) { - Map row = new LinkedHashMap<>(columns); - for (int i = 1; i <= columns; ++i) { - if (rs.getObject(i) == null) { - row.put(metaData.getColumnName(i), ""); - } else { - if (metaData.getColumnTypeName(i).compareToIgnoreCase("blob") == 0) { - row.put(metaData.getColumnName(i), Bundle.SQLiteViewer_BlobNotShown_message()); - } else { - row.put(metaData.getColumnName(i), rs.getObject(i)); - } - } - } - rowlist.add(row); - } - - return rowlist; - } @NbBundle.Messages({"SQLiteViewer.exportTableToCsv.write.errText=Failed to export table content to csv file.", "SQLiteViewer.exportTableToCsv.FileName=File name: ", @@ -562,60 +457,28 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { }) private void exportTableToCsv(File file) { String tableName = (String) this.tablesDropdownList.getSelectedItem(); - try ( - Statement statement = connection.createStatement(); - ResultSet resultSet = statement.executeQuery("SELECT * FROM " + tableName)) { - List> currentTableRows = resultSetToArrayList(resultSet); + try { + List> currentTableRows = + SQLiteExtractor.getRowsFromTable(connection, tableName); if (Objects.isNull(currentTableRows) || currentTableRows.isEmpty()) { - logger.log(Level.INFO, String.format("The table %s is empty. (objId=%d)", tableName, sqliteDbFile.getId())); //NON-NLS + logger.log(Level.INFO, String.format( + "The table %s is empty. (objId=%d)", tableName, //NON-NLS + sqliteDbFile.getId())); } else { - File csvFile; - String fileName = file.getName(); - if (FilenameUtils.getExtension(fileName).equalsIgnoreCase("csv")) { - csvFile = file; - } else { - csvFile = new File(file.toString() + ".csv"); - } - - try (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()); - } - } + SQLiteExtractor.exportTableToCSV(file, tableName, currentTableRows); } } 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)); + logger.log(Level.SEVERE, String.format( + "Failed to read table %s from DB file '%s' (objId=%d)", //NON-NLS + tableName, sqliteDbFile.getName(), sqliteDbFile.getId()), ex); + 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()); + 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/textextraction/SQLiteExtractor.java b/Core/src/org/sleuthkit/autopsy/textextraction/SQLiteExtractor.java new file mode 100755 index 0000000000..c06a626d59 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/textextraction/SQLiteExtractor.java @@ -0,0 +1,247 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2013-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.textextraction; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.stream.Collectors; +import org.apache.commons.io.FilenameUtils; +import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.casemodule.services.FileManager; +import org.sleuthkit.autopsy.casemodule.services.Services; +import org.sleuthkit.autopsy.datamodel.ContentUtils; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Abstracts the need to open (and close) statements and result sets from the + * JDBC. Classes using the SQLiteExtractor must maintain a reference to the + * Connection object generated in getDatabaseConnection. + */ +public class SQLiteExtractor { + + private SQLiteExtractor(){} + + public static Connection getDatabaseConnection(AbstractFile sqliteDbFile) + throws ClassNotFoundException, SQLException, IOException, + NoCurrentCaseException, TskCoreException { + + String tmpDBPathName = Case.getCurrentCaseThrows().getTempDirectory() + + File.separator + sqliteDbFile.getName(); + + moveDbToTempFile(sqliteDbFile, tmpDBPathName); + + // Load the SQLite JDBC driver, if necessary. + Class.forName("org.sqlite.JDBC"); //NON-NLS + Connection connection = DriverManager.getConnection( + "jdbc:sqlite:" + tmpDBPathName); //NON-NLS + return connection; + } + + /** + * Gets the table names and schemas from the SQLite database file. + * + * @return A mapping of table names to SQL CREATE TABLE statements. + */ + public static Map getTableNameAndSchemaPairs(Connection connection) + throws SQLException { + + Map dbTablesMap = new TreeMap<>(); + + try (Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery( + "SELECT name, sql FROM sqlite_master " //NON-NLS + + " WHERE type= 'table' " //NON-NLS + + " ORDER BY name;")){ //NON-NLS + + while (resultSet.next()) { + String tableName = resultSet.getString("name"); //NON-NLS + String tableSQL = resultSet.getString("sql"); //NON-NLS + dbTablesMap.put(tableName, tableSQL); + } + } + + return dbTablesMap; + } + + public static Integer getTableRowCount(Connection connection, + String tableName) throws SQLException { + try (Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery( + "SELECT count (*) as count FROM " + tableName)){ + return resultSet.getInt("count"); + } + } + + public static List> getRowsFromTable(Connection connection, + String tableName) throws SQLException { + try(Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery( + "SELECT * FROM " + tableName)) { + return resultSetToList(resultSet); + } + } + + @NbBundle.Messages({"# {0} - tableName", + "SQLiteExtractor.readTable.errorText=Error getting rows for table: {0}"}) + public static List> getRowsFromTable(Connection connection, String tableName, + int startRow, int numRowsToRead) throws SQLException{ + + try(Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery( + "SELECT * FROM " + tableName + + " LIMIT " + Integer.toString(numRowsToRead) + + " OFFSET " + Integer.toString(startRow - 1))) { + return resultSetToList(resultSet); + } + } + + @NbBundle.Messages({"SQLiteExtractor.exportTableToCsv.write.errText=Failed to export table content to csv file.", + "SQLiteExtractor.exportTableToCsv.FileName=File name: ", + "SQLiteExtractor.exportTableToCsv.TableName=Table name: " + }) + public static void exportTableToCSV(File file, String tableName, + List> keyValuePairsInRows) throws FileNotFoundException, IOException{ + + File csvFile; + String fileName = file.getName(); + if (FilenameUtils.getExtension(fileName).equalsIgnoreCase("csv")) { + csvFile = file; + } else { + csvFile = new File(file.toString() + ".csv"); + } + + try (FileOutputStream out = new FileOutputStream(csvFile, false)) { + + out.write((Bundle.SQLiteExtractor_exportTableToCsv_FileName() + csvFile.getName() + "\n").getBytes()); + out.write((Bundle.SQLiteExtractor_exportTableToCsv_TableName() + tableName + "\n").getBytes()); + + String header = createColumnHeader(keyValuePairsInRows.get(0)).concat("\n"); + out.write(header.getBytes()); + + for (Map maps : keyValuePairsInRows) { + String row = maps.values() + .stream() + .map(Object::toString) + .collect(Collectors.joining(",")).concat("\n"); + out.write(row.getBytes()); + } + } + } + + public static void exportTableToCSV(File file, String tableName, + ResultSet resultSet) throws SQLException, IOException { + exportTableToCSV(file, tableName, resultSetToList(resultSet)); + } + + /** + * The map holds the column name to value pairs for a particular row. For example, (id, 123) + * is a valid key-value pair in the map. + */ + @NbBundle.Messages("SQLiteExtractor.BlobNotShown.message=BLOB Data not shown") + private static List> resultSetToList(ResultSet rs) throws SQLException { + + ResultSetMetaData metaData = rs.getMetaData(); + int columns = metaData.getColumnCount(); + List> keyValuePairsInRows = new ArrayList<>(); + while (rs.next()) { + Map row = new LinkedHashMap<>(columns); + for (int i = 1; i <= columns; ++i) { + if (rs.getObject(i) == null) { + row.put(metaData.getColumnName(i), ""); + } else { + if (metaData.getColumnTypeName(i).compareToIgnoreCase("blob") == 0) { + row.put(metaData.getColumnName(i), Bundle.SQLiteExtractor_BlobNotShown_message()); + } else { + row.put(metaData.getColumnName(i), rs.getObject(i)); + } + } + } + keyValuePairsInRows.add(row); + } + + return keyValuePairsInRows; + } + + private static String createColumnHeader(Map row) { + + return row.entrySet() + .stream() + .map(Map.Entry::getKey) + .collect(Collectors.joining(",")); + } + + private static void moveDbToTempFile(AbstractFile sqliteDbFile, String tempDbPath) + throws IOException, NoCurrentCaseException, TskCoreException { + + File tmpDbFile = new File(tempDbPath); + if (!tmpDbFile.exists()) { + ContentUtils.writeToFile(sqliteDbFile, tmpDbFile); + + // Look for any meta files associated with this DB - WAL, SHM, etc. + findAndCopySQLiteMetaFile(sqliteDbFile, sqliteDbFile.getName() + "-wal"); + findAndCopySQLiteMetaFile(sqliteDbFile, sqliteDbFile.getName() + "-shm"); + } + } + + /** + * Searches for a meta file associated with the give SQLite db If found, + * copies the file to the temp folder + * + * @param sqliteFile - SQLIte db file being processed + * @param metaFileName name of meta file to look for + */ + private static void findAndCopySQLiteMetaFile(AbstractFile sqliteFile, + String metaFileName) throws NoCurrentCaseException, TskCoreException, IOException { + + Case openCase = Case.getCurrentCaseThrows(); + SleuthkitCase sleuthkitCase = openCase.getSleuthkitCase(); + Services services = new Services(sleuthkitCase); + FileManager fileManager = services.getFileManager(); + + List metaFiles = fileManager.findFiles( + sqliteFile.getDataSource(), metaFileName, + sqliteFile.getParent().getName()); + + if (metaFiles != null) { + for (AbstractFile metaFile : metaFiles) { + String tmpMetafilePathName = openCase.getTempDirectory() + + File.separator + metaFile.getName(); + File tmpMetafile = new File(tmpMetafilePathName); + ContentUtils.writeToFile(metaFile, tmpMetafile); + } + } + } +} From 7c39e8e6671052c8e40c00b990f46ae64b1c2244 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Wed, 1 Aug 2018 16:58:00 -0400 Subject: [PATCH 006/150] Removed copying DB and DB metafile from SQLiteExtractor --- .../autopsy/contentviewers/SQLiteViewer.java | 53 +++++++- .../textextraction/SQLiteExtractor.java | 114 +++++------------- 2 files changed, 81 insertions(+), 86 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java index 6ace716240..aaaf0a368a 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java @@ -40,11 +40,15 @@ import org.openide.util.NbBundle; import org.openide.windows.WindowManager; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.casemodule.services.FileManager; +import org.sleuthkit.autopsy.casemodule.services.Services; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; +import org.sleuthkit.autopsy.datamodel.ContentUtils; import org.sleuthkit.autopsy.textextraction.SQLiteExtractor; +import org.sleuthkit.datamodel.SleuthkitCase; /** * A file content viewer for SQLite database files. @@ -363,8 +367,13 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { tablesDropdownList.removeAllItems(); try { - connection = SQLiteExtractor.getDatabaseConnection(sqliteDbFile); + String tmpDBPathName = Case.getCurrentCaseThrows().getTempDirectory() + + File.separator + sqliteDbFile.getName(); + moveDbToTempFile(sqliteDbFile, tmpDBPathName); + + connection = SQLiteExtractor.getDatabaseConnection(tmpDBPathName); Map dbTablesMap = SQLiteExtractor.getTableNameAndSchemaPairs(connection); + if (dbTablesMap.isEmpty()) { tablesDropdownList.addItem(Bundle.SQLiteViewer_comboBox_noTableEntry()); tablesDropdownList.setEnabled(false); @@ -481,4 +490,46 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { Bundle.SQLiteViewer_exportTableToCsv_write_errText()); } } + + private static void moveDbToTempFile(AbstractFile sqliteDbFile, String tempDbPath) + throws IOException, NoCurrentCaseException, TskCoreException { + + File tmpDbFile = new File(tempDbPath); + if (!tmpDbFile.exists()) { + ContentUtils.writeToFile(sqliteDbFile, tmpDbFile); + + // Look for any meta files associated with this DB - WAL, SHM, etc. + findAndCopySQLiteMetaFile(sqliteDbFile, sqliteDbFile.getName() + "-wal"); + findAndCopySQLiteMetaFile(sqliteDbFile, sqliteDbFile.getName() + "-shm"); + } + } + + /** + * Searches for a meta file associated with the give SQLite db If found, + * copies the file to the temp folder + * + * @param sqliteFile - SQLIte db file being processed + * @param metaFileName name of meta file to look for + */ + private static void findAndCopySQLiteMetaFile(AbstractFile sqliteFile, + String metaFileName) throws NoCurrentCaseException, TskCoreException, IOException { + + Case openCase = Case.getCurrentCaseThrows(); + SleuthkitCase sleuthkitCase = openCase.getSleuthkitCase(); + Services services = new Services(sleuthkitCase); + FileManager fileManager = services.getFileManager(); + + List metaFiles = fileManager.findFiles( + sqliteFile.getDataSource(), metaFileName, + sqliteFile.getParent().getName()); + + if (metaFiles != null) { + for (AbstractFile metaFile : metaFiles) { + String tmpMetafilePathName = openCase.getTempDirectory() + + File.separator + metaFile.getName(); + File tmpMetafile = new File(tmpMetafilePathName); + ContentUtils.writeToFile(metaFile, tmpMetafile); + } + } + } } diff --git a/Core/src/org/sleuthkit/autopsy/textextraction/SQLiteExtractor.java b/Core/src/org/sleuthkit/autopsy/textextraction/SQLiteExtractor.java index c06a626d59..a3966c76c5 100755 --- a/Core/src/org/sleuthkit/autopsy/textextraction/SQLiteExtractor.java +++ b/Core/src/org/sleuthkit/autopsy/textextraction/SQLiteExtractor.java @@ -36,38 +36,23 @@ import java.util.TreeMap; import java.util.stream.Collectors; import org.apache.commons.io.FilenameUtils; import org.openide.util.NbBundle; -import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; -import org.sleuthkit.autopsy.casemodule.services.FileManager; -import org.sleuthkit.autopsy.casemodule.services.Services; -import org.sleuthkit.autopsy.datamodel.ContentUtils; -import org.sleuthkit.datamodel.AbstractFile; -import org.sleuthkit.datamodel.SleuthkitCase; -import org.sleuthkit.datamodel.TskCoreException; /** * Abstracts the need to open (and close) statements and result sets from the * JDBC. Classes using the SQLiteExtractor must maintain a reference to the * Connection object generated in getDatabaseConnection. */ -public class SQLiteExtractor { +public final class SQLiteExtractor { private SQLiteExtractor(){} - public static Connection getDatabaseConnection(AbstractFile sqliteDbFile) - throws ClassNotFoundException, SQLException, IOException, - NoCurrentCaseException, TskCoreException { - - String tmpDBPathName = Case.getCurrentCaseThrows().getTempDirectory() + - File.separator + sqliteDbFile.getName(); - - moveDbToTempFile(sqliteDbFile, tmpDBPathName); + public static Connection getDatabaseConnection(String dbPath) + throws ClassNotFoundException, SQLException { // Load the SQLite JDBC driver, if necessary. Class.forName("org.sqlite.JDBC"); //NON-NLS - Connection connection = DriverManager.getConnection( - "jdbc:sqlite:" + tmpDBPathName); //NON-NLS - return connection; + return DriverManager.getConnection( + "jdbc:sqlite:" + dbPath); //NON-NLS } /** @@ -155,7 +140,8 @@ public class SQLiteExtractor { String row = maps.values() .stream() .map(Object::toString) - .collect(Collectors.joining(",")).concat("\n"); + .collect(Collectors.joining(",")) + .concat("\n"); out.write(row.getBytes()); } } @@ -166,35 +152,6 @@ public class SQLiteExtractor { exportTableToCSV(file, tableName, resultSetToList(resultSet)); } - /** - * The map holds the column name to value pairs for a particular row. For example, (id, 123) - * is a valid key-value pair in the map. - */ - @NbBundle.Messages("SQLiteExtractor.BlobNotShown.message=BLOB Data not shown") - private static List> resultSetToList(ResultSet rs) throws SQLException { - - ResultSetMetaData metaData = rs.getMetaData(); - int columns = metaData.getColumnCount(); - List> keyValuePairsInRows = new ArrayList<>(); - while (rs.next()) { - Map row = new LinkedHashMap<>(columns); - for (int i = 1; i <= columns; ++i) { - if (rs.getObject(i) == null) { - row.put(metaData.getColumnName(i), ""); - } else { - if (metaData.getColumnTypeName(i).compareToIgnoreCase("blob") == 0) { - row.put(metaData.getColumnName(i), Bundle.SQLiteExtractor_BlobNotShown_message()); - } else { - row.put(metaData.getColumnName(i), rs.getObject(i)); - } - } - } - keyValuePairsInRows.add(row); - } - - return keyValuePairsInRows; - } - private static String createColumnHeader(Map row) { return row.entrySet() @@ -203,45 +160,32 @@ public class SQLiteExtractor { .collect(Collectors.joining(",")); } - private static void moveDbToTempFile(AbstractFile sqliteDbFile, String tempDbPath) - throws IOException, NoCurrentCaseException, TskCoreException { - - File tmpDbFile = new File(tempDbPath); - if (!tmpDbFile.exists()) { - ContentUtils.writeToFile(sqliteDbFile, tmpDbFile); - - // Look for any meta files associated with this DB - WAL, SHM, etc. - findAndCopySQLiteMetaFile(sqliteDbFile, sqliteDbFile.getName() + "-wal"); - findAndCopySQLiteMetaFile(sqliteDbFile, sqliteDbFile.getName() + "-shm"); - } - } - /** - * Searches for a meta file associated with the give SQLite db If found, - * copies the file to the temp folder - * - * @param sqliteFile - SQLIte db file being processed - * @param metaFileName name of meta file to look for + * The map holds the column name to value pairs for a particular row. For example, (id, 123) + * is a valid key-value pair in the map. */ - private static void findAndCopySQLiteMetaFile(AbstractFile sqliteFile, - String metaFileName) throws NoCurrentCaseException, TskCoreException, IOException { + @NbBundle.Messages("SQLiteExtractor.BlobNotShown.message=BLOB Data not shown") + private static List> resultSetToList(ResultSet resultSet) throws SQLException { - Case openCase = Case.getCurrentCaseThrows(); - SleuthkitCase sleuthkitCase = openCase.getSleuthkitCase(); - Services services = new Services(sleuthkitCase); - FileManager fileManager = services.getFileManager(); - - List metaFiles = fileManager.findFiles( - sqliteFile.getDataSource(), metaFileName, - sqliteFile.getParent().getName()); - - if (metaFiles != null) { - for (AbstractFile metaFile : metaFiles) { - String tmpMetafilePathName = openCase.getTempDirectory() + - File.separator + metaFile.getName(); - File tmpMetafile = new File(tmpMetafilePathName); - ContentUtils.writeToFile(metaFile, tmpMetafile); + ResultSetMetaData metaData = resultSet.getMetaData(); + int columns = metaData.getColumnCount(); + List> keyValuePairsInRows = new ArrayList<>(); + while (resultSet.next()) { + Map row = new LinkedHashMap<>(columns); + for (int i = 1; i <= columns; ++i) { + if (resultSet.getObject(i) == null) { + row.put(metaData.getColumnName(i), ""); + } else { + if (metaData.getColumnTypeName(i).compareToIgnoreCase("blob") == 0) { + row.put(metaData.getColumnName(i), Bundle.SQLiteExtractor_BlobNotShown_message()); + } else { + row.put(metaData.getColumnName(i), resultSet.getObject(i)); + } + } } + keyValuePairsInRows.add(row); } + + return keyValuePairsInRows; } } From a1c4db14bd55b1c9567f2886a7698a5b7707c1d2 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dgrove" Date: Thu, 2 Aug 2018 10:22:08 -0400 Subject: [PATCH 007/150] Codacy issues resolved. --- .../contentviewer/DataContentViewerOtherCases.java | 1 - .../contentviewer/DataContentViewerOtherCasesTableModel.java | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java index 635605a904..6579b15e08 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java @@ -49,7 +49,6 @@ import javax.swing.JPanel; import javax.swing.filechooser.FileNameExtensionFilter; import javax.swing.table.TableCellRenderer; import javax.swing.table.TableColumn; -import javax.swing.table.TableColumnModel; import org.joda.time.DateTimeZone; import org.joda.time.LocalDateTime; import org.openide.nodes.Node; diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCasesTableModel.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCasesTableModel.java index 9ae79259c2..1f04f1f478 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCasesTableModel.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCasesTableModel.java @@ -172,6 +172,8 @@ public class DataContentViewerOtherCasesTableModel extends AbstractTableModel { case COMMENT: value = nodeData.getComment(); break; + default: // This shouldn't occur! Use default "No data" value. + break; } return value; } From 8aba921666c74b8fb817af405b8c6e6d1849fcda Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Thu, 2 Aug 2018 12:17:48 -0400 Subject: [PATCH 008/150] Functional SevenZipExtractor using standard interface. SevenZipException exception handling during extract vs extractSlow still untested --- .../SevenZipExtractor.java | 371 +++++++++++++----- 1 file changed, 267 insertions(+), 104 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java index ee9415ddca..a959345ca6 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java +++ b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java @@ -25,22 +25,27 @@ import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Paths; import java.util.ArrayList; +import java.util.Arrays; 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.ConcurrentHashMap; import java.util.logging.Level; import net.sf.sevenzipjbinding.ArchiveFormat; import static net.sf.sevenzipjbinding.ArchiveFormat.RAR; +import net.sf.sevenzipjbinding.ExtractAskMode; import net.sf.sevenzipjbinding.ISequentialOutStream; import net.sf.sevenzipjbinding.ISevenZipInArchive; import net.sf.sevenzipjbinding.SevenZip; import net.sf.sevenzipjbinding.SevenZipException; import net.sf.sevenzipjbinding.SevenZipNativeInitializationException; -import net.sf.sevenzipjbinding.simple.ISimpleInArchive; -import net.sf.sevenzipjbinding.simple.ISimpleInArchiveItem; import net.sf.sevenzipjbinding.ExtractOperationResult; +import net.sf.sevenzipjbinding.IArchiveExtractCallback; +import net.sf.sevenzipjbinding.ICryptoGetTextPassword; +import net.sf.sevenzipjbinding.PropID; import org.netbeans.api.progress.ProgressHandle; import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; @@ -167,19 +172,22 @@ class SevenZipExtractor { * * @return true if potential zip bomb, false otherwise */ - private boolean isZipBombArchiveItemCheck(AbstractFile archiveFile, ISimpleInArchiveItem archiveFileItem, ConcurrentHashMap depthMap, String escapedFilePath) { + private boolean isZipBombArchiveItemCheck(AbstractFile archiveFile, ISevenZipInArchive inArchive, int inArchiveItemIndex, ConcurrentHashMap depthMap, String escapedFilePath) { try { - final Long archiveItemSize = archiveFileItem.getSize(); + final Long archiveItemSize = (Long) inArchive.getProperty( + inArchiveItemIndex, PropID.SIZE); //skip the check for small files if (archiveItemSize == null || archiveItemSize < MIN_COMPRESSION_RATIO_SIZE) { return false; } - final Long archiveItemPackedSize = archiveFileItem.getPackedSize(); + final Long archiveItemPackedSize = (Long) inArchive.getProperty( + inArchiveItemIndex, PropID.PACKED_SIZE); if (archiveItemPackedSize == null || archiveItemPackedSize <= 0) { - logger.log(Level.WARNING, "Cannot getting compression ratio, cannot detect if zipbomb: {0}, item: {1}", new Object[]{archiveFile.getName(), archiveFileItem.getPath()}); //NON-NLS + logger.log(Level.WARNING, "Cannot getting compression ratio, cannot detect if zipbomb: {0}, item: {1}", //NON-NLS + new Object[]{archiveFile.getName(), (String) inArchive.getProperty(inArchiveItemIndex, PropID.PATH)}); //NON-NLS return false; } @@ -366,8 +374,9 @@ class SevenZipExtractor { * * @throws SevenZipException */ - private String getPathInArchive(ISimpleInArchiveItem item, int itemNumber, AbstractFile archiveFile) throws SevenZipException { - String pathInArchive = item.getPath(); + private String getPathInArchive(ISevenZipInArchive archive, int inArchiveItemIndex, AbstractFile archiveFile) throws SevenZipException { + String pathInArchive = (String) archive.getProperty( + inArchiveItemIndex, PropID.PATH); if (pathInArchive == null || pathInArchive.isEmpty()) { //some formats (.tar.gz) may not be handled correctly -- file in archive has no name/path @@ -400,7 +409,7 @@ class SevenZipExtractor { } } if (useName == null) { - pathInArchive = "/" + archName + "/" + Integer.toString(itemNumber); + pathInArchive = "/" + archName + "/" + Integer.toString(inArchiveItemIndex); } else { pathInArchive = "/" + useName; } @@ -428,69 +437,6 @@ class SevenZipExtractor { return node == null ? null : archiveFilePath + "/" + node.getFileName(); } - /** - * Unpack an archive item to the disk using a password if specified. - * - * @param item - the archive item to unpack - * @param unpackedNode - the unpackedNode to add derivedInfo to - * @param password - the password for the archive, null if not - * used - * @param freeDiskSpace - the amount of free disk space - * @param uniqueExtractedName - the name of the file to extract the item to - * - * @return unpackedNode - the updated unpackedNode - * - * @throws SevenZipException - */ - private SevenZipExtractor.UnpackedTree.UnpackedNode unpackNode(ISimpleInArchiveItem item, SevenZipExtractor.UnpackedTree.UnpackedNode unpackedNode, String password, long freeDiskSpace, String uniqueExtractedName) throws SevenZipException { - //unpack locally if a file - final String localAbsPath = moduleDirAbsolute + File.separator + uniqueExtractedName; - final String localRelPath = moduleDirRelative + File.separator + uniqueExtractedName; - final Date createTime = item.getCreationTime(); - final Date accessTime = item.getLastAccessTime(); - final Date writeTime = item.getLastWriteTime(); - final long createtime = createTime == null ? 0L : createTime.getTime() / 1000; - final long modtime = writeTime == null ? 0L : writeTime.getTime() / 1000; - final long accesstime = accessTime == null ? 0L : accessTime.getTime() / 1000; - SevenZipExtractor.UnpackStream unpackStream = null; - boolean isDir = item.isFolder(); - if (!isDir) { - try { - // NOTE: item.getSize() may return null in case of certain - // archiving formats. Eg: BZ2 - if (item.getSize() != null) { - unpackStream = new SevenZipExtractor.KnownSizeUnpackStream(localAbsPath, item.getSize()); - } else { - unpackStream = new SevenZipExtractor.UnknownSizeUnpackStream(localAbsPath, freeDiskSpace); - } - ExtractOperationResult result; - if (password == null) { - result = item.extractSlow(unpackStream); - } else { - result = item.extractSlow(unpackStream, password); - } - if (result != ExtractOperationResult.OK) { - logger.log(Level.WARNING, "Extraction of : {0} encountered error {1}", new Object[]{localAbsPath, result}); //NON-NLS - return null; - } - - } catch (SevenZipException e) { - //could be something unexpected with this file, move on - logger.log(Level.WARNING, "Could not extract file from archive: " + localAbsPath, e); //NON-NLS - } finally { - if (unpackStream != null) { - //record derived data in unode, to be traversed later after unpacking the archive - unpackedNode.addDerivedInfo(unpackStream.getSize(), !isDir, - 0L, createtime, accesstime, modtime, localRelPath); - unpackStream.close(); - } - } - } else { // this is a directory, size is always 0 - unpackedNode.addDerivedInfo(0, !isDir, 0L, createtime, accesstime, modtime, localRelPath); - } - return unpackedNode; - } - /** * Unpack the file to local folder and return a list of derived files * @@ -530,6 +476,8 @@ class SevenZipExtractor { List unpackedFiles = Collections.emptyList(); ISevenZipInArchive inArchive = null; + Map archiveDetailsMap = new LinkedHashMap<>(); + SevenZipContentReadStream stream = null; final ProgressHandle progress = ProgressHandle.createHandle(Bundle.EmbeddedFileExtractorIngestModule_ArchiveExtractor_moduleName()); //recursion depth check for zip bomb @@ -590,7 +538,7 @@ class SevenZipExtractor { logger.log(Level.INFO, "Count of items in archive: {0}: {1}", new Object[]{escapedArchiveFilePath, numItems}); //NON-NLS progress.start(numItems); progressStarted = true; - final ISimpleInArchive simpleInArchive = inArchive.getSimpleInterface(); + progress.progress(archiveFile.getName() + ": Analyzing archive metadata and creating local files"); //setup the archive local root folder final String uniqueArchiveFileName = FileUtil.escapeFileName(EmbeddedFileExtractorIngestModule.getUniqueName(archiveFile)); @@ -614,25 +562,22 @@ class SevenZipExtractor { //currently getFreeDiskSpace always returns DISK_FREE_SPACE_UNKNOWN freeDiskSpace = IngestMonitor.DISK_FREE_SPACE_UNKNOWN; } - //unpack and process every item in archive - int itemNumber = 0; - for (ISimpleInArchiveItem item : simpleInArchive.getArchiveItems()) { - String pathInArchive = getPathInArchive(item, itemNumber, archiveFile); + for (int inArchiveItemIndex = 0; inArchiveItemIndex < numItems; inArchiveItemIndex++) { + String pathInArchive = getPathInArchive(inArchive, inArchiveItemIndex, archiveFile); + String archiveItemPath = (String) inArchive.getProperty( + inArchiveItemIndex, PropID.PATH); + Long archiveItemSize = (Long) inArchive.getProperty( + inArchiveItemIndex, PropID.SIZE); - //query for path in db - ++itemNumber; - - //check if possible zip bomb - if (isZipBombArchiveItemCheck(archiveFile, item, depthMap, escapedArchiveFilePath)) { + if (isZipBombArchiveItemCheck(archiveFile, inArchive, inArchiveItemIndex, depthMap, escapedArchiveFilePath)) { unpackSuccessful = false; return unpackSuccessful; } - SevenZipExtractor.UnpackedTree.UnpackedNode unpackedNode = unpackedTree.addNode(pathInArchive); - //update progress bar - progress.progress(archiveFile.getName() + ": " + item.getPath(), processedItems); - final boolean isEncrypted = item.isEncrypted(); + SevenZipExtractor.UnpackedTree.UnpackedNode unpackedNode = unpackedTree.addNode(pathInArchive); + + final boolean isEncrypted = (Boolean) inArchive.getProperty(inArchiveItemIndex, PropID.ENCRYPTED); if (isEncrypted && password == null) { logger.log(Level.WARNING, "Skipping encrypted file in archive: {0}", pathInArchive); //NON-NLS @@ -642,20 +587,21 @@ class SevenZipExtractor { } else { fullEncryption = false; } + // NOTE: item.getSize() may return null in case of certain // archiving formats. Eg: BZ2 //check if unpacking this file will result in out of disk space //this is additional to zip bomb prevention mechanism - if (freeDiskSpace != IngestMonitor.DISK_FREE_SPACE_UNKNOWN && item.getSize() != null && item.getSize() > 0) { //if free space is known and file is not empty. - long newDiskSpace = freeDiskSpace - item.getSize(); + if (freeDiskSpace != IngestMonitor.DISK_FREE_SPACE_UNKNOWN && archiveItemSize != null && archiveItemSize > 0) { //if free space is known and file is not empty. + long newDiskSpace = freeDiskSpace - archiveItemSize; if (newDiskSpace < MIN_FREE_DISK_SPACE) { String msg = NbBundle.getMessage(SevenZipExtractor.class, "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.notEnoughDiskSpace.msg", - escapedArchiveFilePath, item.getPath()); + escapedArchiveFilePath, archiveItemPath); String details = NbBundle.getMessage(SevenZipExtractor.class, "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.notEnoughDiskSpace.details"); services.postMessage(IngestMessage.createErrorMessage(EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details)); - logger.log(Level.INFO, "Skipping archive item due to insufficient disk space: {0}, {1}", new String[]{escapedArchiveFilePath, item.getPath()}); //NON-NLS + logger.log(Level.INFO, "Skipping archive item due to insufficient disk space: {0}, {1}", new String[]{escapedArchiveFilePath, archiveItemPath}); //NON-NLS logger.log(Level.INFO, "Available disk space: {0}", new Object[]{freeDiskSpace}); //NON-NLS unpackSuccessful = false; continue; //skip this file @@ -664,42 +610,61 @@ class SevenZipExtractor { freeDiskSpace = newDiskSpace; } } - final String uniqueExtractedName = FileUtil.escapeFileName(uniqueArchiveFileName + File.separator + (item.getItemIndex() / 1000) + File.separator + item.getItemIndex() + "_" + new File(pathInArchive).getName()); + final String uniqueExtractedName = FileUtil.escapeFileName(uniqueArchiveFileName + File.separator + (inArchiveItemIndex / 1000) + File.separator + inArchiveItemIndex + "_" + new File(pathInArchive).getName()); + final String localAbsPath = moduleDirAbsolute + File.separator + uniqueExtractedName; + final String localRelPath = moduleDirRelative + File.separator + uniqueExtractedName; //create local dirs and empty files before extracted - File localFile = new java.io.File(moduleDirAbsolute + File.separator + uniqueExtractedName); + File localFile = new java.io.File(localAbsPath); //cannot rely on files in top-bottom order if (!localFile.exists()) { try { - if (item.isFolder()) { + if ((Boolean) inArchive.getProperty( + inArchiveItemIndex, PropID.IS_FOLDER)) { localFile.mkdirs(); } else { localFile.getParentFile().mkdirs(); try { localFile.createNewFile(); } catch (IOException e) { - logger.log(Level.SEVERE, "Error creating extracted file: " + localFile.getAbsolutePath(), e); //NON-NLS + logger.log(Level.SEVERE, "Error creating extracted file: " + + localFile.getAbsolutePath(), e); //NON-NLS } } } catch (SecurityException e) { - logger.log(Level.SEVERE, "Error setting up output path for unpacked file: {0}", pathInArchive); //NON-NLS + logger.log(Level.SEVERE, "Error setting up output path for unpacked file: {0}", + pathInArchive); //NON-NLS //TODO consider bail out / msg to the user } } // skip the rest of this loop if we couldn't create the file + //continue will skip details from being added to the map if (localFile.exists() == false) { continue; } - //find this node in the hierarchy, create if neede; - unpackedNode = unpackNode(item, unpackedNode, password, - freeDiskSpace, uniqueExtractedName); - if (unpackedNode == null) { - unpackSuccessful = false; - } - //update units for progress bar - ++processedItems; + //Store archiveItemIndex with local paths and unpackedNode reference. + //Necessary for the extract call back to write the current archive + //file to the correct disk location and to correctly update it's + //corresponding unpackedNode + archiveDetailsMap.put(inArchiveItemIndex, new InArchiveItemDetails( + unpackedNode, localAbsPath, localRelPath)); } + + int[] extractionIndices = getExtractableFilesFromArchiveDetails(archiveDetailsMap); + + StandardIArchiveExtractCallback archiveCallBack + = new StandardIArchiveExtractCallback( + inArchive, archiveFile, progress, + archiveDetailsMap, password, freeDiskSpace); + + //According to the documentation, indices in sorted order are optimal + //for efficiency. Hence, the LinkedHashMap and linear processing of + //inArchiveItemIndex. + inArchive.extract(extractionIndices, false, archiveCallBack); + + unpackSuccessful = unpackSuccessful & archiveCallBack.getSuccessFlag(); + // add them to the DB. We wait until the end so that we have the metadata on all of the // intermediate nodes since the order is not guaranteed try { @@ -799,6 +764,21 @@ class SevenZipExtractor { return unpackSuccessful; } + /** + * Produce a list of archive indices needed for the call to extract, which + * will open the archive and begin unpacking the files. + */ + private int[] getExtractableFilesFromArchiveDetails( + Map archiveDetailsMap) { + + Integer[] wrappedExtractionIndices = archiveDetailsMap.keySet() + .toArray(new Integer[archiveDetailsMap.size()]); + + return Arrays.stream(wrappedExtractionIndices) + .mapToInt(Integer::intValue) + .toArray(); + } + /** * Stream used to unpack the archive to local file */ @@ -933,6 +913,188 @@ class SevenZipExtractor { } } + /** + * Wrapper for necessary details used in StandardIArchiveExtractCallback + */ + private static class InArchiveItemDetails { + + private SevenZipExtractor.UnpackedTree.UnpackedNode unpackedNode; + private String localAbsPath; + private String localRelPath; + + public InArchiveItemDetails( + SevenZipExtractor.UnpackedTree.UnpackedNode unpackedNode, + String localAbsPath, String localRelPath) { + this.unpackedNode = unpackedNode; + this.localAbsPath = localAbsPath; + this.localRelPath = localRelPath; + } + + public SevenZipExtractor.UnpackedTree.UnpackedNode getUnpackedNode() { + return unpackedNode; + } + + public String getLocalAbsPath() { + return localAbsPath; + } + + public String getLocalRelPath() { + return localRelPath; + } + } + + /** + * Call back class used by extract to expand archive files. This is the most + * efficient way to process according to the sevenzip binding documentation. + */ + private static class StandardIArchiveExtractCallback + implements IArchiveExtractCallback, ICryptoGetTextPassword { + + private int inArchiveItemIndex; + private ISevenZipInArchive inArchive; + private SevenZipExtractor.UnpackStream unpackStream = null; + private long freeDiskSpace; + private Map archiveDetailsMap; + private ProgressHandle progressHandle; + private AbstractFile archiveFile; + + private long createTimeInSeconds; + private long modTimeInSeconds; + private long accessTimeInSeconds; + + private boolean skipExtraction; + private String password; + + private boolean unpackSuccessful = true; + + public StandardIArchiveExtractCallback(ISevenZipInArchive inArchive, + AbstractFile archiveFile, ProgressHandle progressHandle, + Map archiveDetailsMap, + String password, long freeDiskSpace) { + + this.inArchive = inArchive; + this.freeDiskSpace = freeDiskSpace; + this.progressHandle = progressHandle; + this.archiveFile = archiveFile; + this.archiveDetailsMap = archiveDetailsMap; + this.password = password; + } + + @Override + public ISequentialOutStream getStream( + int i, ExtractAskMode EAM) throws SevenZipException { + + inArchiveItemIndex = i; + + skipExtraction = (Boolean) inArchive + .getProperty(inArchiveItemIndex, PropID.IS_FOLDER); + if (skipExtraction || EAM != ExtractAskMode.EXTRACT) { + return null; + } + + final Long archiveItemSize = (Long) inArchive.getProperty( + inArchiveItemIndex, PropID.SIZE); + final String localAbsPath = archiveDetailsMap.get( + inArchiveItemIndex).getLocalAbsPath(); + + if (archiveItemSize != null) { + unpackStream = new SevenZipExtractor.KnownSizeUnpackStream( + localAbsPath, archiveItemSize); + } else { + unpackStream = new SevenZipExtractor.UnknownSizeUnpackStream( + localAbsPath, freeDiskSpace); + } + + return unpackStream; + } + + @Override + public void prepareOperation(ExtractAskMode eam) throws SevenZipException { + final Date createTime = (Date) inArchive.getProperty( + inArchiveItemIndex, PropID.CREATION_TIME); + final Date accessTime = (Date) inArchive.getProperty( + inArchiveItemIndex, PropID.LAST_ACCESS_TIME); + final Date writeTime = (Date) inArchive.getProperty( + inArchiveItemIndex, PropID.LAST_WRITE_TIME); + + createTimeInSeconds = createTime == null ? 0L + : createTime.getTime() / 1000; + modTimeInSeconds = writeTime == null ? 0L + : writeTime.getTime() / 1000; + accessTimeInSeconds = accessTime == null ? 0L + : accessTime.getTime() / 1000; + } + + /** + * Pull necessary details from map to appropriately update unpackedNode + * info and provide useful logging messages. + * + * + * @param EOR - ExtractOperationResult + * + * @throws SevenZipException + */ + @Override + public void setOperationResult(ExtractOperationResult EOR) throws SevenZipException { + final SevenZipExtractor.UnpackedTree.UnpackedNode unpackedNode + = archiveDetailsMap.get(inArchiveItemIndex).getUnpackedNode(); + final String localRelPath = archiveDetailsMap.get( + inArchiveItemIndex).getLocalRelPath(); + final String localAbsPath = archiveDetailsMap.get( + inArchiveItemIndex).getLocalAbsPath(); + + progressHandle.progress(archiveFile.getName() + ": " + + (String) inArchive.getProperty(inArchiveItemIndex, PropID.PATH), + inArchiveItemIndex); + + if (skipExtraction) { + unpackedNode.addDerivedInfo(0, + !(Boolean) inArchive.getProperty(inArchiveItemIndex, PropID.IS_FOLDER), + 0L, createTimeInSeconds, accessTimeInSeconds, modTimeInSeconds, + localRelPath); + return; + } + + //TODO - causes unpack to be unsuccessful, implement fix. + if (EOR != ExtractOperationResult.OK) { + logger.log(Level.WARNING, "Extraction of : {0} encountered error {1}", //NON-NLS + new Object[]{localAbsPath, EOR}); + unpackSuccessful = false; + } + + //record derived data in unode, to be traversed later after unpacking the archive + unpackedNode.addDerivedInfo(unpackStream.getSize(), + !(Boolean) inArchive.getProperty(inArchiveItemIndex, PropID.IS_FOLDER), + 0L, createTimeInSeconds, accessTimeInSeconds, modTimeInSeconds, localRelPath); + + unpackStream.close(); + } + + @Override + public void setTotal(long l) throws SevenZipException { + } + + @Override + public void setCompleted(long l) throws SevenZipException { + } + + /** + * Called when opening encrypted archive files. + * + * @return - Password supplied by user + * + * @throws SevenZipException + */ + @Override + public String cryptoGetTextPassword() throws SevenZipException { + return password; + } + + public boolean getSuccessFlag() { + return unpackSuccessful; + } + } + /** * Representation of the files in the archive. Used to track of local tree * file hierarchy, archive depth, and files created to easily and reliably @@ -1290,7 +1452,8 @@ class SevenZipExtractor { } /** - * Set the flag which identifies whether this file has been determined to be a zip bomb to true. + * Set the flag which identifies whether this file has been determined + * to be a zip bomb to true. */ synchronized void flagAsZipBomb() { flaggedAsZipBomb = true; From b1d242ace39b127584869bb13b608b6428a5549c Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Thu, 2 Aug 2018 12:22:43 -0400 Subject: [PATCH 009/150] Fixed //NON-NLS comment to be inline with the logger messages --- .../modules/embeddedfileextractor/SevenZipExtractor.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java index a959345ca6..ca1dd28233 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java +++ b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java @@ -627,12 +627,12 @@ class SevenZipExtractor { try { localFile.createNewFile(); } catch (IOException e) { - logger.log(Level.SEVERE, "Error creating extracted file: " - + localFile.getAbsolutePath(), e); //NON-NLS + logger.log(Level.SEVERE, "Error creating extracted file: "//NON-NLS + + localFile.getAbsolutePath(), e); } } } catch (SecurityException e) { - logger.log(Level.SEVERE, "Error setting up output path for unpacked file: {0}", + logger.log(Level.SEVERE, "Error setting up output path for unpacked file: {0}", //NON-NLS pathInArchive); //NON-NLS //TODO consider bail out / msg to the user } From 74bf4bd272c5cb2d85aa589fd48510853fc4aee6 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Thu, 2 Aug 2018 14:14:24 -0400 Subject: [PATCH 010/150] 3964 - change name of new image to reflect naming schema in 3771 --- Core/build.xml | 2 +- .../src/org/sleuthkit/autopsy/ingest/EmbeddedFileTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/build.xml b/Core/build.xml index 322fe961ff..aa654b0f36 100644 --- a/Core/build.xml +++ b/Core/build.xml @@ -86,7 +86,7 @@ - + diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/ingest/EmbeddedFileTest.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/ingest/EmbeddedFileTest.java index 0bb8144527..7af0a429d8 100755 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/ingest/EmbeddedFileTest.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/ingest/EmbeddedFileTest.java @@ -47,7 +47,7 @@ import org.sleuthkit.datamodel.TskCoreException; public class EmbeddedFileTest extends NbTestCase { private static final String CASE_NAME = "EmbeddedFileTest"; - private final Path IMAGE_PATH = Paths.get(this.getDataDir().toString(), "EmbeddedIM_img2_v1.vhd"); + private final Path IMAGE_PATH = Paths.get(this.getDataDir().toString(), "EmbeddedIM_img1_v2.vhd"); public static final String HASH_VALUE = "098f6bcd4621d373cade4e832627b4f6"; private static final int DEEP_FOLDER_COUNT = 25; private Case openCase; From 0c571ab20ece0fb87b29e7ca7539f7ea59bd2e14 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dgrove" Date: Thu, 2 Aug 2018 15:27:27 -0400 Subject: [PATCH 011/150] Added user-defined path for extraction. --- .../autopsy/directorytree/ExtractAction.java | 66 ++++++++++++++++--- .../directorytree/ExtractUnallocAction.java | 44 ++++++++++++- 2 files changed, 99 insertions(+), 11 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/ExtractAction.java b/Core/src/org/sleuthkit/autopsy/directorytree/ExtractAction.java index 2f45e44331..b0b847d1a6 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/ExtractAction.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/ExtractAction.java @@ -33,7 +33,6 @@ import javax.swing.JOptionPane; import javax.swing.SwingWorker; import org.netbeans.api.progress.ProgressHandle; import org.openide.util.Cancellable; -import org.openide.util.Exceptions; import org.openide.util.NbBundle; import org.openide.util.Utilities; import org.sleuthkit.autopsy.casemodule.Case; @@ -54,6 +53,8 @@ public final class ExtractAction extends AbstractAction { private Logger logger = Logger.getLogger(ExtractAction.class.getName()); + private String userDefinedExportPath; + // This class is a singleton to support multi-selection of nodes, since // org.openide.nodes.NodeOp.findActions(Node[] nodes) will only pick up an Action if every // node in the array returns a reference to the same action object from Node.getActions(boolean). @@ -97,7 +98,7 @@ public final class ExtractAction extends AbstractAction { * @param e * @param selectedFile Selected file */ - @NbBundle.Messages ({"ExtractAction.noOpenCase.errMsg=No open case available."}) + @NbBundle.Messages({"ExtractAction.noOpenCase.errMsg=No open case available."}) private void extractFile(ActionEvent e, AbstractFile selectedFile) { Case openCase; try { @@ -108,10 +109,12 @@ public final class ExtractAction extends AbstractAction { return; } JFileChooser fileChooser = new JFileChooser(); - fileChooser.setCurrentDirectory(new File(openCase.getExportDirectory())); + fileChooser.setCurrentDirectory(new File(getExportDirectory(openCase))); // If there is an attribute name, change the ":". Otherwise the extracted file will be hidden fileChooser.setSelectedFile(new File(FileUtil.escapeFileName(selectedFile.getName()))); if (fileChooser.showSaveDialog((Component) e.getSource()) == JFileChooser.APPROVE_OPTION) { + updateExportDirectory(fileChooser.getSelectedFile().getParent(), openCase); + ArrayList fileExtractionTasks = new ArrayList<>(); fileExtractionTasks.add(new FileExtractionTask(selectedFile, fileChooser.getSelectedFile())); runExtractionTasks(e, fileExtractionTasks); @@ -135,7 +138,7 @@ public final class ExtractAction extends AbstractAction { } JFileChooser folderChooser = new JFileChooser(); folderChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); - folderChooser.setCurrentDirectory(new File(openCase.getExportDirectory())); + folderChooser.setCurrentDirectory(new File(getExportDirectory(openCase))); if (folderChooser.showSaveDialog((Component) e.getSource()) == JFileChooser.APPROVE_OPTION) { File destinationFolder = folderChooser.getSelectedFile(); if (!destinationFolder.exists()) { @@ -148,12 +151,16 @@ public final class ExtractAction extends AbstractAction { return; } } + updateExportDirectory(destinationFolder.getPath(), openCase); - /* get the unique set of files from the list. A user once reported extraction taking - * days because it was extracting the same PST file 20k times. They selected 20k - * email messages in the tree and chose to extract them. */ + /* + * get the unique set of files from the list. A user once reported + * extraction taking days because it was extracting the same PST + * file 20k times. They selected 20k email messages in the tree and + * chose to extract them. + */ Set uniqueFiles = new HashSet<>(selectedFiles); - + // make a task for each file ArrayList fileExtractionTasks = new ArrayList<>(); for (AbstractFile source : uniqueFiles) { @@ -164,6 +171,45 @@ public final class ExtractAction extends AbstractAction { } } + /** + * Get the export directory path. + * + * @param openCase The current case. + * + * @return The export directory path. + */ + private String getExportDirectory(Case openCase) { + String caseExportPath = openCase.getExportDirectory(); + + if (userDefinedExportPath == null) { + return caseExportPath; + } + + File file = new File(userDefinedExportPath); + if (file.exists() == false || file.isDirectory() == false) { + return caseExportPath; + } + + return userDefinedExportPath; + } + + /** + * Update the default export directory. If the directory path matches the + * case export directory, then the directory used will always match the + * export directory of any given case. Otherwise, the path last used will be + * saved. + * + * @param exportPath The export path. + * @param openCase The current case. + */ + private void updateExportDirectory(String exportPath, Case openCase) { + if (exportPath.equalsIgnoreCase(openCase.getExportDirectory())) { + userDefinedExportPath = null; + } else { + userDefinedExportPath = exportPath; + } + } + private void runExtractionTasks(ActionEvent e, ArrayList fileExtractionTasks) { // verify all of the sources and destinations are OK @@ -177,8 +223,8 @@ public final class ExtractAction extends AbstractAction { } /* - * This code assumes that each destination is unique. We previously satisfied - * that by adding the unique ID. + * This code assumes that each destination is unique. We previously + * satisfied that by adding the unique ID. */ if (task.destination.exists()) { if (JOptionPane.showConfirmDialog((Component) e.getSource(), diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/ExtractUnallocAction.java b/Core/src/org/sleuthkit/autopsy/directorytree/ExtractUnallocAction.java index b0729d24c6..83fe882e45 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/ExtractUnallocAction.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/ExtractUnallocAction.java @@ -66,6 +66,7 @@ final class ExtractUnallocAction extends AbstractAction { private final List filesToExtract = new ArrayList<>(); private static final Set volumesInProgress = new HashSet<>(); private static final Set imagesInProgress = new HashSet<>(); + private static String userDefinedExportPath; private long currentImage = 0L; private final boolean isImage; @@ -145,7 +146,7 @@ final class ExtractUnallocAction extends AbstractAction { } }; - fileChooser.setCurrentDirectory(new File(openCase.getExportDirectory())); + fileChooser.setCurrentDirectory(new File(getExportDirectory(openCase))); fileChooser.setDialogTitle( NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.dlgTitle.selectDirToSaveTo.msg")); fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); @@ -153,6 +154,9 @@ final class ExtractUnallocAction extends AbstractAction { int returnValue = fileChooser.showSaveDialog((Component) e.getSource()); if (returnValue == JFileChooser.APPROVE_OPTION) { String destination = fileChooser.getSelectedFile().getPath(); + + updateExportDirectory(destination, openCase); + for (OutputFileData outputFileData : filesToExtract) { outputFileData.setPath(destination); @@ -214,7 +218,45 @@ final class ExtractUnallocAction extends AbstractAction { } } } + } + + /** + * Get the export directory path. + * + * @param openCase The current case. + * + * @return The export directory path. + */ + private String getExportDirectory(Case openCase) { + String caseExportPath = openCase.getExportDirectory(); + if (userDefinedExportPath == null) { + return caseExportPath; + } + + File file = new File(userDefinedExportPath); + if (file.exists() == false || file.isDirectory() == false) { + return caseExportPath; + } + + return userDefinedExportPath; + } + + /** + * Update the default export directory. If the directory path matches the + * case export directory, then the directory used will always match the + * export directory of any given case. Otherwise, the path last used will be + * saved. + * + * @param exportPath The export path. + * @param openCase The current case. + */ + private void updateExportDirectory(String exportPath, Case openCase) { + if (exportPath.equalsIgnoreCase(openCase.getExportDirectory())) { + userDefinedExportPath = null; + } else { + userDefinedExportPath = exportPath; + } } /** From 21fc9a67a952300e4b2bd556ce42b175540da706 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Thu, 2 Aug 2018 16:08:21 -0400 Subject: [PATCH 012/150] 3964 adjust failure messages for zip bomb test to give more context --- .../autopsy/ingest/EmbeddedFileTest.java | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/ingest/EmbeddedFileTest.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/ingest/EmbeddedFileTest.java index 7af0a429d8..995aac1818 100755 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/ingest/EmbeddedFileTest.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/ingest/EmbeddedFileTest.java @@ -96,43 +96,45 @@ public class EmbeddedFileTest extends NbTestCase { public void testEncryptionAndZipBomb() { try { - List results = openCase.getSleuthkitCase().findAllFilesWhere("name LIKE '%%'"); - String protectedName1 = "password_protected.zip"; - String protectedName2 = "level1_protected.zip"; - String protectedName3 = "42.zip"; - String depthZipBomb = "DepthTriggerZipBomb.zip"; - String ratioZipBomb = "RatioTriggerZipBomb.zip"; + List results = openCase.getSleuthkitCase().findAllFilesWhere("name LIKE '%%'"); + final String zipBombSetName = "Possible Zip Bomb"; + final String protectedName1 = "password_protected.zip"; + final String protectedName2 = "level1_protected.zip"; + final String protectedName3 = "42.zip"; + final String depthZipBomb = "DepthTriggerZipBomb.zip"; + final String ratioZipBomb = "RatioTriggerZipBomb.zip"; int zipBombs = 0; - assertEquals(2221, results.size()); + assertEquals("The number of files in the test image has changed", 2221, results.size()); int passwdProtectedZips = 0; for (AbstractFile file : results) { //.zip file has artifact TSK_ENCRYPTION_DETECTED if (file.getName().equalsIgnoreCase(protectedName1) || file.getName().equalsIgnoreCase(protectedName2) || file.getName().equalsIgnoreCase(protectedName3)){ ArrayList artifacts = file.getAllArtifacts(); - assertEquals(1, artifacts.size()); + assertEquals("Password protected zip file " + file.getName() + " has incorrect number of artifacts", 1, artifacts.size()); for (BlackboardArtifact artifact : artifacts) { - assertEquals(artifact.getArtifactTypeID(), BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED.getTypeID()); + assertEquals("Artifact for password protected zip file " + file.getName() + " has incorrect type ID", artifact.getArtifactTypeID(), BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED.getTypeID()); passwdProtectedZips++; } } else if (file.getName().equalsIgnoreCase(depthZipBomb) || file.getName().equalsIgnoreCase(ratioZipBomb)){ ArrayList artifacts = file.getAllArtifacts(); - assertEquals(1, artifacts.size()); + assertEquals("Zip bomb " + file.getName() + " has incorrect number of artifacts", 1, artifacts.size()); for (BlackboardArtifact artifact : artifacts) { - assertEquals(artifact.getArtifactTypeID(), BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID()); + assertEquals("Artifact for Zip bomb " + file.getName() + " has incorrect type ID", artifact.getArtifactTypeID(), BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID()); BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME)); - assertNotNull("Possible Zip Bomb", attribute); + assertNotNull("No attribute found for artifact on zip bomb " + file.getName(), attribute); + assertEquals("Interesting artifact on file, " + file.getName() + ", does not reflect it being a zip bomb", zipBombSetName, attribute.getDisplayString()); zipBombs++; } } else {//No other files have artifact defined - assertEquals(0, file.getAllArtifacts().size()); + assertEquals("Unexpected file, " + file.getName() + ", has artifacts", 0, file.getAllArtifacts().size()); } } //Make sure 3 password protected zip files have been tested: password_protected.zip, level1_protected.zip and 42.zip that we download for bomb testing. - assertEquals(3, passwdProtectedZips); + assertEquals("Unexpected number of artifacts reflecting password protected zip files found", 3, passwdProtectedZips); //Make sure 2 zip bomb files have been tested: DepthTriggerZipBomb.zip and RatioTriggerZipBomb.zip. - assertEquals(2, zipBombs); + assertEquals("Unexpected number of artifacts reflecting zip bombs found", 2, zipBombs); } catch (TskCoreException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex); From f5ac4a8635595bf97b7db08633af41dcea99e3e8 Mon Sep 17 00:00:00 2001 From: rishwanth1995 Date: Thu, 2 Aug 2018 16:24:12 -0400 Subject: [PATCH 013/150] live autopsy folder warning --- .../autopsy/casemodule/Bundle.properties | 2 + .../casemodule/NewCaseVisualPanel1.java | 11 ++++- .../autopsy/core/AutopsyOptionProcessor.java | 48 +++++++++++++++++++ .../autopsy/core/RuntimeProperties.java | 15 ++++++ .../autopsy/coreutils/PathValidator.java | 14 ++++-- .../CreateLiveTriageDriveAction.java | 2 +- 6 files changed, 87 insertions(+), 5 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/core/AutopsyOptionProcessor.java diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties index 9c04508c1f..aa8efe676d 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties @@ -147,6 +147,8 @@ UpdateRecentCases.menuItem.clearRecentCases.text=Clear Recent Cases UpdateRecentCases.menuItem.empty=-Empty- AddImageWizardIngestConfigPanel.CANCEL_BUTTON.text=Cancel NewCaseVisualPanel1.CaseFolderOnCDriveError.text=Warning: Path to multi-user case folder is on \"C:\" drive +NewCaseVisualPanel1.CaseFolderOnInternalDriveWindowsError.text=Warning Path to case folder is on \"C:\" drive. Create case folder in external drive +NewCaseVisualPanel1.CaseFolderOnInternalDriveLinuxError.text=Warning Path to case folder is not in /media or /mnt drive. Create folder in external drive CollaborationMonitor.addingDataSourceStatus.msg={0} adding data source CollaborationMonitor.analyzingDataSourceStatus.msg={0} analyzing {1} MissingImageDialog.lbWarning.text= diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java b/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java index 6959883de1..2257573cb7 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java @@ -29,6 +29,7 @@ import javax.swing.event.DocumentListener; import org.sleuthkit.autopsy.casemodule.Case.CaseType; import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.coreutils.PathValidator; +import org.sleuthkit.autopsy.coreutils.PlatformUtil; /** * The JPanel for the first page of the new case wizard. @@ -156,9 +157,17 @@ final class NewCaseVisualPanel1 extends JPanel implements DocumentListener { caseParentDirWarningLabel.setText(NbBundle.getMessage(this.getClass(), "NewCaseVisualPanel1.CaseFolderOnCDriveError.text")); } + /** + * Check the base case directory if it can persist data and show a + * warning if it is a wrong choice + */ if(!PathValidator.isCasedataPersistable(parentDir)){ caseParentDirWarningLabel.setVisible(true); - caseParentDirWarningLabel.setText("Please make the case directory in a mounted drive to save case progress"); + if(PlatformUtil.isWindowsOS()){ + caseParentDirWarningLabel.setText(NbBundle.getMessage(this.getClass(), "NewCaseVisualPanel1.CaseFolderOnInternalDriveWindowsError.text" )); + } else if(System.getProperty("os.name").toLowerCase().contains("nux")) { + caseParentDirWarningLabel.setText(NbBundle.getMessage(this.getClass(), "NewCaseVisualPanel1.CaseFolderOnInternalDriveLinuxError.text")); + } } /** diff --git a/Core/src/org/sleuthkit/autopsy/core/AutopsyOptionProcessor.java b/Core/src/org/sleuthkit/autopsy/core/AutopsyOptionProcessor.java new file mode 100644 index 0000000000..20882da110 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/core/AutopsyOptionProcessor.java @@ -0,0 +1,48 @@ +/* + * 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. + */ +package org.sleuthkit.autopsy.core; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.netbeans.api.sendopts.CommandException; +import org.netbeans.spi.sendopts.Env; +import org.netbeans.spi.sendopts.Option; +import org.netbeans.spi.sendopts.OptionProcessor; +import org.openide.util.lookup.ServiceProvider; + +/** + * This class can be used to add command line options to Autopsy + * + */ +@ServiceProvider(service=OptionProcessor.class) +public class AutopsyOptionProcessor extends OptionProcessor { + + private static final Logger logger = Logger.getLogger(AutopsyOptionProcessor.class.getName()); + private final Option liveAutopsyOption = Option.withoutArgument('l', "liveAutopsy"); + + + @Override + protected Set + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java index bf1765e310..1cf1dbf7ca 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java @@ -80,8 +80,10 @@ public final class CommonAttributePanel extends javax.swing.JDialog { if (CommonAttributePanel.isEamDbAvailable()) { this.setupCases(); + this.enablePercentageOptions(); } else { this.disableIntercaseSearch(); + this.disablePercentageOptions(); } } @@ -157,21 +159,27 @@ public final class CommonAttributePanel extends javax.swing.JDialog { filterByDocuments = true; } } + + int percentageThreshold = 0; + if(CommonAttributePanel.this.percentageThresholdCheck.isSelected()){ + String percentageThresholdAsString = CommonAttributePanel.this.percentageThreshold.getText(); + percentageThreshold = Integer.parseInt(percentageThresholdAsString); + } if (CommonAttributePanel.this.interCaseRadio.isSelected()) { if (caseId == InterCasePanel.NO_CASE_SELECTED) { - builder = new AllInterCaseCommonAttributeSearcher(intraCasePanel.getDataSourceMap(), filterByMedia, filterByDocuments); + builder = new AllInterCaseCommonAttributeSearcher(intraCasePanel.getDataSourceMap(), filterByMedia, filterByDocuments, percentageThreshold); } else { - builder = new SingleInterCaseCommonAttributeSearcher(caseId, intraCasePanel.getDataSourceMap(), filterByMedia, filterByDocuments); + builder = new SingleInterCaseCommonAttributeSearcher(caseId, intraCasePanel.getDataSourceMap(), filterByMedia, filterByDocuments, percentageThreshold); } } else { if (dataSourceId == CommonAttributePanel.NO_DATA_SOURCE_SELECTED) { - builder = new AllIntraCaseCommonAttributeSearcher(intraCasePanel.getDataSourceMap(), filterByMedia, filterByDocuments); + builder = new AllIntraCaseCommonAttributeSearcher(intraCasePanel.getDataSourceMap(), filterByMedia, filterByDocuments, percentageThreshold); setTitleForAllDataSources(); } else { - builder = new SingleIntraCaseCommonAttributeSearcher(dataSourceId, intraCasePanel.getDataSourceMap(), filterByMedia, filterByDocuments); + builder = new SingleIntraCaseCommonAttributeSearcher(dataSourceId, intraCasePanel.getDataSourceMap(), filterByMedia, filterByDocuments, percentageThreshold); setTitleForSingleSource(dataSourceId); } @@ -405,9 +413,11 @@ public final class CommonAttributePanel extends javax.swing.JDialog { layoutPanel = new java.awt.Panel(); intraCasePanel = new org.sleuthkit.autopsy.commonfilesearch.IntraCasePanel(); interCasePanel = new org.sleuthkit.autopsy.commonfilesearch.InterCasePanel(); + percentageThresholdCheck = new javax.swing.JCheckBox(); + percentageThreshold = new javax.swing.JTextField(); + jLabel1 = new javax.swing.JLabel(); setMinimumSize(new java.awt.Dimension(412, 350)); - setPreferredSize(new java.awt.Dimension(412, 350)); setResizable(false); addWindowListener(new java.awt.event.WindowAdapter() { public void windowClosed(java.awt.event.WindowEvent evt) { @@ -503,42 +513,53 @@ public final class CommonAttributePanel extends javax.swing.JDialog { layoutPanel.add(intraCasePanel, "card3"); layoutPanel.add(interCasePanel, "card2"); + org.openide.awt.Mnemonics.setLocalizedText(percentageThresholdCheck, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.percentageThresholdCheck.text_1")); // NOI18N + percentageThresholdCheck.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + percentageThresholdCheckActionPerformed(evt); + } + }); + + percentageThreshold.setText(org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.percentageThreshold.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.jLabel1.text_1")); // NOI18N + javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1); jPanel1.setLayout(jPanel1Layout); jPanel1Layout.setHorizontalGroup( jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(jPanel1Layout.createSequentialGroup() + .addContainerGap() .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(commonFilesSearchLabel2) + .addComponent(intraCaseRadio) + .addComponent(interCaseRadio) + .addComponent(commonFilesSearchLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(categoriesLabel) + .addComponent(selectedFileCategoriesButton) + .addGroup(jPanel1Layout.createSequentialGroup() + .addGap(29, 29, 29) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(documentsCheckbox) + .addComponent(pictureVideoCheckbox))) + .addComponent(allFileCategoriesRadioButton) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup() + .addGap(10, 10, 10) + .addComponent(layoutPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addGroup(jPanel1Layout.createSequentialGroup() + .addComponent(percentageThresholdCheck) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(percentageThreshold, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jLabel1)) .addGroup(jPanel1Layout.createSequentialGroup() - .addContainerGap() .addComponent(searchButton) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(cancelButton) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(errorText)) - .addGroup(jPanel1Layout.createSequentialGroup() - .addContainerGap() - .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(commonFilesSearchLabel2) - .addComponent(intraCaseRadio) - .addComponent(interCaseRadio) - .addComponent(commonFilesSearchLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(categoriesLabel) - .addComponent(selectedFileCategoriesButton))) - .addGroup(jPanel1Layout.createSequentialGroup() - .addGap(35, 35, 35) - .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(documentsCheckbox) - .addComponent(pictureVideoCheckbox))) - .addGroup(jPanel1Layout.createSequentialGroup() - .addContainerGap() - .addComponent(allFileCategoriesRadioButton))) + .addComponent(errorText))) .addContainerGap()) - .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(jPanel1Layout.createSequentialGroup() - .addGap(20, 20, 20) - .addComponent(layoutPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(10, 10, 10))) ); jPanel1Layout.setVerticalGroup( jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -551,7 +572,9 @@ public final class CommonAttributePanel extends javax.swing.JDialog { .addComponent(intraCaseRadio) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(interCaseRadio) - .addGap(79, 79, 79) + .addGap(2, 2, 2) + .addComponent(layoutPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(categoriesLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(selectedFileCategoriesButton) @@ -562,16 +585,16 @@ public final class CommonAttributePanel extends javax.swing.JDialog { .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(allFileCategoriesRadioButton) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(percentageThresholdCheck) + .addComponent(percentageThreshold, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(jLabel1)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(searchButton) .addComponent(cancelButton) .addComponent(errorText)) - .addContainerGap()) - .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup() - .addGap(98, 98, 98) - .addComponent(layoutPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(180, 180, 180))) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); getContentPane().add(jPanel1, java.awt.BorderLayout.CENTER); @@ -628,6 +651,14 @@ public final class CommonAttributePanel extends javax.swing.JDialog { SwingUtilities.windowForComponent(this).dispose(); }//GEN-LAST:event_formWindowClosed + private void percentageThresholdCheckActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_percentageThresholdCheckActionPerformed + if(this.percentageThresholdCheck.isSelected()){ + + } else { + + } + }//GEN-LAST:event_percentageThresholdCheckActionPerformed + public void handleInterCaseSearchCriteriaChanged() { if (this.areInterCaseSearchCriteriaMet()) { this.searchButton.setEnabled(true); @@ -685,8 +716,11 @@ public final class CommonAttributePanel extends javax.swing.JDialog { private javax.swing.ButtonGroup interIntraButtonGroup; private org.sleuthkit.autopsy.commonfilesearch.IntraCasePanel intraCasePanel; private javax.swing.JRadioButton intraCaseRadio; + private javax.swing.JLabel jLabel1; private javax.swing.JPanel jPanel1; private java.awt.Panel layoutPanel; + private javax.swing.JTextField percentageThreshold; + private javax.swing.JCheckBox percentageThresholdCheck; private javax.swing.JCheckBox pictureVideoCheckbox; private javax.swing.JButton searchButton; private javax.swing.JRadioButton selectedFileCategoriesButton; @@ -717,4 +751,15 @@ public final class CommonAttributePanel extends javax.swing.JDialog { this.errorText.setText(this.interCasePanel.getErrorMessage()); this.errorText.setVisible(true); } + + private void enablePercentageOptions() { + this.percentageThreshold.setEnabled(true); + this.percentageThresholdCheck.setEnabled(true); + this.percentageThresholdCheck.setEnabled(true); + } + + private void disablePercentageOptions() { + this.percentageThreshold.setEnabled(false); + this.percentageThresholdCheck.setEnabled(false); + } } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResultRootNode.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResultRootNode.java index 8e33401af7..ef36025d8d 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResultRootNode.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResultRootNode.java @@ -19,10 +19,14 @@ package org.sleuthkit.autopsy.commonfilesearch; import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; import org.openide.nodes.ChildFactory; import org.openide.nodes.Children; import org.openide.nodes.Node; +import org.openide.util.Exceptions; import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; import org.sleuthkit.autopsy.datamodel.DisplayableItemNode; import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor; @@ -65,6 +69,8 @@ final public class CommonAttributeSearchResultRootNode extends DisplayableItemNo */ static class InstanceCountNodeFactory extends ChildFactory{ + private static final Logger LOGGER = Logger.getLogger(InstanceCountNodeFactory.class.getName()); + private final CommonAttributeSearchResults searchResults; /** @@ -78,7 +84,11 @@ final public class CommonAttributeSearchResultRootNode extends DisplayableItemNo @Override protected boolean createKeys(List list) { - list.addAll(this.searchResults.getMetadata().keySet()); + try { + list.addAll(this.searchResults.getMetadata().keySet()); + } catch (EamDbException ex) { + LOGGER.log(Level.SEVERE, "Unable to create keys.", ex); + } return true; } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResults.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResults.java index e404790cb7..35b64e17b2 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResults.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResults.java @@ -36,6 +36,8 @@ final public class CommonAttributeSearchResults { // maps instance count to list of attribute values. private final Map> instanceCountToAttributeValues; + private final int percentageThreshold; + /** * Create a values object which can be handed off to the node factories. * @@ -44,6 +46,18 @@ final public class CommonAttributeSearchResults { */ CommonAttributeSearchResults(Map> metadata) { this.instanceCountToAttributeValues = metadata; + this.percentageThreshold = 0; + } + + /** + * Create a values object which can be handed off to the node factories. + * + * @param values list of CommonAttributeValue indexed by size of + * CommonAttributeValue + */ + CommonAttributeSearchResults(Map> metadata, int percentageThreshold) { + this.instanceCountToAttributeValues = metadata; + this.percentageThreshold = percentageThreshold; } /** @@ -52,7 +66,7 @@ final public class CommonAttributeSearchResults { * This is a convenience method - you can also iterate over * getValues(). * - * @param isntanceCound key + * @param instanceCount key * @return list of values which represent matches */ List getAttributeValuesForInstanceCount(Integer instanceCount) { @@ -66,8 +80,12 @@ final public class CommonAttributeSearchResults { * * @return map of sizes of children to list of matches */ - public Map> getMetadata() { - return Collections.unmodifiableMap(this.instanceCountToAttributeValues); + public Map> getMetadata() throws EamDbException { + if(this.percentageThreshold == 0){ + return Collections.unmodifiableMap(this.instanceCountToAttributeValues); + } else { + return this.getMetadata(this.percentageThreshold); + } } /** @@ -80,7 +98,7 @@ final public class CommonAttributeSearchResults { * * @return */ - public Map> getMetadata(int minimumPercentageThreshold) throws EamDbException { + private Map> getMetadata(int minimumPercentageThreshold) throws EamDbException { CorrelationAttribute.Type fileAttributeType = CorrelationAttribute .getDefaultCorrelationTypes() diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCaseCommonAttributeSearcher.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCaseCommonAttributeSearcher.java index 2c745b5271..d1bff54c99 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCaseCommonAttributeSearcher.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCaseCommonAttributeSearcher.java @@ -43,8 +43,8 @@ abstract class InterCaseCommonAttributeSearcher extends AbstractCommonAttributeS * * @throws EamDbException */ - InterCaseCommonAttributeSearcher(Map dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) throws EamDbException { - super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType); + InterCaseCommonAttributeSearcher(Map dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType, int percentageThreshold) throws EamDbException { + super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType, percentageThreshold); dbManager = EamDb.getInstance(); } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/IntraCaseCommonAttributeSearcher.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/IntraCaseCommonAttributeSearcher.java index 172108d5ef..bd26648196 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/IntraCaseCommonAttributeSearcher.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/IntraCaseCommonAttributeSearcher.java @@ -55,8 +55,8 @@ public abstract class IntraCaseCommonAttributeSearcher extends AbstractCommonAtt * @param filterByDocMimeType match only on files whose mime types can be * broadly categorized as document types */ - IntraCaseCommonAttributeSearcher(Map dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) { - super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType); + IntraCaseCommonAttributeSearcher(Map dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType, int percentageThreshold) { + super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType, percentageThreshold); } /** diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/SingleInterCaseCommonAttributeSearcher.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/SingleInterCaseCommonAttributeSearcher.java index 6c06da8b38..9289bd8f90 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/SingleInterCaseCommonAttributeSearcher.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/SingleInterCaseCommonAttributeSearcher.java @@ -44,8 +44,8 @@ public class SingleInterCaseCommonAttributeSearcher extends InterCaseCommonAttri * @param filterByDocMimeType * @throws EamDbException */ - public SingleInterCaseCommonAttributeSearcher(int correlationCaseId, Map dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) throws EamDbException { - super(dataSourceIdMap,filterByMediaMimeType, filterByDocMimeType); + public SingleInterCaseCommonAttributeSearcher(int correlationCaseId, Map dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType, int percentageThreshold) throws EamDbException { + super(dataSourceIdMap,filterByMediaMimeType, filterByDocMimeType, percentageThreshold); this.corrleationCaseId = correlationCaseId; this.correlationCaseName = ""; diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/SingleIntraCaseCommonAttributeSearcher.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/SingleIntraCaseCommonAttributeSearcher.java index 02bd480093..f28f0d0bf0 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/SingleIntraCaseCommonAttributeSearcher.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/SingleIntraCaseCommonAttributeSearcher.java @@ -41,8 +41,8 @@ final public class SingleIntraCaseCommonAttributeSearcher extends IntraCaseCommo * @param filterByDocMimeType match only on files whose mime types can be * broadly categorized as document types */ - public SingleIntraCaseCommonAttributeSearcher(Long dataSourceId, Map dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) { - super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType); + public SingleIntraCaseCommonAttributeSearcher(Long dataSourceId, Map dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType, int percentageThreshold) { + super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType, percentageThreshold); this.selectedDataSourceId = dataSourceId; this.dataSourceName = dataSourceIdMap.get(this.selectedDataSourceId); } From 2f105eaf2d1b91510684ac4007dcff6eafd1b151 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Wed, 15 Aug 2018 10:08:42 -0400 Subject: [PATCH 084/150] Add back a deprecated PathValidater.isValid method for public API --- .../autopsy/coreutils/PathValidator.java | 60 +++++++++++++------ 1 file changed, 42 insertions(+), 18 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/coreutils/PathValidator.java b/Core/src/org/sleuthkit/autopsy/coreutils/PathValidator.java index 71c8ec292c..e3b091174a 100644 --- a/Core/src/org/sleuthkit/autopsy/coreutils/PathValidator.java +++ b/Core/src/org/sleuthkit/autopsy/coreutils/PathValidator.java @@ -1,15 +1,15 @@ /* * 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"); * 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. @@ -32,6 +32,14 @@ public final class PathValidator { private static final Pattern driveLetterPattern = Pattern.compile("^[Cc]:.*$"); private static final Pattern unixMediaDrivePattern = Pattern.compile("^\\/(media|mnt)\\/.*$"); + /** + * Checks if the provided path is valid given the case type. + * + * @param path - the path to validate + * @param caseType - the type of case which the path is being validated for + * + * @return - boolean true for valid path, false for invalid path + */ public static boolean isValidForMultiUserCase(String path, Case.CaseType caseType) { if (caseType == Case.CaseType.MULTI_USER_CASE) { @@ -41,45 +49,45 @@ public final class PathValidator { } } else { // single user case - no validation needed - } + } return true; } - + public static boolean isValidForRunningOnTarget(String path) { - if(checkForLiveAutopsy()) { - if(PlatformUtil.isWindowsOS()) { - if(pathOnCDrive(path)){ + if (checkForLiveAutopsy()) { + if (PlatformUtil.isWindowsOS()) { + if (pathOnCDrive(path)) { return false; } - }else if(System.getProperty("os.name").toLowerCase().contains("nux") && !pathIsMedia(path)){ + } else if (System.getProperty("os.name").toLowerCase().contains("nux") && !pathIsMedia(path)) { return false; } } return true; } - + /** * Checks whether Autopsy is running from the external disk - * + * * @return true if Autopsy is running from external USB or CD */ private static boolean checkForLiveAutopsy() { return RuntimeProperties.isRunningInTarget(); } - + /** * Checks whether a file path contains "/mnt" or "/media" - * + * * @param filePath Input file absolute path - * - * @return true if path matches the pattern, false otherwise + * + * @return true if path matches the pattern, false otherwise */ private static boolean pathIsMedia(String filePath) { Matcher matcher = unixMediaDrivePattern.matcher(filePath); return matcher.find(); } - + /** * Checks whether a file path contains drive letter defined by pattern. * @@ -91,4 +99,20 @@ public final class PathValidator { Matcher m = driveLetterPattern.matcher(filePath); return m.find(); } + + /** + * Checks if the provided path is valid given the case type. + * + * @param path - the path to validate + * @param caseType - the type of case which the path is being validated for + * + * @return - boolean true for valid path, false for invalid path + * + * @deprecated - PathValidator.isValidForMultiUserCase directly replaces + * PathValidator.isValid + */ + @Deprecated + public static boolean isValid(String path, Case.CaseType caseType) { + return isValidForMultiUserCase(path, caseType); + } } From 9bb103359b25dd2f2cadb415e780afb43ea7edae Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Wed, 15 Aug 2018 13:07:53 -0400 Subject: [PATCH 085/150] Added xlsx support (XML model) --- .../autopsy/sqlitereader/ExcelReader.java | 62 ++++++++----------- .../sqlitereader/FileReaderFactory.java | 5 +- 2 files changed, 30 insertions(+), 37 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/sqlitereader/ExcelReader.java b/Core/src/org/sleuthkit/autopsy/sqlitereader/ExcelReader.java index d9c1a2f2c2..c67d620c9c 100755 --- a/Core/src/org/sleuthkit/autopsy/sqlitereader/ExcelReader.java +++ b/Core/src/org/sleuthkit/autopsy/sqlitereader/ExcelReader.java @@ -34,51 +34,46 @@ import org.apache.poi.ss.usermodel.DateUtil; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; -import org.openide.util.NbBundle; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.ingest.IngestServices; import org.sleuthkit.datamodel.AbstractFile; + /** - * Reads Excel files and returns results in a list collection. + * + * @author dsmyda */ -@NbBundle.Messages({ - "ExcelReader.ReadExcelFiles.moduleName=ExcelReader" -}) -public class ExcelReader extends AbstractReader { +public class ExcelReader extends AbstractReader { - private Workbook xlsWorkbook; + private Workbook workbook; private final IngestServices services = IngestServices.getInstance(); private final Logger logger = services.getLogger(ExcelReader.class.getName()); - - /** - * - * @param file - * @param localDiskPath - * @throws org.sleuthkit.autopsy.sqlitereader.AbstractReader.FileReaderInitException - */ - public ExcelReader(AbstractFile file, String localDiskPath) + private String XLSXMimeType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; + private String XLSMimeType = "application/vnd.ms-excel"; + + public ExcelReader(AbstractFile file, String localDiskPath, String mimeType) throws FileReaderInitException { super(file, localDiskPath); - try { - getWorkbookFromLocalDisk(localDiskPath); + this.workbook = createWorkbook(localDiskPath, mimeType); } catch (IOException ex) { throw new FileReaderInitException(ex); } } - /** - * Opens an apache poi workbook instance on the file contents copied from the - * abstract file. - * - * @param localDiskPath Location of the file contents on local disk - * @throws IOException Error opening file at local disk path - */ - private void getWorkbookFromLocalDisk(String localDiskPath) throws IOException{ - xlsWorkbook = new HSSFWorkbook(new FileInputStream(new File(localDiskPath))); + private Workbook createWorkbook(String localDiskPath, String mimeType) throws + IOException, FileReaderInitException { + if(mimeType.equals(XLSMimeType)) { + return new HSSFWorkbook(new FileInputStream(new File(localDiskPath))); + } else if(mimeType.equals(XLSXMimeType)) { + return new XSSFWorkbook(new FileInputStream(new File(localDiskPath))); + } else { + throw new FileReaderInitException(String.format("Excel reader for mime " + + "type [%s] is not supported", mimeType)); + } } - + /** * Returns the number of rows in a given excel table (aka sheet). * @@ -88,7 +83,7 @@ public class ExcelReader extends AbstractReader { */ @Override public Integer getRowCountFromTable(String tableName) throws FileReaderException { - return xlsWorkbook.getSheet(tableName).getLastRowNum(); + return workbook.getSheet(tableName).getLastRowNum(); } /** @@ -101,7 +96,7 @@ public class ExcelReader extends AbstractReader { @Override public List> getRowsFromTable(String tableName) throws FileReaderException { List> rowContents = new ArrayList<>(); - Iterator iterator = xlsWorkbook.getSheet(tableName).rowIterator(); + Iterator iterator = workbook.getSheet(tableName).rowIterator(); //Consume header if(iterator.hasNext()) { //Consume header @@ -171,7 +166,7 @@ public class ExcelReader extends AbstractReader { @Override public Map getTableSchemas() throws FileReaderException { Map tableSchemas = new HashMap<>(); - for(Sheet sheet : xlsWorkbook) { + for(Sheet sheet : workbook) { Row header = sheet.getRow(0); if(header != null) { String headerStringFormat = StringUtils.join(header.cellIterator(), ", "); @@ -183,17 +178,14 @@ public class ExcelReader extends AbstractReader { return tableSchemas; } - /** - * - */ @Override public void close() { try { - xlsWorkbook.close(); + workbook.close(); } catch (IOException ex) { //Non-essential exception, user has no need for the connection //object at this stage so closing details are not important logger.log(Level.WARNING, "Could not close excel file input stream", ex); } } -} \ No newline at end of file +} diff --git a/Core/src/org/sleuthkit/autopsy/sqlitereader/FileReaderFactory.java b/Core/src/org/sleuthkit/autopsy/sqlitereader/FileReaderFactory.java index a650bc3b0f..af9d657277 100755 --- a/Core/src/org/sleuthkit/autopsy/sqlitereader/FileReaderFactory.java +++ b/Core/src/org/sleuthkit/autopsy/sqlitereader/FileReaderFactory.java @@ -36,11 +36,12 @@ public final class FileReaderFactory { */ public static AbstractReader createReader(String mimeType, AbstractFile file, String localDiskPath) throws FileReaderInitException { - switch(mimeType) { + switch (mimeType) { case "application/x-sqlite3": return new SQLiteReader(file, localDiskPath); case "application/vnd.ms-excel": - return new ExcelReader(file, localDiskPath); + case "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": + return new ExcelReader(file, localDiskPath, mimeType); default: throw new FileReaderInitException(String.format("Reader for mime " + "type [%s] is not supported", mimeType)); From 91aa55838b218b661f1ee9fd1e225ab88180e335 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Wed, 15 Aug 2018 14:31:49 -0400 Subject: [PATCH 086/150] Added streaming verison of apache poi for xlsx files --- .../org/sleuthkit/autopsy/sqlitereader/ExcelReader.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/sqlitereader/ExcelReader.java b/Core/src/org/sleuthkit/autopsy/sqlitereader/ExcelReader.java index c67d620c9c..7ddcc7aca8 100755 --- a/Core/src/org/sleuthkit/autopsy/sqlitereader/ExcelReader.java +++ b/Core/src/org/sleuthkit/autopsy/sqlitereader/ExcelReader.java @@ -34,6 +34,7 @@ import org.apache.poi.ss.usermodel.DateUtil; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.xssf.streaming.SXSSFWorkbook; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.ingest.IngestServices; @@ -67,7 +68,7 @@ public class ExcelReader extends AbstractReader { if(mimeType.equals(XLSMimeType)) { return new HSSFWorkbook(new FileInputStream(new File(localDiskPath))); } else if(mimeType.equals(XLSXMimeType)) { - return new XSSFWorkbook(new FileInputStream(new File(localDiskPath))); + return new SXSSFWorkbook(new XSSFWorkbook(new FileInputStream(new File(localDiskPath)))); } else { throw new FileReaderInitException(String.format("Excel reader for mime " + "type [%s] is not supported", mimeType)); @@ -97,11 +98,6 @@ public class ExcelReader extends AbstractReader { public List> getRowsFromTable(String tableName) throws FileReaderException { List> rowContents = new ArrayList<>(); Iterator iterator = workbook.getSheet(tableName).rowIterator(); - //Consume header - if(iterator.hasNext()) { - //Consume header - iterator.next(); - } while(iterator.hasNext()) { Map contents = new HashMap<>(); From 5570aa70037f92f58fc92db6066f6f8e8bb2c60e Mon Sep 17 00:00:00 2001 From: esaunders Date: Wed, 15 Aug 2018 17:45:23 -0400 Subject: [PATCH 087/150] Check extensions first in thumbnailSupported(). --- .../autopsy/coreutils/ImageUtils.java | 31 +++++++++++++++++-- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/coreutils/ImageUtils.java b/Core/src/org/sleuthkit/autopsy/coreutils/ImageUtils.java index e856b25418..0f7fb6b576 100644 --- a/Core/src/org/sleuthkit/autopsy/coreutils/ImageUtils.java +++ b/Core/src/org/sleuthkit/autopsy/coreutils/ImageUtils.java @@ -205,6 +205,18 @@ public class ImageUtils { } AbstractFile file = (AbstractFile) content; + /** + * Before taking on the potentially costly task of calculating the MIME + * type that can happen in isMediaThumbnailSupported() below, let's + * first see if the file extension is in the set of supported media file + * extensions. + */ + List supportedExtensions = new ArrayList<>(SUPPORTED_IMAGE_EXTENSIONS); + supportedExtensions.addAll(VideoUtils.getSupportedVideoExtensions()); + if (isSupportedMediaExtension(file, supportedExtensions)) { + return true; + } + return VideoUtils.isVideoThumbnailSupported(file) || isImageThumbnailSupported(file); } @@ -258,9 +270,7 @@ public class ImageUtils { return false; } - String extension = file.getNameExtension(); - - if (StringUtils.isNotBlank(extension) && supportedExtension.contains(extension)) { + if (isSupportedMediaExtension(file, supportedExtension)) { return true; } else { try { @@ -276,6 +286,21 @@ public class ImageUtils { } } + /** + * Does the given file have an extension in the given list of supported + * extensions. + * + * @param file + * @param supportedExtensions + * + * @return + */ + static boolean isSupportedMediaExtension(final AbstractFile file, final List supportedExtensions) { + String extension = file.getNameExtension(); + + return (StringUtils.isNotBlank(extension) && supportedExtensions.contains(extension)); + } + /** * //TODO: AUT-2057 this FileTypeDetector needs to be recreated when the * user adds new user defined file types. From a3eb28f1dc6be25d1c6ce1a8efd1af1ca79c8497 Mon Sep 17 00:00:00 2001 From: Andrew Ziehl Date: Wed, 15 Aug 2018 16:38:18 -0700 Subject: [PATCH 088/150] update tests for instance refactor. --- .../CorrelationAttributeInstance.java | 6 +- .../datamodel/CentralRepoDatamodelTest.java | 240 ++++++++---------- 2 files changed, 117 insertions(+), 129 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java index 2ceb644a6b..718437286c 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java @@ -91,7 +91,7 @@ public class CorrelationAttributeInstance implements Serializable { } CorrelationAttributeInstance( - Type type, + Type type, String value, int instanceId, CorrelationCase eamCase, @@ -103,6 +103,10 @@ public class CorrelationAttributeInstance implements Serializable { if (filePath == null) { throw new EamDbException("file path is null"); } + + if (value == null) { + throw new EamDbException("md5 value is null"); + } this.correlationType = type; this.correlationValue = value; 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 3d02f3ec49..9321559d1f 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 @@ -155,9 +155,9 @@ public class CentralRepoDatamodelTest extends TestCase { org2 = EamDb.getInstance().newOrganization(org2); // Store the file type object for later use - fileType = EamDb.getInstance().getCorrelationTypeById(CorrelationAttribute.FILES_TYPE_ID); + fileType = EamDb.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.FILES_TYPE_ID); assertTrue("getCorrelationTypeById(FILES_TYPE_ID) returned null", fileType != null); - usbDeviceType = EamDb.getInstance().getCorrelationTypeById(CorrelationAttribute.USBID_TYPE_ID); + usbDeviceType = EamDb.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.USBID_TYPE_ID); assertTrue("getCorrelationTypeById(USBID_TYPE_ID) returned null", usbDeviceType != null); } catch (EamDbException ex) { @@ -220,12 +220,13 @@ public class CentralRepoDatamodelTest extends TestCase { // Add two instances with notable status try { - CorrelationAttribute attr = new CorrelationAttribute(fileType, notableHashInBothCases); - attr.addInstance(new CorrelationAttributeInstance(case1, dataSource1fromCase1, "path1", - "", TskData.FileKnown.BAD)); - attr.addInstance(new CorrelationAttributeInstance(case2, dataSource1fromCase2, "path2", - "", TskData.FileKnown.BAD)); - EamDb.getInstance().addArtifactInstance(attr); + CorrelationAttributeInstance attr1 = new CorrelationAttributeInstance(notableHashInBothCases, fileType, case1, dataSource1fromCase1, "path1", + "", TskData.FileKnown.BAD); + EamDb.getInstance().addArtifactInstance(attr1); + CorrelationAttributeInstance attr2 = new CorrelationAttributeInstance(notableHashInBothCases, fileType, case2, dataSource1fromCase2, "path2", + "", TskData.FileKnown.BAD); + + EamDb.getInstance().addArtifactInstance(attr2); List attrs = EamDb.getInstance().getArtifactInstancesByTypeValue(fileType, notableHashInBothCases); assertTrue("getArtifactInstancesByTypeValue returned " + attrs.size() + " values - expected 2", attrs.size() == 2); @@ -239,12 +240,15 @@ public class CentralRepoDatamodelTest extends TestCase { // Add two instances with one notable, one known try { - CorrelationAttribute attr = new CorrelationAttribute(fileType, notableHashInOneCaseKnownOther); - attr.addInstance(new CorrelationAttributeInstance(case1, dataSource1fromCase1, "path3", - "", TskData.FileKnown.BAD)); - attr.addInstance(new CorrelationAttributeInstance(case2, dataSource1fromCase2, "path4", - "", TskData.FileKnown.KNOWN)); - EamDb.getInstance().addArtifactInstance(attr); + CorrelationAttributeInstance attr1 = new CorrelationAttributeInstance(notableHashInOneCaseKnownOther, fileType, case1, dataSource1fromCase1, "path3", + "", TskData.FileKnown.BAD); + + EamDb.getInstance().addArtifactInstance(attr1); + + CorrelationAttributeInstance attr2 = new CorrelationAttributeInstance(notableHashInOneCaseKnownOther, fileType, case2, dataSource1fromCase2, "path4", + "", TskData.FileKnown.KNOWN); + + EamDb.getInstance().addArtifactInstance(attr2); List attrs = EamDb.getInstance().getArtifactInstancesByTypeValue(fileType, notableHashInOneCaseKnownOther); assertTrue("getArtifactInstancesByTypeValue returned " + attrs.size() + " values - expected 2", attrs.size() == 2); @@ -264,9 +268,9 @@ public class CentralRepoDatamodelTest extends TestCase { // Add an artifact and then update its status try { - CorrelationAttribute attr = new CorrelationAttribute(fileType, hashToChangeToNotable); - attr.addInstance(new CorrelationAttributeInstance(case1, dataSource1fromCase2, "path5", - "", TskData.FileKnown.KNOWN)); + CorrelationAttributeInstance attr = new CorrelationAttributeInstance(hashToChangeToNotable, fileType, case1, dataSource1fromCase2, "path5", + "", TskData.FileKnown.KNOWN); + EamDb.getInstance().addArtifactInstance(attr); EamDb.getInstance().setAttributeInstanceKnownStatus(attr, TskData.FileKnown.BAD); @@ -280,14 +284,15 @@ public class CentralRepoDatamodelTest extends TestCase { } // Try to update artifact with two CorrelationAttributeInstance instances + // TODO, this seems like it is no longer a valid test. try { - CorrelationAttribute attr = new CorrelationAttribute(fileType, "badHash"); - attr.addInstance(new CorrelationAttributeInstance(case1, dataSource1fromCase1, "badPath", - "", TskData.FileKnown.KNOWN)); - attr.addInstance(new CorrelationAttributeInstance(case1, dataSource1fromCase2, "badPath", - "", TskData.FileKnown.KNOWN)); - - EamDb.getInstance().setAttributeInstanceKnownStatus(attr, TskData.FileKnown.BAD); + CorrelationAttributeInstance attr1 = new CorrelationAttributeInstance("badHash", fileType, case1, dataSource1fromCase1, "badPath", + "", TskData.FileKnown.KNOWN); + CorrelationAttributeInstance attr2 = new CorrelationAttributeInstance("badHash", fileType, case1, dataSource1fromCase2, "badPath", + "", TskData.FileKnown.KNOWN); + + EamDb.getInstance().setAttributeInstanceKnownStatus(attr1, TskData.FileKnown.BAD); + EamDb.getInstance().setAttributeInstanceKnownStatus(attr2, TskData.FileKnown.BAD); Assert.fail("setArtifactInstanceKnownStatus failed to throw exception for multiple Correlation Attribute Instances"); } catch (EamDbException ex) { // This is the expected behavior @@ -303,9 +308,8 @@ public class CentralRepoDatamodelTest extends TestCase { // Try to update artifact with null known status try { - CorrelationAttribute attr = new CorrelationAttribute(fileType, "badHash"); - attr.addInstance(new CorrelationAttributeInstance(case1, dataSource1fromCase1, "badPath", - "", TskData.FileKnown.KNOWN)); + CorrelationAttributeInstance attr = new CorrelationAttributeInstance("badHash", fileType, case1, dataSource1fromCase1, "badPath", + "", TskData.FileKnown.KNOWN); EamDb.getInstance().setAttributeInstanceKnownStatus(attr, null); Assert.fail("setArtifactInstanceKnownStatus failed to throw exception for null known status"); @@ -315,9 +319,8 @@ public class CentralRepoDatamodelTest extends TestCase { // Try to update artifact with null case try { - CorrelationAttribute attr = new CorrelationAttribute(fileType, "badHash"); - attr.addInstance(new CorrelationAttributeInstance(null, dataSource1fromCase1, "badPath", - "", TskData.FileKnown.KNOWN)); + CorrelationAttributeInstance attr = new CorrelationAttributeInstance("badHash", fileType, null, dataSource1fromCase1, "badPath", + "", TskData.FileKnown.KNOWN); EamDb.getInstance().setAttributeInstanceKnownStatus(attr, TskData.FileKnown.BAD); Assert.fail("setArtifactInstanceKnownStatus failed to throw exception for null case"); @@ -327,9 +330,8 @@ public class CentralRepoDatamodelTest extends TestCase { // Try to update artifact with null data source try { - CorrelationAttribute attr = new CorrelationAttribute(fileType, "badHash"); - attr.addInstance(new CorrelationAttributeInstance(case1, null, "badPath", - "", TskData.FileKnown.KNOWN)); + CorrelationAttributeInstance attr = new CorrelationAttributeInstance("badHash", fileType, case1, null, "badPath", + "", TskData.FileKnown.KNOWN); EamDb.getInstance().setAttributeInstanceKnownStatus(attr, TskData.FileKnown.BAD); Assert.fail("setArtifactInstanceKnownStatus failed to throw exception for null case"); @@ -473,18 +475,17 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("getCountArtifactInstancesByCaseDataSource returned non-zero count", originalArtifactCount == 0); // Create the first list, which will have (bulkThreshold / 2) entries - List list1 = new ArrayList<>(); + List list1 = new ArrayList<>(); for (int i = 0; i < DEFAULT_BULK_THRESHOLD / 2; i++) { String value = "bulkInsertValue1_" + String.valueOf(i); String path = "C:\\bulkInsertPath1\\file" + String.valueOf(i); - CorrelationAttribute attr = new CorrelationAttribute(fileType, value); - attr.addInstance(new CorrelationAttributeInstance(case1, dataSource1fromCase1, path)); + CorrelationAttributeInstance attr = new CorrelationAttributeInstance(value, fileType, case1, dataSource1fromCase1, path); list1.add(attr); } // Queue up the current list. There should not be enough to trigger the insert - for (CorrelationAttribute attr : list1) { + for (CorrelationAttributeInstance attr : list1) { EamDb.getInstance().addAttributeInstanceBulk(attr); } @@ -493,18 +494,17 @@ public class CentralRepoDatamodelTest extends TestCase { originalArtifactCount == EamDb.getInstance().getCountArtifactInstancesByCaseDataSource(case1.getCaseUUID(), dataSource1fromCase1.getDeviceID())); // Make a second list with length equal to bulkThreshold - List list2 = new ArrayList<>(); + List list2 = new ArrayList<>(); for (int i = 0; i < DEFAULT_BULK_THRESHOLD; i++) { String value = "bulkInsertValue2_" + String.valueOf(i); String path = "C:\\bulkInsertPath2\\file" + String.valueOf(i); - CorrelationAttribute attr = new CorrelationAttribute(fileType, value); - attr.addInstance(new CorrelationAttributeInstance(case1, dataSource1fromCase1, path)); + CorrelationAttributeInstance attr = new CorrelationAttributeInstance(value, fileType, case1, dataSource1fromCase1, path); list2.add(attr); } // Queue up the current list. This will trigger an insert partway through - for (CorrelationAttribute attr : list2) { + for (CorrelationAttributeInstance attr : list2) { EamDb.getInstance().addAttributeInstanceBulk(attr); } @@ -525,7 +525,7 @@ public class CentralRepoDatamodelTest extends TestCase { // Test preparing artifact with null type try { - CorrelationAttribute attr = new CorrelationAttribute(null, "value"); + CorrelationAttributeInstance attr = new CorrelationAttributeInstance(null, "value"); EamDb.getInstance().addAttributeInstanceBulk(attr); Assert.fail("prepareBulkArtifact failed to throw exception for null type"); } catch (EamDbException ex) { @@ -534,8 +534,7 @@ public class CentralRepoDatamodelTest extends TestCase { // Test preparing artifact with null case try { - CorrelationAttribute attr = new CorrelationAttribute(fileType, "value"); - attr.addInstance(new CorrelationAttributeInstance(null, dataSource1fromCase1, "path")); + CorrelationAttributeInstance attr = new CorrelationAttributeInstance("value", fileType, null, dataSource1fromCase1, "path"); EamDb.getInstance().addAttributeInstanceBulk(attr); EamDb.getInstance().commitAttrtibuteInstancesBulk(); Assert.fail("bulkInsertArtifacts failed to throw exception for null case"); @@ -545,8 +544,7 @@ public class CentralRepoDatamodelTest extends TestCase { // Test preparing artifact with null data source try { - CorrelationAttribute attr = new CorrelationAttribute(fileType, "value"); - attr.addInstance(new CorrelationAttributeInstance(case1, null, "path")); + CorrelationAttributeInstance attr = new CorrelationAttributeInstance("value", fileType, case1, null, "path"); EamDb.getInstance().addAttributeInstanceBulk(attr); EamDb.getInstance().commitAttrtibuteInstancesBulk(); Assert.fail("prepareBulkArtifact failed to throw exception for null data source"); @@ -556,9 +554,9 @@ public class CentralRepoDatamodelTest extends TestCase { // Test preparing artifact with null path // CorrelationAttributeInstance will throw an exception + // TODO, still valid? try { - CorrelationAttribute attr = new CorrelationAttribute(fileType, "value"); - attr.addInstance(new CorrelationAttributeInstance(case1, dataSource1fromCase1, null)); + CorrelationAttributeInstance attr = new CorrelationAttributeInstance("value", fileType, case1, dataSource1fromCase1, null); Assert.fail("CorrelationAttributeInstance failed to throw exception for null path"); } catch (EamDbException ex) { // This is the expected behavior @@ -566,8 +564,7 @@ public class CentralRepoDatamodelTest extends TestCase { // Test preparing artifact with null known status try { - CorrelationAttribute attr = new CorrelationAttribute(fileType, "value"); - attr.addInstance(new CorrelationAttributeInstance(case1, dataSource1fromCase1, "path", "comment", null)); + CorrelationAttributeInstance attr = new CorrelationAttributeInstance("value", fileType, case1, dataSource1fromCase1, "path", "comment", null); EamDb.getInstance().addAttributeInstanceBulk(attr); EamDb.getInstance().commitAttrtibuteInstancesBulk(); Assert.fail("prepareBulkArtifact failed to throw exception for null known status"); @@ -653,7 +650,7 @@ public class CentralRepoDatamodelTest extends TestCase { // Store the email type CorrelationAttributeInstance.Type emailType; try { - emailType = EamDb.getInstance().getCorrelationTypeById(CorrelationAttribute.EMAIL_TYPE_ID); + emailType = EamDb.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.EMAIL_TYPE_ID); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex); @@ -662,9 +659,7 @@ public class CentralRepoDatamodelTest extends TestCase { // Test adding attribute with one instance try { - CorrelationAttribute attr = new CorrelationAttribute(fileType, onlyInDataSource3Hash); - CorrelationAttributeInstance inst = new CorrelationAttributeInstance(case2, dataSource1fromCase2, onlyInDataSource3Path); - attr.addInstance(inst); + CorrelationAttributeInstance attr = new CorrelationAttributeInstance(onlyInDataSource3Hash, fileType, case2, dataSource1fromCase2, onlyInDataSource3Path); EamDb.getInstance().addArtifactInstance(attr); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); @@ -673,14 +668,13 @@ public class CentralRepoDatamodelTest extends TestCase { // Test adding attribute with an instance in each data source try { - CorrelationAttribute attr = new CorrelationAttribute(fileType, inAllDataSourcesHash); - CorrelationAttributeInstance inst1 = new CorrelationAttributeInstance(case1, dataSource1fromCase1, inAllDataSourcesPath); - attr.addInstance(inst1); - CorrelationAttributeInstance inst2 = new CorrelationAttributeInstance(case1, dataSource2fromCase1, inAllDataSourcesPath); - attr.addInstance(inst2); - CorrelationAttributeInstance inst3 = new CorrelationAttributeInstance(case2, dataSource1fromCase2, inAllDataSourcesPath); - attr.addInstance(inst3); - EamDb.getInstance().addArtifactInstance(attr); + CorrelationAttributeInstance attr1 = new CorrelationAttributeInstance(inAllDataSourcesHash, fileType, case1, dataSource1fromCase1, inAllDataSourcesPath); + EamDb.getInstance().addArtifactInstance(attr1); + CorrelationAttributeInstance attr2 = new CorrelationAttributeInstance(inAllDataSourcesHash, fileType, case1, dataSource2fromCase1, inAllDataSourcesPath); + EamDb.getInstance().addArtifactInstance(attr2); + CorrelationAttributeInstance attr3 = new CorrelationAttributeInstance(inAllDataSourcesHash, fileType, case2, dataSource1fromCase2, inAllDataSourcesPath); + EamDb.getInstance().addArtifactInstance(attr3); + } catch (EamDbException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex); @@ -688,12 +682,11 @@ public class CentralRepoDatamodelTest extends TestCase { // Test adding attribute with two instances in one data source try { - CorrelationAttribute attr = new CorrelationAttribute(fileType, inDataSource1twiceHash); - CorrelationAttributeInstance inst1 = new CorrelationAttributeInstance(case1, dataSource1fromCase1, inDataSource1twicePath1); - attr.addInstance(inst1); - CorrelationAttributeInstance inst2 = new CorrelationAttributeInstance(case1, dataSource1fromCase1, inDataSource1twicePath2); - attr.addInstance(inst2); - EamDb.getInstance().addArtifactInstance(attr); + CorrelationAttributeInstance attr1 = new CorrelationAttributeInstance(inDataSource1twiceHash, fileType, case1, dataSource1fromCase1, inDataSource1twicePath1); + EamDb.getInstance().addArtifactInstance(attr1); + CorrelationAttributeInstance attr2 = new CorrelationAttributeInstance(inDataSource1twiceHash, fileType, case1, dataSource1fromCase1, inDataSource1twicePath2); + EamDb.getInstance().addArtifactInstance(attr2); + } catch (EamDbException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex); @@ -702,9 +695,7 @@ public class CentralRepoDatamodelTest extends TestCase { // Test adding the other types // Test adding an email artifact try { - CorrelationAttribute attr = new CorrelationAttribute(emailType, emailValue); - CorrelationAttributeInstance inst = new CorrelationAttributeInstance(case1, dataSource1fromCase1, emailPath); - attr.addInstance(inst); + CorrelationAttributeInstance attr = new CorrelationAttributeInstance(emailValue, emailType, case1, dataSource1fromCase1, emailPath); EamDb.getInstance().addArtifactInstance(attr); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); @@ -713,10 +704,11 @@ public class CentralRepoDatamodelTest extends TestCase { // Test adding a phone artifact try { - CorrelationAttribute attr = new CorrelationAttribute(EamDb.getInstance().getCorrelationTypeById(CorrelationAttribute.PHONE_TYPE_ID), - phoneValue); - CorrelationAttributeInstance inst = new CorrelationAttributeInstance(case1, dataSource1fromCase1, phonePath); - attr.addInstance(inst); + CorrelationAttributeInstance attr = new CorrelationAttributeInstance( + phoneValue, + EamDb.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.PHONE_TYPE_ID), + case1, dataSource1fromCase1, phonePath); + EamDb.getInstance().addArtifactInstance(attr); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); @@ -725,10 +717,10 @@ public class CentralRepoDatamodelTest extends TestCase { // Test adding a domain artifact try { - CorrelationAttribute attr = new CorrelationAttribute(EamDb.getInstance().getCorrelationTypeById(CorrelationAttribute.DOMAIN_TYPE_ID), - domainValue); - CorrelationAttributeInstance inst = new CorrelationAttributeInstance(case1, dataSource1fromCase1, domainPath); - attr.addInstance(inst); + CorrelationAttributeInstance attr = new CorrelationAttributeInstance( + domainValue, + EamDb.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.DOMAIN_TYPE_ID), + case1, dataSource1fromCase1, domainPath); EamDb.getInstance().addArtifactInstance(attr); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); @@ -737,10 +729,11 @@ public class CentralRepoDatamodelTest extends TestCase { // Test adding a device ID artifact try { - CorrelationAttribute attr = new CorrelationAttribute(EamDb.getInstance().getCorrelationTypeById(CorrelationAttribute.USBID_TYPE_ID), - devIdValue); - CorrelationAttributeInstance inst = new CorrelationAttributeInstance(case1, dataSource1fromCase1, devIdPath); - attr.addInstance(inst); + CorrelationAttributeInstance attr = new CorrelationAttributeInstance( + devIdValue, + EamDb.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.USBID_TYPE_ID), + case1, dataSource1fromCase1, devIdPath); + EamDb.getInstance().addArtifactInstance(attr); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); @@ -749,9 +742,9 @@ public class CentralRepoDatamodelTest extends TestCase { // Test CorrelationAttributeInstance failure cases // Create an attribute to use in the next few tests - CorrelationAttribute failAttr; + CorrelationAttributeInstance failAttr; try { - failAttr = new CorrelationAttribute(fileType, "badInstances"); + failAttr = new CorrelationAttributeInstance(fileType, "badInstances"); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex); @@ -760,9 +753,8 @@ public class CentralRepoDatamodelTest extends TestCase { // Test adding instance with null case try { - CorrelationAttributeInstance inst = new CorrelationAttributeInstance(null, dataSource1fromCase2, "badPath"); - failAttr.addInstance(inst); - EamDb.getInstance().addArtifactInstance(failAttr); + CorrelationAttributeInstance failAttrInst = new CorrelationAttributeInstance("badInstances", fileType, null, dataSource1fromCase2, "badPath"); + EamDb.getInstance().addArtifactInstance(failAttrInst); Assert.fail("addArtifact failed to throw exception for null case"); } catch (EamDbException ex) { // This is the expected behavior @@ -771,9 +763,8 @@ public class CentralRepoDatamodelTest extends TestCase { // Test adding instance with invalid case ID try { CorrelationCase badCase = new CorrelationCase("badCaseUuid", "badCaseName"); - CorrelationAttributeInstance inst = new CorrelationAttributeInstance(badCase, dataSource1fromCase2, "badPath"); - failAttr.addInstance(inst); - EamDb.getInstance().addArtifactInstance(failAttr); + CorrelationAttributeInstance failAttrInst2 = new CorrelationAttributeInstance("badInstances", fileType, badCase, dataSource1fromCase2, "badPath"); + EamDb.getInstance().addArtifactInstance(failAttrInst2); Assert.fail("addArtifact failed to throw exception for invalid case"); } catch (EamDbException ex) { // This is the expected behavior @@ -781,9 +772,8 @@ public class CentralRepoDatamodelTest extends TestCase { // Test adding instance with null data source try { - CorrelationAttributeInstance inst = new CorrelationAttributeInstance(case1, null, "badPath"); - failAttr.addInstance(inst); - EamDb.getInstance().addArtifactInstance(failAttr); + CorrelationAttributeInstance failAttrInst3 = new CorrelationAttributeInstance("badInstances", fileType, case1, null, "badPath"); + EamDb.getInstance().addArtifactInstance(failAttrInst3); Assert.fail("addArtifact failed to throw exception for null data source"); } catch (EamDbException ex) { // This is the expected behavior @@ -792,9 +782,8 @@ public class CentralRepoDatamodelTest extends TestCase { // Test adding instance with invalid data source ID try { CorrelationDataSource badDS = new CorrelationDataSource(case1, "badDSUuid", "badDSName"); - CorrelationAttributeInstance inst = new CorrelationAttributeInstance(case1, badDS, "badPath"); - failAttr.addInstance(inst); - EamDb.getInstance().addArtifactInstance(failAttr); + CorrelationAttributeInstance failAttrInst4 = new CorrelationAttributeInstance("badInstances", fileType, case1, badDS, "badPath"); + EamDb.getInstance().addArtifactInstance(failAttrInst4); Assert.fail("addArtifact failed to throw exception for invalid data source"); } catch (EamDbException ex) { // This is the expected behavior @@ -803,7 +792,7 @@ public class CentralRepoDatamodelTest extends TestCase { // Test adding instance with null path // This will fail in the CorrelationAttributeInstance constructor try { - new CorrelationAttributeInstance(case1, dataSource1fromCase1, null); + new CorrelationAttributeInstance("badInstances", fileType, case1, dataSource1fromCase1, null); Assert.fail("CorrelationAttributeInstance failed to throw exception for null path"); } catch (EamDbException ex) { // This is the expected behavior @@ -811,9 +800,8 @@ public class CentralRepoDatamodelTest extends TestCase { // Test adding instance with null known status try { - CorrelationAttributeInstance inst = new CorrelationAttributeInstance(case1, dataSource1fromCase1, null, "comment", null); - failAttr.addInstance(inst); - EamDb.getInstance().addArtifactInstance(failAttr); + CorrelationAttributeInstance failAttrInst5 = new CorrelationAttributeInstance("badInstances", fileType, case1, dataSource1fromCase1, null, "comment", null); + EamDb.getInstance().addArtifactInstance(failAttrInst5); Assert.fail("addArtifact failed to throw exception for null known status"); } catch (EamDbException ex) { // This is the expected behavior @@ -822,7 +810,7 @@ public class CentralRepoDatamodelTest extends TestCase { // Test CorrelationAttribute failure cases // Test null type try { - CorrelationAttribute attr = new CorrelationAttribute(null, "badInstances"); + CorrelationAttributeInstance attr = new CorrelationAttributeInstance(null, "badInstances"); EamDb.getInstance().addArtifactInstance(attr); Assert.fail("addArtifact failed to throw exception for null type"); } catch (EamDbException ex) { @@ -832,8 +820,8 @@ public class CentralRepoDatamodelTest extends TestCase { // Test null value // This will fail in the CorrelationAttribute constructor try { - new CorrelationAttribute(fileType, null); - Assert.fail("addArtifact failed to throw exception for null value"); + new CorrelationAttributeInstance(fileType, null); + Assert.fail("addArtifactInsance failed to throw exception for null value"); } catch (EamDbException ex) { // This is the expected behavior } @@ -951,7 +939,7 @@ public class CentralRepoDatamodelTest extends TestCase { // Test getting frequency of value that is in all three data sources try { - CorrelationAttribute attr = new CorrelationAttribute(fileType, inAllDataSourcesHash); + CorrelationAttributeInstance attr = new CorrelationAttributeInstance(fileType, inAllDataSourcesHash); int freq = EamDb.getInstance().getFrequencyPercentage(attr); assertTrue("getFrequencyPercentage returned " + freq + " - expected 100", freq == 100); } catch (EamDbException ex) { @@ -961,7 +949,7 @@ public class CentralRepoDatamodelTest extends TestCase { // Test getting frequency of value that appears twice in a single data source try { - CorrelationAttribute attr = new CorrelationAttribute(fileType, inDataSource1twiceHash); + CorrelationAttributeInstance attr = new CorrelationAttributeInstance(fileType, inDataSource1twiceHash); int freq = EamDb.getInstance().getFrequencyPercentage(attr); assertTrue("getFrequencyPercentage returned " + freq + " - expected 33", freq == 33); } catch (EamDbException ex) { @@ -971,7 +959,7 @@ public class CentralRepoDatamodelTest extends TestCase { // Test getting frequency of non-file type try { - CorrelationAttribute attr = new CorrelationAttribute(emailType, emailValue); + CorrelationAttributeInstance attr = new CorrelationAttributeInstance(emailType, emailValue); int freq = EamDb.getInstance().getFrequencyPercentage(attr); assertTrue("getFrequencyPercentage returned " + freq + " - expected 33", freq == 33); } catch (EamDbException ex) { @@ -981,7 +969,7 @@ public class CentralRepoDatamodelTest extends TestCase { // Test getting frequency of non-existent value try { - CorrelationAttribute attr = new CorrelationAttribute(fileType, "randomValue"); + CorrelationAttributeInstance attr = new CorrelationAttributeInstance(fileType, "randomValue"); int freq = EamDb.getInstance().getFrequencyPercentage(attr); assertTrue("getFrequencyPercentage returned " + freq + " - expected 0", freq == 0); } catch (EamDbException ex) { @@ -991,7 +979,7 @@ public class CentralRepoDatamodelTest extends TestCase { // Test getting frequency with null type try { - CorrelationAttribute attr = new CorrelationAttribute(null, "randomValue"); + CorrelationAttributeInstance attr = new CorrelationAttributeInstance(null, "randomValue"); EamDb.getInstance().getFrequencyPercentage(attr); Assert.fail("getFrequencyPercentage failed to throw exception for null type"); } catch (EamDbException ex) { @@ -1008,18 +996,18 @@ public class CentralRepoDatamodelTest extends TestCase { // Test updating a correlation attribute instance comment try { - CorrelationAttribute correlationAttribute = EamDb.getInstance().getCorrelationAttribute( + CorrelationAttributeInstance correlationAttribute = EamDb.getInstance().getCorrelationAttributeInstance( usbDeviceType, case1, dataSource1fromCase1, devIdValue, devIdPath); assertNotNull("getCorrelationAttribute returned null", correlationAttribute); - correlationAttribute.getInstances().get(0).setComment("new comment"); + correlationAttribute.setComment("new comment"); EamDb.getInstance().updateAttributeInstanceComment(correlationAttribute); // Get a fresh copy to verify the update. - correlationAttribute = EamDb.getInstance().getCorrelationAttribute( + correlationAttribute = EamDb.getInstance().getCorrelationAttributeInstance( usbDeviceType, case1, dataSource1fromCase1, devIdValue, devIdPath); assertEquals("updateAttributeInstanceComment did not set comment to \"new comment\".", - "new comment", correlationAttribute.getInstances().get(0).getComment()); + "new comment", correlationAttribute.getComment()); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex); @@ -1099,13 +1087,11 @@ public class CentralRepoDatamodelTest extends TestCase { // 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); + CorrelationAttributeInstance attr1 = new CorrelationAttributeInstance(callbackTestFileHash, fileType, case1, dataSource1fromCase1, callbackTestFilePath1); + CorrelationAttributeInstance attr2 = new CorrelationAttributeInstance(callbackTestFileHash, fileType, case1, dataSource1fromCase1, callbackTestFilePath2); EamDb DbManager = EamDb.getInstance(); - DbManager.addArtifactInstance(attr); + DbManager.addArtifactInstance(attr1); + DbManager.addArtifactInstance(attr2); AttributeInstanceTableCallback instancetableCallback = new AttributeInstanceTableCallback(); DbManager.processInstanceTable(fileType, instancetableCallback); int count1 = instancetableCallback.getCounter(); @@ -1127,15 +1113,13 @@ public class CentralRepoDatamodelTest extends TestCase { // 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); + CorrelationAttributeInstance attr1 = new CorrelationAttributeInstance(callbackTestFileHash, fileType, case1, dataSource1fromCase1, callbackTestFilePath1); + CorrelationAttributeInstance attr2 = new CorrelationAttributeInstance(callbackTestFileHash, fileType, case1, dataSource1fromCase1, callbackTestFilePath2); EamDb DbManager = EamDb.getInstance(); - DbManager.addArtifactInstance(attr); + DbManager.addArtifactInstance(attr1); + DbManager.addArtifactInstance(attr2); AttributeInstanceTableCallback instancetableCallback = new AttributeInstanceTableCallback(); - DbManager.processInstanceTableWhere(fileType, String.format("id = %s", attr.getID()), instancetableCallback); + DbManager.processInstanceTableWhere(fileType, String.format("id = %s", attr1.getID()), instancetableCallback); int count1 = instancetableCallback.getCounter(); int count2 = instancetableCallback.getCounterNamingConvention(); assertTrue("Process Instance count with filepath naming convention: " + count2 + "-expected 2", count2 == 2); @@ -1606,7 +1590,7 @@ public class CentralRepoDatamodelTest extends TestCase { // Store the email type object for later use try { - emailType = EamDb.getInstance().getCorrelationTypeById(CorrelationAttribute.EMAIL_TYPE_ID); + emailType = EamDb.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.EMAIL_TYPE_ID); assertTrue("getCorrelationTypeById(EMAIL_TYPE_ID) returned null", emailType != null); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); @@ -2133,7 +2117,7 @@ public class CentralRepoDatamodelTest extends TestCase { // Test getting all email reference sets try { - List referenceSets = EamDb.getInstance().getAllReferenceSets(EamDb.getInstance().getCorrelationTypeById(CorrelationAttribute.EMAIL_TYPE_ID)); + List referenceSets = EamDb.getInstance().getAllReferenceSets(EamDb.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.EMAIL_TYPE_ID)); assertTrue("getAllReferenceSets(EMAIL) returned unexpected number", referenceSets.isEmpty()); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); From 2b4059a6131c0c5fb89ecd97e94942e81ad61243 Mon Sep 17 00:00:00 2001 From: Andrew Ziehl Date: Wed, 15 Aug 2018 16:54:26 -0700 Subject: [PATCH 089/150] Add fields to DB returns to properly create CorrelationAttributeInstances --- .../datamodel/AbstractSqlEamDb.java | 17 +++++++++++++---- .../datamodel/CentralRepoDatamodelTest.java | 7 +++---- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java index 0eb210a282..6f0e408339 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java @@ -723,7 +723,10 @@ abstract class AbstractSqlEamDb implements EamDb { 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 " + + ".id," + + tableName + + ".value," + + " 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 @@ -783,7 +786,10 @@ abstract class AbstractSqlEamDb implements EamDb { 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 " + + ".id, " + + tableName + + ".value," + + " 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 @@ -1468,7 +1474,10 @@ abstract class AbstractSqlEamDb implements EamDb { 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 " + + ".id, " + + tableName + + ".value, " + + "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 @@ -1525,7 +1534,7 @@ abstract class AbstractSqlEamDb implements EamDb { 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 " + = "SELECT cases.case_name, cases.case_uid, data_sources.name, device_id, file_path, known_status, comment, data_sources.case_id, id, value FROM " + tableName + " LEFT JOIN cases ON " + tableName 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 9321559d1f..e07debddb2 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 @@ -284,7 +284,7 @@ public class CentralRepoDatamodelTest extends TestCase { } // Try to update artifact with two CorrelationAttributeInstance instances - // TODO, this seems like it is no longer a valid test. + // TODO, this seems like it is no longer a valid test, already moved fail to the catch since it no longe fails. try { CorrelationAttributeInstance attr1 = new CorrelationAttributeInstance("badHash", fileType, case1, dataSource1fromCase1, "badPath", "", TskData.FileKnown.KNOWN); @@ -293,9 +293,8 @@ public class CentralRepoDatamodelTest extends TestCase { EamDb.getInstance().setAttributeInstanceKnownStatus(attr1, TskData.FileKnown.BAD); EamDb.getInstance().setAttributeInstanceKnownStatus(attr2, TskData.FileKnown.BAD); - Assert.fail("setArtifactInstanceKnownStatus failed to throw exception for multiple Correlation Attribute Instances"); } catch (EamDbException ex) { - // This is the expected behavior + Assert.fail("setArtifactInstanceKnownStatus threw an exception for sequential Correlation Attribute Instances updates"); } // Try to update null artifact @@ -998,7 +997,7 @@ public class CentralRepoDatamodelTest extends TestCase { try { CorrelationAttributeInstance correlationAttribute = EamDb.getInstance().getCorrelationAttributeInstance( usbDeviceType, case1, dataSource1fromCase1, devIdValue, devIdPath); - assertNotNull("getCorrelationAttribute returned null", correlationAttribute); + assertNotNull("getCorrelationAttributeInstance returned null", correlationAttribute); correlationAttribute.setComment("new comment"); EamDb.getInstance().updateAttributeInstanceComment(correlationAttribute); From 11b36391916151f7385e5d1a51a09cbe834a1f37 Mon Sep 17 00:00:00 2001 From: Brian Sweeney Date: Thu, 16 Aug 2018 09:23:40 -0600 Subject: [PATCH 090/150] ui error message stuff --- .../CommonAttributePanel.form | 3 ++ .../CommonAttributePanel.java | 32 ++++++++++++++--- .../commonfilesearch/PanelErrorMessages.java | 34 +++++++++++++++++++ .../netbeans/core/startup/Bundle.properties | 2 +- .../core/windows/view/ui/Bundle.properties | 2 +- 5 files changed, 67 insertions(+), 6 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/commonfilesearch/PanelErrorMessages.java diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.form b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.form index 4bf83be1da..30afda0b70 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.form +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.form @@ -310,6 +310,9 @@ + + + diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java index 1cf1dbf7ca..8e32c072f0 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java @@ -54,13 +54,16 @@ import org.sleuthkit.datamodel.TskCoreException; @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public final class CommonAttributePanel extends javax.swing.JDialog { + private static final Logger LOGGER = Logger.getLogger(CommonAttributePanel.class.getName()); private static final long serialVersionUID = 1L; - + private static final Long NO_DATA_SOURCE_SELECTED = -1L; - private static final Logger LOGGER = Logger.getLogger(CommonAttributePanel.class.getName()); private boolean pictureViewCheckboxState; + private boolean documentsCheckboxState; + + private int percentageThresholdValue = 20; /** * Creates new form CommonFilesPanel @@ -521,6 +524,11 @@ public final class CommonAttributePanel extends javax.swing.JDialog { }); percentageThreshold.setText(org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.percentageThreshold.text")); // NOI18N + percentageThreshold.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + percentageThresholdActionPerformed(evt); + } + }); org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.jLabel1.text_1")); // NOI18N @@ -653,12 +661,27 @@ public final class CommonAttributePanel extends javax.swing.JDialog { private void percentageThresholdCheckActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_percentageThresholdCheckActionPerformed if(this.percentageThresholdCheck.isSelected()){ + this.percentageThreshold.setEnabled(true); } else { - + this.percentageThreshold.setEnabled(false); } }//GEN-LAST:event_percentageThresholdCheckActionPerformed + private void percentageThresholdActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_percentageThresholdActionPerformed + String percentageString = this.percentageThreshold.getText(); + + Integer percentage; + + try{ + percentage = Integer.parseInt(percentageString); + this.percentageThresholdValue = percentage; + } catch(Exception e){ + this.percentageThresholdValue = -1; + } + + }//GEN-LAST:event_percentageThresholdActionPerformed + public void handleInterCaseSearchCriteriaChanged() { if (this.areInterCaseSearchCriteriaMet()) { this.searchButton.setEnabled(true); @@ -755,11 +778,12 @@ public final class CommonAttributePanel extends javax.swing.JDialog { private void enablePercentageOptions() { this.percentageThreshold.setEnabled(true); this.percentageThresholdCheck.setEnabled(true); - this.percentageThresholdCheck.setEnabled(true); + this.percentageThresholdCheck.setSelected(true); } private void disablePercentageOptions() { this.percentageThreshold.setEnabled(false); this.percentageThresholdCheck.setEnabled(false); + this.percentageThresholdCheck.setSelected(false); } } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/PanelErrorMessages.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/PanelErrorMessages.java new file mode 100644 index 0000000000..fb23b9da14 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/PanelErrorMessages.java @@ -0,0 +1,34 @@ +/* + * + * 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; + +/** + * + * @author bsweeney + */ +class PanelErrorMessages { + + private class ErrorMessage{ + int id; + String message; + boolean showMessage; + } + +} diff --git a/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties b/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties index 693f4b9f89..af8cf7f30a 100644 --- a/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties +++ b/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties @@ -1,5 +1,5 @@ #Updated by build script -#Mon, 25 Jun 2018 17:19:36 -0400 +#Thu, 16 Aug 2018 08:52:27 -0600 LBL_splash_window_title=Starting Autopsy SPLASH_HEIGHT=314 SPLASH_WIDTH=538 diff --git a/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties b/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties index 3de464ea54..9f3352a25c 100644 --- a/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties +++ b/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties @@ -1,4 +1,4 @@ #Updated by build script -#Mon, 25 Jun 2018 17:19:36 -0400 +#Thu, 16 Aug 2018 08:52:27 -0600 CTL_MainWindow_Title=Autopsy 4.8.0 CTL_MainWindow_Title_No_Project=Autopsy 4.8.0 From 11b482f4eed39374cae973d37a7e07a34cedce06 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Thu, 16 Aug 2018 14:40:42 -0400 Subject: [PATCH 091/150] Fixed the out of memory issue with excel xml document reading, caught the old excel format exception, and refactored the code --- Core/ivy.xml | 1 + Core/nbproject/project.properties | 3 +- Core/nbproject/project.xml | 7 +- .../autopsy/contentviewers/SQLiteViewer.java | 8 +- .../autopsy/sqlitereader/ExcelReader.java | 187 -------------- .../AbstractReader.java | 7 +- .../tabulardatareader/ExcelReader.java | 230 ++++++++++++++++++ .../FileReaderFactory.java | 11 +- .../SQLiteReader.java | 2 +- 9 files changed, 254 insertions(+), 202 deletions(-) delete mode 100755 Core/src/org/sleuthkit/autopsy/sqlitereader/ExcelReader.java rename Core/src/org/sleuthkit/autopsy/{sqlitereader => tabulardatareader}/AbstractReader.java (97%) create mode 100755 Core/src/org/sleuthkit/autopsy/tabulardatareader/ExcelReader.java rename Core/src/org/sleuthkit/autopsy/{sqlitereader => tabulardatareader}/FileReaderFactory.java (80%) rename Core/src/org/sleuthkit/autopsy/{sqlitereader => tabulardatareader}/SQLiteReader.java (99%) diff --git a/Core/ivy.xml b/Core/ivy.xml index 5e0bcbbc4b..5124aa2fd8 100644 --- a/Core/ivy.xml +++ b/Core/ivy.xml @@ -30,6 +30,7 @@ + diff --git a/Core/nbproject/project.properties b/Core/nbproject/project.properties index 46b4d6e90c..2d65b137b8 100644 --- a/Core/nbproject/project.properties +++ b/Core/nbproject/project.properties @@ -16,7 +16,7 @@ file.reference.postgresql-9.4.1211.jre7.jar=release/modules/ext/postgresql-9.4.1 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 -file.reference.sqlite-jdbc-3.8.11.jar=release/modules/ext/sqlite-jdbc-3.8.11.jar +file.reference.sqlite-jdbc-3.8.11.jar=release\\modules\\ext\\sqlite-jdbc-3.8.11.jar file.reference.StixLib.jar=release/modules/ext/StixLib.jar file.reference.bcprov-jdk15on-1.54.jar=release/modules/ext/bcprov-jdk15on-1.54.jar file.reference.jackcess-2.1.8.jar=release/modules/ext/jackcess-2.1.8.jar @@ -36,6 +36,7 @@ file.reference.tika-parsers-1.17.jar=release/modules/ext/tika-parsers-1.17.jar file.reference.curator-client-2.8.0.jar=release/modules/ext/curator-client-2.8.0.jar file.reference.curator-framework-2.8.0.jar=release/modules/ext/curator-framework-2.8.0.jar file.reference.curator-recipes-2.8.0.jar=release/modules/ext/curator-recipes-2.8.0.jar +file.reference.xlsx-streamer-1.2.1.jar=release\\modules\\ext\\xlsx-streamer-1.2.1.jar file.reference.xmpcore-5.1.3.jar=release/modules/ext/xmpcore-5.1.3.jar file.reference.xz-1.6.jar=release/modules/ext/xz-1.6.jar file.reference.zookeeper-3.4.6.jar=release/modules/ext/zookeeper-3.4.6.jar diff --git a/Core/nbproject/project.xml b/Core/nbproject/project.xml index ca87c4f2f4..dbd89f2761 100644 --- a/Core/nbproject/project.xml +++ b/Core/nbproject/project.xml @@ -337,6 +337,7 @@ org.sleuthkit.autopsy.modules.vmextractor org.sleuthkit.autopsy.progress org.sleuthkit.autopsy.report + org.sleuthkit.autopsy.tabulardatareader org.sleuthkit.datamodel @@ -445,7 +446,7 @@ ext/sqlite-jdbc-3.8.11.jar - release/modules/ext/sqlite-jdbc-3.8.11.jar + release\modules\ext\sqlite-jdbc-3.8.11.jar ext/activemq-all-5.11.1.jar @@ -495,6 +496,10 @@ ext/SparseBitSet-1.1.jar release/modules/ext/SparseBitSet-1.1.jar + + ext/xlsx-streamer-1.2.1.jar + release\modules\ext\xlsx-streamer-1.2.1.jar + ext/pdfbox-2.0.8.jar release/modules/ext/pdfbox-2.0.8.jar diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java index 076748d2af..23aa450c4c 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java @@ -47,10 +47,10 @@ import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; -import org.sleuthkit.autopsy.sqlitereader.AbstractReader.FileReaderException; -import org.sleuthkit.autopsy.sqlitereader.AbstractReader.FileReaderInitException; -import org.sleuthkit.autopsy.sqlitereader.FileReaderFactory; -import org.sleuthkit.autopsy.sqlitereader.SQLiteReader; +import org.sleuthkit.autopsy.tabulardatareader.AbstractReader.FileReaderException; +import org.sleuthkit.autopsy.tabulardatareader.AbstractReader.FileReaderInitException; +import org.sleuthkit.autopsy.tabulardatareader.FileReaderFactory; +import org.sleuthkit.autopsy.tabulardatareader.SQLiteReader; /** * A file content viewer for SQLite database files. diff --git a/Core/src/org/sleuthkit/autopsy/sqlitereader/ExcelReader.java b/Core/src/org/sleuthkit/autopsy/sqlitereader/ExcelReader.java deleted file mode 100755 index 7ddcc7aca8..0000000000 --- a/Core/src/org/sleuthkit/autopsy/sqlitereader/ExcelReader.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2018-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.sqlitereader; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.logging.Level; -import org.apache.commons.lang3.StringUtils; -import org.apache.poi.hssf.usermodel.HSSFWorkbook; -import org.apache.poi.ss.usermodel.Cell; -import org.apache.poi.ss.usermodel.DateUtil; -import org.apache.poi.ss.usermodel.Row; -import org.apache.poi.ss.usermodel.Sheet; -import org.apache.poi.ss.usermodel.Workbook; -import org.apache.poi.xssf.streaming.SXSSFWorkbook; -import org.apache.poi.xssf.usermodel.XSSFWorkbook; -import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.ingest.IngestServices; -import org.sleuthkit.datamodel.AbstractFile; - - -/** - * - * @author dsmyda - */ -public class ExcelReader extends AbstractReader { - - private Workbook workbook; - private final IngestServices services = IngestServices.getInstance(); - private final Logger logger = services.getLogger(ExcelReader.class.getName()); - private String XLSXMimeType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; - private String XLSMimeType = "application/vnd.ms-excel"; - - public ExcelReader(AbstractFile file, String localDiskPath, String mimeType) - throws FileReaderInitException { - super(file, localDiskPath); - try { - this.workbook = createWorkbook(localDiskPath, mimeType); - } catch (IOException ex) { - throw new FileReaderInitException(ex); - } - } - - private Workbook createWorkbook(String localDiskPath, String mimeType) throws - IOException, FileReaderInitException { - if(mimeType.equals(XLSMimeType)) { - return new HSSFWorkbook(new FileInputStream(new File(localDiskPath))); - } else if(mimeType.equals(XLSXMimeType)) { - return new SXSSFWorkbook(new XSSFWorkbook(new FileInputStream(new File(localDiskPath)))); - } else { - throw new FileReaderInitException(String.format("Excel reader for mime " - + "type [%s] is not supported", mimeType)); - } - } - - /** - * Returns the number of rows in a given excel table (aka sheet). - * - * @param tableName Name of table to count total rows from - * @return row count for requested table name - * @throws org.sleuthkit.autopsy.sqlitereader.AbstractReader.FileReaderException - */ - @Override - public Integer getRowCountFromTable(String tableName) throws FileReaderException { - return workbook.getSheet(tableName).getLastRowNum(); - } - - /** - * Returns a collection of all the rows from a given table in an excel document. - * - * @param tableName - * @return - * @throws org.sleuthkit.autopsy.sqlitereader.AbstractReader.FileReaderException - */ - @Override - public List> getRowsFromTable(String tableName) throws FileReaderException { - List> rowContents = new ArrayList<>(); - Iterator iterator = workbook.getSheet(tableName).rowIterator(); - - while(iterator.hasNext()) { - Map contents = new HashMap<>(); - Row r = iterator.next(); - for(Cell c : r) { - addCellValueToMap(c, contents); - } - rowContents.add(contents); - } - return rowContents; - } - - /** - * - * @param cell - * @param contents - */ - private void addCellValueToMap(Cell cell, Map contents){ - String columnName = getCellValuesColumnName(cell); - switch (cell.getCellTypeEnum()) { - case BOOLEAN: - contents.put(columnName, cell.getBooleanCellValue()); - break; - case STRING: - contents.put(columnName, cell.getRichStringCellValue().getString()); - break; - case NUMERIC: - if (DateUtil.isCellDateFormatted(cell)) { - contents.put(columnName, cell.getDateCellValue()); - } else { - contents.put(columnName, cell.getNumericCellValue()); - } - break; - case FORMULA: - contents.put(columnName, cell.getCellFormula()); - break; - default: - contents.put(columnName,""); - } - } - - /** - * - * @param cell - * @return - */ - private String getCellValuesColumnName(Cell cell) { - Row header = cell.getSheet().getRow(0); - if(header != null) { - Cell columnCell = header.getCell(cell.getRowIndex()); - - } - return ""; - } - - /** - * - * - * @return - * @throws org.sleuthkit.autopsy.sqlitereader.AbstractReader.FileReaderException - */ - @Override - public Map getTableSchemas() throws FileReaderException { - Map tableSchemas = new HashMap<>(); - for(Sheet sheet : workbook) { - Row header = sheet.getRow(0); - if(header != null) { - String headerStringFormat = StringUtils.join(header.cellIterator(), ", "); - tableSchemas.put(sheet.getSheetName(), headerStringFormat); - } else { - tableSchemas.put(sheet.getSheetName(), ""); - } - } - return tableSchemas; - } - - @Override - public void close() { - try { - workbook.close(); - } catch (IOException ex) { - //Non-essential exception, user has no need for the connection - //object at this stage so closing details are not important - logger.log(Level.WARNING, "Could not close excel file input stream", ex); - } - } -} diff --git a/Core/src/org/sleuthkit/autopsy/sqlitereader/AbstractReader.java b/Core/src/org/sleuthkit/autopsy/tabulardatareader/AbstractReader.java similarity index 97% rename from Core/src/org/sleuthkit/autopsy/sqlitereader/AbstractReader.java rename to Core/src/org/sleuthkit/autopsy/tabulardatareader/AbstractReader.java index 46f4242454..981552ce27 100755 --- a/Core/src/org/sleuthkit/autopsy/sqlitereader/AbstractReader.java +++ b/Core/src/org/sleuthkit/autopsy/tabulardatareader/AbstractReader.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.sqlitereader; +package org.sleuthkit.autopsy.tabulardatareader; import java.io.File; import java.io.IOException; @@ -27,8 +27,7 @@ import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.TskCoreException; /** - * - * @author dsmyda + * An abstract reader interface to */ public abstract class AbstractReader implements AutoCloseable { @@ -96,4 +95,4 @@ public abstract class AbstractReader implements AutoCloseable { super(message); } } -} +} \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/tabulardatareader/ExcelReader.java b/Core/src/org/sleuthkit/autopsy/tabulardatareader/ExcelReader.java new file mode 100755 index 0000000000..3b5afe452c --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/tabulardatareader/ExcelReader.java @@ -0,0 +1,230 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2018-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.tabulardatareader; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import org.apache.commons.lang3.StringUtils; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.DateUtil; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.ingest.IngestServices; +import org.sleuthkit.datamodel.AbstractFile; +import com.monitorjbl.xlsx.StreamingReader; +import org.apache.poi.hssf.OldExcelFormatException; + + +/** + * + */ +public class ExcelReader extends AbstractReader { + /* Boilerplate code */ + private final IngestServices services = IngestServices.getInstance(); + private final Logger logger = services.getLogger(ExcelReader.class.getName()); + + private Workbook workbook; + private final String XLSXMimeType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; + private final String XLSMimeType = "application/vnd.ms-excel"; + private final String EMPTY_CELL_STRING = ""; + + public ExcelReader(AbstractFile file, String localDiskPath, String mimeType) + throws FileReaderInitException { + super(file, localDiskPath); + try { + this.workbook = createWorkbook(localDiskPath, mimeType); + } catch (IOException ex) { + throw new FileReaderInitException(ex); + } + } + + /** + * Internal factory for creating the correct workbook given the mime type. The + * file reader factory in this module passes both the XLSMimeType and XLSXMimeType + * into this constructor for the reader to handle. This avoided the need for creating + * an AbstractExcelReader class and two sub classes overriding the workbook field. + * Additionally, I don't forsee needing to support more than these two mime types. + * + * @param localDiskPath To open an input stream for poi to read from + * @param mimeType The mimeType passed to the constructor + * @return The corrent workbook instance + * @throws IOException Issue with input stream and opening file location at + * localDiskPath + * @throws FileReaderInitException mimetype unsupported + */ + private Workbook createWorkbook(String localDiskPath, String mimeType) throws + IOException, FileReaderInitException { + if(mimeType.equals(XLSMimeType)) { + try { + //Apache POI only supports BIFF8 format, anything below is considered + //old excel format and is not a concern for us. + return new HSSFWorkbook(new FileInputStream(new File(localDiskPath))); + } catch (OldExcelFormatException e) { + throw new FileReaderInitException(e); + } + } else if(mimeType.equals(XLSXMimeType)) { + InputStream is = new FileInputStream(new File(localDiskPath)); + //StreamingReader is part of the xlsx streamer dependency that creates + //a streaming version of XSSFWorkbook for reading (SXSSFWorkbook is only for writing + //large workbooks, not reading). This libary provides a workbook interface + //that is mostly identical to the poi workbook api, hence both the HSSFWorkbook + //and this can use the same functions below. + return StreamingReader.builder().rowCacheSize(500).bufferSize(4096).open(is); + } else { + throw new FileReaderInitException(String.format("Excel reader for mime " + + "type [%s] is not supported", mimeType)); + } + } + + /** + * Returns the number of rows in a given excel table (aka sheet). + * + * @param tableName Name of table to count total rows from + * @return row count for requested table name + * @throws org.sleuthkit.autopsy.sqlitereader.AbstractReader.FileReaderException + */ + @Override + public Integer getRowCountFromTable(String tableName) throws FileReaderException { + return workbook.getSheet(tableName).getLastRowNum(); + } + + /** + * Returns a collection of all the rows from a given table in an excel document. + * + * @param tableName Current sheet name being read + * @return A collection of row maps + * @throws org.sleuthkit.autopsy.sqlitereader.AbstractReader.FileReaderException + */ + @Override + public List> getRowsFromTable(String tableName) throws FileReaderException { + List> rowList = new ArrayList<>(); + Iterator iterator = workbook.getSheet(tableName).rowIterator(); + + while(iterator.hasNext()) { + Map row = new HashMap<>(); + Row currRow = iterator.next(); + for(Cell cell : currRow) { + String columnName = getColumnName(cell, tableName); + Object value = getCellValue(cell); + row.put(columnName, value); + } + rowList.add(row); + } + + return rowList; + } + + /** + * Returns the value of a given cell. The correct value function must be + * called on a cell depending on its type, hence the switch. + * + * @param cell Cell object containing a getter function for its value type + * @return A generic object pointer to the cell's value + */ + private Object getCellValue(Cell cell){ + switch (cell.getCellTypeEnum()) { + case BOOLEAN: + return cell.getBooleanCellValue(); + case STRING: + return cell.getRichStringCellValue().getString(); + case NUMERIC: + if (DateUtil.isCellDateFormatted(cell)) { + return cell.getDateCellValue(); + } else { + return cell.getNumericCellValue(); + } + case FORMULA: + return cell.getCellFormula(); + default: + //Cell must be empty at this branch + return EMPTY_CELL_STRING; + } + } + + /** + * Returns the name of the column that the cell currently lives in + * Cell Value: 6784022342 -> Header name: Phone Number + * + * @param cell current cell being read + * @param tableName current sheet name being read + * @return the name of the column the current cell lives in + */ + private String getColumnName(Cell cell, String tableName) { + Iterator sheetIter = workbook.getSheet(tableName).rowIterator(); + if(sheetIter.hasNext()) { + Row header = sheetIter.next(); + Cell columnHeaderCell = header.getCell(cell.getRowIndex()); + if(columnHeaderCell == null) { + return EMPTY_CELL_STRING; + } + Object columnHeaderValue = getCellValue(columnHeaderCell); + if(columnHeaderValue instanceof String) { + return (String) columnHeaderValue; + } else { + return columnHeaderValue.toString(); + } + } + //No header present + return EMPTY_CELL_STRING; + } + + /** + * Returns a map of sheet names to headers (header is in a comma-seperated string). + * + * @return A map of sheet names to header strings. + * @throws org.sleuthkit.autopsy.sqlitereader.AbstractReader.FileReaderException + */ + @Override + public Map getTableSchemas() throws FileReaderException { + Map tableSchemas = new HashMap<>(); + for(Sheet sheet : workbook) { + Iterator iterator = sheet.rowIterator(); + if(iterator.hasNext()) { + //Consume header + Row header = iterator.next(); + String headerStringFormat = StringUtils.join(header.cellIterator(), ", "); + tableSchemas.put(sheet.getSheetName(), headerStringFormat); + } + } + + return tableSchemas; + } + + @Override + public void close() { + try { + workbook.close(); + } catch (IOException ex) { + //Non-essential exception, user has no need for the connection + //object at this stage so closing details are not important + logger.log(Level.WARNING, "Could not close excel file input stream", ex); + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/sqlitereader/FileReaderFactory.java b/Core/src/org/sleuthkit/autopsy/tabulardatareader/FileReaderFactory.java similarity index 80% rename from Core/src/org/sleuthkit/autopsy/sqlitereader/FileReaderFactory.java rename to Core/src/org/sleuthkit/autopsy/tabulardatareader/FileReaderFactory.java index af9d657277..8b2e678ec3 100755 --- a/Core/src/org/sleuthkit/autopsy/sqlitereader/FileReaderFactory.java +++ b/Core/src/org/sleuthkit/autopsy/tabulardatareader/FileReaderFactory.java @@ -16,9 +16,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.sqlitereader; +package org.sleuthkit.autopsy.tabulardatareader; -import org.sleuthkit.autopsy.sqlitereader.AbstractReader.FileReaderInitException; +import org.sleuthkit.autopsy.tabulardatareader.AbstractReader.FileReaderInitException; import org.sleuthkit.datamodel.AbstractFile; /** @@ -27,9 +27,12 @@ import org.sleuthkit.datamodel.AbstractFile; public final class FileReaderFactory { /** + * Instantiates the appropriate reader given the mimeType argument. Currently + * supports SQLite files and Excel files (.xls and .xlsx). BIFF5 format of .xls + * is not supported. * - * @param mimeType - * @param file + * @param mimeType mimeType passed in from the ingest module + * @param file * @param localDiskPath * @return * @throws org.sleuthkit.autopsy.sqlitereader.AbstractReader.FileReaderInitException diff --git a/Core/src/org/sleuthkit/autopsy/sqlitereader/SQLiteReader.java b/Core/src/org/sleuthkit/autopsy/tabulardatareader/SQLiteReader.java similarity index 99% rename from Core/src/org/sleuthkit/autopsy/sqlitereader/SQLiteReader.java rename to Core/src/org/sleuthkit/autopsy/tabulardatareader/SQLiteReader.java index dff4d21269..1a2ea4dc06 100755 --- a/Core/src/org/sleuthkit/autopsy/sqlitereader/SQLiteReader.java +++ b/Core/src/org/sleuthkit/autopsy/tabulardatareader/SQLiteReader.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.sqlitereader; +package org.sleuthkit.autopsy.tabulardatareader; import java.io.File; import java.io.IOException; From abb32cb11f6ccdd57bc2c6980d4dd14b1d66c2c8 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Thu, 16 Aug 2018 15:12:26 -0400 Subject: [PATCH 092/150] Added some more comments and fixed old exception comments --- .../tabulardatareader/AbstractReader.java | 34 +++++++++++- .../tabulardatareader/ExcelReader.java | 52 ++++++++++--------- .../tabulardatareader/FileReaderFactory.java | 8 +-- .../tabulardatareader/SQLiteReader.java | 10 ++-- 4 files changed, 69 insertions(+), 35 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/tabulardatareader/AbstractReader.java b/Core/src/org/sleuthkit/autopsy/tabulardatareader/AbstractReader.java index 981552ce27..fddf02c3d2 100755 --- a/Core/src/org/sleuthkit/autopsy/tabulardatareader/AbstractReader.java +++ b/Core/src/org/sleuthkit/autopsy/tabulardatareader/AbstractReader.java @@ -27,7 +27,8 @@ import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.TskCoreException; /** - * An abstract reader interface to + * An abstract reader interface for retrieving contents from files via a common + * API. */ public abstract class AbstractReader implements AutoCloseable { @@ -39,6 +40,7 @@ public abstract class AbstractReader implements AutoCloseable { /** * Copies the data source file contents to local drive for processing. + * This function is common to all readers. * * @param file AbstractFile from the data source * @param localDiskPath Local drive path to copy AbstractFile contents @@ -59,15 +61,41 @@ public abstract class AbstractReader implements AutoCloseable { } } + /** + * Return the a mapping of table names to table schemas (may be in the form of + * headers or create table statements for databases). + * + * @return + * @throws org.sleuthkit.autopsy.tabulardatareader.AbstractReader.FileReaderException + */ public abstract Map getTableSchemas() throws FileReaderException; + /** + * Returns the row count fo the given table name. + * + * @param tableName + * @return + * @throws org.sleuthkit.autopsy.tabulardatareader.AbstractReader.FileReaderException + */ public abstract Integer getRowCountFromTable(String tableName) throws FileReaderException; + /** + * Returns a collection view of the rows in a table. + * + * @param tableName + * @return + * @throws org.sleuthkit.autopsy.tabulardatareader.AbstractReader.FileReaderException + */ public abstract List> getRowsFromTable(String tableName) throws FileReaderException; @Override public abstract void close(); + /** + * Checked exceptions are specific to a given implementation, so this custom + * exception allows for a common interface to accommodate all of them. Init + * exception allows for more flexibility in logging. + */ public static class FileReaderInitException extends Exception { public FileReaderInitException(String message, Throwable cause) { super(message, cause); @@ -82,6 +110,10 @@ public abstract class AbstractReader implements AutoCloseable { } } + /** + * Checked exceptions are specific to a given implementation, so this custom + * exception allows for a common interface to accommodate all of them. + */ public class FileReaderException extends Exception { public FileReaderException(String message, Throwable cause) { super(message, cause); diff --git a/Core/src/org/sleuthkit/autopsy/tabulardatareader/ExcelReader.java b/Core/src/org/sleuthkit/autopsy/tabulardatareader/ExcelReader.java index 3b5afe452c..e5430bd2ab 100755 --- a/Core/src/org/sleuthkit/autopsy/tabulardatareader/ExcelReader.java +++ b/Core/src/org/sleuthkit/autopsy/tabulardatareader/ExcelReader.java @@ -43,7 +43,8 @@ import org.apache.poi.hssf.OldExcelFormatException; /** - * + * Reads excel files and implements the abstract reader api for interfacing with the + * content. Supports .xls and .xlsx files. */ public class ExcelReader extends AbstractReader { /* Boilerplate code */ @@ -81,24 +82,25 @@ public class ExcelReader extends AbstractReader { */ private Workbook createWorkbook(String localDiskPath, String mimeType) throws IOException, FileReaderInitException { - if(mimeType.equals(XLSMimeType)) { - try { - //Apache POI only supports BIFF8 format, anything below is considered - //old excel format and is not a concern for us. - return new HSSFWorkbook(new FileInputStream(new File(localDiskPath))); - } catch (OldExcelFormatException e) { - throw new FileReaderInitException(e); - } - } else if(mimeType.equals(XLSXMimeType)) { - InputStream is = new FileInputStream(new File(localDiskPath)); - //StreamingReader is part of the xlsx streamer dependency that creates - //a streaming version of XSSFWorkbook for reading (SXSSFWorkbook is only for writing - //large workbooks, not reading). This libary provides a workbook interface - //that is mostly identical to the poi workbook api, hence both the HSSFWorkbook - //and this can use the same functions below. - return StreamingReader.builder().rowCacheSize(500).bufferSize(4096).open(is); - } else { - throw new FileReaderInitException(String.format("Excel reader for mime " + switch (mimeType) { + case XLSMimeType: + try { + //Apache POI only supports BIFF8 format, anything below is considered + //old excel format and is not a concern for us. + return new HSSFWorkbook(new FileInputStream(new File(localDiskPath))); + } catch (OldExcelFormatException e) { + throw new FileReaderInitException(e); + } + case XLSXMimeType: + InputStream is = new FileInputStream(new File(localDiskPath)); + //StreamingReader is part of the xlsx streamer dependency that creates + //a streaming version of XSSFWorkbook for reading (SXSSFWorkbook is only for writing + //large workbooks, not reading). This libary provides a workbook interface + //that is mostly identical to the poi workbook api, hence both the HSSFWorkbook + //and this can use the same functions below. + return StreamingReader.builder().rowCacheSize(500).bufferSize(4096).open(is); + default: + throw new FileReaderInitException(String.format("Excel reader for mime " + "type [%s] is not supported", mimeType)); } } @@ -107,8 +109,8 @@ public class ExcelReader extends AbstractReader { * Returns the number of rows in a given excel table (aka sheet). * * @param tableName Name of table to count total rows from - * @return row count for requested table name - * @throws org.sleuthkit.autopsy.sqlitereader.AbstractReader.FileReaderException + * @return row count for requested table name + * @throws org.sleuthkit.autopsy.tabulardatareader.AbstractReader.FileReaderException */ @Override public Integer getRowCountFromTable(String tableName) throws FileReaderException { @@ -119,8 +121,8 @@ public class ExcelReader extends AbstractReader { * Returns a collection of all the rows from a given table in an excel document. * * @param tableName Current sheet name being read - * @return A collection of row maps - * @throws org.sleuthkit.autopsy.sqlitereader.AbstractReader.FileReaderException + * @return A collection of row maps + * @throws org.sleuthkit.autopsy.tabulardatareader.AbstractReader.FileReaderException */ @Override public List> getRowsFromTable(String tableName) throws FileReaderException { @@ -198,8 +200,8 @@ public class ExcelReader extends AbstractReader { /** * Returns a map of sheet names to headers (header is in a comma-seperated string). * - * @return A map of sheet names to header strings. - * @throws org.sleuthkit.autopsy.sqlitereader.AbstractReader.FileReaderException + * @return A map of sheet names to header strings. + * @throws org.sleuthkit.autopsy.tabulardatareader.AbstractReader.FileReaderException */ @Override public Map getTableSchemas() throws FileReaderException { diff --git a/Core/src/org/sleuthkit/autopsy/tabulardatareader/FileReaderFactory.java b/Core/src/org/sleuthkit/autopsy/tabulardatareader/FileReaderFactory.java index 8b2e678ec3..8e98e0447c 100755 --- a/Core/src/org/sleuthkit/autopsy/tabulardatareader/FileReaderFactory.java +++ b/Core/src/org/sleuthkit/autopsy/tabulardatareader/FileReaderFactory.java @@ -32,10 +32,10 @@ public final class FileReaderFactory { * is not supported. * * @param mimeType mimeType passed in from the ingest module - * @param file - * @param localDiskPath - * @return - * @throws org.sleuthkit.autopsy.sqlitereader.AbstractReader.FileReaderInitException + * @param file current file under inspection + * @param localDiskPath path for abstract file contents to be written + * @return The correct reader class needed to read the file contents + * @throws org.sleuthkit.autopsy.tabulardatareader.AbstractReader.FileReaderInitException */ public static AbstractReader createReader(String mimeType, AbstractFile file, String localDiskPath) throws FileReaderInitException { diff --git a/Core/src/org/sleuthkit/autopsy/tabulardatareader/SQLiteReader.java b/Core/src/org/sleuthkit/autopsy/tabulardatareader/SQLiteReader.java index 1a2ea4dc06..c7bad9aade 100755 --- a/Core/src/org/sleuthkit/autopsy/tabulardatareader/SQLiteReader.java +++ b/Core/src/org/sleuthkit/autopsy/tabulardatareader/SQLiteReader.java @@ -62,7 +62,7 @@ public class SQLiteReader extends AbstractReader { * * @param sqliteDbFile Data source abstract file * @param localDiskPath Location for database contents to be copied to - * @throws org.sleuthkit.autopsy.sqlitereader.AbstractReader.FileReaderInitException + * @throws org.sleuthkit.autopsy.tabulardatareader.AbstractReader.FileReaderInitException */ public SQLiteReader(AbstractFile sqliteDbFile, String localDiskPath) throws FileReaderInitException { super(sqliteDbFile, localDiskPath); @@ -134,7 +134,7 @@ public class SQLiteReader extends AbstractReader { * CREATE TABLE statments). * * @return A map of table names to table schemas - * @throws org.sleuthkit.autopsy.sqlitereader.AbstractReader.FileReaderException + * @throws org.sleuthkit.autopsy.tabulardatareader.AbstractReader.FileReaderException */ @Override public Map getTableSchemas() throws FileReaderException { @@ -165,7 +165,7 @@ public class SQLiteReader extends AbstractReader { * * @param tableName * @return Row count from tableName - * @throws org.sleuthkit.autopsy.sqlitereader.AbstractReader.FileReaderException + * @throws org.sleuthkit.autopsy.tabulardatareader.AbstractReader.FileReaderException */ @Override public Integer getRowCountFromTable(String tableName) @@ -187,7 +187,7 @@ public class SQLiteReader extends AbstractReader { * @param tableName * @return List of rows, where each row is * represented as a column-value map. - * @throws org.sleuthkit.autopsy.sqlitereader.AbstractReader.FileReaderException + * @throws org.sleuthkit.autopsy.tabulardatareader.AbstractReader.FileReaderException */ @Override public List> getRowsFromTable(String tableName) @@ -213,7 +213,7 @@ public class SQLiteReader extends AbstractReader { * @param numRowsToRead Number of rows past the start index * @return List of rows, where each row is * represented as a column-value map. - * @throws org.sleuthkit.autopsy.sqlitereader.AbstractReader.FileReaderException + * @throws org.sleuthkit.autopsy.tabulardatareader.AbstractReader.FileReaderException */ public List> getRowsFromTable(String tableName, int startRow, int numRowsToRead) throws FileReaderException{ From f98cb902d77b4d01abebecbe956b334cf5812f8c Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Thu, 16 Aug 2018 15:21:24 -0400 Subject: [PATCH 093/150] Changed some project files back to the original state --- Core/nbproject/project.properties | 10 +++++----- Core/nbproject/project.xml | 4 ++-- .../sleuthkit/autopsy/contentviewers/SQLiteViewer.java | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Core/nbproject/project.properties b/Core/nbproject/project.properties index 2d65b137b8..cdfddfc078 100644 --- a/Core/nbproject/project.properties +++ b/Core/nbproject/project.properties @@ -1,9 +1,9 @@ file.reference.activemq-all-5.11.1.jar=release/modules/ext/activemq-all-5.11.1.jar 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.commons-validator-1.6.jar=release\\modules\\ext\\commons-validator-1.6.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.commons-validator-1.6.jar=release/modules/ext/commons-validator-1.6.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 @@ -16,7 +16,7 @@ file.reference.postgresql-9.4.1211.jre7.jar=release/modules/ext/postgresql-9.4.1 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 -file.reference.sqlite-jdbc-3.8.11.jar=release\\modules\\ext\\sqlite-jdbc-3.8.11.jar +file.reference.sqlite-jdbc-3.8.11.jar=release/modules/ext/sqlite-jdbc-3.8.11.jar file.reference.StixLib.jar=release/modules/ext/StixLib.jar file.reference.bcprov-jdk15on-1.54.jar=release/modules/ext/bcprov-jdk15on-1.54.jar file.reference.jackcess-2.1.8.jar=release/modules/ext/jackcess-2.1.8.jar @@ -36,7 +36,7 @@ file.reference.tika-parsers-1.17.jar=release/modules/ext/tika-parsers-1.17.jar file.reference.curator-client-2.8.0.jar=release/modules/ext/curator-client-2.8.0.jar file.reference.curator-framework-2.8.0.jar=release/modules/ext/curator-framework-2.8.0.jar file.reference.curator-recipes-2.8.0.jar=release/modules/ext/curator-recipes-2.8.0.jar -file.reference.xlsx-streamer-1.2.1.jar=release\\modules\\ext\\xlsx-streamer-1.2.1.jar +file.reference.xlsx-streamer-1.2.1.jar=release/modules/ext/xlsx-streamer-1.2.1.jar file.reference.xmpcore-5.1.3.jar=release/modules/ext/xmpcore-5.1.3.jar file.reference.xz-1.6.jar=release/modules/ext/xz-1.6.jar file.reference.zookeeper-3.4.6.jar=release/modules/ext/zookeeper-3.4.6.jar diff --git a/Core/nbproject/project.xml b/Core/nbproject/project.xml index dbd89f2761..fcf968d5c5 100644 --- a/Core/nbproject/project.xml +++ b/Core/nbproject/project.xml @@ -446,7 +446,7 @@ ext/sqlite-jdbc-3.8.11.jar - release\modules\ext\sqlite-jdbc-3.8.11.jar + release/modules/ext/sqlite-jdbc-3.8.11.jar ext/activemq-all-5.11.1.jar @@ -498,7 +498,7 @@ ext/xlsx-streamer-1.2.1.jar - release\modules\ext\xlsx-streamer-1.2.1.jar + release/modules/ext/xlsx-streamer-1.2.1.jar ext/pdfbox-2.0.8.jar diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java index 23aa450c4c..0bee767b63 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java @@ -454,7 +454,7 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { * * @param file * @param tableName - * @param rowMap -- A list of rows in the table, where each row is represented as a column-value + * @param rowMap A list of rows in the table, where each row is represented as a column-value * map. * @throws FileNotFoundException * @throws IOException @@ -527,8 +527,8 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { * Returns a comma seperated header string from the keys of the column * row map. * - * @param row -- column header row map - * @return -- comma seperated header string + * @param row column header row map + * @return comma seperated header string */ private String createColumnHeader(Map row) { return row.entrySet() From 2e0ff95a8cf15467e190b9cd14709a1f8fba8a89 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Thu, 16 Aug 2018 16:31:03 -0400 Subject: [PATCH 094/150] Added a new api call and refactored/commented some more code --- .../tabulardatareader/AbstractReader.java | 11 +++++++ .../tabulardatareader/ExcelReader.java | 31 ++++++++++++++++--- .../tabulardatareader/SQLiteReader.java | 7 +++-- 3 files changed, 42 insertions(+), 7 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/tabulardatareader/AbstractReader.java b/Core/src/org/sleuthkit/autopsy/tabulardatareader/AbstractReader.java index fddf02c3d2..c9c0bce055 100755 --- a/Core/src/org/sleuthkit/autopsy/tabulardatareader/AbstractReader.java +++ b/Core/src/org/sleuthkit/autopsy/tabulardatareader/AbstractReader.java @@ -88,6 +88,17 @@ public abstract class AbstractReader implements AutoCloseable { */ public abstract List> getRowsFromTable(String tableName) throws FileReaderException; + /** + * + * @param tableName + * @param offset + * @param numRowsToRead + * @return + * @throws org.sleuthkit.autopsy.tabulardatareader.AbstractReader.FileReaderException + */ + public abstract List> getRowsFromTable(String tableName, + int offset, int numRowsToRead) throws FileReaderException; + @Override public abstract void close(); diff --git a/Core/src/org/sleuthkit/autopsy/tabulardatareader/ExcelReader.java b/Core/src/org/sleuthkit/autopsy/tabulardatareader/ExcelReader.java index e5430bd2ab..a830e9e2b5 100755 --- a/Core/src/org/sleuthkit/autopsy/tabulardatareader/ExcelReader.java +++ b/Core/src/org/sleuthkit/autopsy/tabulardatareader/ExcelReader.java @@ -126,12 +126,35 @@ public class ExcelReader extends AbstractReader { */ @Override public List> getRowsFromTable(String tableName) throws FileReaderException { + return getRowsFromTable(tableName, 0, getRowCountFromTable(tableName)); + } + + /** + * Returns a window of rows starting at the offset and ending when the number of rows read + * equals the 'numRowsToRead' parameter or the iterator has nothing left to read. + * + * For instance: offset 1, numRowsToRead 5 would return 5 results (1-5). + * offset 0, numRowsToRead 5 would return 5 results (0-4). + * + * @param tableName Current name of sheet to be read + * @param offset start index to begin reading (documents are 0 indexed) + * @param numRowsToRead number of rows to read + * @return + * @throws org.sleuthkit.autopsy.tabulardatareader.AbstractReader.FileReaderException + */ + @Override + public List> getRowsFromTable(String tableName, + int offset, int numRowsToRead) throws FileReaderException { + Iterator sheetIter = workbook.getSheet(tableName).rowIterator(); List> rowList = new ArrayList<>(); - Iterator iterator = workbook.getSheet(tableName).rowIterator(); - while(iterator.hasNext()) { + int currRowCount = 0; + while(sheetIter.hasNext() && currRowCount < (offset + numRowsToRead)) { + Row currRow = sheetIter.next(); + if(currRowCount++ < offset) { + continue; + } Map row = new HashMap<>(); - Row currRow = iterator.next(); for(Cell cell : currRow) { String columnName = getColumnName(cell, tableName); Object value = getCellValue(cell); @@ -218,7 +241,7 @@ public class ExcelReader extends AbstractReader { return tableSchemas; } - + @Override public void close() { try { diff --git a/Core/src/org/sleuthkit/autopsy/tabulardatareader/SQLiteReader.java b/Core/src/org/sleuthkit/autopsy/tabulardatareader/SQLiteReader.java index c7bad9aade..b939ced24e 100755 --- a/Core/src/org/sleuthkit/autopsy/tabulardatareader/SQLiteReader.java +++ b/Core/src/org/sleuthkit/autopsy/tabulardatareader/SQLiteReader.java @@ -209,20 +209,21 @@ public class SQLiteReader extends AbstractReader { * Retrieves a subset of the rows from a given table in the SQLite database. * * @param tableName - * @param startRow Desired start index (rows begin at 1) + * @param offset Desired start index (rows begin at 1) * @param numRowsToRead Number of rows past the start index * @return List of rows, where each row is * represented as a column-value map. * @throws org.sleuthkit.autopsy.tabulardatareader.AbstractReader.FileReaderException */ + @Override public List> getRowsFromTable(String tableName, - int startRow, int numRowsToRead) throws FileReaderException{ + int offset, int numRowsToRead) throws FileReaderException{ tableName = wrapTableNameStringWithQuotes(tableName); try(Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery( "SELECT * FROM " + tableName //NON-NLS + " LIMIT " + Integer.toString(numRowsToRead) //NON-NLS - + " OFFSET " + Integer.toString(startRow - 1))) { //NON-NLS + + " OFFSET " + Integer.toString(offset - 1))) { //NON-NLS return resultSetToList(resultSet); } catch (SQLException ex) { throw new FileReaderException(ex); From 0539b53da8d151c49921e909b5397f94ecded2d8 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Thu, 16 Aug 2018 16:33:31 -0400 Subject: [PATCH 095/150] Changed content viewer to use abstract reader interface --- .../sleuthkit/autopsy/contentviewers/SQLiteViewer.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java index 0bee767b63..47a33dc578 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java @@ -25,7 +25,6 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; -import java.sql.SQLException; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -38,19 +37,17 @@ import javax.swing.JFileChooser; import javax.swing.JOptionPane; import javax.swing.filechooser.FileNameExtensionFilter; import org.apache.commons.io.FilenameUtils; -import org.openide.util.Exceptions; import org.openide.util.NbBundle; import org.openide.windows.WindowManager; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.AbstractFile; -import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; +import org.sleuthkit.autopsy.tabulardatareader.AbstractReader; import org.sleuthkit.autopsy.tabulardatareader.AbstractReader.FileReaderException; import org.sleuthkit.autopsy.tabulardatareader.AbstractReader.FileReaderInitException; import org.sleuthkit.autopsy.tabulardatareader.FileReaderFactory; -import org.sleuthkit.autopsy.tabulardatareader.SQLiteReader; /** * A file content viewer for SQLite database files. @@ -65,7 +62,7 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { private final SQLiteTableView selectedTableView = new SQLiteTableView(); private AbstractFile sqliteDbFile; private File tmpDbFile; - private SQLiteReader sqliteReader; + private AbstractReader sqliteReader; private int numRows; // num of rows in the selected table private int currPage = 0; // curr page of rows being displayed @@ -367,7 +364,7 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { String localDiskPath = Case.getCurrentCaseThrows().getTempDirectory() + File.separator + sqliteDbFile.getName(); - sqliteReader = (SQLiteReader) FileReaderFactory.createReader(SUPPORTED_MIMETYPES[0], sqliteDbFile, localDiskPath); + sqliteReader = FileReaderFactory.createReader(SUPPORTED_MIMETYPES[0], sqliteDbFile, localDiskPath); Map dbTablesMap = sqliteReader.getTableSchemas(); From 1aea3198cf8a88e317c60b1e59b74be8cc1c9577 Mon Sep 17 00:00:00 2001 From: rishwanth1995 Date: Thu, 16 Aug 2018 16:47:44 -0400 Subject: [PATCH 096/150] replaced python asserts with junut asserts --- .../datamodel/CentralRepoDatamodelTest.java | 244 +++++++++--------- ...stedWithHashAndFileTypeInterCaseTests.java | 8 +- ...stedWithHashAndFileTypeIntraCaseTests.java | 22 +- ...IngestedWithNoFileTypesIntraCaseTests.java | 8 +- .../commonfilessearch/InterCaseTestUtils.java | 8 +- .../commonfilessearch/IntraCaseTestUtils.java | 4 +- ...chesInAtLeastTwoSourcesIntraCaseTests.java | 6 +- .../UningestedCasesIntraCaseTests.java | 6 +- .../autopsy/ingest/EmbeddedFileTest.java | 20 +- .../autopsy/ingest/IngestFileFiltersTest.java | 18 +- .../EncryptionDetectionTest.java | 10 +- .../autopsy/testutils/CaseUtils.java | 8 +- .../autopsy/testutils/IngestUtils.java | 6 +- 13 files changed, 184 insertions(+), 184 deletions(-) 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 05c454a962..ba5521f4a4 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 @@ -35,7 +35,7 @@ import junit.framework.TestCase; import org.apache.commons.io.FileUtils; import org.netbeans.junit.NbModuleSuite; import org.openide.util.Exceptions; -import org.python.icu.impl.Assert; +import junit.framework.Assert; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.CaseActionException; import org.sleuthkit.autopsy.casemodule.CaseDetails; @@ -85,7 +85,7 @@ public class CentralRepoDatamodelTest extends TestCase { try { FileUtils.deleteDirectory(testDirectory.toFile()); } catch (IOException ex) { - Assert.fail(ex); + Assert.fail(ex.getMessage()); } } assertFalse("Unable to delete existing test directory", testDirectory.toFile().exists()); @@ -117,7 +117,7 @@ public class CentralRepoDatamodelTest extends TestCase { EamDbPlatformEnum.saveSelectedPlatform(); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } Path crDbFilePath = Paths.get(testDirectory.toString(), CR_DB_NAME); @@ -162,7 +162,7 @@ public class CentralRepoDatamodelTest extends TestCase { } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } } @@ -179,7 +179,7 @@ public class CentralRepoDatamodelTest extends TestCase { FileUtils.deleteDirectory(testDirectory.toFile()); } catch (EamDbException | IOException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } assertFalse("Error deleting test directory " + testDirectory.toString(), testDirectory.toFile().exists()); } @@ -234,7 +234,7 @@ public class CentralRepoDatamodelTest extends TestCase { } } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Add two instances with one notable, one known @@ -259,7 +259,7 @@ public class CentralRepoDatamodelTest extends TestCase { } } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Add an artifact and then update its status @@ -276,7 +276,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("Artifact status did not change to BAD", attrs.get(0).getKnownStatus().equals(TskData.FileKnown.BAD)); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Try to update artifact with two CorrelationAttributeInstance instances @@ -343,7 +343,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("getArtifactInstancesKnownBad returned " + attrs.size() + " values - expected 2", attrs.size() == 2); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test getting notable instances where one instance is notable and the other is known @@ -352,7 +352,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("getArtifactInstancesKnownBad returned " + attrs.size() + " values - expected 1", attrs.size() == 1); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test getting notable instances with null type @@ -369,7 +369,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("getArtifactInstancesKnownBad returned " + attrs.size() + " values - expected ", attrs.isEmpty()); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test getting count of two notable instances @@ -378,7 +378,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("getCountArtifactInstancesKnownBad returned " + count + " values - expected 2", count == 2); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test getting notable instance count where one instance is notable and the other is known @@ -387,7 +387,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("getCountArtifactInstancesKnownBad returned " + count + " values - expected 1", count == 1); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test getting notable instance count with null type @@ -404,7 +404,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("getCountArtifactInstancesKnownBad returned " + count + " values - expected ", count == 0); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test getting cases with notable instances (all instances are notable) @@ -413,7 +413,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("getListCasesHavingArtifactInstancesKnownBad returned " + cases.size() + " values - expected 2", cases.size() == 2); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test getting cases with notable instances (only one instance is notable) @@ -423,7 +423,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("getListCasesHavingArtifactInstancesKnownBad returned unexpected case " + cases.get(0), case1.getDisplayName().equals(cases.get(0))); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test getting cases with null type @@ -440,7 +440,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("getListCasesHavingArtifactInstancesKnownBad returned " + cases.size() + " values - expected ", cases.isEmpty()); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } } @@ -520,7 +520,7 @@ public class CentralRepoDatamodelTest extends TestCase { } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test preparing artifact with null type @@ -656,7 +656,7 @@ public class CentralRepoDatamodelTest extends TestCase { emailType = EamDb.getInstance().getCorrelationTypeById(CorrelationAttribute.EMAIL_TYPE_ID); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); return; } @@ -668,7 +668,7 @@ public class CentralRepoDatamodelTest extends TestCase { EamDb.getInstance().addArtifact(attr); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test adding attribute with an instance in each data source @@ -683,7 +683,7 @@ public class CentralRepoDatamodelTest extends TestCase { EamDb.getInstance().addArtifact(attr); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test adding attribute with two instances in one data source @@ -696,7 +696,7 @@ public class CentralRepoDatamodelTest extends TestCase { EamDb.getInstance().addArtifact(attr); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test adding the other types @@ -708,7 +708,7 @@ public class CentralRepoDatamodelTest extends TestCase { EamDb.getInstance().addArtifact(attr); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test adding a phone artifact @@ -720,7 +720,7 @@ public class CentralRepoDatamodelTest extends TestCase { EamDb.getInstance().addArtifact(attr); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test adding a domain artifact @@ -732,7 +732,7 @@ public class CentralRepoDatamodelTest extends TestCase { EamDb.getInstance().addArtifact(attr); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test adding a device ID artifact @@ -744,7 +744,7 @@ public class CentralRepoDatamodelTest extends TestCase { EamDb.getInstance().addArtifact(attr); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test CorrelationAttributeInstance failure cases @@ -754,7 +754,7 @@ public class CentralRepoDatamodelTest extends TestCase { failAttr = new CorrelationAttribute(fileType, "badInstances"); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); return; } @@ -850,7 +850,7 @@ public class CentralRepoDatamodelTest extends TestCase { } } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test getting instances expecting no results @@ -860,7 +860,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("getArtifactInstancesByTypeValue returned " + instances.size() + " results - expected 0", instances.isEmpty()); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test getting instances with null type @@ -878,7 +878,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("getArtifactInstancesByTypeValue returned non-empty list for null value", instances.isEmpty()); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test getting instances with path that should produce results @@ -887,7 +887,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("getArtifactInstancesByPath returned " + instances.size() + " objects - expected 3", instances.size() == 3); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test getting instances with path that should not produce results @@ -896,7 +896,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("getArtifactInstancesByPath returned " + instances.size() + " objects - expected 0", instances.isEmpty()); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test getting instances with null type @@ -921,7 +921,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("getCountArtifactInstancesByTypeValue returned " + count + " - expected 3", count == 3); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test getting instance count with path that should not produce results @@ -930,7 +930,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("getCountArtifactInstancesByTypeValue returned " + count + " - expected 0", count == 0); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test getting instance count with null type @@ -956,7 +956,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("getFrequencyPercentage returned " + freq + " - expected 100", freq == 100); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test getting frequency of value that appears twice in a single data source @@ -966,7 +966,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("getFrequencyPercentage returned " + freq + " - expected 33", freq == 33); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test getting frequency of non-file type @@ -976,7 +976,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("getFrequencyPercentage returned " + freq + " - expected 33", freq == 33); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test getting frequency of non-existent value @@ -986,7 +986,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("getFrequencyPercentage returned " + freq + " - expected 0", freq == 0); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test getting frequency with null type @@ -1022,7 +1022,7 @@ public class CentralRepoDatamodelTest extends TestCase { "new comment", correlationAttribute.getInstances().get(0).getComment()); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test getting count for dataSource1fromCase1 (includes all types) @@ -1031,7 +1031,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("getCountArtifactInstancesByCaseDataSource returned " + count + " - expected 7", count == 7); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test getting count with null case UUID @@ -1040,7 +1040,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("getCountArtifactInstancesByCaseDataSource returned " + count + " - expected 0", count == 0); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test getting count with null device ID @@ -1049,7 +1049,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("getCountArtifactInstancesByCaseDataSource returned " + count + " - expected 0", count == 0); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test getting data source count for entry that is in all three @@ -1058,7 +1058,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("getCountUniqueCaseDataSourceTuplesHavingTypeValue returned " + count + " - expected 3", count == 3); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test getting data source count for entry that is in one data source twice @@ -1067,7 +1067,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("getCountUniqueCaseDataSourceTuplesHavingTypeValue returned " + count + " - expected 1", count == 1); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test getting data source count for entry that is not in any data sources @@ -1076,7 +1076,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("getCountUniqueCaseDataSourceTuplesHavingTypeValue returned " + count + " - expected 0", count == 0); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test getting data source count for null type @@ -1093,7 +1093,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("getCountUniqueCaseDataSourceTuplesHavingTypeValue returned " + count + " - expected 0", count == 0); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test running processinstance which queries all rows from instances table @@ -1188,7 +1188,7 @@ public class CentralRepoDatamodelTest extends TestCase { customType.setId(EamDb.getInstance().newCorrelationType(customType)); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); return; } @@ -1235,7 +1235,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("getDefinedCorrelationTypes returned " + types.size() + " entries - expected 6", types.size() == 6); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test getting enabled correlation types @@ -1246,7 +1246,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("getDefinedCorrelationTypes returned " + types.size() + " enabled entries - expected 5", types.size() == 5); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test getting supported correlation types @@ -1257,7 +1257,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("getDefinedCorrelationTypes returned " + types.size() + " supported entries - expected 5", types.size() == 5); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test getting the type with a valid ID @@ -1267,7 +1267,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("getCorrelationTypeById returned type with unexpected db table name " + temp.getDbTableName(), customTypeDb.equals(temp.getDbTableName())); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test getting the type with a invalid ID @@ -1298,7 +1298,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("updateCorrelationType failed to update supported status", temp.isSupported()); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test updating a type with an invalid ID @@ -1309,7 +1309,7 @@ public class CentralRepoDatamodelTest extends TestCase { EamDb.getInstance().updateCorrelationType(temp); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test updating a type to a null name @@ -1372,7 +1372,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("Organization ID is still -1 after adding to db", orgA.getOrgID() != -1); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); return; } @@ -1383,7 +1383,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("Organization ID is still -1 after adding to db", orgB.getOrgID() != -1); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); return; } @@ -1421,7 +1421,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("getOrganizations returned " + orgs.size() + " orgs - expected 5", orgs.size() == 5); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test getting org with valid ID @@ -1434,7 +1434,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("getOrganizationByID returned unexpected poc phone for organization", orgBpocPhone.equals(temp.getPocPhone())); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test getting org with invalid ID @@ -1467,7 +1467,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("updateOrganization failed to update poc phone", newPocPhone.equals(copyOfA.getPocPhone())); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test updating invalid org @@ -1508,7 +1508,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("getOrganizations returned unexpected count after deletion", orgCount - 1 == EamDb.getInstance().getOrganizations().size()); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test deleting existing org that is in use @@ -1610,7 +1610,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("getCorrelationTypeById(EMAIL_TYPE_ID) returned null", emailType != null); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); return; } @@ -1624,7 +1624,7 @@ public class CentralRepoDatamodelTest extends TestCase { knownSet1id = EamDb.getInstance().newReferenceSet(knownSet1); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); return; } @@ -1646,7 +1646,7 @@ public class CentralRepoDatamodelTest extends TestCase { EamDb.getInstance().addReferenceInstance(temp, fileType); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test adding file instance with invalid reference set ID @@ -1707,7 +1707,7 @@ public class CentralRepoDatamodelTest extends TestCase { } } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test bulk add file instance with null list @@ -1742,7 +1742,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("getReferenceInstancesByTypeValue returned " + temp.size() + " instances - expected 3", temp.size() == 3); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test getting reference instances with non-existent data @@ -1751,7 +1751,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("getReferenceInstancesByTypeValue returned " + temp.size() + " instances for non-existent value - expected 0", temp.isEmpty()); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test getting reference instances an invalid type (the email table is not yet implemented) @@ -1776,7 +1776,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("getReferenceInstancesByTypeValue returned non-empty list given null value", temp.isEmpty()); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test checking existing hash/ID @@ -1784,7 +1784,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("isFileHashInReferenceSet returned false for valid data", EamDb.getInstance().isFileHashInReferenceSet(knownHash1, knownSet1id)); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test checking non-existent (but valid) hash/ID @@ -1792,7 +1792,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertFalse("isFileHashInReferenceSet returned true for non-existent data", EamDb.getInstance().isFileHashInReferenceSet(knownHash1, notableSet1id)); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test checking invalid reference set ID @@ -1800,7 +1800,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertFalse("isFileHashInReferenceSet returned true for invalid data", EamDb.getInstance().isFileHashInReferenceSet(knownHash1, 5678)); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test checking null hash @@ -1808,7 +1808,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertFalse("isFileHashInReferenceSet returned true for null hash", EamDb.getInstance().isFileHashInReferenceSet(null, knownSet1id)); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test checking existing hash/ID @@ -1817,7 +1817,7 @@ public class CentralRepoDatamodelTest extends TestCase { EamDb.getInstance().isValueInReferenceSet(knownHash1, knownSet1id, fileType.getId())); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test checking non-existent (but valid) hash/ID @@ -1826,7 +1826,7 @@ public class CentralRepoDatamodelTest extends TestCase { EamDb.getInstance().isValueInReferenceSet(knownHash1, notableSet1id, fileType.getId())); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test checking invalid reference set ID @@ -1835,7 +1835,7 @@ public class CentralRepoDatamodelTest extends TestCase { EamDb.getInstance().isValueInReferenceSet(knownHash1, 5678, fileType.getId())); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test checking null hash @@ -1844,7 +1844,7 @@ public class CentralRepoDatamodelTest extends TestCase { EamDb.getInstance().isValueInReferenceSet(null, knownSet1id, fileType.getId())); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test checking invalid type @@ -1861,7 +1861,7 @@ public class CentralRepoDatamodelTest extends TestCase { EamDb.getInstance().isArtifactKnownBadByReference(fileType, notableHash1)); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test known bad with known data @@ -1870,7 +1870,7 @@ public class CentralRepoDatamodelTest extends TestCase { EamDb.getInstance().isArtifactKnownBadByReference(fileType, knownHash1)); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test known bad with non-existent data @@ -1879,7 +1879,7 @@ public class CentralRepoDatamodelTest extends TestCase { EamDb.getInstance().isArtifactKnownBadByReference(fileType, "abcdef")); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test known bad with null hash @@ -1888,7 +1888,7 @@ public class CentralRepoDatamodelTest extends TestCase { EamDb.getInstance().isArtifactKnownBadByReference(fileType, null)); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test known bad with null type @@ -1904,7 +1904,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertFalse("isArtifactKnownBadByReference returned true for invalid type", EamDb.getInstance().isArtifactKnownBadByReference(emailType, null)); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } } @@ -1960,7 +1960,7 @@ public class CentralRepoDatamodelTest extends TestCase { set1id = EamDb.getInstance().newReferenceSet(set1); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); return; } @@ -1970,7 +1970,7 @@ public class CentralRepoDatamodelTest extends TestCase { EamDb.getInstance().newReferenceSet(set2); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); return; } @@ -1989,7 +1989,7 @@ public class CentralRepoDatamodelTest extends TestCase { EamDb.getInstance().newReferenceSet(set3); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); return; } @@ -2043,7 +2043,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("referenceSetIsValid returned false for valid reference set", EamDb.getInstance().referenceSetIsValid(set1id, set1name, set1version)); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test validation with an invalid reference set @@ -2051,7 +2051,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertFalse("referenceSetIsValid returned true for invalid reference set", EamDb.getInstance().referenceSetIsValid(5000, set1name, set1version)); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test validation with a null name @@ -2059,7 +2059,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertFalse("referenceSetIsValid returned true with null name", EamDb.getInstance().referenceSetIsValid(set1id, null, set1version)); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test validation with a null version @@ -2067,7 +2067,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertFalse("referenceSetIsValid returned true with null version", EamDb.getInstance().referenceSetIsValid(set1id, set1name, null)); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test existence with a valid reference set @@ -2075,7 +2075,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("referenceSetExists returned false for valid reference set", EamDb.getInstance().referenceSetExists(set1name, set1version)); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test existence with an invalid reference set @@ -2083,7 +2083,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertFalse("referenceSetExists returned true for invalid reference set", EamDb.getInstance().referenceSetExists(set1name, "5.5")); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test existence with null name @@ -2091,7 +2091,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertFalse("referenceSetExists returned true for null name", EamDb.getInstance().referenceSetExists(null, "1.0")); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test existence with null version @@ -2099,7 +2099,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertFalse("referenceSetExists returned true for null version", EamDb.getInstance().referenceSetExists(set1name, null)); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test getting global set with valid ID @@ -2110,7 +2110,7 @@ public class CentralRepoDatamodelTest extends TestCase { set1name.equals(temp.getSetName()) && set1version.equals(temp.getVersion())); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test getting global set with invalid ID @@ -2119,7 +2119,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("getReferenceSetByID returned non-null result for invalid ID", temp == null); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test getting all file reference sets @@ -2128,7 +2128,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("getAllReferenceSets(FILES) returned unexpected number", referenceSets.size() == 3); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test getting all email reference sets @@ -2137,7 +2137,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("getAllReferenceSets(EMAIL) returned unexpected number", referenceSets.isEmpty()); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test null argument to getAllReferenceSets @@ -2163,7 +2163,7 @@ public class CentralRepoDatamodelTest extends TestCase { } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test deleting a non-existent reference set @@ -2174,7 +2174,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("Number of reference sets changed after deleting non-existent set", currentCount == EamDb.getInstance().getAllReferenceSets(fileType).size()); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test getting reference set organization for valid ID with org set @@ -2184,7 +2184,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("getReferenceSetOrganization returned the incorrect organization", org.getOrgID() == org1.getOrgID()); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test getting reference set organization for non-existent reference set @@ -2227,7 +2227,7 @@ public class CentralRepoDatamodelTest extends TestCase { EamDb.getInstance().newDataSource(dataSourceA); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); return; } @@ -2246,7 +2246,7 @@ public class CentralRepoDatamodelTest extends TestCase { EamDb.getInstance().newDataSource(dataSourceB); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); return; } @@ -2284,7 +2284,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("Failed to get data source", temp != null); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test getting a data source with non-existent ID @@ -2293,7 +2293,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("getDataSource returned non-null value for non-existent data source", temp == null); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test getting a data source with a null case @@ -2310,7 +2310,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("getDataSource returned non-null value for null data source", temp == null); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test getting the list of data sources @@ -2327,7 +2327,7 @@ public class CentralRepoDatamodelTest extends TestCase { && devIdList.contains(dataSource1fromCase2.getDeviceID())); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test the data source count @@ -2336,7 +2336,7 @@ public class CentralRepoDatamodelTest extends TestCase { EamDb.getInstance().getCountUniqueDataSources() == 5); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } } @@ -2377,7 +2377,7 @@ public class CentralRepoDatamodelTest extends TestCase { Case.createAsCurrentCase(Case.CaseType.SINGLE_USER_CASE, testDirectory.toString(), new CaseDetails("CentralRepoDatamodelTestCase")); } catch (CaseActionException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } assertTrue("Failed to create test case", testDirectory.toFile().exists()); @@ -2388,7 +2388,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("Failed to create case", caseA != null); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); return; } @@ -2421,7 +2421,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("newCase created a new case for an already existing UUID", nCases == EamDb.getInstance().getCases().size()); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test creating a case from an Autopsy case @@ -2431,7 +2431,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("Failed to create correlation case from Autopsy case", caseB != null); } catch (EamDbException | NoCurrentCaseException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); return; } @@ -2483,7 +2483,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("updateCase failed to update org (org ID is wrong)", org1.getOrgID() == updatedCase.getOrg().getOrgID()); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test update case with null case @@ -2500,7 +2500,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("getCase returned null for current Autopsy case", tempCase != null); } catch (EamDbException | NoCurrentCaseException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test getting a case by UUID @@ -2509,7 +2509,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("Failed to get case by UUID", tempCase != null); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test getting a case with a non-existent UUID @@ -2518,7 +2518,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("getCaseByUUID returned non-null case for non-existent UUID", tempCase == null); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test getting a case with null UUID @@ -2527,7 +2527,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("getCaseByUUID returned non-null case for null UUID", tempCase == null); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test getting the list of cases @@ -2541,7 +2541,7 @@ public class CentralRepoDatamodelTest extends TestCase { && uuidList.contains(caseB.getCaseUUID())); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test bulk case insert @@ -2566,7 +2566,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("bulkInsertCases did not insert the expected number of cases", nCases + cases.size() == EamDb.getInstance().getCases().size()); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test bulk case insert with null list @@ -2587,7 +2587,7 @@ public class CentralRepoDatamodelTest extends TestCase { } } catch (CaseActionException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } } } @@ -2620,7 +2620,7 @@ public class CentralRepoDatamodelTest extends TestCase { EamDb.getInstance().newDbInfo(name1, value1); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Test null name @@ -2645,7 +2645,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("dbInfo value for name1 does not match", value1.equals(tempVal)); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Try getting the dbInfo entry that should not exist @@ -2654,7 +2654,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("dbInfo value is unexpectedly non-null given non-existent name", tempVal == null); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Try getting dbInfo for a null value @@ -2663,7 +2663,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("dbInfo value is unexpectedly non-null given null name", tempVal == null); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Try updating an existing value to a valid new value @@ -2672,7 +2672,7 @@ public class CentralRepoDatamodelTest extends TestCase { assertTrue("dbInfo value failed to update to expected value", value2.equals(EamDb.getInstance().getDbInfo(name1))); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Try updating an existing value to null @@ -2689,7 +2689,7 @@ public class CentralRepoDatamodelTest extends TestCase { EamDb.getInstance().updateDbInfo(null, value1); } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } // Try updating the value for a non-existant name diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithHashAndFileTypeInterCaseTests.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithHashAndFileTypeInterCaseTests.java index 74a42172cb..da1caf380d 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithHashAndFileTypeInterCaseTests.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithHashAndFileTypeInterCaseTests.java @@ -25,7 +25,7 @@ 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 junit.framework.Assert; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; import org.sleuthkit.autopsy.commonfilesearch.AbstractCommonAttributeSearcher; @@ -68,7 +68,7 @@ public class IngestedWithHashAndFileTypeInterCaseTests extends NbTestCase { this.utils.createCases(this.utils.getIngestSettingsForHashAndFileType(), InterCaseTestUtils.CASE3); } catch (TskCoreException | EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } } @@ -126,7 +126,7 @@ public class IngestedWithHashAndFileTypeInterCaseTests extends NbTestCase { } catch (TskCoreException | NoCurrentCaseException | SQLException | EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } } @@ -179,7 +179,7 @@ public class IngestedWithHashAndFileTypeInterCaseTests extends NbTestCase { } catch (TskCoreException | NoCurrentCaseException | SQLException | EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } } } diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithHashAndFileTypeIntraCaseTests.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithHashAndFileTypeIntraCaseTests.java index d1a004d281..b8746675d7 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithHashAndFileTypeIntraCaseTests.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithHashAndFileTypeIntraCaseTests.java @@ -27,7 +27,7 @@ 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 junit.framework.Assert; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; @@ -82,7 +82,7 @@ public class IngestedWithHashAndFileTypeIntraCaseTests extends NbTestCase { IngestUtils.runIngestJob(Case.getCurrentCaseThrows().getDataSources(), ingestJobSettings); } catch (NoCurrentCaseException | TskCoreException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } } @@ -128,7 +128,7 @@ public class IngestedWithHashAndFileTypeIntraCaseTests extends NbTestCase { } catch (NoCurrentCaseException | TskCoreException | SQLException | EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } } @@ -169,7 +169,7 @@ public class IngestedWithHashAndFileTypeIntraCaseTests extends NbTestCase { } catch (NoCurrentCaseException | TskCoreException | SQLException | EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } } @@ -210,7 +210,7 @@ public class IngestedWithHashAndFileTypeIntraCaseTests extends NbTestCase { } catch (NoCurrentCaseException | TskCoreException | SQLException | EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } } @@ -252,7 +252,7 @@ public class IngestedWithHashAndFileTypeIntraCaseTests extends NbTestCase { } catch (NoCurrentCaseException | TskCoreException | SQLException | EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } } @@ -294,7 +294,7 @@ public class IngestedWithHashAndFileTypeIntraCaseTests extends NbTestCase { } catch (NoCurrentCaseException | TskCoreException | SQLException | EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } } @@ -336,7 +336,7 @@ public class IngestedWithHashAndFileTypeIntraCaseTests extends NbTestCase { } catch (NoCurrentCaseException | TskCoreException | SQLException | EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } } @@ -378,7 +378,7 @@ public class IngestedWithHashAndFileTypeIntraCaseTests extends NbTestCase { } catch (NoCurrentCaseException | TskCoreException | SQLException | EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } } @@ -419,7 +419,7 @@ public class IngestedWithHashAndFileTypeIntraCaseTests extends NbTestCase { } catch (NoCurrentCaseException | TskCoreException | SQLException | EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } } @@ -460,7 +460,7 @@ public class IngestedWithHashAndFileTypeIntraCaseTests extends NbTestCase { } catch (NoCurrentCaseException | TskCoreException | SQLException | EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } } } diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithNoFileTypesIntraCaseTests.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithNoFileTypesIntraCaseTests.java index d51dfda2fa..1c26248766 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithNoFileTypesIntraCaseTests.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithNoFileTypesIntraCaseTests.java @@ -28,7 +28,7 @@ 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 junit.framework.Assert; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.commonfilesearch.AllIntraCaseCommonAttributeSearcher; @@ -83,7 +83,7 @@ public class IngestedWithNoFileTypesIntraCaseTests extends NbTestCase { IngestUtils.runIngestJob(Case.getCurrentCaseThrows().getDataSources(), ingestJobSettings); } catch (NoCurrentCaseException | TskCoreException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } } @@ -112,7 +112,7 @@ public class IngestedWithNoFileTypesIntraCaseTests extends NbTestCase { } catch (TskCoreException | NoCurrentCaseException | SQLException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } } @@ -136,7 +136,7 @@ public class IngestedWithNoFileTypesIntraCaseTests extends NbTestCase { } catch (Exception ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } } } diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/InterCaseTestUtils.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/InterCaseTestUtils.java index cf6bd36fbc..d1a354e76b 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/InterCaseTestUtils.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/InterCaseTestUtils.java @@ -45,7 +45,7 @@ import org.sleuthkit.autopsy.modules.hashdatabase.HashLookupModuleFactory; import org.sleuthkit.autopsy.testutils.CaseUtils; import org.sleuthkit.autopsy.testutils.IngestUtils; import org.sleuthkit.datamodel.TskCoreException; -import org.python.icu.impl.Assert; +import junit.framework.Assert; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase; @@ -182,7 +182,7 @@ class InterCaseTestUtils { FileUtils.deleteDirectory(CASE_DIRECTORY_PATH.toFile()); } catch(IOException | EamDbException ex){ Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } } CASE_DIRECTORY_PATH.toFile().exists(); @@ -280,7 +280,7 @@ class InterCaseTestUtils { } if (currentCase == null) { - Assert.fail(new IllegalArgumentException("caseReferenceToStore should be one of: CASE1, CASE2, CASE3")); + Assert.fail(new IllegalArgumentException("caseReferenceToStore should be one of: CASE1, CASE2, CASE3").getMessage()); return null; } else { return currentCase; @@ -386,7 +386,7 @@ class InterCaseTestUtils { } } catch (IOException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } } } diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IntraCaseTestUtils.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IntraCaseTestUtils.java index 82c38cdf56..126295337a 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IntraCaseTestUtils.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IntraCaseTestUtils.java @@ -29,7 +29,7 @@ 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 junit.framework.Assert; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.ImageDSProcessor; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; @@ -226,7 +226,7 @@ class IntraCaseTestUtils { files.add(file); } catch (NoCurrentCaseException | TskCoreException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } } diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/MatchesInAtLeastTwoSourcesIntraCaseTests.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/MatchesInAtLeastTwoSourcesIntraCaseTests.java index f4c397af8d..b2c1f14cb4 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/MatchesInAtLeastTwoSourcesIntraCaseTests.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/MatchesInAtLeastTwoSourcesIntraCaseTests.java @@ -27,7 +27,7 @@ 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 junit.framework.Assert; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.ImageDSProcessor; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; @@ -92,7 +92,7 @@ public class MatchesInAtLeastTwoSourcesIntraCaseTests extends NbTestCase { IngestUtils.runIngestJob(Case.getCurrentCaseThrows().getDataSources(), ingestJobSettings); } catch (NoCurrentCaseException | TskCoreException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } } @@ -123,7 +123,7 @@ public class MatchesInAtLeastTwoSourcesIntraCaseTests extends NbTestCase { } catch (NoCurrentCaseException | TskCoreException | SQLException | EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } } } diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/UningestedCasesIntraCaseTests.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/UningestedCasesIntraCaseTests.java index b55707c771..3570d0a11c 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/UningestedCasesIntraCaseTests.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/UningestedCasesIntraCaseTests.java @@ -26,7 +26,7 @@ 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 junit.framework.Assert; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; import org.sleuthkit.autopsy.commonfilesearch.AllIntraCaseCommonAttributeSearcher; @@ -89,7 +89,7 @@ public class UningestedCasesIntraCaseTests extends NbTestCase { } catch (TskCoreException | NoCurrentCaseException | SQLException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } } @@ -109,7 +109,7 @@ public class UningestedCasesIntraCaseTests extends NbTestCase { } catch (TskCoreException | NoCurrentCaseException | SQLException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } } } diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/ingest/EmbeddedFileTest.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/ingest/EmbeddedFileTest.java index 995aac1818..534523851f 100755 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/ingest/EmbeddedFileTest.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/ingest/EmbeddedFileTest.java @@ -28,7 +28,7 @@ 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 junit.framework.Assert; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.ImageDSProcessor; import org.sleuthkit.autopsy.ingest.IngestJobSettings.IngestType; @@ -85,7 +85,7 @@ public class EmbeddedFileTest extends NbTestCase { IngestUtils.runIngestJob(openCase.getDataSources(), ingestJobSettings); } catch (TskCoreException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } } @@ -137,7 +137,7 @@ public class EmbeddedFileTest extends NbTestCase { assertEquals("Unexpected number of artifacts reflecting zip bombs found", 2, zipBombs); } catch (TskCoreException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } testSucceeded = true; @@ -164,7 +164,7 @@ public class EmbeddedFileTest extends NbTestCase { assertEquals(numOfFilesToTest, numOfFilesTested); } catch (TskCoreException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } testSucceeded = true; @@ -187,7 +187,7 @@ public class EmbeddedFileTest extends NbTestCase { assertTrue(fileReached.get(0).endsWith(dirReached.toString() + "file.txt")); } catch (TskCoreException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } testSucceeded = true; @@ -213,7 +213,7 @@ public class EmbeddedFileTest extends NbTestCase { assertTrue(checkFileInEmbeddedFolder(results.get(0))); } catch (TskCoreException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } testSucceeded = true; @@ -239,7 +239,7 @@ public class EmbeddedFileTest extends NbTestCase { } catch (TskCoreException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } testSucceeded = true; @@ -253,7 +253,7 @@ public class EmbeddedFileTest extends NbTestCase { assertEquals("file.txt wasn't extracted from the file: zipFileWithTxtExtension.txt", "file.txt", results.get(0).getName()); } catch (TskCoreException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } testSucceeded = true; @@ -272,7 +272,7 @@ public class EmbeddedFileTest extends NbTestCase { } } catch (TskCoreException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } } else if (file.isFile() && !file.getName().endsWith("slack")) { assertEquals(errMsg, "file.txt", file.getName()); @@ -291,7 +291,7 @@ public class EmbeddedFileTest extends NbTestCase { } } catch (TskCoreException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } } else { assertTrue(file.getNameExtension().equalsIgnoreCase("txt")); diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/ingest/IngestFileFiltersTest.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/ingest/IngestFileFiltersTest.java index 16b0f2f4a4..bc8bffea2e 100755 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/ingest/IngestFileFiltersTest.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/ingest/IngestFileFiltersTest.java @@ -28,7 +28,7 @@ import org.sleuthkit.autopsy.casemodule.Case; import junit.framework.Test; import org.netbeans.junit.NbTestCase; import org.openide.util.Exceptions; -import org.python.icu.impl.Assert; +import junit.framework.Assert; import org.sleuthkit.autopsy.casemodule.ImageDSProcessor; import org.sleuthkit.autopsy.casemodule.LocalFilesDSProcessor; import org.sleuthkit.autopsy.casemodule.services.FileManager; @@ -117,7 +117,7 @@ public class IngestFileFiltersTest extends NbTestCase { } } catch (TskCoreException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } testSucceeded = true; @@ -153,7 +153,7 @@ public class IngestFileFiltersTest extends NbTestCase { } } catch (TskCoreException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } testSucceeded = true; @@ -198,7 +198,7 @@ public class IngestFileFiltersTest extends NbTestCase { } } catch (TskCoreException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } testSucceeded = true; @@ -234,7 +234,7 @@ public class IngestFileFiltersTest extends NbTestCase { } } catch (TskCoreException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } testSucceeded = true; @@ -283,7 +283,7 @@ public class IngestFileFiltersTest extends NbTestCase { } catch (TskCoreException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } testSucceeded = true; @@ -313,11 +313,11 @@ public class IngestFileFiltersTest extends NbTestCase { assertEquals("PhotoRec Carver", errs.get(0).getModuleDisplayName()); } catch (InterruptedException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } } catch (TskCoreException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } testSucceeded = true; @@ -364,7 +364,7 @@ public class IngestFileFiltersTest extends NbTestCase { assertEquals(11, numTypeJpgFiles); } catch (TskCoreException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } testSucceeded = true; diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionTest.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionTest.java index dc1f85afcd..e8b5d2e214 100755 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionTest.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionTest.java @@ -27,7 +27,7 @@ import org.sleuthkit.autopsy.casemodule.Case; import junit.framework.Test; import org.netbeans.junit.NbTestCase; import org.openide.util.Exceptions; -import org.python.icu.impl.Assert; +import junit.framework.Assert; import org.sleuthkit.autopsy.casemodule.ImageDSProcessor; import org.sleuthkit.autopsy.casemodule.services.FileManager; import org.sleuthkit.autopsy.ingest.IngestJobSettings; @@ -151,7 +151,7 @@ public class EncryptionDetectionTest extends NbTestCase { assertEquals(errorMessage, true, vol2Found); } catch (TskCoreException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } testSucceeded = true; @@ -238,7 +238,7 @@ public class EncryptionDetectionTest extends NbTestCase { } } catch (TskCoreException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } testSucceeded = true; @@ -293,7 +293,7 @@ public class EncryptionDetectionTest extends NbTestCase { assertEquals("Encrypted Container file should have one encyption suspected artifact", 1, numberOfEncryptedContainers); } catch (TskCoreException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } testSucceeded = true; @@ -358,7 +358,7 @@ public class EncryptionDetectionTest extends NbTestCase { } } catch (TskCoreException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } } 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 224f2c9dd6..54647ab232 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 @@ -26,7 +26,7 @@ import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; import org.apache.commons.io.FileUtils; import org.openide.util.Exceptions; -import org.python.icu.impl.Assert; +import junit.framework.Assert; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.CaseActionException; import org.sleuthkit.autopsy.casemodule.CaseDetails; @@ -54,7 +54,7 @@ public final class CaseUtils { deleteCaseDir(caseDir); } catch (IOException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } assertFalse("Unable to delete existing test directory", caseDir.exists()); // Create the test directory @@ -66,7 +66,7 @@ public final class CaseUtils { currentCase = Case.getCurrentCaseThrows(); } catch (CaseActionException | NoCurrentCaseException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } assertTrue(caseDir.exists()); @@ -100,7 +100,7 @@ public final class CaseUtils { } } catch (CaseActionException | IOException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } } diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/testutils/IngestUtils.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/testutils/IngestUtils.java index 234cbc80a4..fd0c8e38ab 100755 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/testutils/IngestUtils.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/testutils/IngestUtils.java @@ -22,7 +22,7 @@ import java.nio.file.Path; import java.util.List; import static junit.framework.Assert.assertEquals; import org.openide.util.Exceptions; -import org.python.icu.impl.Assert; +import junit.framework.Assert; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback; import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor; import org.sleuthkit.autopsy.ingest.IngestJobSettings; @@ -69,7 +69,7 @@ public final class IngestUtils { } } catch (AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException | InterruptedException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } } @@ -91,7 +91,7 @@ public final class IngestUtils { assertEquals(joinedErrors.toString(), 0, errs.size()); } catch (InterruptedException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } } From d63167fd91bc3e2c482c7c25c12ca83a1e6d9310 Mon Sep 17 00:00:00 2001 From: rishwanth1995 Date: Thu, 16 Aug 2018 16:59:38 -0400 Subject: [PATCH 097/150] added files for live_Cd --- unix/autopsy.png | Bin 0 -> 48497 bytes unix/autopsy_wallpaper1.png | Bin 0 -> 48326 bytes unix/autopsy_wallpaper2.png | Bin 0 -> 48782 bytes unix/launch_script_bootable.sh | 50 ++++++++++++++++++--------------- 4 files changed, 27 insertions(+), 23 deletions(-) create mode 100644 unix/autopsy.png create mode 100644 unix/autopsy_wallpaper1.png create mode 100644 unix/autopsy_wallpaper2.png diff --git a/unix/autopsy.png b/unix/autopsy.png new file mode 100644 index 0000000000000000000000000000000000000000..3173fb97b55ba1b34ec5dd4e41383e402f37b482 GIT binary patch literal 48497 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 literal 0 HcmV?d00001 diff --git a/unix/autopsy_wallpaper1.png b/unix/autopsy_wallpaper1.png new file mode 100644 index 0000000000000000000000000000000000000000..ec0e514db3158899bec5d27653ab19eebe7f167d GIT binary patch literal 48326 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 OM`J_N???vU|M*|PeJODO literal 0 HcmV?d00001 diff --git a/unix/autopsy_wallpaper2.png b/unix/autopsy_wallpaper2.png new file mode 100644 index 0000000000000000000000000000000000000000..15e455d7854738732c39ca8b1a865b46293c5b81 GIT binary patch literal 48782 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 literal 0 HcmV?d00001 diff --git a/unix/launch_script_bootable.sh b/unix/launch_script_bootable.sh index 9223c42661..c39fecbcdb 100644 --- a/unix/launch_script_bootable.sh +++ b/unix/launch_script_bootable.sh @@ -25,24 +25,22 @@ errorLog () { } #Verify we can find the script -if [[ -x "$AUTOPSY_BIN" ]]; then - infoLog "Autopsy found" -else - errorLog "Autopsy binaries not found at $AUTOPSY_BIN. Exiting....." +if [[ ! -x "$AUTOPSY_BIN" ]]; then + errorLog "Autopsy binaries not found at $AUTOPSY_BIN. Exiting....." fi # Create folders on external drive createConfigDirectories () { + if [ ! -d "$1" ]; then + mkdir $1 if [ ! -d "$1" ]; then - mkdir $1 - if [ ! -d "$1" ]; then - errorLog "error while creating $1" - else - infoLog "$1 successfully created" - fi + errorLog "error while creating $1" + else + infoLog "$1 successfully created" fi - return 0 + fi + return 0 } @@ -53,23 +51,29 @@ showAndReadOptions () { echo "Select a mounted disk to create config directory" # Maintainers: Adjust these grep statements based on where your # platform mounts media. - mnt=( $(mount | grep "media" | grep "rw" | awk '{print $3}') ) + mnt=( $(findmnt -lo source,target,fstype,label,options,size | grep "media" | grep "rw" | awk '{print $1, $2, $4, $6}') ) - # Add option to user to not use mounted media length=${#mnt[@]} mnt[length]="Do not store on mounted disk" - options_length=$(( length + 1 )) + options_length=$(( length / 4 + 1 )) + if [ $length -gt 0 ]; then + printf "%10s\t%10s\t%10s\t%10s\t%10s\n" "OPTION" "SOURCE" "TARGET" "LABEL" "SIZE" + fi - x=1 - for word in "${mnt[@]}" + for ((i=0;i< $options_length;i++)); do - echo [$x] "${word}" - x=$((x + 1)) + printf "%10s\t" "[$(( i+1 ))]" + for((j=0;j<4;j++)); + do + printf "%10s\t" "${mnt[j + i * 4]}" + + done + echo done read -n 1 option if [[ $option = "" ]] || ! [[ "$option" =~ ^[0-9]+$ ]]; then - echo "Please choose a valid option" - showAndReadOptions + echo "Please choose a valid option" + showAndReadOptions fi } @@ -85,7 +89,7 @@ do done if [ "$option" != "$options_length" ]; then - index=$(( option - 1 )) + index=$(( (option - 1) * 4 + 1 )) echo "Autopsy configurations will be stored in" "${mnt[$index]}"". Are you sure? (y/n)" read affirmation if [ "$affirmation" == "y" ] || [ "$affirmation" == "Y" ]; then @@ -106,9 +110,9 @@ if [ "$option" != "$options_length" ]; then createConfigDirectories $autopsyConfigDir && createConfigDirectories $userDirectory if [ $? -eq 0 ]; then - sh $AUTOPSY_BIN --userdir $userDirectory + sh $AUTOPSY_BIN --userdir $userDirectory fi fi else - sh $AUTOPSY_BIN + sh $AUTOPSY_BIN fi From 2b616bc5de87d6b22b82315b1b152c71b0602b61 Mon Sep 17 00:00:00 2001 From: rishwanth1995 Date: Thu, 16 Aug 2018 17:11:57 -0400 Subject: [PATCH 098/150] added live_iso setup script --- unix/live_iso.sh | 73 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 unix/live_iso.sh diff --git a/unix/live_iso.sh b/unix/live_iso.sh new file mode 100644 index 0000000000..0d1b252285 --- /dev/null +++ b/unix/live_iso.sh @@ -0,0 +1,73 @@ +#This script is used automate the process of creating the live ISO. + +#!/bin/bash + + +set -e + +#installing java8 + +apt-get purge ubiquity +add-apt-repository ppa:webupd8team/java; +apt-get update; +apt-get install oracle-java8-installer; +source /etc/profile.d/jdk.sh + +#installing sleuthkit dependencies + +apt-get update && apt-get upgrade; +apt-get install libtool automake libpq-dev postgresql libewf-dev libafflib-dev libvhdi-dev libvmdk-dev git testdisk ant build-essential aptitude wget unzip; +apt-get install libstdc++6; + +#installing sleuthkit using the debian files + +if [ "$1" != "" ]; then +[ ! -f "./$1" ] && wget https://github.com/sleuthkit/sleuthkit/releases/download/sleuthkit-${1:15:5}/$1; + apt-get -y install ./$1; + rm ./$1 +fi + + +installAutopsy () { +[ ! -f "./$1" ] && wget https://github.com/sleuthkit/autopsy/releases/download/${1%.*}/$1; + [ ! -d "/${1%.*}" ] && unzip ./$1 -d / + cd /${1%.*} && sh unix_setup.sh && cd - + rm ./$1 > /dev/null + } + + +#installing autopsy using the zip files + +if [ "$2" != "" ]; then + installAutopsy $2; +fi + +[ ! -f "./launch_script_bootable.sh" ] && wget https://raw.githubusercontent.com/sleuthkit/autopsy/develop/unix/launch_script_bootable.sh +mv launch_script_bootable.sh /${2%.*}/autopsy.sh +sed -i -e "s/\/usr\/share\/autopsy-4.7.0\/bin\/autopsy/\/${2%.*}\/bin\/autopsy/g" /${2%.*}/autopsy.sh +chmod +x /${2%.*}/autopsy.sh + +ln -s /${2%.*}/autopsy.sh /usr/local/bin/autopsy + +touch /usr/share/applications/autopsy.desktop + +echo -e "[Desktop Entry]\nVersion=1.0\nName=Autopsy\nComment=Complete Digital forensics analysis suite\nExec=sudo /usr/local/bin/autopsy\nIcon=/usr/share/icons/autopsy.png\nTerminal=true\nType=Application\nCategories=Utility;System;" > /usr/share/applications/autopsy.desktop + +chmod +x /usr/share/applications/autopsy.desktop + +#setup desktop files +mkdir /etc/skel/Desktop +cp /usr/share/applications/autopsy.desktop /etc/skel/Desktop/ +cp /usr/share/applications/lxterminal.desktop /etc/skel/Desktop/ + +#setup autopsy icon +[ ! -f "./autopsy.png" ] && wget https://github.com/sleuthkit/autopsy/raw/develop/unix/autopsy.png +mv ./autopsy.png /usr/share/icons + +#setup iso wallpaper +[ ! -f "./autopsy_wallpaper1.png" ]wget https://github.com/sleuthkit/autopsy/raw/develop/unix/autopsy_wallpaper1.png +mv ./autopsy_wallpaper1.png /usr/share/lubuntu/wallpapers/autopsy_wallpaper.png +unlink lubuntu-default-wallpaper.png +unlink lubuntu-default-wallpaper.jpg +ln -s autopsy_wallpaper.png lubuntu-default-wallpaper.png +ln -s autopsy-wallpaper.png lubuntu-default-wallpaper.jpg From 4e401c52f81c71f0333140681477d02e0b20c6f9 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Thu, 16 Aug 2018 17:17:56 -0400 Subject: [PATCH 099/150] Final comment additions --- .../tabulardatareader/AbstractReader.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/tabulardatareader/AbstractReader.java b/Core/src/org/sleuthkit/autopsy/tabulardatareader/AbstractReader.java index c9c0bce055..2e0c24d1db 100755 --- a/Core/src/org/sleuthkit/autopsy/tabulardatareader/AbstractReader.java +++ b/Core/src/org/sleuthkit/autopsy/tabulardatareader/AbstractReader.java @@ -65,7 +65,7 @@ public abstract class AbstractReader implements AutoCloseable { * Return the a mapping of table names to table schemas (may be in the form of * headers or create table statements for databases). * - * @return + * @return Mapping of table names to schemas * @throws org.sleuthkit.autopsy.tabulardatareader.AbstractReader.FileReaderException */ public abstract Map getTableSchemas() throws FileReaderException; @@ -74,7 +74,7 @@ public abstract class AbstractReader implements AutoCloseable { * Returns the row count fo the given table name. * * @param tableName - * @return + * @return number of rows in the current table * @throws org.sleuthkit.autopsy.tabulardatareader.AbstractReader.FileReaderException */ public abstract Integer getRowCountFromTable(String tableName) throws FileReaderException; @@ -83,17 +83,19 @@ public abstract class AbstractReader implements AutoCloseable { * Returns a collection view of the rows in a table. * * @param tableName - * @return + * @return List view of the rows in the table * @throws org.sleuthkit.autopsy.tabulardatareader.AbstractReader.FileReaderException */ public abstract List> getRowsFromTable(String tableName) throws FileReaderException; /** + * Returns a window of rows starting at the offset and ending when the number of rows read + * equals the 'numRowsToRead' parameter or there is nothing left to read. * - * @param tableName - * @param offset - * @param numRowsToRead - * @return + * @param tableName table name to be read from + * @param offset start index to begin reading + * @param numRowsToRead number of rows to read past offset + * @return List view of the rows in the table * @throws org.sleuthkit.autopsy.tabulardatareader.AbstractReader.FileReaderException */ public abstract List> getRowsFromTable(String tableName, From 17d49ef11f1e132f3c5ec7e901248dfd6d3e7e08 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Thu, 16 Aug 2018 17:25:55 -0400 Subject: [PATCH 100/150] Removed commons validator dependency from autopsy and moved it --- Core/nbproject/project.properties | 5 ++--- Core/nbproject/project.xml | 4 ---- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/Core/nbproject/project.properties b/Core/nbproject/project.properties index cdfddfc078..08bc1496f9 100644 --- a/Core/nbproject/project.properties +++ b/Core/nbproject/project.properties @@ -1,9 +1,8 @@ file.reference.activemq-all-5.11.1.jar=release/modules/ext/activemq-all-5.11.1.jar 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.commons-validator-1.6.jar=release/modules/ext/commons-validator-1.6.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.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 diff --git a/Core/nbproject/project.xml b/Core/nbproject/project.xml index fcf968d5c5..f07138c40f 100644 --- a/Core/nbproject/project.xml +++ b/Core/nbproject/project.xml @@ -356,10 +356,6 @@ ext/cxf-rt-transports-http-3.0.16.jar release/modules/ext/cxf-rt-transports-http-3.0.16.jar - - ext/commons-validator-1.6.jar - release\modules\ext\commons-validator-1.6.jar - ext/curator-framework-2.8.0.jar release/modules/ext/curator-framework-2.8.0.jar From ee9f961bdd73655b79bc77c93103bcedac135fb0 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Thu, 16 Aug 2018 17:26:58 -0400 Subject: [PATCH 101/150] Ivy change --- Core/ivy.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/Core/ivy.xml b/Core/ivy.xml index 5124aa2fd8..1ed71d69cf 100644 --- a/Core/ivy.xml +++ b/Core/ivy.xml @@ -29,7 +29,6 @@ - From 6d5c5662da9aee8ed46e6eb62a830aa5bc77b9ce Mon Sep 17 00:00:00 2001 From: Andrew Ziehl Date: Thu, 16 Aug 2018 14:51:33 -0700 Subject: [PATCH 102/150] fix normalization discrepancies causing tests to fail. may conflict with. 3868 --- .../centralrepository/datamodel/AbstractSqlEamDb.java | 11 ++++------- .../datamodel/CorrelationAttributeInstance.java | 2 +- .../datamodel/CentralRepoDatamodelTest.java | 5 +++-- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java index 6f0e408339..86e9c68977 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java @@ -675,8 +675,8 @@ abstract class AbstractSqlEamDb implements EamDb { preparedStatement.setString(1, eamArtifact.getCorrelationCase().getCaseUUID()); preparedStatement.setString(2, eamArtifact.getCorrelationDataSource().getDeviceID()); preparedStatement.setInt(3, eamArtifact.getCorrelationDataSource().getCaseID()); - preparedStatement.setString(4, eamArtifact.getCorrelationValue()); - preparedStatement.setString(5, eamArtifact.getFilePath()); + preparedStatement.setString(4, eamArtifact.getCorrelationValue().toLowerCase()); + preparedStatement.setString(5, eamArtifact.getFilePath().toLowerCase()); preparedStatement.setByte(6, eamArtifact.getKnownStatus().getFileKnownValue()); if ("".equals(eamArtifact.getComment())) { preparedStatement.setNull(7, Types.INTEGER); @@ -1230,9 +1230,6 @@ abstract class AbstractSqlEamDb implements EamDb { */ @Override public void updateAttributeInstanceComment(CorrelationAttributeInstance eamArtifact) throws EamDbException { - if (eamArtifact == null) { - throw new EamDbException("CorrelationAttribute is null"); - } if (eamArtifact == null) { throw new EamDbException("CorrelationAttributeInstance is null"); @@ -1262,8 +1259,8 @@ abstract class AbstractSqlEamDb implements EamDb { preparedQuery.setString(1, eamArtifact.getComment()); preparedQuery.setString(2, eamArtifact.getCorrelationCase().getCaseUUID()); preparedQuery.setString(3, eamArtifact.getCorrelationDataSource().getDeviceID()); - preparedQuery.setString(4, eamArtifact.getCorrelationValue()); - preparedQuery.setString(5, eamArtifact.getFilePath()); + preparedQuery.setString(4, eamArtifact.getCorrelationValue().toLowerCase()); + preparedQuery.setString(5, eamArtifact.getFilePath().toLowerCase()); preparedQuery.executeUpdate(); } catch (SQLException ex) { throw new EamDbException("Error getting/setting artifact instance comment=" + eamArtifact.getComment(), ex); // NON-NLS diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java index 718437286c..1a17b8d366 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java @@ -183,7 +183,7 @@ public class CorrelationAttributeInstance implements Serializable { /** * @return the database ID */ - int getID() { + public int getID() { return ID; } 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 e07debddb2..6cebfd98c2 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 @@ -995,6 +995,7 @@ public class CentralRepoDatamodelTest extends TestCase { // Test updating a correlation attribute instance comment try { + // TODO CorrelationAttributeInstance correlationAttribute = EamDb.getInstance().getCorrelationAttributeInstance( usbDeviceType, case1, dataSource1fromCase1, devIdValue, devIdPath); assertNotNull("getCorrelationAttributeInstance returned null", correlationAttribute); @@ -1118,7 +1119,7 @@ public class CentralRepoDatamodelTest extends TestCase { DbManager.addArtifactInstance(attr1); DbManager.addArtifactInstance(attr2); AttributeInstanceTableCallback instancetableCallback = new AttributeInstanceTableCallback(); - DbManager.processInstanceTableWhere(fileType, String.format("id = %s", attr1.getID()), instancetableCallback); + DbManager.processInstanceTableWhere(fileType, String.format("value = %s", callbackTestFileHash), instancetableCallback); int count1 = instancetableCallback.getCounter(); int count2 = instancetableCallback.getCounterNamingConvention(); assertTrue("Process Instance count with filepath naming convention: " + count2 + "-expected 2", count2 == 2); @@ -2693,7 +2694,7 @@ public class CentralRepoDatamodelTest extends TestCase { public void process(ResultSet resultSet) { try { while(resultSet.next()){ - if(InstanceTableCallback.getFilePath(resultSet).contains("processinstancecallback")){ + if(InstanceTableCallback.getFilePath(resultSet).toLowerCase().contains("processinstancecallback")){ counterNamingConvention++; }else{ counter++; From 38a3b1604e470d3677d627943b56e7dc0b048b6f Mon Sep 17 00:00:00 2001 From: Andrew Ziehl Date: Fri, 17 Aug 2018 10:07:03 -0700 Subject: [PATCH 103/150] test fix --- .../sleuthkit/autopsy/commonfilessearch/IntraCaseTestUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IntraCaseTestUtils.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IntraCaseTestUtils.java index 82c38cdf56..ed80a784aa 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IntraCaseTestUtils.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IntraCaseTestUtils.java @@ -172,7 +172,7 @@ class IntraCaseTestUtils { String dataSourceName = objectIdToDataSourceMap.get(objectId); - if (name.equals(name) && dataSourceName.equals(dataSource)) { + if (name.equalsIgnoreCase(fileName) && dataSourceName.equalsIgnoreCase(dataSource)) { tally++; } } From 934fd0198cdc83250940326d792f7dfdaa413362 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Fri, 17 Aug 2018 13:39:52 -0400 Subject: [PATCH 104/150] Fixed the iterator error introduced from the xlsx streamer approach. --- .../tabulardatareader/ExcelReader.java | 62 ++++++++++++------- .../tabulardatareader/SQLiteReader.java | 2 +- 2 files changed, 42 insertions(+), 22 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/tabulardatareader/ExcelReader.java b/Core/src/org/sleuthkit/autopsy/tabulardatareader/ExcelReader.java index a830e9e2b5..607fc6de98 100755 --- a/Core/src/org/sleuthkit/autopsy/tabulardatareader/ExcelReader.java +++ b/Core/src/org/sleuthkit/autopsy/tabulardatareader/ExcelReader.java @@ -21,7 +21,6 @@ package org.sleuthkit.autopsy.tabulardatareader; import java.io.File; import java.io.FileInputStream; import java.io.IOException; -import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; @@ -55,12 +54,14 @@ public class ExcelReader extends AbstractReader { private final String XLSXMimeType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; private final String XLSMimeType = "application/vnd.ms-excel"; private final String EMPTY_CELL_STRING = ""; + private HashMap headerCache; public ExcelReader(AbstractFile file, String localDiskPath, String mimeType) throws FileReaderInitException { super(file, localDiskPath); try { this.workbook = createWorkbook(localDiskPath, mimeType); + headerCache = new HashMap<>(); } catch (IOException ex) { throw new FileReaderInitException(ex); } @@ -92,13 +93,12 @@ public class ExcelReader extends AbstractReader { throw new FileReaderInitException(e); } case XLSXMimeType: - InputStream is = new FileInputStream(new File(localDiskPath)); //StreamingReader is part of the xlsx streamer dependency that creates //a streaming version of XSSFWorkbook for reading (SXSSFWorkbook is only for writing //large workbooks, not reading). This libary provides a workbook interface //that is mostly identical to the poi workbook api, hence both the HSSFWorkbook //and this can use the same functions below. - return StreamingReader.builder().rowCacheSize(500).bufferSize(4096).open(is); + return StreamingReader.builder().rowCacheSize(500).open(new File(localDiskPath)); default: throw new FileReaderInitException(String.format("Excel reader for mime " + "type [%s] is not supported", mimeType)); @@ -126,7 +126,12 @@ public class ExcelReader extends AbstractReader { */ @Override public List> getRowsFromTable(String tableName) throws FileReaderException { - return getRowsFromTable(tableName, 0, getRowCountFromTable(tableName)); + //Pad with + 1 because rows are zero index, thus a LastRowNum() (in getRowCountFromTable()) of 1 + //indicates that there are records in 0 and 1 and so a total row count of + //2. This also implies there is no way to determine if a workbook is empty, + //since a last row num of 0 doesnt differentiate between a record in 0 or + //nothing in the workbook. Such a HSSF. + return getRowsFromTable(tableName, 0, getRowCountFromTable(tableName) + 1); } /** @@ -145,27 +150,45 @@ public class ExcelReader extends AbstractReader { @Override public List> getRowsFromTable(String tableName, int offset, int numRowsToRead) throws FileReaderException { + //StreamingReader maintains the same pointer to a sheet rowIterator, so this + //call returns an iterator that could have already been iterated on instead + //of a fresh copy. We must cache the header value from the call to + //getTableSchemas as important information in the first row could have been + //missed. Iterator sheetIter = workbook.getSheet(tableName).rowIterator(); List> rowList = new ArrayList<>(); int currRowCount = 0; + + //Read the header value as the header may be a row of data in the + //excel sheet + if(headerCache.containsKey(tableName)) { + Row header = headerCache.get(tableName); + if(currRowCount++ >= offset) { + rowList.add(getRowMap(tableName, header)); + } + } + while(sheetIter.hasNext() && currRowCount < (offset + numRowsToRead)) { Row currRow = sheetIter.next(); - if(currRowCount++ < offset) { - continue; + if(currRowCount++ >= offset) { + rowList.add(getRowMap(tableName, currRow)); } - Map row = new HashMap<>(); - for(Cell cell : currRow) { - String columnName = getColumnName(cell, tableName); - Object value = getCellValue(cell); - row.put(columnName, value); - } - rowList.add(row); } return rowList; } + private Map getRowMap(String tableName, Row row) { + Map rowMap = new HashMap<>(); + for(Cell cell : row) { + String columnName = getColumnName(cell, tableName); + Object value = getCellValue(cell); + rowMap.put(columnName, value); + } + return rowMap; + } + /** * Returns the value of a given cell. The correct value function must be * called on a cell depending on its type, hence the switch. @@ -202,19 +225,14 @@ public class ExcelReader extends AbstractReader { * @return the name of the column the current cell lives in */ private String getColumnName(Cell cell, String tableName) { - Iterator sheetIter = workbook.getSheet(tableName).rowIterator(); - if(sheetIter.hasNext()) { - Row header = sheetIter.next(); + if(headerCache.containsKey(tableName)) { + Row header = headerCache.get(tableName); Cell columnHeaderCell = header.getCell(cell.getRowIndex()); if(columnHeaderCell == null) { return EMPTY_CELL_STRING; } Object columnHeaderValue = getCellValue(columnHeaderCell); - if(columnHeaderValue instanceof String) { - return (String) columnHeaderValue; - } else { - return columnHeaderValue.toString(); - } + return columnHeaderValue.toString(); } //No header present return EMPTY_CELL_STRING; @@ -222,6 +240,7 @@ public class ExcelReader extends AbstractReader { /** * Returns a map of sheet names to headers (header is in a comma-seperated string). + * Warning: Only call this ONCE per excel file. * * @return A map of sheet names to header strings. * @throws org.sleuthkit.autopsy.tabulardatareader.AbstractReader.FileReaderException @@ -234,6 +253,7 @@ public class ExcelReader extends AbstractReader { if(iterator.hasNext()) { //Consume header Row header = iterator.next(); + headerCache.put(sheet.getSheetName(), header); String headerStringFormat = StringUtils.join(header.cellIterator(), ", "); tableSchemas.put(sheet.getSheetName(), headerStringFormat); } diff --git a/Core/src/org/sleuthkit/autopsy/tabulardatareader/SQLiteReader.java b/Core/src/org/sleuthkit/autopsy/tabulardatareader/SQLiteReader.java index b939ced24e..91eba0c5c6 100755 --- a/Core/src/org/sleuthkit/autopsy/tabulardatareader/SQLiteReader.java +++ b/Core/src/org/sleuthkit/autopsy/tabulardatareader/SQLiteReader.java @@ -82,7 +82,7 @@ public class SQLiteReader extends AbstractReader { * Searches for a meta file associated with the give SQLite database. If found, * copies the file to the local disk folder * - * @param file file being processed + * @param sqliteFile file being processed * @param metaFileName name of meta file to look for * @throws NoCurrentCaseException Case has been closed. * @throws TskCoreException fileManager cannot find AbstractFile files. From f4f874fff2468de449612d3fb8a1417d1e0475d2 Mon Sep 17 00:00:00 2001 From: Andrew Ziehl Date: Fri, 17 Aug 2018 11:17:17 -0700 Subject: [PATCH 105/150] address codeacy complaints. --- .../contentviewer/DataContentViewerOtherCases.java | 1 - .../contentviewer/OtherOccurrenceNodeData.java | 9 --------- .../OtherOccurrenceNodeInstanceData.java | 3 --- .../datamodel/CorrelationAttributeInstance.java | 4 ++-- .../centralrepository/datamodel/EamArtifactUtil.java | 6 ++---- .../autopsy/centralrepository/datamodel/EamDb.java | 12 ++++++------ 6 files changed, 10 insertions(+), 25 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java index 1fc3dd9e84..f7991628ea 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java @@ -52,7 +52,6 @@ import javax.swing.table.TableColumn; import org.joda.time.DateTimeZone; import org.joda.time.LocalDateTime; import org.openide.nodes.Node; -import org.openide.util.Exceptions; import org.openide.util.NbBundle.Messages; import org.openide.util.lookup.ServiceProvider; import org.sleuthkit.autopsy.casemodule.Case; diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeData.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeData.java index 0498cc4c38..da4915e75f 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeData.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeData.java @@ -17,15 +17,6 @@ * limitations under the License. */ package org.sleuthkit.autopsy.centralrepository.contentviewer; - -import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; -import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; -import org.sleuthkit.datamodel.AbstractFile; -import org.sleuthkit.datamodel.DataSource; -import org.sleuthkit.datamodel.TskCoreException; -import org.sleuthkit.datamodel.TskData; -import org.sleuthkit.datamodel.TskDataException; /** * Marker interface for Other Occurrences nodes. */ diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeInstanceData.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeInstanceData.java index 9d23ea181d..6eb23455c0 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeInstanceData.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeInstanceData.java @@ -41,7 +41,6 @@ class OtherOccurrenceNodeInstanceData implements OtherOccurrenceNodeData { private String dataSourceName; private final String filePath; private final String typeStr; - private final CorrelationAttributeInstance.Type type; private final String value; private TskData.FileKnown known; private String comment; @@ -61,7 +60,6 @@ class OtherOccurrenceNodeInstanceData implements OtherOccurrenceNodeData { dataSourceName = instance.getCorrelationDataSource().getName(); filePath = instance.getFilePath(); this.typeStr = type.getDisplayName(); - this.type = type; this.value = value; known = instance.getKnownStatus(); comment = instance.getComment(); @@ -87,7 +85,6 @@ class OtherOccurrenceNodeInstanceData implements OtherOccurrenceNodeData { filePath = newFile.getParentPath() + newFile.getName(); typeStr = FILE_TYPE_STR; - this.type = null; value = newFile.getMd5Hash(); known = newFile.getKnown(); comment = ""; diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java index 1a17b8d366..349faab736 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java @@ -276,7 +276,7 @@ public class CorrelationAttributeInstance implements Serializable { private String dbTableName; private Boolean supported; private Boolean enabled; - private final String DB_NAMES_REGEX = "[a-z][a-z0-9_]*"; + private final static String DB_NAMES_REGEX = "[a-z][a-z0-9_]*"; /** * @@ -361,7 +361,7 @@ public class CorrelationAttributeInstance implements Serializable { @Override public String toString() { - StringBuilder str = new StringBuilder(); + StringBuilder str = new StringBuilder(55); str.append("(id=").append(getId()); str.append(", displayName=").append(getDisplayName()); str.append(", dbTableName=").append(getDbTableName()); diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamArtifactUtil.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamArtifactUtil.java index 7f51f1ebe2..02737a5010 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamArtifactUtil.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamArtifactUtil.java @@ -202,7 +202,7 @@ public class EamArtifactUtil { if (null == correlationCase) { correlationCase = EamDb.getInstance().newCase(Case.getCurrentCaseThrows()); } - CorrelationAttributeInstance eamInstance = new CorrelationAttributeInstance( + return new CorrelationAttributeInstance( value, correlationType, correlationCase, @@ -211,7 +211,6 @@ public class EamArtifactUtil { "", TskData.FileKnown.UNKNOWN ); - return eamInstance; } catch (TskCoreException | EamDbException ex) { logger.log(Level.SEVERE, "Error creating artifact instance.", ex); // NON-NLS @@ -315,14 +314,13 @@ public class EamArtifactUtil { if (null == correlationCase) { correlationCase = EamDb.getInstance().newCase(Case.getCurrentCaseThrows()); } - CorrelationAttributeInstance eamArtifact = new CorrelationAttributeInstance( + return new CorrelationAttributeInstance( filesType, af.getMd5Hash(), correlationCase, CorrelationDataSource.fromTSKDataSource(correlationCase, af.getDataSource()), af.getParentPath() + af.getName()); - return eamArtifact; } catch (TskCoreException | EamDbException ex) { logger.log(Level.SEVERE, "Error making correlation attribute.", ex); return null; diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDb.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDb.java index f202810258..d05cb64ba5 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDb.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDb.java @@ -622,7 +622,7 @@ public interface EamDb { * * @throws EamDbException */ - public int newCorrelationType(CorrelationAttributeInstance.Type newType) throws EamDbException; + int newCorrelationType(CorrelationAttributeInstance.Type newType) throws EamDbException; /** * Get the list of EamArtifact.Type's that are defined in the DB and can be @@ -633,7 +633,7 @@ public interface EamDb { * * @throws EamDbException */ - public List getDefinedCorrelationTypes() throws EamDbException; + List getDefinedCorrelationTypes() throws EamDbException; /** * Get the list of enabled EamArtifact.Type's that will be used to correlate @@ -644,7 +644,7 @@ public interface EamDb { * * @throws EamDbException */ - public List getEnabledCorrelationTypes() throws EamDbException; + List getEnabledCorrelationTypes() throws EamDbException; /** * Get the list of supported EamArtifact.Type's that can be used to @@ -655,7 +655,7 @@ public interface EamDb { * * @throws EamDbException */ - public List getSupportedCorrelationTypes() throws EamDbException; + List getSupportedCorrelationTypes() throws EamDbException; /** * Update a EamArtifact.Type. @@ -664,7 +664,7 @@ public interface EamDb { * * @throws EamDbException */ - public void updateCorrelationType(CorrelationAttributeInstance.Type aType) throws EamDbException; + void updateCorrelationType(CorrelationAttributeInstance.Type aType) throws EamDbException; /** * Get the EamArtifact.Type that has the given Type.Id. @@ -675,7 +675,7 @@ public interface EamDb { * * @throws EamDbException */ - public CorrelationAttributeInstance.Type getCorrelationTypeById(int typeId) throws EamDbException; + CorrelationAttributeInstance.Type getCorrelationTypeById(int typeId) throws EamDbException; /** * Upgrade the schema of the database (if needed) From 43fb8c8eba31b3a1432ae623b8a4bfbf5424c477 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Fri, 17 Aug 2018 14:26:24 -0400 Subject: [PATCH 106/150] Fixed the bugs in the getRowsFromTable method, everything should be working as intended. --- .../tabulardatareader/ExcelReader.java | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/tabulardatareader/ExcelReader.java b/Core/src/org/sleuthkit/autopsy/tabulardatareader/ExcelReader.java index 607fc6de98..71c3b2e8a7 100755 --- a/Core/src/org/sleuthkit/autopsy/tabulardatareader/ExcelReader.java +++ b/Core/src/org/sleuthkit/autopsy/tabulardatareader/ExcelReader.java @@ -131,7 +131,7 @@ public class ExcelReader extends AbstractReader { //2. This also implies there is no way to determine if a workbook is empty, //since a last row num of 0 doesnt differentiate between a record in 0 or //nothing in the workbook. Such a HSSF. - return getRowsFromTable(tableName, 0, getRowCountFromTable(tableName) + 1); + return getRowsFromTable(tableName, 0, getRowCountFromTable(tableName)); } /** @@ -158,22 +158,29 @@ public class ExcelReader extends AbstractReader { Iterator sheetIter = workbook.getSheet(tableName).rowIterator(); List> rowList = new ArrayList<>(); - int currRowCount = 0; - //Read the header value as the header may be a row of data in the //excel sheet if(headerCache.containsKey(tableName)) { Row header = headerCache.get(tableName); - if(currRowCount++ >= offset) { + if(header.getRowNum() >= offset + && header.getRowNum() < (offset + numRowsToRead)) { rowList.add(getRowMap(tableName, header)); } } - while(sheetIter.hasNext() && currRowCount < (offset + numRowsToRead)) { + while(sheetIter.hasNext()) { Row currRow = sheetIter.next(); - if(currRowCount++ >= offset) { + //If the current row number is within the window of our row capture + if(currRow.getRowNum() >= offset + && currRow.getRowNum() < (offset + numRowsToRead)) { rowList.add(getRowMap(tableName, currRow)); } + + //if current row number is equal to our upper bound + //of rows requested to be read. + if(currRow.getRowNum() >= (offset + numRowsToRead)) { + break; + } } return rowList; From 70deebeffa8cb29ba9e7db1f5665cc4e4a113949 Mon Sep 17 00:00:00 2001 From: rishwanth1995 Date: Fri, 17 Aug 2018 14:38:54 -0400 Subject: [PATCH 107/150] used try with resources in JythonModuleLoader --- Core/src/org/sleuthkit/autopsy/python/JythonModuleLoader.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/python/JythonModuleLoader.java b/Core/src/org/sleuthkit/autopsy/python/JythonModuleLoader.java index 700aa0cc5c..04f5e49a1e 100644 --- a/Core/src/org/sleuthkit/autopsy/python/JythonModuleLoader.java +++ b/Core/src/org/sleuthkit/autopsy/python/JythonModuleLoader.java @@ -97,8 +97,7 @@ public final class JythonModuleLoader { if (file.isDirectory()) { File[] pythonScripts = file.listFiles(new PythonScriptFileFilter()); for (File script : pythonScripts) { - try { - Scanner fileScanner = new Scanner(script); + try (Scanner fileScanner = new Scanner(script)) { while (fileScanner.hasNextLine()) { String line = fileScanner.nextLine(); if (line.startsWith("class ") && filter.accept(line)) { //NON-NLS From 71d933698188652489a402217ea9cf5cfe6085a5 Mon Sep 17 00:00:00 2001 From: Brian Sweeney Date: Mon, 20 Aug 2018 09:07:55 -0600 Subject: [PATCH 108/150] resize dialog and add checkbox/text field for frequency feature --- .../commonfilesearch/CommonAttributePanel.form | 15 ++++++++++++--- .../commonfilesearch/CommonAttributePanel.java | 7 +++++-- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.form b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.form index 30afda0b70..f926dfdafa 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.form +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.form @@ -9,7 +9,10 @@ - + + + + @@ -29,15 +32,21 @@ - + + + + + + + - + diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java index 8e32c072f0..737d33de7d 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java @@ -420,7 +420,8 @@ public final class CommonAttributePanel extends javax.swing.JDialog { percentageThreshold = new javax.swing.JTextField(); jLabel1 = new javax.swing.JLabel(); - setMinimumSize(new java.awt.Dimension(412, 350)); + setMinimumSize(new java.awt.Dimension(412, 375)); + setPreferredSize(new java.awt.Dimension(412, 375)); setResizable(false); addWindowListener(new java.awt.event.WindowAdapter() { public void windowClosed(java.awt.event.WindowEvent evt) { @@ -428,7 +429,9 @@ public final class CommonAttributePanel extends javax.swing.JDialog { } }); - jPanel1.setPreferredSize(new java.awt.Dimension(412, 350)); + jPanel1.setMaximumSize(new java.awt.Dimension(412, 375)); + jPanel1.setMinimumSize(new java.awt.Dimension(412, 375)); + jPanel1.setPreferredSize(new java.awt.Dimension(412, 375)); org.openide.awt.Mnemonics.setLocalizedText(commonFilesSearchLabel2, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.commonFilesSearchLabel2.text")); // NOI18N commonFilesSearchLabel2.setFocusable(false); From a0ca35d6ce73bdf6bfc4058a194c48b82fdb1632 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Thu, 16 Aug 2018 14:40:42 -0400 Subject: [PATCH 109/150] Fixed the out of memory issue with excel xml document reading, caught the old excel format exception, and refactored the code --- Core/nbproject/project.properties | 2 +- Core/nbproject/project.xml | 2 +- .../sleuthkit/autopsy/tabulardatareader/AbstractReader.java | 2 +- .../sleuthkit/autopsy/tabulardatareader/ExcelReader.java | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Core/nbproject/project.properties b/Core/nbproject/project.properties index 08bc1496f9..90ce31dab9 100644 --- a/Core/nbproject/project.properties +++ b/Core/nbproject/project.properties @@ -15,7 +15,7 @@ file.reference.postgresql-9.4.1211.jre7.jar=release/modules/ext/postgresql-9.4.1 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 -file.reference.sqlite-jdbc-3.8.11.jar=release/modules/ext/sqlite-jdbc-3.8.11.jar +file.reference.sqlite-jdbc-3.8.11.jar=release\\modules\\ext\\sqlite-jdbc-3.8.11.jar file.reference.StixLib.jar=release/modules/ext/StixLib.jar file.reference.bcprov-jdk15on-1.54.jar=release/modules/ext/bcprov-jdk15on-1.54.jar file.reference.jackcess-2.1.8.jar=release/modules/ext/jackcess-2.1.8.jar diff --git a/Core/nbproject/project.xml b/Core/nbproject/project.xml index f07138c40f..303d14422c 100644 --- a/Core/nbproject/project.xml +++ b/Core/nbproject/project.xml @@ -442,7 +442,7 @@ ext/sqlite-jdbc-3.8.11.jar - release/modules/ext/sqlite-jdbc-3.8.11.jar + release\modules\ext\sqlite-jdbc-3.8.11.jar ext/activemq-all-5.11.1.jar diff --git a/Core/src/org/sleuthkit/autopsy/tabulardatareader/AbstractReader.java b/Core/src/org/sleuthkit/autopsy/tabulardatareader/AbstractReader.java index 2e0c24d1db..cb74819142 100755 --- a/Core/src/org/sleuthkit/autopsy/tabulardatareader/AbstractReader.java +++ b/Core/src/org/sleuthkit/autopsy/tabulardatareader/AbstractReader.java @@ -28,7 +28,7 @@ import org.sleuthkit.datamodel.TskCoreException; /** * An abstract reader interface for retrieving contents from files via a common - * API. + * API. */ public abstract class AbstractReader implements AutoCloseable { diff --git a/Core/src/org/sleuthkit/autopsy/tabulardatareader/ExcelReader.java b/Core/src/org/sleuthkit/autopsy/tabulardatareader/ExcelReader.java index 71c3b2e8a7..2d4bc7f9be 100755 --- a/Core/src/org/sleuthkit/autopsy/tabulardatareader/ExcelReader.java +++ b/Core/src/org/sleuthkit/autopsy/tabulardatareader/ExcelReader.java @@ -100,8 +100,8 @@ public class ExcelReader extends AbstractReader { //and this can use the same functions below. return StreamingReader.builder().rowCacheSize(500).open(new File(localDiskPath)); default: - throw new FileReaderInitException(String.format("Excel reader for mime " - + "type [%s] is not supported", mimeType)); + throw new FileReaderInitException(String.format("Excel reader for mime " + + "type [%s] is not supported", mimeType)); } } @@ -268,7 +268,7 @@ public class ExcelReader extends AbstractReader { return tableSchemas; } - + @Override public void close() { try { From 77bb15ec358bf9d5d995f8efc857727bdd22db65 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Mon, 20 Aug 2018 13:58:03 -0400 Subject: [PATCH 110/150] Patched bug with not catching pois run time exceptions --- .../autopsy/tabulardatareader/FileReaderFactory.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/tabulardatareader/FileReaderFactory.java b/Core/src/org/sleuthkit/autopsy/tabulardatareader/FileReaderFactory.java index 8e98e0447c..5ce902a234 100755 --- a/Core/src/org/sleuthkit/autopsy/tabulardatareader/FileReaderFactory.java +++ b/Core/src/org/sleuthkit/autopsy/tabulardatareader/FileReaderFactory.java @@ -44,7 +44,15 @@ public final class FileReaderFactory { return new SQLiteReader(file, localDiskPath); case "application/vnd.ms-excel": case "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": - return new ExcelReader(file, localDiskPath, mimeType); + try { + return new ExcelReader(file, localDiskPath, mimeType); + //Catches runtime exceptions being emitted from Apache + //POI (such as EncryptedDocumentException) as wraps them + //into FileReaderInitException to be caught and logged + //in the ingest module. + } catch(Exception poiInitException) { + throw new FileReaderInitException(poiInitException); + } default: throw new FileReaderInitException(String.format("Reader for mime " + "type [%s] is not supported", mimeType)); From ede32f6fe1ce533cb68b33d0a0ac8e61d74d6443 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Mon, 20 Aug 2018 14:26:48 -0400 Subject: [PATCH 111/150] Type fix in factory comment' --- .../autopsy/tabulardatareader/FileReaderFactory.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/tabulardatareader/FileReaderFactory.java b/Core/src/org/sleuthkit/autopsy/tabulardatareader/FileReaderFactory.java index 5ce902a234..5295bb924e 100755 --- a/Core/src/org/sleuthkit/autopsy/tabulardatareader/FileReaderFactory.java +++ b/Core/src/org/sleuthkit/autopsy/tabulardatareader/FileReaderFactory.java @@ -32,7 +32,7 @@ public final class FileReaderFactory { * is not supported. * * @param mimeType mimeType passed in from the ingest module - * @param file current file under inspection +g * @param file current file under inspection * @param localDiskPath path for abstract file contents to be written * @return The correct reader class needed to read the file contents * @throws org.sleuthkit.autopsy.tabulardatareader.AbstractReader.FileReaderInitException @@ -47,7 +47,7 @@ public final class FileReaderFactory { try { return new ExcelReader(file, localDiskPath, mimeType); //Catches runtime exceptions being emitted from Apache - //POI (such as EncryptedDocumentException) as wraps them + //POI (such as EncryptedDocumentException) and wraps them //into FileReaderInitException to be caught and logged //in the ingest module. } catch(Exception poiInitException) { From 60402ba2d20368fe32b09257d8dc4dc2b48ecaef Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Mon, 20 Aug 2018 15:12:28 -0400 Subject: [PATCH 112/150] Added final keyword to the readers --- .../org/sleuthkit/autopsy/tabulardatareader/ExcelReader.java | 2 +- .../org/sleuthkit/autopsy/tabulardatareader/SQLiteReader.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/tabulardatareader/ExcelReader.java b/Core/src/org/sleuthkit/autopsy/tabulardatareader/ExcelReader.java index 2d4bc7f9be..6352741d22 100755 --- a/Core/src/org/sleuthkit/autopsy/tabulardatareader/ExcelReader.java +++ b/Core/src/org/sleuthkit/autopsy/tabulardatareader/ExcelReader.java @@ -45,7 +45,7 @@ import org.apache.poi.hssf.OldExcelFormatException; * Reads excel files and implements the abstract reader api for interfacing with the * content. Supports .xls and .xlsx files. */ -public class ExcelReader extends AbstractReader { +public final class ExcelReader extends AbstractReader { /* Boilerplate code */ private final IngestServices services = IngestServices.getInstance(); private final Logger logger = services.getLogger(ExcelReader.class.getName()); diff --git a/Core/src/org/sleuthkit/autopsy/tabulardatareader/SQLiteReader.java b/Core/src/org/sleuthkit/autopsy/tabulardatareader/SQLiteReader.java index 91eba0c5c6..328f1ac628 100755 --- a/Core/src/org/sleuthkit/autopsy/tabulardatareader/SQLiteReader.java +++ b/Core/src/org/sleuthkit/autopsy/tabulardatareader/SQLiteReader.java @@ -50,7 +50,7 @@ import org.sleuthkit.datamodel.TskCoreException; @NbBundle.Messages({ "SQLiteReader.ReadSQLiteFiles.moduleName=SQLiteReader" }) -public class SQLiteReader extends AbstractReader { +public final class SQLiteReader extends AbstractReader { private final Connection connection; private final IngestServices services = IngestServices.getInstance(); From 137917c37e2c7da99b778c2f4bcd4b18ee45fb33 Mon Sep 17 00:00:00 2001 From: rishwanth1995 Date: Mon, 20 Aug 2018 17:05:13 -0400 Subject: [PATCH 113/150] added casedir as optional in autopsyoption --- .../org/sleuthkit/autopsy/core/AutopsyOptionProcessor.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/core/AutopsyOptionProcessor.java b/Core/src/org/sleuthkit/autopsy/core/AutopsyOptionProcessor.java index a6a122da8a..bf7cb7cd99 100644 --- a/Core/src/org/sleuthkit/autopsy/core/AutopsyOptionProcessor.java +++ b/Core/src/org/sleuthkit/autopsy/core/AutopsyOptionProcessor.java @@ -40,7 +40,7 @@ import org.sleuthkit.autopsy.coreutils.PlatformUtil; public class AutopsyOptionProcessor extends OptionProcessor { private static final Logger logger = Logger.getLogger(AutopsyOptionProcessor.class.getName()); - private final Option liveAutopsyOption = Option.withoutArgument('l', "liveAutopsy"); + private final Option liveAutopsyOption = Option.optionalArgument('l', "liveAutopsy"); private final static String PROP_BASECASE = "LBL_BaseCase_PATH"; @@ -56,7 +56,9 @@ public class AutopsyOptionProcessor extends OptionProcessor { if(values.containsKey(liveAutopsyOption)){ try { RuntimeProperties.setRunningInTarget(true); - ModuleSettings.setConfigSetting(ModuleSettings.MAIN_SETTINGS, PROP_BASECASE , PlatformUtil.getUserDirectory().toString()); + String[] dir= values.get(liveAutopsyOption); + String directory = dir == null ? PlatformUtil.getUserDirectory().toString() : dir[0]; + ModuleSettings.setConfigSetting(ModuleSettings.MAIN_SETTINGS, PROP_BASECASE, directory); } catch (RuntimeProperties.RuntimePropertiesException ex) { logger.log(Level.SEVERE, ex.getMessage(), ex); } From 59094c688f375c9d54d3609e63305fae74f5496f Mon Sep 17 00:00:00 2001 From: rishwanth1995 Date: Mon, 20 Aug 2018 17:07:50 -0400 Subject: [PATCH 114/150] added live_iso files --- unix/launch_script_bootable.sh | 84 ++++++++++++++++++++++++++++------ 1 file changed, 71 insertions(+), 13 deletions(-) diff --git a/unix/launch_script_bootable.sh b/unix/launch_script_bootable.sh index c39fecbcdb..56e905dae2 100644 --- a/unix/launch_script_bootable.sh +++ b/unix/launch_script_bootable.sh @@ -48,35 +48,81 @@ options_length=0 # Display list of mounted devices, prompt user, and store # results in global variables showAndReadOptions () { - echo "Select a mounted disk to create config directory" + echo -e "\n" + echo "Select a mounted disk to create config directory: " # Maintainers: Adjust these grep statements based on where your # platform mounts media. - mnt=( $(findmnt -lo source,target,fstype,label,options,size | grep "media" | grep "rw" | awk '{print $1, $2, $4, $6}') ) + echo -e "\n" + mnt=( $(findmnt -n -lo source,target,fstype,label,options,size | grep "media" | grep "rw" | awk '{print $1, $2, $4, $6}') ) - length=${#mnt[@]} + local length=${#mnt[@]} mnt[length]="Do not store on mounted disk" options_length=$(( length / 4 + 1 )) - if [ $length -gt 0 ]; then - printf "%10s\t%10s\t%10s\t%10s\t%10s\n" "OPTION" "SOURCE" "TARGET" "LABEL" "SIZE" - fi + + printf "%-10s\t%-10s\t%-30s\t%-10s\t%-10s\t\n" "Selection" "Source" "Target" "Label" "Size" + + echo "-----------------------------------------------------------------------------------------------------" for ((i=0;i< $options_length;i++)); do - printf "%10s\t" "[$(( i+1 ))]" + printf "%-10s\t" "$(( i+1 ))" for((j=0;j<4;j++)); do - printf "%10s\t" "${mnt[j + i * 4]}" - + printf "%-10s\t" "${mnt[j + i * 4]}" done - echo + if [[ -d "${mnt[1 + i * 4]}/AutopsyConfig" ]]; then + printf "%-10s\t" "Contains Autopsy Config data" + fi + echo -e "\n\n" done read -n 1 option + echo -e "\n" if [[ $option = "" ]] || ! [[ "$option" =~ ^[0-9]+$ ]]; then echo "Please choose a valid option" showAndReadOptions fi } +showCaseDirOptions () { + + echo "Please select a drive to store case data: " + echo -e "\n" + casedirremovable=( $(lsblk -lno NAME,RM,MOUNTPOINT,LABEL | awk '$3 != "" {print $1,$2,$3,$4}' | awk '$2 == 1 {print $3}') ) + casedir=( $(lsblk -lno NAME,SIZE,MOUNTPOINT | awk '$3 != "" {print $1,$2,$3}') ) + local lengthCaseDir=${#casedir[@]} + optionsCasedirLength=$(( lengthCaseDir / 3 )) + printf "%-10s\t%-10s\t%-10s\t%-30s\t\n" "Selection" "Disk" "Size" "Mount" + + echo "-----------------------------------------------------------------------------------------------------" + + for ((i=0;i<$optionsCasedirLength;i++)); + do + printf "%-10s\t" "$(( i+1 ))" + for((j=0;j<3;j++)); + do + printf "%-10s\t" "${casedir[j + i * 3]}" + done + if [[ -d "${casedir[2 + i * 3 ]}/AutopsyConfig" ]]; then + printf "%-10s\t" "Contains Autopsy config data" + fi + echo -e "\n\n" + done + read -n 1 casedirOption + echo -e "\n" + if [[ $casedirOption = "" ]] || ! [[ "$casedirOption" =~ ^[0-9]+$ ]]; then + echo "Please choose a valid option" + showCaseDirOptions + fi +} + +showWarning() { + RED='\033[0;31m' + NC='\033[0m' + local e match="$1" + shift + for e; do [[ "$e" != "$match" ]] && echo -e "${RED}Warning: Case data stored in non removable disk cannot be saved${NC}"; done +} + # Show mounted drives and loop until it is valid while true @@ -88,10 +134,21 @@ do echo "Please choose a valid option" done +#Show case drives and loop until it is valid + +while true +do + showCaseDirOptions + if [ "$casedirOption" -ge "1" ] && [ "$casedirOption" -le "$optionsCasedirLength" ]; then + break + fi + echo "Please choose a valid option" +done + if [ "$option" != "$options_length" ]; then index=$(( (option - 1) * 4 + 1 )) - echo "Autopsy configurations will be stored in" "${mnt[$index]}"". Are you sure? (y/n)" - read affirmation + casedirIndex=$(( (casedirOption - 1) * 3 + 1 )) + read -p 'Autopsy configurations will be stored in" "${mnt[$index]}"". Are you sure? (y/n): ' affirmation if [ "$affirmation" == "y" ] || [ "$affirmation" == "Y" ]; then if [[ -d "${mnt[$index]}" ]]; then selectedMount=${mnt[$index]} @@ -105,12 +162,13 @@ if [ "$option" != "$options_length" ]; then errorLog "Mount point $selectedMount does not have write permission" fi + showWarning "${casedir[$casedirIndex]}" "${casedirremovable[@]}" # Make the directories on the media userDirectory="$autopsyConfigDir/userdir" createConfigDirectories $autopsyConfigDir && createConfigDirectories $userDirectory if [ $? -eq 0 ]; then - sh $AUTOPSY_BIN --userdir $userDirectory + sh $AUTOPSY_BIN --userdir $userDirectory --liveAutopsy ${casedir[$casedirIndex]} fi fi else From 6c2ba70fa9174ddb0458c05de1278a0200601157 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Mon, 20 Aug 2018 17:57:52 -0400 Subject: [PATCH 115/150] Revert "Accounting for associated artifact." --- .../org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java | 7 ++++--- .../org/sleuthkit/autopsy/keywordsearch/RegexQuery.java | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java index 5d68430ea0..b3065dcad6 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java @@ -232,9 +232,6 @@ class LuceneQuery implements KeywordSearchQuery { if (StringUtils.isNotBlank(listName)) { attributesList.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, listName)); } - hit.getArtifactID().ifPresent(artifactID - -> attributesList.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, artifactID)) - ); try { SleuthkitCase tskCase = Case.getCurrentCaseThrows().getSleuthkitCase(); @@ -260,6 +257,10 @@ class LuceneQuery implements KeywordSearchQuery { attributesList.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_PREVIEW, MODULE_NAME, snippet)); } + hit.getArtifactID().ifPresent(artifactID + -> attributesList.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, artifactID)) + ); + try { bba.addAttributes(attributesList); //write out to bb return bba; diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java index 694cfb7903..102682c4fe 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java @@ -466,9 +466,6 @@ final class RegexQuery implements KeywordSearchQuery { if (StringUtils.isNotBlank(listName)) { attributesList.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, listName)); } - hit.getArtifactID().ifPresent(artifactID - -> attributesList.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, artifactID)) - ); try { SleuthkitCase tskCase = Case.getCurrentCaseThrows().getSleuthkitCase(); @@ -499,6 +496,10 @@ final class RegexQuery implements KeywordSearchQuery { attributesList.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_PREVIEW, MODULE_NAME, snippet)); } + hit.getArtifactID().ifPresent(artifactID + -> attributesList.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, artifactID)) + ); + try { newArtifact.addAttributes(attributesList); return newArtifact; From 071a545108538a2d5325d3bb5ea0cdc0815c467c Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Mon, 20 Aug 2018 17:59:24 -0400 Subject: [PATCH 116/150] Revert "4067 prevent duplicate keyword hits" --- .../autopsy/keywordsearch/LuceneQuery.java | 66 +++++++----------- .../autopsy/keywordsearch/RegexQuery.java | 68 ++++++++----------- 2 files changed, 53 insertions(+), 81 deletions(-) diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java index b3065dcad6..e13531da26 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2018 Basis Technology Corp. + * Copyright 2011-2017 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -32,18 +32,14 @@ import org.apache.solr.client.solrj.response.QueryResponse; import org.apache.solr.common.SolrDocument; import org.apache.solr.common.SolrDocumentList; import org.apache.solr.common.params.CursorMarkParams; -import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.EscapeUtil; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Version; -import org.sleuthkit.datamodel.Blackboard; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; import org.sleuthkit.datamodel.Content; -import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskException; @@ -207,44 +203,15 @@ class LuceneQuery implements KeywordSearchQuery { * @param listName The name of the keyword list that contained the * keyword for which the hit was found. * - * @return The newly created artifact, or null if one wasn't created due to - * either the artifact already existing or an error while trying to - * create it. + * + * @return The newly created artifact or null if there was a problem + * creating it. */ @Override public BlackboardArtifact postKeywordHitToBlackboard(Content content, Keyword foundKeyword, KeywordHit hit, String snippet, String listName) { final String MODULE_NAME = KeywordSearchModuleFactory.getModuleName(); - List attributesList = new ArrayList<>(); - attributesList.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD, MODULE_NAME, foundKeyword.getSearchTerm())); - if (originalKeyword != null) { - BlackboardAttribute.ATTRIBUTE_TYPE selType = originalKeyword.getArtifactAttributeType(); - if (selType != null) { - attributesList.add(new BlackboardAttribute(selType, MODULE_NAME, foundKeyword.getSearchTerm())); - } - - if (originalKeyword.searchTermIsWholeWord()) { - attributesList.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE, MODULE_NAME, KeywordSearch.QueryType.LITERAL.ordinal())); - } else { - attributesList.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE, MODULE_NAME, KeywordSearch.QueryType.SUBSTRING.ordinal())); - } - } - if (StringUtils.isNotBlank(listName)) { - attributesList.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, listName)); - } - - try { - SleuthkitCase tskCase = Case.getCurrentCaseThrows().getSleuthkitCase(); - Blackboard blackboard = tskCase.getBlackboard(); - if (blackboard.artifactExists(content, BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT, attributesList)) { - return null; - } - } catch (NoCurrentCaseException | TskCoreException ex) { - logger.log(Level.SEVERE, String.format( - "A problem occurred while checking for existing artifacts for file '%s' (id=%d).", - content.getName(), content.getId()), ex); //NON-NLS - } - + Collection attributes = new ArrayList<>(); BlackboardArtifact bba; try { bba = content.newArtifact(ARTIFACT_TYPE.TSK_KEYWORD_HIT); @@ -254,15 +221,32 @@ class LuceneQuery implements KeywordSearchQuery { } if (snippet != null) { - attributesList.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_PREVIEW, MODULE_NAME, snippet)); + attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_PREVIEW, MODULE_NAME, snippet)); + } + attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD, MODULE_NAME, foundKeyword.getSearchTerm())); + if (StringUtils.isNotBlank(listName)) { + attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, listName)); + } + + if (originalKeyword != null) { + BlackboardAttribute.ATTRIBUTE_TYPE selType = originalKeyword.getArtifactAttributeType(); + if (selType != null) { + attributes.add(new BlackboardAttribute(selType, MODULE_NAME, foundKeyword.getSearchTerm())); + } + + if (originalKeyword.searchTermIsWholeWord()) { + attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE, MODULE_NAME, KeywordSearch.QueryType.LITERAL.ordinal())); + } else { + attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE, MODULE_NAME, KeywordSearch.QueryType.SUBSTRING.ordinal())); + } } hit.getArtifactID().ifPresent(artifactID - -> attributesList.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, artifactID)) + -> attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, artifactID)) ); try { - bba.addAttributes(attributesList); //write out to bb + bba.addAttributes(attributes); //write out to bb return bba; } catch (TskCoreException e) { logger.log(Level.WARNING, "Error adding bb attributes to artifact", e); //NON-NLS diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java index 102682c4fe..cb640e20bd 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java @@ -36,6 +36,7 @@ import org.apache.solr.client.solrj.response.QueryResponse; import org.apache.solr.common.SolrDocument; import org.apache.solr.common.SolrDocumentList; import org.apache.solr.common.params.CursorMarkParams; +import org.openide.util.Exceptions; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; @@ -49,13 +50,11 @@ import static org.sleuthkit.autopsy.keywordsearch.TermsComponentQuery.KEYWORD_SE import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.Account; import org.sleuthkit.datamodel.AccountFileInstance; -import org.sleuthkit.datamodel.Blackboard; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; import org.sleuthkit.datamodel.Content; -import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; @@ -75,7 +74,7 @@ import org.sleuthkit.datamodel.TskData; */ final class RegexQuery implements KeywordSearchQuery { - public static final Logger logger = Logger.getLogger(RegexQuery.class.getName()); + public static final Logger LOGGER = Logger.getLogger(RegexQuery.class.getName()); /** * Lucene regular expressions do not support the following Java predefined @@ -214,7 +213,7 @@ final class RegexQuery implements KeywordSearchQuery { hitsForKeyword.add(hit); } } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error creating keyword hits", ex); //NON-NLS + LOGGER.log(Level.SEVERE, "Error creating keyword hits", ex); //NON-NLS } } @@ -224,7 +223,7 @@ final class RegexQuery implements KeywordSearchQuery { } cursorMark = nextCursorMark; } catch (KeywordSearchModuleException ex) { - logger.log(Level.SEVERE, "Error executing Regex Solr Query: " + keywordString, ex); //NON-NLS + LOGGER.log(Level.SEVERE, "Error executing Regex Solr Query: " + keywordString, ex); //NON-NLS MessageNotifyUtil.Notify.error(NbBundle.getMessage(Server.class, "Server.query.exception.msg", keywordString), ex.getCause().getMessage()); } } @@ -438,16 +437,16 @@ final class RegexQuery implements KeywordSearchQuery { * @param listName The name of the keyword list that contained the * keyword for which the hit was found. * - * @return The newly created artifact, or null if one wasn't created due to - * either the artifact already existing or an error while trying to - * create it. + * + * @return The newly created artifact or null if there was a problem + * creating it. */ @Override public BlackboardArtifact postKeywordHitToBlackboard(Content content, Keyword foundKeyword, KeywordHit hit, String snippet, String listName) { final String MODULE_NAME = KeywordSearchModuleFactory.getModuleName(); if (content == null) { - logger.log(Level.WARNING, "Error adding artifact for keyword hit to blackboard"); //NON-NLS + LOGGER.log(Level.WARNING, "Error adding artifact for keyword hit to blackboard"); //NON-NLS return null; } @@ -459,52 +458,41 @@ final class RegexQuery implements KeywordSearchQuery { return null; } - List attributesList = new ArrayList<>(); - attributesList.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD, MODULE_NAME, foundKeyword.getSearchTerm())); - attributesList.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE, MODULE_NAME, KeywordSearch.QueryType.REGEX.ordinal())); - attributesList.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP, MODULE_NAME, getQueryString())); - if (StringUtils.isNotBlank(listName)) { - attributesList.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, listName)); - } - - try { - SleuthkitCase tskCase = Case.getCurrentCaseThrows().getSleuthkitCase(); - Blackboard blackboard = tskCase.getBlackboard(); - if (blackboard.artifactExists(content, BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT, attributesList)) { - return null; - } - } catch (NoCurrentCaseException | TskCoreException ex) { - logger.log(Level.SEVERE, String.format( - "A problem occurred while checking for existing artifacts for file '%s' (id=%d).", - content.getName(), content.getId()), ex); //NON-NLS - } - /* * Create a "plain vanilla" keyword hit artifact with keyword and * regex attributes */ BlackboardArtifact newArtifact; + Collection attributes = new ArrayList<>(); + + attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD, MODULE_NAME, foundKeyword.getSearchTerm())); + attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP, MODULE_NAME, getQueryString())); try { newArtifact = content.newArtifact(ARTIFACT_TYPE.TSK_KEYWORD_HIT); } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error adding artifact for keyword hit to blackboard", ex); //NON-NLS + LOGGER.log(Level.SEVERE, "Error adding artifact for keyword hit to blackboard", ex); //NON-NLS return null; } + if (StringUtils.isNotBlank(listName)) { + attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, listName)); + } if (snippet != null) { - attributesList.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_PREVIEW, MODULE_NAME, snippet)); + attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_PREVIEW, MODULE_NAME, snippet)); } hit.getArtifactID().ifPresent(artifactID - -> attributesList.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, artifactID)) + -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, artifactID)) ); + attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE, MODULE_NAME, KeywordSearch.QueryType.REGEX.ordinal())); + try { - newArtifact.addAttributes(attributesList); + newArtifact.addAttributes(attributes); return newArtifact; } catch (TskCoreException e) { - logger.log(Level.SEVERE, "Error adding bb attributes for terms search artifact", e); //NON-NLS + LOGGER.log(Level.SEVERE, "Error adding bb attributes for terms search artifact", e); //NON-NLS return null; } } @@ -514,7 +502,7 @@ final class RegexQuery implements KeywordSearchQuery { final String MODULE_NAME = KeywordSearchModuleFactory.getModuleName(); if (originalKeyword.getArtifactAttributeType() != ATTRIBUTE_TYPE.TSK_CARD_NUMBER) { - logger.log(Level.SEVERE, "Keyword hit is not a credit card number"); //NON-NLS + LOGGER.log(Level.SEVERE, "Keyword hit is not a credit card number"); //NON-NLS return; } /* @@ -537,13 +525,13 @@ final class RegexQuery implements KeywordSearchQuery { if (ccnAttribute == null || StringUtils.isBlank(ccnAttribute.getValueString())) { if (hit.isArtifactHit()) { - logger.log(Level.SEVERE, String.format("Failed to parse credit card account number for artifact keyword hit: term = %s, snippet = '%s', artifact id = %d", foundKeyword.getSearchTerm(), hit.getSnippet(), hit.getArtifactID().get())); //NON-NLS + LOGGER.log(Level.SEVERE, String.format("Failed to parse credit card account number for artifact keyword hit: term = %s, snippet = '%s', artifact id = %d", foundKeyword.getSearchTerm(), hit.getSnippet(), hit.getArtifactID().get())); //NON-NLS } else { try { - logger.log(Level.SEVERE, String.format("Failed to parse credit card account number for content keyword hit: term = %s, snippet = '%s', object id = %d", foundKeyword.getSearchTerm(), hit.getSnippet(), hit.getContentID())); //NON-NLS + LOGGER.log(Level.SEVERE, String.format("Failed to parse credit card account number for content keyword hit: term = %s, snippet = '%s', object id = %d", foundKeyword.getSearchTerm(), hit.getSnippet(), hit.getContentID())); //NON-NLS } catch (TskCoreException ex) { - logger.log(Level.SEVERE, String.format("Failed to parse credit card account number for content keyword hit: term = %s, snippet = '%s' ", foundKeyword.getSearchTerm(), hit.getSnippet())); //NON-NLS - logger.log(Level.SEVERE, "There was a error getting contentID for keyword hit.", ex); //NON-NLS + LOGGER.log(Level.SEVERE, String.format("Failed to parse credit card account number for content keyword hit: term = %s, snippet = '%s' ", foundKeyword.getSearchTerm(), hit.getSnippet())); //NON-NLS + LOGGER.log(Level.SEVERE, "There was a error getting contentID for keyword hit.", ex); //NON-NLS } } return; @@ -611,7 +599,7 @@ final class RegexQuery implements KeywordSearchQuery { ccAccountInstance.addAttributes(attributes); } catch (TskCoreException | NoCurrentCaseException ex) { - logger.log(Level.SEVERE, "Error creating CCN account instance", ex); //NON-NLS + LOGGER.log(Level.SEVERE, "Error creating CCN account instance", ex); //NON-NLS } From 67c5505bec9d22512454b6ba5aff2e70c51eed15 Mon Sep 17 00:00:00 2001 From: Rishwanth Senthilkumar Date: Tue, 21 Aug 2018 11:04:46 -0400 Subject: [PATCH 117/150] live_iso.sh update --- unix/live_iso.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unix/live_iso.sh b/unix/live_iso.sh index 0d1b252285..f37af42875 100644 --- a/unix/live_iso.sh +++ b/unix/live_iso.sh @@ -65,7 +65,7 @@ cp /usr/share/applications/lxterminal.desktop /etc/skel/Desktop/ mv ./autopsy.png /usr/share/icons #setup iso wallpaper -[ ! -f "./autopsy_wallpaper1.png" ]wget https://github.com/sleuthkit/autopsy/raw/develop/unix/autopsy_wallpaper1.png +[ ! -f "./autopsy_wallpaper1.png" ] && wget https://github.com/sleuthkit/autopsy/raw/develop/unix/autopsy_wallpaper1.png mv ./autopsy_wallpaper1.png /usr/share/lubuntu/wallpapers/autopsy_wallpaper.png unlink lubuntu-default-wallpaper.png unlink lubuntu-default-wallpaper.jpg From 598a0ffaa06ee4f63c08b2a79920e7114328c988 Mon Sep 17 00:00:00 2001 From: Rishwanth Senthilkumar Date: Tue, 21 Aug 2018 11:07:58 -0400 Subject: [PATCH 118/150] update live_iso.sh --- unix/live_iso.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/unix/live_iso.sh b/unix/live_iso.sh index f37af42875..bac804dd8c 100644 --- a/unix/live_iso.sh +++ b/unix/live_iso.sh @@ -67,7 +67,7 @@ mv ./autopsy.png /usr/share/icons #setup iso wallpaper [ ! -f "./autopsy_wallpaper1.png" ] && wget https://github.com/sleuthkit/autopsy/raw/develop/unix/autopsy_wallpaper1.png mv ./autopsy_wallpaper1.png /usr/share/lubuntu/wallpapers/autopsy_wallpaper.png -unlink lubuntu-default-wallpaper.png -unlink lubuntu-default-wallpaper.jpg -ln -s autopsy_wallpaper.png lubuntu-default-wallpaper.png -ln -s autopsy-wallpaper.png lubuntu-default-wallpaper.jpg +unlink /usr/share/lubuntu/wallpapers/lubuntu-default-wallpaper.png +unlink /usr/share/lubuntu/wallpapers/lubuntu-default-wallpaper.jpg +ln -s /usr/share/lubuntu/wallpapers/autopsy_wallpaper.png /usr/share/lubuntu/wallpapers/lubuntu-default-wallpaper.png +ln -s /usr/share/lubuntu/wallpapers/lubuntu-default-wallpaper.png /usr/share/lubuntu/wallpapers/lubuntu-default-wallpaper.jpg From c147a3ceb905e1944fa6ff10644841127731cd4b Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Tue, 21 Aug 2018 11:33:38 -0400 Subject: [PATCH 119/150] 4114 - Add comments to DataResultPanel to clarify purpose of RootNodeListener --- .../corecomponents/DataResultPanel.java | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultPanel.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultPanel.java index 9d0a83c2ad..1ed434d080 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultPanel.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultPanel.java @@ -370,6 +370,13 @@ public class DataResultPanel extends javax.swing.JPanel implements DataResult, C this.currentRootNode = rootNode; if (this.currentRootNode != null) { + /* + * The only place we reset the rootNodeListener allowing the + * contents of the results tab represented by this node to be + * changed a single time before it is necessary to reset it again. + * Necessary when transitioning from "Please wait..." node to having + * contents. + */ rootNodeListener.reset(); this.currentRootNode.addNodeListener(rootNodeListener); } @@ -518,7 +525,6 @@ public class DataResultPanel extends javax.swing.JPanel implements DataResult, C /** * Closes down the component. Intended to be called by the parent top ->>>>>>> custom-release-may-2018 * component when it is closed. */ void close() { @@ -529,7 +535,7 @@ public class DataResultPanel extends javax.swing.JPanel implements DataResult, C this.resultViewers.forEach((viewer) -> viewer.setNode(null)); - if (!this.isMain) { // RJCTODO: What? + if (!this.isMain) { this.resultViewers.forEach(DataResultViewer::clearComponent); this.descriptionLabel.removeAll(); this.numberOfChildNodesLabel.removeAll(); @@ -581,11 +587,13 @@ public class DataResultPanel extends javax.swing.JPanel implements DataResult, C /** * Responds to changes in the root node due to asynchronous child node - * creation. + * creation. This listener allows for the tabs of the result viewer to be + * set up again after the "Please wait..." node has ended and actual content + * should be displayed in the table. */ - // RJCTODO: Why do we need this? private class RootNodeListener implements NodeListener { + //it is assumed we are still waiting for data when the node is initially constructed private volatile boolean waitingForData = true; public void reset() { @@ -598,10 +606,12 @@ public class DataResultPanel extends javax.swing.JPanel implements DataResult, C updateMatches(); /* - * There is a known issue in this code whereby we will only call - * setupTabs() once even though childrenAdded could be called - * multiple times. That means that each panel may not have access to - * all of the children when they decide if they support the content + * Ensures that after the initial call to setupTabs in the + * DataResultPanel.setNode method that we only call setupTabs one + * additional time. This is to account for the transition that is + * possible from a "Please wait..." node or a tab with no results in + * it and a tab containing data and thereby having all of it's + * columns. */ if (waitingForData && containsReal(delta)) { waitingForData = false; From addb312dc044763b36503f0b521a51a625a26ac7 Mon Sep 17 00:00:00 2001 From: Brian Sweeney Date: Tue, 21 Aug 2018 10:51:31 -0600 Subject: [PATCH 120/150] renames and usage or error manager --- .../CommonAttributePanel.java | 46 +++++-- .../commonfilesearch/PanelErrorMessages.java | 34 ------ .../UserInputErrorManager.java | 115 ++++++++++++++++++ 3 files changed, 152 insertions(+), 43 deletions(-) delete mode 100644 Core/src/org/sleuthkit/autopsy/commonfilesearch/PanelErrorMessages.java create mode 100644 Core/src/org/sleuthkit/autopsy/commonfilesearch/UserInputErrorManager.java diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java index 737d33de7d..016bc0f2ff 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java @@ -58,6 +58,8 @@ public final class CommonAttributePanel extends javax.swing.JDialog { private static final long serialVersionUID = 1L; private static final Long NO_DATA_SOURCE_SELECTED = -1L; + + private final UserInputErrorManager errorManager; private boolean pictureViewCheckboxState; @@ -88,6 +90,8 @@ public final class CommonAttributePanel extends javax.swing.JDialog { this.disableIntercaseSearch(); this.disablePercentageOptions(); } + + this.errorManager = new UserInputErrorManager(); } private static boolean isEamDbAvailable() { @@ -621,20 +625,20 @@ public final class CommonAttributePanel extends javax.swing.JDialog { }//GEN-LAST:event_cancelButtonActionPerformed private void allFileCategoriesRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_allFileCategoriesRadioButtonActionPerformed - this.manageCheckBoxState(); - this.toggleErrorTextAndSearchBox(); + this.manageFileTypeCheckBoxState(); + this.updateErrorTextAndSearchBox(); }//GEN-LAST:event_allFileCategoriesRadioButtonActionPerformed private void selectedFileCategoriesButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_selectedFileCategoriesButtonActionPerformed - this.manageCheckBoxState(); + this.manageFileTypeCheckBoxState(); }//GEN-LAST:event_selectedFileCategoriesButtonActionPerformed private void pictureVideoCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pictureVideoCheckboxActionPerformed - this.toggleErrorTextAndSearchBox(); + this.updateErrorTextAndSearchBox(); }//GEN-LAST:event_pictureVideoCheckboxActionPerformed private void documentsCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_documentsCheckboxActionPerformed - this.toggleErrorTextAndSearchBox(); + this.updateErrorTextAndSearchBox(); }//GEN-LAST:event_documentsCheckboxActionPerformed private void intraCaseRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_intraCaseRadioActionPerformed @@ -669,12 +673,14 @@ public final class CommonAttributePanel extends javax.swing.JDialog { } else { this.percentageThreshold.setEnabled(false); } + + this.handleFrequencyPercentageCriteriaMet(); }//GEN-LAST:event_percentageThresholdCheckActionPerformed private void percentageThresholdActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_percentageThresholdActionPerformed String percentageString = this.percentageThreshold.getText(); - Integer percentage; + Integer percentage = null; try{ percentage = Integer.parseInt(percentageString); @@ -683,6 +689,9 @@ public final class CommonAttributePanel extends javax.swing.JDialog { this.percentageThresholdValue = -1; } + this.percentageThresholdValue = percentage.intValue(); + + this.handleFrequencyPercentageCriteriaMet(); }//GEN-LAST:event_percentageThresholdActionPerformed public void handleInterCaseSearchCriteriaChanged() { @@ -696,7 +705,18 @@ public final class CommonAttributePanel extends javax.swing.JDialog { } } - private void toggleErrorTextAndSearchBox() { + private void updateErrorTextAndSearchBox() { + + if(this.errorManager.anyErrors()){ + this.searchButton.setEnabled(false); + //grab the first error error and show it + this.errorText.setText(this.errorManager.getErrors().get(0)); + this.errorText.setVisible(true); + } else { + this.searchButton.setEnabled(true); + this.errorText.setVisible(false); + } + if (!this.pictureVideoCheckbox.isSelected() && !this.documentsCheckbox.isSelected() && !this.allFileCategoriesRadioButton.isSelected()) { this.searchButton.setEnabled(false); this.errorText.setVisible(true); @@ -706,7 +726,7 @@ public final class CommonAttributePanel extends javax.swing.JDialog { } } - private void manageCheckBoxState() { + private void manageFileTypeCheckBoxState() { this.pictureViewCheckboxState = this.pictureVideoCheckbox.isSelected(); this.documentsCheckboxState = this.documentsCheckbox.isSelected(); @@ -724,7 +744,7 @@ public final class CommonAttributePanel extends javax.swing.JDialog { this.pictureVideoCheckbox.setEnabled(true); this.documentsCheckbox.setEnabled(true); - this.toggleErrorTextAndSearchBox(); + this.updateErrorTextAndSearchBox(); } } @@ -789,4 +809,12 @@ public final class CommonAttributePanel extends javax.swing.JDialog { this.percentageThresholdCheck.setEnabled(false); this.percentageThresholdCheck.setSelected(false); } + + private void handleFrequencyPercentageCriteriaMet() { + if(this.percentageThresholdValue > 0 && this.percentageThresholdValue<= 100){ + this.errorManager.setError(UserInputErrorManager.FREQUENCY_PERCENTAGE_OUT_OF_RANGE_KEY, false); + } else { + this.errorManager.setError(UserInputErrorManager.FREQUENCY_PERCENTAGE_OUT_OF_RANGE_KEY, true); + } + } } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/PanelErrorMessages.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/PanelErrorMessages.java deleted file mode 100644 index fb23b9da14..0000000000 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/PanelErrorMessages.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * - * 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; - -/** - * - * @author bsweeney - */ -class PanelErrorMessages { - - private class ErrorMessage{ - int id; - String message; - boolean showMessage; - } - -} diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/UserInputErrorManager.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/UserInputErrorManager.java new file mode 100644 index 0000000000..7490cb440a --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/UserInputErrorManager.java @@ -0,0 +1,115 @@ +/* + * + * 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.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * Manager for present state of errors on the Common Files Search. + */ +class UserInputErrorManager { + + static final int FREQUENCY_PERCENTAGE_OUT_OF_RANGE_KEY = 1; + + private final Map currentErrors; + + UserInputErrorManager (){ + this.currentErrors = new HashMap<>(); + this.currentErrors.put(FREQUENCY_PERCENTAGE_OUT_OF_RANGE_KEY, new ErrorMessage("Frequency percentage must be greater than zero and less than or equal to 100.")); + } + + /** + * Toggle the given error message on, or off + * @param errorId the error to toggle + * @param errorState true for on, false for off + */ + void setError(int errorId, boolean errorState){ + if(this.currentErrors.containsKey(errorId)){ + this.currentErrors.get(errorId).setStatus(errorState); + } else { + throw new IllegalArgumentException(String.format("The given errorId is not mapped to an ErrorMessage: %s.", errorId)); + } + } + + /** + * Are any user settings presently in an error state? + * @return true for yes, else false + */ + boolean anyErrors(){ + return this.currentErrors.values().stream().anyMatch(errorMessage -> errorMessage.getStatus() == true); + } + + /** + * Get a list of distinct string messages describing the various error states. + */ + List getErrors(){ + return this.currentErrors.values().stream() + .filter(errorMessage -> errorMessage.getStatus() == true) + .map(ErrorMessage::getMessage) + .collect(Collectors.toList()); + } + + /** + * Represents an error message for the CommonFilesSearch panel, it's + * uniqueId, and it's status. + */ + private class ErrorMessage { + + private final String message; + private boolean status; + + /** + * Create a message with a unique uniqueId. Default status is false (off). + * @param uniqueId unique uniqueId + * @param message message to display + */ + ErrorMessage(String message){ + this.message = message; + this.status = false; + } + + /** + * Update the status of this message + * @param status + */ + void setStatus(boolean status){ + this.status = status; + } + + /** + * Return the message + * @return + */ + String getMessage(){ + return this.message; + } + + /** + * Return the status (true for error status, false for no error) + * @return + */ + boolean getStatus(){ + return this.status; + } + } +} From 632cc6927642a26537825ec8e71e1a735b669c4b Mon Sep 17 00:00:00 2001 From: Rishwanth Senthilkumar Date: Tue, 21 Aug 2018 13:35:34 -0400 Subject: [PATCH 121/150] Update launch_bootable_Script.sh --- unix/launch_script_bootable.sh | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/unix/launch_script_bootable.sh b/unix/launch_script_bootable.sh index 56e905dae2..ac415d34f3 100644 --- a/unix/launch_script_bootable.sh +++ b/unix/launch_script_bootable.sh @@ -120,7 +120,8 @@ showWarning() { NC='\033[0m' local e match="$1" shift - for e; do [[ "$e" != "$match" ]] && echo -e "${RED}Warning: Case data stored in non removable disk cannot be saved${NC}"; done + for e; do [[ "$e" == "$match" ]] && return 0; done + echo -e "${RED}Warning: Case data stored in non removable disk cannot be saved${NC}" } @@ -147,8 +148,8 @@ done if [ "$option" != "$options_length" ]; then index=$(( (option - 1) * 4 + 1 )) - casedirIndex=$(( (casedirOption - 1) * 3 + 1 )) - read -p 'Autopsy configurations will be stored in" "${mnt[$index]}"". Are you sure? (y/n): ' affirmation + casedirIndex=$(( (casedirOption - 1) * 3 + 2 )) + read -p "Autopsy configurations will be stored in ${mnt[$index]}. Are you sure? (y/n): " affirmation if [ "$affirmation" == "y" ] || [ "$affirmation" == "Y" ]; then if [[ -d "${mnt[$index]}" ]]; then selectedMount=${mnt[$index]} @@ -168,7 +169,7 @@ if [ "$option" != "$options_length" ]; then createConfigDirectories $autopsyConfigDir && createConfigDirectories $userDirectory if [ $? -eq 0 ]; then - sh $AUTOPSY_BIN --userdir $userDirectory --liveAutopsy ${casedir[$casedirIndex]} + sh $AUTOPSY_BIN --userdir $userDirectory --liveAutopsy=${casedir[$casedirIndex]} fi fi else From 0946d96b9433e258c206ef71da39b5c31d910c06 Mon Sep 17 00:00:00 2001 From: Brian Sweeney Date: Tue, 21 Aug 2018 12:37:22 -0600 Subject: [PATCH 122/150] superior error state management --- .../CommonAttributePanel.form | 3 - .../CommonAttributePanel.java | 179 +++++++----------- .../commonfilesearch/InterCasePanel.java | 22 +-- .../commonfilesearch/IntraCasePanel.java | 21 -- .../UserInputErrorManager.java | 13 +- 5 files changed, 77 insertions(+), 161 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.form b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.form index f926dfdafa..bc520796f3 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.form +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.form @@ -11,9 +11,6 @@ - - - diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java index 016bc0f2ff..91d32c441b 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java @@ -52,19 +52,19 @@ import org.sleuthkit.datamodel.TskCoreException; * logic. Nested within CommonFilesDialog. */ @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives -public final class CommonAttributePanel extends javax.swing.JDialog { +public final class CommonAttributePanel extends javax.swing.JDialog { private static final Logger LOGGER = Logger.getLogger(CommonAttributePanel.class.getName()); private static final long serialVersionUID = 1L; - + private static final Long NO_DATA_SOURCE_SELECTED = -1L; - + private final UserInputErrorManager errorManager; private boolean pictureViewCheckboxState; - + private boolean documentsCheckboxState; - + private int percentageThresholdValue = 20; /** @@ -90,18 +90,18 @@ public final class CommonAttributePanel extends javax.swing.JDialog { this.disableIntercaseSearch(); this.disablePercentageOptions(); } - + this.errorManager = new UserInputErrorManager(); } private static boolean isEamDbAvailable() { try { - return EamDb.isEnabled() && - EamDb.getInstance() != null && - EamDb.getInstance().getCases().size() > 1 && - Case.isCaseOpen() && - Case.getCurrentCase() != null && - EamDb.getInstance().getCase(Case.getCurrentCase()) != null; + return EamDb.isEnabled() + && EamDb.getInstance() != null + && EamDb.getInstance().getCases().size() > 1 + && Case.isCaseOpen() + && Case.getCurrentCase() != null + && EamDb.getInstance().getCase(Case.getCurrentCase()) != null; } catch (EamDbException ex) { LOGGER.log(Level.SEVERE, "Unexpected exception while checking for EamDB enabled.", ex); } @@ -166,9 +166,10 @@ public final class CommonAttributePanel extends javax.swing.JDialog { filterByDocuments = true; } } + + int percentageThreshold = CommonAttributePanel.this.percentageThresholdValue; - int percentageThreshold = 0; - if(CommonAttributePanel.this.percentageThresholdCheck.isSelected()){ + if (CommonAttributePanel.this.percentageThresholdCheck.isSelected()) { String percentageThresholdAsString = CommonAttributePanel.this.percentageThreshold.getText(); percentageThreshold = Integer.parseInt(percentageThresholdAsString); } @@ -245,7 +246,6 @@ public final class CommonAttributePanel extends javax.swing.JDialog { }.execute(); } - /** * Sets up the data sources dropdown and returns the data sources map for * future usage. @@ -278,8 +278,7 @@ public final class CommonAttributePanel extends javax.swing.JDialog { boolean multipleDataSources = this.caseHasMultipleSources(); CommonAttributePanel.this.intraCasePanel.rigForMultipleDataSources(multipleDataSources); - //TODO this should be attached to the intra/inter radio buttons - CommonAttributePanel.this.setSearchButtonEnabled(true); + CommonAttributePanel.this.updateErrorTextAndSearchBox(); } } @@ -625,41 +624,27 @@ public final class CommonAttributePanel extends javax.swing.JDialog { }//GEN-LAST:event_cancelButtonActionPerformed private void allFileCategoriesRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_allFileCategoriesRadioButtonActionPerformed - this.manageFileTypeCheckBoxState(); - this.updateErrorTextAndSearchBox(); + this.handleFileTypeCheckBoxState(); }//GEN-LAST:event_allFileCategoriesRadioButtonActionPerformed private void selectedFileCategoriesButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_selectedFileCategoriesButtonActionPerformed - this.manageFileTypeCheckBoxState(); + this.handleFileTypeCheckBoxState(); }//GEN-LAST:event_selectedFileCategoriesButtonActionPerformed private void pictureVideoCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pictureVideoCheckboxActionPerformed - this.updateErrorTextAndSearchBox(); + this.handleFileTypeCheckBoxState(); }//GEN-LAST:event_pictureVideoCheckboxActionPerformed private void documentsCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_documentsCheckboxActionPerformed - this.updateErrorTextAndSearchBox(); + this.handleFileTypeCheckBoxState(); }//GEN-LAST:event_documentsCheckboxActionPerformed private void intraCaseRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_intraCaseRadioActionPerformed ((java.awt.CardLayout) this.layoutPanel.getLayout()).first(this.layoutPanel); - handleIntraCaseSearchCriteriaChanged(); }//GEN-LAST:event_intraCaseRadioActionPerformed - public void handleIntraCaseSearchCriteriaChanged() { - if (this.areIntraCaseSearchCriteriaMet()) { - this.searchButton.setEnabled(true); - this.hideErrorMessages(); - } else { - this.searchButton.setEnabled(false); - this.hideErrorMessages(); - this.showIntraCaseErrorMessage(); - } - } - private void interCaseRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_interCaseRadioActionPerformed ((java.awt.CardLayout) this.layoutPanel.getLayout()).last(this.layoutPanel); - handleInterCaseSearchCriteriaChanged(); }//GEN-LAST:event_interCaseRadioActionPerformed private void formWindowClosed(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowClosed @@ -667,47 +652,35 @@ public final class CommonAttributePanel extends javax.swing.JDialog { }//GEN-LAST:event_formWindowClosed private void percentageThresholdCheckActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_percentageThresholdCheckActionPerformed - if(this.percentageThresholdCheck.isSelected()){ + if (this.percentageThresholdCheck.isSelected()) { this.percentageThreshold.setEnabled(true); - } else { this.percentageThreshold.setEnabled(false); } - - this.handleFrequencyPercentageCriteriaMet(); + + this.handleFrequencyPercentageState(); }//GEN-LAST:event_percentageThresholdCheckActionPerformed private void percentageThresholdActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_percentageThresholdActionPerformed String percentageString = this.percentageThreshold.getText(); - + Integer percentage = null; - - try{ + + try { percentage = Integer.parseInt(percentageString); this.percentageThresholdValue = percentage; - } catch(Exception e){ + } catch (Exception e) { this.percentageThresholdValue = -1; } - + this.percentageThresholdValue = percentage.intValue(); - - this.handleFrequencyPercentageCriteriaMet(); + + this.handleFrequencyPercentageState(); }//GEN-LAST:event_percentageThresholdActionPerformed - public void handleInterCaseSearchCriteriaChanged() { - if (this.areInterCaseSearchCriteriaMet()) { - this.searchButton.setEnabled(true); - this.hideErrorMessages(); - } else { - this.searchButton.setEnabled(false); - this.hideErrorMessages(); - this.showInterCaseErrorMessage(); - } - } - private void updateErrorTextAndSearchBox() { - - if(this.errorManager.anyErrors()){ + + if (this.errorManager.anyErrors()) { this.searchButton.setEnabled(false); //grab the first error error and show it this.errorText.setText(this.errorManager.getErrors().get(0)); @@ -716,17 +689,21 @@ public final class CommonAttributePanel extends javax.swing.JDialog { this.searchButton.setEnabled(true); this.errorText.setVisible(false); } - - if (!this.pictureVideoCheckbox.isSelected() && !this.documentsCheckbox.isSelected() && !this.allFileCategoriesRadioButton.isSelected()) { - this.searchButton.setEnabled(false); - this.errorText.setVisible(true); - } else { - this.searchButton.setEnabled(true); - this.errorText.setVisible(false); - } } - private void manageFileTypeCheckBoxState() { + private void enablePercentageOptions() { + this.percentageThreshold.setEnabled(true); + this.percentageThresholdCheck.setEnabled(true); + this.percentageThresholdCheck.setSelected(true); + } + + private void disablePercentageOptions() { + this.percentageThreshold.setEnabled(false); + this.percentageThresholdCheck.setEnabled(false); + this.percentageThresholdCheck.setSelected(false); + } + + private void handleFileTypeCheckBoxState() { this.pictureViewCheckboxState = this.pictureVideoCheckbox.isSelected(); this.documentsCheckboxState = this.documentsCheckbox.isSelected(); @@ -734,6 +711,8 @@ public final class CommonAttributePanel extends javax.swing.JDialog { if (this.allFileCategoriesRadioButton.isSelected()) { this.pictureVideoCheckbox.setEnabled(false); this.documentsCheckbox.setEnabled(false); + + this.errorManager.setError(UserInputErrorManager.NO_FILE_CATEGORIES_SELECTED_KEY, false); } if (this.selectedFileCategoriesButton.isSelected()) { @@ -744,8 +723,24 @@ public final class CommonAttributePanel extends javax.swing.JDialog { this.pictureVideoCheckbox.setEnabled(true); this.documentsCheckbox.setEnabled(true); - this.updateErrorTextAndSearchBox(); + if (!this.pictureVideoCheckbox.isSelected() && !this.documentsCheckbox.isSelected() && !this.allFileCategoriesRadioButton.isSelected()) { + this.errorManager.setError(UserInputErrorManager.NO_FILE_CATEGORIES_SELECTED_KEY, true); + } else { + this.errorManager.setError(UserInputErrorManager.NO_FILE_CATEGORIES_SELECTED_KEY, false); + } } + + this.updateErrorTextAndSearchBox(); + } + + private void handleFrequencyPercentageState() { + if (this.percentageThresholdValue > 0 && this.percentageThresholdValue <= 100) { + this.errorManager.setError(UserInputErrorManager.FREQUENCY_PERCENTAGE_OUT_OF_RANGE_KEY, false); + } else { + this.errorManager.setError(UserInputErrorManager.FREQUENCY_PERCENTAGE_OUT_OF_RANGE_KEY, true); + } + + this.updateErrorTextAndSearchBox(); } // Variables declaration - do not modify//GEN-BEGIN:variables @@ -771,50 +766,4 @@ public final class CommonAttributePanel extends javax.swing.JDialog { private javax.swing.JButton searchButton; private javax.swing.JRadioButton selectedFileCategoriesButton; // End of variables declaration//GEN-END:variables - - void setSearchButtonEnabled(boolean enabled) { - this.searchButton.setEnabled(enabled); - } - - private boolean areIntraCaseSearchCriteriaMet() { - return this.intraCasePanel.areSearchCriteriaMet(); - } - - private boolean areInterCaseSearchCriteriaMet() { - return this.interCasePanel.areSearchCriteriaMet(); - } - - private void hideErrorMessages() { - this.errorText.setVisible(false); - } - - private void showIntraCaseErrorMessage() { - this.errorText.setText(this.intraCasePanel.getErrorMessage()); - this.errorText.setVisible(true); - } - - private void showInterCaseErrorMessage() { - this.errorText.setText(this.interCasePanel.getErrorMessage()); - this.errorText.setVisible(true); - } - - private void enablePercentageOptions() { - this.percentageThreshold.setEnabled(true); - this.percentageThresholdCheck.setEnabled(true); - this.percentageThresholdCheck.setSelected(true); - } - - private void disablePercentageOptions() { - this.percentageThreshold.setEnabled(false); - this.percentageThresholdCheck.setEnabled(false); - this.percentageThresholdCheck.setSelected(false); - } - - private void handleFrequencyPercentageCriteriaMet() { - if(this.percentageThresholdValue > 0 && this.percentageThresholdValue<= 100){ - this.errorManager.setError(UserInputErrorManager.FREQUENCY_PERCENTAGE_OUT_OF_RANGE_KEY, false); - } else { - this.errorManager.setError(UserInputErrorManager.FREQUENCY_PERCENTAGE_OUT_OF_RANGE_KEY, true); - } - } } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCasePanel.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCasePanel.java index 67aa40134b..8fc4efd01e 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCasePanel.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCasePanel.java @@ -24,7 +24,6 @@ import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import javax.swing.ComboBoxModel; -import org.openide.util.NbBundle; /** * UI controls for Common Files Search scenario where the user intends to find @@ -39,9 +38,7 @@ public class InterCasePanel extends javax.swing.JPanel { private ComboBoxModel casesList = new DataSourceComboBoxModel(); private final Map caseMap; - - private String errorMessage; - + //True if we are looking in any or all cases, // false if we must find matches in a given case plus the current case private boolean anyCase; @@ -51,7 +48,6 @@ public class InterCasePanel extends javax.swing.JPanel { */ public InterCasePanel() { initComponents(); - this.errorMessage = ""; this.caseMap = new HashMap<>(); this.anyCase = true; } @@ -64,10 +60,6 @@ public class InterCasePanel extends javax.swing.JPanel { } } - String getErrorMessage(){ - return this.errorMessage; - } - /** * 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 @@ -189,16 +181,4 @@ public class InterCasePanel extends javax.swing.JPanel { return InterCasePanel.NO_CASE_SELECTED; } - - @NbBundle.Messages({ - "InterCasePanel.showInterCaseErrorMessage.message=Cannot run intercase correlation search: no cases in Central Repository." - }) - boolean areSearchCriteriaMet() { - if(this.caseMap.isEmpty()){ - this.errorMessage = Bundle.InterCasePanel_showInterCaseErrorMessage_message(); - return false; - } else { - return true; - } - } } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/IntraCasePanel.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/IntraCasePanel.java index c62552082b..babbbd4096 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/IntraCasePanel.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/IntraCasePanel.java @@ -24,8 +24,6 @@ import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import javax.swing.ComboBoxModel; -import org.openide.util.NbBundle; - /** * UI controls for Common Files Search scenario where the user intends to find * common files between datasources. It is an inner panel which provides the ability @@ -40,15 +38,12 @@ public class IntraCasePanel extends javax.swing.JPanel { private boolean singleDataSource; private ComboBoxModel dataSourcesList = new DataSourceComboBoxModel(); private final Map dataSourceMap; - - private String errorMessage; /** * Creates new form IntraCasePanel */ public IntraCasePanel() { initComponents(); - this.errorMessage = ""; this.dataSourceMap = new HashMap<>(); this.singleDataSource = true; } @@ -175,20 +170,4 @@ public class IntraCasePanel extends javax.swing.JPanel { this.dataSourceMap.clear(); this.dataSourceMap.putAll(dataSourceMap); } - - @NbBundle.Messages({ - "IntraCasePanel.areSearchCriteriaMet.message=Cannot run intra-case correlation search." - }) - boolean areSearchCriteriaMet() { - if(this.dataSourceMap.isEmpty()){ - this.errorMessage = Bundle.IntraCasePanel_areSearchCriteriaMet_message(); - return false; - } else { - return true; - } - } - - String getErrorMessage() { - return this.errorMessage; - } } \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/UserInputErrorManager.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/UserInputErrorManager.java index 7490cb440a..11c22a3aec 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/UserInputErrorManager.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/UserInputErrorManager.java @@ -29,13 +29,24 @@ import java.util.stream.Collectors; */ class UserInputErrorManager { - static final int FREQUENCY_PERCENTAGE_OUT_OF_RANGE_KEY = 1; + static final int FREQUENCY_PERCENTAGE_OUT_OF_RANGE_KEY = 1; + static final int NO_FILE_CATEGORIES_SELECTED_KEY = 2; private final Map currentErrors; + /** + * Construct a new ErrorManager which can be used to track the status + * of all known error states, retrieve error messages, and determine if + * anything is in an error state. + */ UserInputErrorManager (){ + + //when new errors are needed for the dialog, define a key and a value + // and add them to the map. + this.currentErrors = new HashMap<>(); this.currentErrors.put(FREQUENCY_PERCENTAGE_OUT_OF_RANGE_KEY, new ErrorMessage("Frequency percentage must be greater than zero and less than or equal to 100.")); + this.currentErrors.put(NO_FILE_CATEGORIES_SELECTED_KEY, new ErrorMessage("No file categories are included in the search.")); } /** From 5cf4d7d8495d371ce1ee4900efc0707a132c98ec Mon Sep 17 00:00:00 2001 From: Brian Sweeney Date: Tue, 21 Aug 2018 15:13:07 -0600 Subject: [PATCH 123/150] text field should not resize --- .../CommonAttributePanel.form | 9 ++-- .../CommonAttributePanel.java | 44 ++++++++++++------- 2 files changed, 34 insertions(+), 19 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.form b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.form index bc520796f3..be96989e91 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.form +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.form @@ -315,10 +315,13 @@ + + + + + + - - - diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java index 91d32c441b..7116ebd2ba 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java @@ -29,6 +29,8 @@ import java.util.logging.Level; import javax.swing.JFrame; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; import org.netbeans.api.progress.ProgressHandle; import org.openide.explorer.ExplorerManager; import org.openide.util.NbBundle; @@ -79,6 +81,7 @@ public final class CommonAttributePanel extends javax.swing.JDialog { super(new JFrame(Bundle.CommonFilesPanel_frame_title()), Bundle.CommonFilesPanel_frame_msg(), true); initComponents(); + this.setLocationRelativeTo(WindowManager.getDefault().getMainWindow()); this.errorText.setVisible(false); this.setupDataSources(); @@ -92,6 +95,23 @@ public final class CommonAttributePanel extends javax.swing.JDialog { } this.errorManager = new UserInputErrorManager(); + + this.percentageThreshold.getDocument().addDocumentListener(new DocumentListener(){ + @Override + public void insertUpdate(DocumentEvent e) { + CommonAttributePanel.this.percentageThresholdChanged(); + } + + @Override + public void removeUpdate(DocumentEvent e) { + CommonAttributePanel.this.percentageThresholdChanged(); + } + + @Override + public void changedUpdate(DocumentEvent e) { + CommonAttributePanel.this.percentageThresholdChanged(); + } + }); } private static boolean isEamDbAvailable() { @@ -424,7 +444,6 @@ public final class CommonAttributePanel extends javax.swing.JDialog { jLabel1 = new javax.swing.JLabel(); setMinimumSize(new java.awt.Dimension(412, 375)); - setPreferredSize(new java.awt.Dimension(412, 375)); setResizable(false); addWindowListener(new java.awt.event.WindowAdapter() { public void windowClosed(java.awt.event.WindowEvent evt) { @@ -530,11 +549,8 @@ public final class CommonAttributePanel extends javax.swing.JDialog { }); percentageThreshold.setText(org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.percentageThreshold.text")); // NOI18N - percentageThreshold.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - percentageThresholdActionPerformed(evt); - } - }); + percentageThreshold.setMaximumSize(new java.awt.Dimension(28, 24)); + percentageThreshold.setMinimumSize(new java.awt.Dimension(28, 24)); org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.jLabel1.text_1")); // NOI18N @@ -661,23 +677,19 @@ public final class CommonAttributePanel extends javax.swing.JDialog { this.handleFrequencyPercentageState(); }//GEN-LAST:event_percentageThresholdCheckActionPerformed - private void percentageThresholdActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_percentageThresholdActionPerformed + private void percentageThresholdChanged(){ String percentageString = this.percentageThreshold.getText(); - Integer percentage = null; - try { - percentage = Integer.parseInt(percentageString); - this.percentageThresholdValue = percentage; + this.percentageThresholdValue = Integer.parseInt(percentageString); + } catch (Exception e) { this.percentageThresholdValue = -1; } - this.percentageThresholdValue = percentage.intValue(); - - this.handleFrequencyPercentageState(); - }//GEN-LAST:event_percentageThresholdActionPerformed - + this.handleFrequencyPercentageState(); + } + private void updateErrorTextAndSearchBox() { if (this.errorManager.anyErrors()) { From 56989e022c826dacd2ba5ca1fecca322b0ffce23 Mon Sep 17 00:00:00 2001 From: Brian Sweeney Date: Tue, 21 Aug 2018 15:39:47 -0600 Subject: [PATCH 124/150] text field stays the same size --- .../CommonAttributePanel.form | 9 ++++++--- .../CommonAttributePanel.java | 20 +++++++++++++++---- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.form b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.form index be96989e91..329de08aef 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.form +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.form @@ -81,7 +81,7 @@ - + @@ -316,10 +316,13 @@ - + - + + + + diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java index 7116ebd2ba..e8fa60ec2d 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java @@ -18,6 +18,7 @@ */ package org.sleuthkit.autopsy.commonfilesearch; +import java.awt.Dimension; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collection; @@ -97,18 +98,28 @@ public final class CommonAttributePanel extends javax.swing.JDialog { this.errorManager = new UserInputErrorManager(); this.percentageThreshold.getDocument().addDocumentListener(new DocumentListener(){ + + private Dimension preferredSize = CommonAttributePanel.this.percentageThreshold.getPreferredSize(); + + private void maintainSize(){ + CommonAttributePanel.this.percentageThreshold.setSize(preferredSize); + } + @Override - public void insertUpdate(DocumentEvent e) { + public void insertUpdate(DocumentEvent e) { + this.maintainSize(); CommonAttributePanel.this.percentageThresholdChanged(); } @Override public void removeUpdate(DocumentEvent e) { + this.maintainSize(); CommonAttributePanel.this.percentageThresholdChanged(); } @Override public void changedUpdate(DocumentEvent e) { + this.maintainSize(); CommonAttributePanel.this.percentageThresholdChanged(); } }); @@ -549,8 +560,9 @@ public final class CommonAttributePanel extends javax.swing.JDialog { }); percentageThreshold.setText(org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.percentageThreshold.text")); // NOI18N - percentageThreshold.setMaximumSize(new java.awt.Dimension(28, 24)); - percentageThreshold.setMinimumSize(new java.awt.Dimension(28, 24)); + percentageThreshold.setMaximumSize(new java.awt.Dimension(40, 24)); + percentageThreshold.setMinimumSize(new java.awt.Dimension(40, 24)); + percentageThreshold.setPreferredSize(new java.awt.Dimension(40, 24)); org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.jLabel1.text_1")); // NOI18N @@ -580,7 +592,7 @@ public final class CommonAttributePanel extends javax.swing.JDialog { .addGroup(jPanel1Layout.createSequentialGroup() .addComponent(percentageThresholdCheck) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(percentageThreshold, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(percentageThreshold, javax.swing.GroupLayout.PREFERRED_SIZE, 40, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jLabel1)) .addGroup(jPanel1Layout.createSequentialGroup() From 4cbf9d10e7f137480dbae202182255839dce7870 Mon Sep 17 00:00:00 2001 From: Brian Sweeney Date: Tue, 21 Aug 2018 16:53:01 -0600 Subject: [PATCH 125/150] had forgotten to hook up the percentage value to the search results --- .../commonfilesearch/AbstractCommonAttributeSearcher.java | 2 ++ .../AllInterCaseCommonAttributeSearcher.java | 2 +- .../autopsy/commonfilesearch/CommonAttributePanel.java | 6 +++--- .../commonfilesearch/IntraCaseCommonAttributeSearcher.java | 2 +- .../SingleInterCaseCommonAttributeSearcher.java | 2 +- 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/AbstractCommonAttributeSearcher.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/AbstractCommonAttributeSearcher.java index 3742f73562..0813b59711 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/AbstractCommonAttributeSearcher.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/AbstractCommonAttributeSearcher.java @@ -42,11 +42,13 @@ public abstract class AbstractCommonAttributeSearcher { private final Map dataSourceIdToNameMap; private boolean filterByMedia; private boolean filterByDoc; + final int frequencyPercentageThreshold; AbstractCommonAttributeSearcher(Map dataSourceIdMap, boolean filterByMedia, boolean filterByDoc, int percentageThreshold){ this.filterByDoc = filterByDoc; this.filterByMedia = filterByMedia; this.dataSourceIdToNameMap = dataSourceIdMap; + this.frequencyPercentageThreshold = percentageThreshold; } Map getDataSourceIdToNameMap(){ diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/AllInterCaseCommonAttributeSearcher.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/AllInterCaseCommonAttributeSearcher.java index c12e1dbca6..595d739504 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/AllInterCaseCommonAttributeSearcher.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/AllInterCaseCommonAttributeSearcher.java @@ -49,7 +49,7 @@ public class AllInterCaseCommonAttributeSearcher extends InterCaseCommonAttribut public CommonAttributeSearchResults findFiles() throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException { InterCaseSearchResultsProcessor eamDbAttrInst = new InterCaseSearchResultsProcessor(this.getDataSourceIdToNameMap()); Map> interCaseCommonFiles = eamDbAttrInst.findInterCaseCommonAttributeValues(Case.getCurrentCase()); - return new CommonAttributeSearchResults(interCaseCommonFiles); + return new CommonAttributeSearchResults(interCaseCommonFiles, this.frequencyPercentageThreshold); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java index e8fa60ec2d..8635694b88 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java @@ -200,9 +200,9 @@ public final class CommonAttributePanel extends javax.swing.JDialog { int percentageThreshold = CommonAttributePanel.this.percentageThresholdValue; - if (CommonAttributePanel.this.percentageThresholdCheck.isSelected()) { - String percentageThresholdAsString = CommonAttributePanel.this.percentageThreshold.getText(); - percentageThreshold = Integer.parseInt(percentageThresholdAsString); + if (!CommonAttributePanel.this.percentageThresholdCheck.isSelected()) { + //0 has the effect of disabling the feature + percentageThreshold = 0; } if (CommonAttributePanel.this.interCaseRadio.isSelected()) { diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/IntraCaseCommonAttributeSearcher.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/IntraCaseCommonAttributeSearcher.java index bd26648196..94bd3b061e 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/IntraCaseCommonAttributeSearcher.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/IntraCaseCommonAttributeSearcher.java @@ -130,7 +130,7 @@ public abstract class IntraCaseCommonAttributeSearcher extends AbstractCommonAtt Map> instanceCollatedCommonFiles = collateMatchesByNumberOfInstances(commonFiles); - return new CommonAttributeSearchResults(instanceCollatedCommonFiles); + return new CommonAttributeSearchResults(instanceCollatedCommonFiles, this.frequencyPercentageThreshold); } /** diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/SingleInterCaseCommonAttributeSearcher.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/SingleInterCaseCommonAttributeSearcher.java index 9289bd8f90..d6f566b8f8 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/SingleInterCaseCommonAttributeSearcher.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/SingleInterCaseCommonAttributeSearcher.java @@ -74,7 +74,7 @@ public class SingleInterCaseCommonAttributeSearcher extends InterCaseCommonAttri InterCaseSearchResultsProcessor eamDbAttrInst = new InterCaseSearchResultsProcessor(this.getDataSourceIdToNameMap()); Map> interCaseCommonFiles = eamDbAttrInst.findSingleInterCaseCommonAttributeValues(Case.getCurrentCase(), correlationCase); - return new CommonAttributeSearchResults(interCaseCommonFiles); + return new CommonAttributeSearchResults(interCaseCommonFiles, this.frequencyPercentageThreshold); } @Override From 2c32fe42e8d4ae3ce54bf13494091c3111aa0980 Mon Sep 17 00:00:00 2001 From: Brian Sweeney Date: Tue, 21 Aug 2018 17:03:33 -0600 Subject: [PATCH 126/150] fixed all the tests --- ...stedWithHashAndFileTypeInterCaseTests.java | 4 +- ...stedWithHashAndFileTypeIntraCaseTests.java | 18 +-- ...IngestedWithNoFileTypesIntraCaseTests.java | 4 +- .../commonfilessearch/InterCaseTestUtils.java | 104 +++++++++--------- .../commonfilessearch/IntraCaseTestUtils.java | 21 ++-- ...chesInAtLeastTwoSourcesIntraCaseTests.java | 2 +- .../UningestedCasesIntraCaseTests.java | 5 +- 7 files changed, 85 insertions(+), 73 deletions(-) diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithHashAndFileTypeInterCaseTests.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithHashAndFileTypeInterCaseTests.java index 74a42172cb..a28d53302a 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithHashAndFileTypeInterCaseTests.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithHashAndFileTypeInterCaseTests.java @@ -86,7 +86,7 @@ public class IngestedWithHashAndFileTypeInterCaseTests extends NbTestCase { Map dataSources = this.utils.getDataSourceMap(); //note that the params false and false are presently meaningless because that feature is not supported yet - AbstractCommonAttributeSearcher builder = new AllInterCaseCommonAttributeSearcher(dataSources, false, false); + AbstractCommonAttributeSearcher builder = new AllInterCaseCommonAttributeSearcher(dataSources, false, false, 0); CommonAttributeSearchResults metadata = builder.findFiles(); @@ -139,7 +139,7 @@ public class IngestedWithHashAndFileTypeInterCaseTests extends NbTestCase { int matchesMustAlsoBeFoundInThisCase = this.utils.getCaseMap().get(CASE2); - AbstractCommonAttributeSearcher builder = new SingleInterCaseCommonAttributeSearcher(matchesMustAlsoBeFoundInThisCase, dataSources, false, false); + AbstractCommonAttributeSearcher builder = new SingleInterCaseCommonAttributeSearcher(matchesMustAlsoBeFoundInThisCase, dataSources, false, false, 0); CommonAttributeSearchResults metadata = builder.findFiles(); diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithHashAndFileTypeIntraCaseTests.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithHashAndFileTypeIntraCaseTests.java index d1a004d281..2d8299eab2 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithHashAndFileTypeIntraCaseTests.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithHashAndFileTypeIntraCaseTests.java @@ -99,7 +99,7 @@ public class IngestedWithHashAndFileTypeIntraCaseTests extends NbTestCase { try { Map dataSources = this.utils.getDataSourceMap(); - AbstractCommonAttributeSearcher allSourcesBuilder = new AllIntraCaseCommonAttributeSearcher(dataSources, false, false); + AbstractCommonAttributeSearcher allSourcesBuilder = new AllIntraCaseCommonAttributeSearcher(dataSources, false, false, 0); CommonAttributeSearchResults metadata = allSourcesBuilder.findFiles(); Map objectIdToDataSource = IntraCaseTestUtils.mapFileInstancesToDataSources(metadata); @@ -140,7 +140,7 @@ public class IngestedWithHashAndFileTypeIntraCaseTests extends NbTestCase { try { Map dataSources = this.utils.getDataSourceMap(); - AbstractCommonAttributeSearcher allSourcesBuilder = new AllIntraCaseCommonAttributeSearcher(dataSources, true, false); + AbstractCommonAttributeSearcher allSourcesBuilder = new AllIntraCaseCommonAttributeSearcher(dataSources, true, false, 0); CommonAttributeSearchResults metadata = allSourcesBuilder.findFiles(); Map objectIdToDataSource = mapFileInstancesToDataSources(metadata); @@ -181,7 +181,7 @@ public class IngestedWithHashAndFileTypeIntraCaseTests extends NbTestCase { try { Map dataSources = this.utils.getDataSourceMap(); - AbstractCommonAttributeSearcher allSourcesBuilder = new AllIntraCaseCommonAttributeSearcher(dataSources, false, true); + AbstractCommonAttributeSearcher allSourcesBuilder = new AllIntraCaseCommonAttributeSearcher(dataSources, false, true, 0); CommonAttributeSearchResults metadata = allSourcesBuilder.findFiles(); Map objectIdToDataSource = mapFileInstancesToDataSources(metadata); @@ -223,7 +223,7 @@ public class IngestedWithHashAndFileTypeIntraCaseTests extends NbTestCase { Map dataSources = this.utils.getDataSourceMap(); Long first = getDataSourceIdByName(SET1, dataSources); - AbstractCommonAttributeSearcher singleSourceBuilder = new SingleIntraCaseCommonAttributeSearcher(first, dataSources, false, false); + AbstractCommonAttributeSearcher singleSourceBuilder = new SingleIntraCaseCommonAttributeSearcher(first, dataSources, false, false, 0); CommonAttributeSearchResults metadata = singleSourceBuilder.findFiles(); Map objectIdToDataSource = mapFileInstancesToDataSources(metadata); @@ -265,7 +265,7 @@ public class IngestedWithHashAndFileTypeIntraCaseTests extends NbTestCase { Map dataSources = this.utils.getDataSourceMap(); Long first = getDataSourceIdByName(SET1, dataSources); - AbstractCommonAttributeSearcher singleSourceBuilder = new SingleIntraCaseCommonAttributeSearcher(first, dataSources, true, false); + AbstractCommonAttributeSearcher singleSourceBuilder = new SingleIntraCaseCommonAttributeSearcher(first, dataSources, true, false, 0); CommonAttributeSearchResults metadata = singleSourceBuilder.findFiles(); Map objectIdToDataSource = mapFileInstancesToDataSources(metadata); @@ -307,7 +307,7 @@ public class IngestedWithHashAndFileTypeIntraCaseTests extends NbTestCase { Map dataSources = this.utils.getDataSourceMap(); Long first = getDataSourceIdByName(SET1, dataSources); - AbstractCommonAttributeSearcher singleSourceBuilder = new SingleIntraCaseCommonAttributeSearcher(first, dataSources, false, true); + AbstractCommonAttributeSearcher singleSourceBuilder = new SingleIntraCaseCommonAttributeSearcher(first, dataSources, false, true, 0); CommonAttributeSearchResults metadata = singleSourceBuilder.findFiles(); Map objectIdToDataSource = mapFileInstancesToDataSources(metadata); @@ -349,7 +349,7 @@ public class IngestedWithHashAndFileTypeIntraCaseTests extends NbTestCase { Map dataSources = this.utils.getDataSourceMap(); Long second = getDataSourceIdByName(SET2, dataSources); - AbstractCommonAttributeSearcher singleSourceBuilder = new SingleIntraCaseCommonAttributeSearcher(second, dataSources, false, false); + AbstractCommonAttributeSearcher singleSourceBuilder = new SingleIntraCaseCommonAttributeSearcher(second, dataSources, false, false, 0); CommonAttributeSearchResults metadata = singleSourceBuilder.findFiles(); Map objectIdToDataSource = mapFileInstancesToDataSources(metadata); @@ -390,7 +390,7 @@ public class IngestedWithHashAndFileTypeIntraCaseTests extends NbTestCase { Map dataSources = this.utils.getDataSourceMap(); Long last = getDataSourceIdByName(SET4, dataSources); - AbstractCommonAttributeSearcher singleSourceBuilder = new SingleIntraCaseCommonAttributeSearcher(last, dataSources, false, false); + AbstractCommonAttributeSearcher singleSourceBuilder = new SingleIntraCaseCommonAttributeSearcher(last, dataSources, false, false, 0); CommonAttributeSearchResults metadata = singleSourceBuilder.findFiles(); Map objectIdToDataSource = mapFileInstancesToDataSources(metadata); @@ -431,7 +431,7 @@ public class IngestedWithHashAndFileTypeIntraCaseTests extends NbTestCase { Map dataSources = this.utils.getDataSourceMap(); Long third = getDataSourceIdByName(SET3, dataSources); - AbstractCommonAttributeSearcher singleSourceBuilder = new SingleIntraCaseCommonAttributeSearcher(third, dataSources, false, false); + AbstractCommonAttributeSearcher singleSourceBuilder = new SingleIntraCaseCommonAttributeSearcher(third, dataSources, false, false, 0); CommonAttributeSearchResults metadata = singleSourceBuilder.findFiles(); Map objectIdToDataSource = mapFileInstancesToDataSources(metadata); diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithNoFileTypesIntraCaseTests.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithNoFileTypesIntraCaseTests.java index d51dfda2fa..c48462929e 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithNoFileTypesIntraCaseTests.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithNoFileTypesIntraCaseTests.java @@ -101,7 +101,7 @@ public class IngestedWithNoFileTypesIntraCaseTests extends NbTestCase { try { Map dataSources = this.utils.getDataSourceMap(); - IntraCaseCommonAttributeSearcher allSourcesBuilder = new AllIntraCaseCommonAttributeSearcher(dataSources, true, false); + IntraCaseCommonAttributeSearcher allSourcesBuilder = new AllIntraCaseCommonAttributeSearcher(dataSources, true, false, 0); CommonAttributeSearchResults metadata = allSourcesBuilder.findFiles(); Map objectIdToDataSource = IntraCaseTestUtils.mapFileInstancesToDataSources(metadata); @@ -125,7 +125,7 @@ public class IngestedWithNoFileTypesIntraCaseTests extends NbTestCase { Map dataSources = this.utils.getDataSourceMap(); Long third = IntraCaseTestUtils.getDataSourceIdByName(IntraCaseTestUtils.SET3, dataSources); - IntraCaseCommonAttributeSearcher singleSourceBuilder = new SingleIntraCaseCommonAttributeSearcher(third, dataSources, true, false); + IntraCaseCommonAttributeSearcher singleSourceBuilder = new SingleIntraCaseCommonAttributeSearcher(third, dataSources, true, false, 0); CommonAttributeSearchResults metadata = singleSourceBuilder.findFiles(); Map objectIdToDataSource = IntraCaseTestUtils.mapFileInstancesToDataSources(metadata); diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/InterCaseTestUtils.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/InterCaseTestUtils.java index cf6bd36fbc..b2d8b57299 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/InterCaseTestUtils.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/InterCaseTestUtils.java @@ -307,67 +307,73 @@ class InterCaseTestUtils { //TODO refactor static boolean verifyInstanceExistanceAndCount(CommonAttributeSearchResults searchDomain, String fileName, String dataSource, String crCase, int instanceCount){ - int tally = 0; - - for(Map.Entry> entry : searchDomain.getMetadata().entrySet()){ + try { + int tally = 0; - for(CommonAttributeValue value : entry.getValue()){ + for(Map.Entry> entry : searchDomain.getMetadata().entrySet()){ - for(AbstractCommonAttributeInstance commonAttribute : value.getInstances()){ + for(CommonAttributeValue value : entry.getValue()){ - if(commonAttribute instanceof CentralRepoCommonAttributeInstance){ - CentralRepoCommonAttributeInstance results = (CentralRepoCommonAttributeInstance) commonAttribute; - for (DisplayableItemNode din : results.generateNodes()){ - - if(din instanceof CentralRepoCommonAttributeInstanceNode){ + for(AbstractCommonAttributeInstance commonAttribute : value.getInstances()){ + + if(commonAttribute instanceof CentralRepoCommonAttributeInstance){ + CentralRepoCommonAttributeInstance results = (CentralRepoCommonAttributeInstance) commonAttribute; + for (DisplayableItemNode din : results.generateNodes()){ - CentralRepoCommonAttributeInstanceNode node = (CentralRepoCommonAttributeInstanceNode) din; - CorrelationAttributeInstance instance = node.getCorrelationAttributeInstance(); + if(din instanceof CentralRepoCommonAttributeInstanceNode){ + + CentralRepoCommonAttributeInstanceNode node = (CentralRepoCommonAttributeInstanceNode) din; + CorrelationAttributeInstance instance = node.getCorrelationAttributeInstance(); + + final String fullPath = instance.getFilePath(); + final File testFile = new File(fullPath); + + final String testCaseName = instance.getCorrelationCase().getDisplayName(); + + final String testFileName = testFile.getName(); + + final String testDataSource = instance.getCorrelationDataSource().getName(); + + boolean sameFileName = testFileName.equalsIgnoreCase(fileName); + boolean sameDataSource = testDataSource.equalsIgnoreCase(dataSource); + boolean sameCrCase = testCaseName.equalsIgnoreCase(crCase); + + if( sameFileName && sameDataSource && sameCrCase){ + tally++; + } + } - final String fullPath = instance.getFilePath(); - final File testFile = new File(fullPath); - - final String testCaseName = instance.getCorrelationCase().getDisplayName(); - - final String testFileName = testFile.getName(); - - final String testDataSource = instance.getCorrelationDataSource().getName(); - - boolean sameFileName = testFileName.equalsIgnoreCase(fileName); - boolean sameDataSource = testDataSource.equalsIgnoreCase(dataSource); - boolean sameCrCase = testCaseName.equalsIgnoreCase(crCase); - - if( sameFileName && sameDataSource && sameCrCase){ - tally++; - } - } - - if(din instanceof CaseDBCommonAttributeInstanceNode){ - - CaseDBCommonAttributeInstanceNode node = (CaseDBCommonAttributeInstanceNode) din; - AbstractFile file = node.getContent(); - - final String testFileName = file.getName(); - final String testCaseName = node.getCase(); - final String testDataSource = node.getDataSource(); - - boolean sameFileName = testFileName.equalsIgnoreCase(fileName); - boolean sameCaseName = testCaseName.equalsIgnoreCase(crCase); - boolean sameDataSource = testDataSource.equalsIgnoreCase(dataSource); - - if(sameFileName && sameDataSource && sameCaseName){ - tally++; + if(din instanceof CaseDBCommonAttributeInstanceNode){ + + CaseDBCommonAttributeInstanceNode node = (CaseDBCommonAttributeInstanceNode) din; + AbstractFile file = node.getContent(); + + final String testFileName = file.getName(); + final String testCaseName = node.getCase(); + final String testDataSource = node.getDataSource(); + + boolean sameFileName = testFileName.equalsIgnoreCase(fileName); + boolean sameCaseName = testCaseName.equalsIgnoreCase(crCase); + boolean sameDataSource = testDataSource.equalsIgnoreCase(dataSource); + + if(sameFileName && sameDataSource && sameCaseName){ + tally++; + } } } + } else { + Assert.fail("Unable to cast AbstractCommonAttributeInstanceNode to InterCaseCommonAttributeSearchResults."); } - } else { - Assert.fail("Unable to cast AbstractCommonAttributeInstanceNode to InterCaseCommonAttributeSearchResults."); } } } + + return tally == instanceCount; + } catch (EamDbException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex); + return false; } - - return tally == instanceCount; } /** diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IntraCaseTestUtils.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IntraCaseTestUtils.java index 82c38cdf56..1ee4799956 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IntraCaseTestUtils.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IntraCaseTestUtils.java @@ -33,6 +33,7 @@ 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.centralrepository.datamodel.EamDbException; import org.sleuthkit.autopsy.commonfilesearch.AbstractCommonAttributeInstance; import org.sleuthkit.autopsy.commonfilesearch.CommonAttributeSearchResults; import org.sleuthkit.autopsy.commonfilesearch.DataSourceLoader; @@ -204,16 +205,22 @@ class IntraCaseTestUtils { */ static Map mapFileInstancesToDataSources(CommonAttributeSearchResults metadata) { Map instanceIdToDataSource = new HashMap<>(); - - for (Map.Entry> entry : metadata.getMetadata().entrySet()) { - for (CommonAttributeValue md : entry.getValue()) { - for (AbstractCommonAttributeInstance fim : md.getInstances()) { - instanceIdToDataSource.put(fim.getAbstractFileObjectId(), fim.getDataSource()); + + try { + for (Map.Entry> entry : metadata.getMetadata().entrySet()) { + for (CommonAttributeValue md : entry.getValue()) { + for (AbstractCommonAttributeInstance fim : md.getInstances()) { + instanceIdToDataSource.put(fim.getAbstractFileObjectId(), fim.getDataSource()); + } } } + + return instanceIdToDataSource; + } catch (EamDbException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex); + return instanceIdToDataSource; } - - return instanceIdToDataSource; } diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/MatchesInAtLeastTwoSourcesIntraCaseTests.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/MatchesInAtLeastTwoSourcesIntraCaseTests.java index f4c397af8d..e0670d9b77 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/MatchesInAtLeastTwoSourcesIntraCaseTests.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/MatchesInAtLeastTwoSourcesIntraCaseTests.java @@ -105,7 +105,7 @@ public class MatchesInAtLeastTwoSourcesIntraCaseTests extends NbTestCase { try { Map dataSources = this.utils.getDataSourceMap(); - AbstractCommonAttributeSearcher allSourcesBuilder = new AllIntraCaseCommonAttributeSearcher(dataSources, false, false); + AbstractCommonAttributeSearcher allSourcesBuilder = new AllIntraCaseCommonAttributeSearcher(dataSources, false, false, 0); CommonAttributeSearchResults metadata = allSourcesBuilder.findFiles(); Map objectIdToDataSource = IntraCaseTestUtils.mapFileInstancesToDataSources(metadata); diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/UningestedCasesIntraCaseTests.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/UningestedCasesIntraCaseTests.java index b55707c771..b581f0d4ed 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/UningestedCasesIntraCaseTests.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/UningestedCasesIntraCaseTests.java @@ -28,7 +28,6 @@ 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.centralrepository.datamodel.EamDbException; import org.sleuthkit.autopsy.commonfilesearch.AllIntraCaseCommonAttributeSearcher; import org.sleuthkit.autopsy.commonfilesearch.CommonAttributeSearchResults; import org.sleuthkit.autopsy.commonfilesearch.IntraCaseCommonAttributeSearcher; @@ -81,7 +80,7 @@ public class UningestedCasesIntraCaseTests extends NbTestCase { try { Map dataSources = this.utils.getDataSourceMap(); - IntraCaseCommonAttributeSearcher allSourcesBuilder = new AllIntraCaseCommonAttributeSearcher(dataSources, false, false); + IntraCaseCommonAttributeSearcher allSourcesBuilder = new AllIntraCaseCommonAttributeSearcher(dataSources, false, false, 0); CommonAttributeSearchResults metadata = allSourcesBuilder.findFiles(); int resultCount = metadata.size(); @@ -101,7 +100,7 @@ public class UningestedCasesIntraCaseTests extends NbTestCase { Map dataSources = this.utils.getDataSourceMap(); Long first = getDataSourceIdByName(SET1, dataSources); - IntraCaseCommonAttributeSearcher singleSourceBuilder = new SingleIntraCaseCommonAttributeSearcher(first, dataSources, false, false); + IntraCaseCommonAttributeSearcher singleSourceBuilder = new SingleIntraCaseCommonAttributeSearcher(first, dataSources, false, false, 0); CommonAttributeSearchResults metadata = singleSourceBuilder.findFiles(); int resultCount = metadata.size(); From 92605055b5113cbc8ee6b4fa62f66015a958acc4 Mon Sep 17 00:00:00 2001 From: Brian Sweeney Date: Wed, 22 Aug 2018 08:34:15 -0600 Subject: [PATCH 127/150] typo in tests --- .../sleuthkit/autopsy/commonfilessearch/IntraCaseTestUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IntraCaseTestUtils.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IntraCaseTestUtils.java index 1ee4799956..c25abbceaf 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IntraCaseTestUtils.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IntraCaseTestUtils.java @@ -173,7 +173,7 @@ class IntraCaseTestUtils { String dataSourceName = objectIdToDataSourceMap.get(objectId); - if (name.equals(name) && dataSourceName.equals(dataSource)) { + if (name.equals(fileName) && dataSourceName.equals(dataSource)) { tally++; } } From e277931d58bb03f9196c971171e621782041ea91 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Wed, 22 Aug 2018 14:05:58 -0400 Subject: [PATCH 128/150] Fixed Codacy issues --- .../autopsy/tabulardatareader/ExcelReader.java | 12 ++++++------ .../autopsy/tabulardatareader/FileReaderFactory.java | 2 ++ .../autopsy/tabulardatareader/SQLiteReader.java | 12 ++++++------ 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/tabulardatareader/ExcelReader.java b/Core/src/org/sleuthkit/autopsy/tabulardatareader/ExcelReader.java index 6352741d22..3b341bb892 100755 --- a/Core/src/org/sleuthkit/autopsy/tabulardatareader/ExcelReader.java +++ b/Core/src/org/sleuthkit/autopsy/tabulardatareader/ExcelReader.java @@ -51,10 +51,10 @@ public final class ExcelReader extends AbstractReader { private final Logger logger = services.getLogger(ExcelReader.class.getName()); private Workbook workbook; - private final String XLSXMimeType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; - private final String XLSMimeType = "application/vnd.ms-excel"; - private final String EMPTY_CELL_STRING = ""; - private HashMap headerCache; + private final static String XLSX_MIME_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; + private final static String XLS_MIME_TYPE = "application/vnd.ms-excel"; + private final static String EMPTY_CELL_STRING = ""; + private Map headerCache; public ExcelReader(AbstractFile file, String localDiskPath, String mimeType) throws FileReaderInitException { @@ -84,7 +84,7 @@ public final class ExcelReader extends AbstractReader { private Workbook createWorkbook(String localDiskPath, String mimeType) throws IOException, FileReaderInitException { switch (mimeType) { - case XLSMimeType: + case XLS_MIME_TYPE: try { //Apache POI only supports BIFF8 format, anything below is considered //old excel format and is not a concern for us. @@ -92,7 +92,7 @@ public final class ExcelReader extends AbstractReader { } catch (OldExcelFormatException e) { throw new FileReaderInitException(e); } - case XLSXMimeType: + case XLSX_MIME_TYPE: //StreamingReader is part of the xlsx streamer dependency that creates //a streaming version of XSSFWorkbook for reading (SXSSFWorkbook is only for writing //large workbooks, not reading). This libary provides a workbook interface diff --git a/Core/src/org/sleuthkit/autopsy/tabulardatareader/FileReaderFactory.java b/Core/src/org/sleuthkit/autopsy/tabulardatareader/FileReaderFactory.java index 5295bb924e..e6af1673b2 100755 --- a/Core/src/org/sleuthkit/autopsy/tabulardatareader/FileReaderFactory.java +++ b/Core/src/org/sleuthkit/autopsy/tabulardatareader/FileReaderFactory.java @@ -26,6 +26,8 @@ import org.sleuthkit.datamodel.AbstractFile; */ public final class FileReaderFactory { + private FileReaderFactory() { + } /** * Instantiates the appropriate reader given the mimeType argument. Currently * supports SQLite files and Excel files (.xls and .xlsx). BIFF5 format of .xls diff --git a/Core/src/org/sleuthkit/autopsy/tabulardatareader/SQLiteReader.java b/Core/src/org/sleuthkit/autopsy/tabulardatareader/SQLiteReader.java index 328f1ac628..2a152f2ffa 100755 --- a/Core/src/org/sleuthkit/autopsy/tabulardatareader/SQLiteReader.java +++ b/Core/src/org/sleuthkit/autopsy/tabulardatareader/SQLiteReader.java @@ -170,10 +170,10 @@ public final class SQLiteReader extends AbstractReader { @Override public Integer getRowCountFromTable(String tableName) throws FileReaderException { - tableName = wrapTableNameStringWithQuotes(tableName); + String quotedTableName = wrapTableNameStringWithQuotes(tableName); try (Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery( - "SELECT count (*) as count FROM " + tableName)){ //NON-NLS + "SELECT count (*) as count FROM " + quotedTableName)){ //NON-NLS return resultSet.getInt("count"); //NON-NLS } catch (SQLException ex) { throw new FileReaderException(ex); @@ -195,10 +195,10 @@ public final class SQLiteReader extends AbstractReader { //This method does not directly call its overloaded counterpart //since the second parameter would need to be retreived from a call to //getTableRowCount(). - tableName = wrapTableNameStringWithQuotes(tableName); + String quotedTableName = wrapTableNameStringWithQuotes(tableName); try(Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery( - "SELECT * FROM " + tableName)) { //NON-NLS + "SELECT * FROM " + quotedTableName)) { //NON-NLS return resultSetToList(resultSet); } catch (SQLException ex) { throw new FileReaderException(ex); @@ -218,10 +218,10 @@ public final class SQLiteReader extends AbstractReader { @Override public List> getRowsFromTable(String tableName, int offset, int numRowsToRead) throws FileReaderException{ - tableName = wrapTableNameStringWithQuotes(tableName); + String quotedTableName = wrapTableNameStringWithQuotes(tableName); try(Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery( - "SELECT * FROM " + tableName //NON-NLS + "SELECT * FROM " + quotedTableName //NON-NLS + " LIMIT " + Integer.toString(numRowsToRead) //NON-NLS + " OFFSET " + Integer.toString(offset - 1))) { //NON-NLS return resultSetToList(resultSet); From 3b80c792be2610904768ce163dfe640fb386cf13 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Wed, 22 Aug 2018 15:13:43 -0400 Subject: [PATCH 129/150] final codacy suggestions...... --- .../tabulardatareader/ExcelReader.java | 4 +-- .../tabulardatareader/SQLiteReader.java | 7 ++--- .../autopsy/testutils/SuiteUtils.java | 26 +++++++++++++++++++ 3 files changed, 30 insertions(+), 7 deletions(-) create mode 100755 Core/src/org/sleuthkit/autopsy/testutils/SuiteUtils.java diff --git a/Core/src/org/sleuthkit/autopsy/tabulardatareader/ExcelReader.java b/Core/src/org/sleuthkit/autopsy/tabulardatareader/ExcelReader.java index 3b341bb892..2fcc79094c 100755 --- a/Core/src/org/sleuthkit/autopsy/tabulardatareader/ExcelReader.java +++ b/Core/src/org/sleuthkit/autopsy/tabulardatareader/ExcelReader.java @@ -47,8 +47,8 @@ import org.apache.poi.hssf.OldExcelFormatException; */ public final class ExcelReader extends AbstractReader { /* Boilerplate code */ - private final IngestServices services = IngestServices.getInstance(); - private final Logger logger = services.getLogger(ExcelReader.class.getName()); + private final static IngestServices services = IngestServices.getInstance(); + private final static Logger logger = services.getLogger(ExcelReader.class.getName()); private Workbook workbook; private final static String XLSX_MIME_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; diff --git a/Core/src/org/sleuthkit/autopsy/tabulardatareader/SQLiteReader.java b/Core/src/org/sleuthkit/autopsy/tabulardatareader/SQLiteReader.java index 2a152f2ffa..c08f571280 100755 --- a/Core/src/org/sleuthkit/autopsy/tabulardatareader/SQLiteReader.java +++ b/Core/src/org/sleuthkit/autopsy/tabulardatareader/SQLiteReader.java @@ -47,14 +47,11 @@ import org.sleuthkit.datamodel.TskCoreException; /** * Reads sqlite databases and returns results in a list collection. */ -@NbBundle.Messages({ - "SQLiteReader.ReadSQLiteFiles.moduleName=SQLiteReader" -}) public final class SQLiteReader extends AbstractReader { private final Connection connection; - private final IngestServices services = IngestServices.getInstance(); - private final Logger logger = services.getLogger(Bundle.SQLiteReader_ReadSQLiteFiles_moduleName()); + private final static IngestServices services = IngestServices.getInstance(); + private final static Logger logger = services.getLogger(SQLiteReader.class.getName()); /** * Writes data source file contents to local disk and opens a sqlite JDBC diff --git a/Core/src/org/sleuthkit/autopsy/testutils/SuiteUtils.java b/Core/src/org/sleuthkit/autopsy/testutils/SuiteUtils.java new file mode 100755 index 0000000000..ebb0a6452a --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/testutils/SuiteUtils.java @@ -0,0 +1,26 @@ +/* + * 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. + */ +package org.sleuthkit.autopsy.testutils; + + +//import junit.framework.Test; +//import org.netbeans.junit.NbModuleSuite; + +/** + * + * @author dsmyda + */ +public final class SuiteUtils { + + /* + public static Test createSuite(Class cls) { + NbModuleSuite.Configuration conf = NbModuleSuite.createConfiguration(cls). + clusters(".*"). + enableModules(".*"); + return conf.suite(); + } + */ +} From 4a68eb7e57265d3e185f35555a37ed7b39dbe0ef Mon Sep 17 00:00:00 2001 From: Brian Sweeney Date: Wed, 22 Aug 2018 17:19:42 -0600 Subject: [PATCH 130/150] updated comments with info pertinent to frequency search tests --- .../commonfilessearch/InterCaseTestUtils.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/InterCaseTestUtils.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/InterCaseTestUtils.java index b2d8b57299..9e41d6f199 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/InterCaseTestUtils.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/InterCaseTestUtils.java @@ -101,6 +101,20 @@ import org.sleuthkit.datamodel.AbstractFile; - Hash-C.jpg - Hash-C.pdf - Hash-D.doc + * + * Frequency Breakdown + * (ratio of datasources a given file appears in to total number of datasources): + * + * Hash-0.dat - moot; these are always excluded + * Hash-A.jpg - 4/6 + * Hash-A.pdf - 4/6 + * Hash-B.jpg - 1/6 + * Hash-B.pdf - 1/6 + * Hash-C.jpg - 2/6 + * Hash-C.pdf - 2/6 + * Hash_D.doc - 2/6 + * Hash-D.jpg - 1/6 + * */ class InterCaseTestUtils { @@ -304,7 +318,6 @@ class InterCaseTestUtils { } } - //TODO refactor static boolean verifyInstanceExistanceAndCount(CommonAttributeSearchResults searchDomain, String fileName, String dataSource, String crCase, int instanceCount){ try { From ac31962c959190edc619589ab1e02e38dbf1b578 Mon Sep 17 00:00:00 2001 From: Brian Sweeney Date: Wed, 22 Aug 2018 17:20:34 -0600 Subject: [PATCH 131/150] frequency search tests --- ...stedWithHashAndFileTypeInterCaseTests.java | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithHashAndFileTypeInterCaseTests.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithHashAndFileTypeInterCaseTests.java index a28d53302a..12277aafba 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithHashAndFileTypeInterCaseTests.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithHashAndFileTypeInterCaseTests.java @@ -182,4 +182,56 @@ public class IngestedWithHashAndFileTypeInterCaseTests extends NbTestCase { Assert.fail(ex); } } + + /** + * We should be able to observe that certain files o no longer returned + * in the result set since they do not appear frequently enough. + */ + public void testThree(){ + try { + Map dataSources = this.utils.getDataSourceMap(); + + //note that the params false and false are presently meaningless because that feature is not supported yet + AbstractCommonAttributeSearcher builder = new AllInterCaseCommonAttributeSearcher(dataSources, false, false, 50); + + CommonAttributeSearchResults metadata = builder.findFiles(); + + assertTrue("Results should not be empty", metadata.size() != 0); + + //case 1 data set 1 + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_0_DAT, CASE1_DATASET_1, CASE1, 0)); + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_PDF, CASE1_DATASET_1, CASE1, 1)); + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_JPG, CASE1_DATASET_1, CASE1, 1)); + + //case 1 data set 2 + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_0_DAT, CASE1_DATASET_2, CASE1, 0)); + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_PDF, CASE1_DATASET_2, CASE1, 1)); + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_JPG, CASE1_DATASET_2, CASE1, 1)); + + //case 2 data set 1 + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_B_PDF, CASE2_DATASET_1, CASE2, 0)); + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_B_JPG, CASE2_DATASET_1, CASE2, 0)); + + //case 2 data set 2 + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_PDF, CASE2_DATASET_2, CASE2, 1)); + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_JPG, CASE2_DATASET_2, CASE2, 1)); + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_D_DOC, CASE2_DATASET_2, CASE2, 0)); + + //case 3 data set 1 + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_JPG, CASE3_DATASET_1, CASE3, 1)); + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_PDF, CASE3_DATASET_1, CASE3, 1)); + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_C_JPG, CASE3_DATASET_1, CASE3, 0)); + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_C_PDF, CASE3_DATASET_1, CASE3, 0)); + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_D_JPG, CASE3_DATASET_1, CASE3, 0)); + + //case 3 data set 2 + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_C_JPG, CASE3_DATASET_2, CASE3, 0)); + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_C_PDF, CASE3_DATASET_2, CASE3, 0)); + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_D_DOC, CASE3_DATASET_2, CASE3, 0)); + + } catch (TskCoreException | NoCurrentCaseException | SQLException | EamDbException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex); + } + } } From 6fa58b9ca70fcbf7500ac97b2dde1f5ca4cd0287 Mon Sep 17 00:00:00 2001 From: Brian Sweeney Date: Wed, 22 Aug 2018 17:21:05 -0600 Subject: [PATCH 132/150] fixed a concurrentmodificationexception when pruning results for frequency percentage search --- .../CommonAttributeSearchResults.java | 33 ++++++++++++++++--- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResults.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResults.java index 35b64e17b2..7539419b14 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResults.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResults.java @@ -19,7 +19,9 @@ */ package org.sleuthkit.autopsy.commonfilesearch; +import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -96,10 +98,14 @@ final public class CommonAttributeSearchResults { * Remove results which are not found in the portion of available data * sources described by minimumPercentageThreshold. * - * @return + * @return metadata */ private Map> getMetadata(int minimumPercentageThreshold) throws EamDbException { + if(minimumPercentageThreshold == 0){ + return Collections.unmodifiableMap(this.instanceCountToAttributeValues); + } + CorrelationAttribute.Type fileAttributeType = CorrelationAttribute .getDefaultCorrelationTypes() .stream() @@ -108,20 +114,39 @@ final public class CommonAttributeSearchResults { EamDb eamDb = EamDb.getInstance(); + Map> itemsToRemove = new HashMap<>(); + for(Entry> listOfValues : Collections.unmodifiableMap(this.instanceCountToAttributeValues).entrySet()){ - Integer key = listOfValues.getKey(); + final Integer key = listOfValues.getKey(); + final List values = listOfValues.getValue(); - for(CommonAttributeValue value : listOfValues.getValue()){ + for(CommonAttributeValue value : values){ int frequencyPercentage = eamDb.getFrequencyPercentage(new CorrelationAttribute(fileAttributeType, value.getValue())); if(frequencyPercentage < minimumPercentageThreshold){ - this.instanceCountToAttributeValues.get(key).remove(value); + if(itemsToRemove.containsKey(key)){ + itemsToRemove.get(key).add(value); + } else { + List toRemove = new ArrayList<>(); + toRemove.add(value); + itemsToRemove.put(key, toRemove); + } } } } + for(Entry> valuesToRemove : itemsToRemove.entrySet()){ + + final Integer key = valuesToRemove.getKey(); + final List values = valuesToRemove.getValue(); + + for (CommonAttributeValue value : values){ + this.instanceCountToAttributeValues.get(key).remove(value); + } + } + return Collections.unmodifiableMap(this.instanceCountToAttributeValues); } From 41797943c2db8e01c67ad4dfaa8d6ba435cc17cf Mon Sep 17 00:00:00 2001 From: Brian Sweeney Date: Wed, 22 Aug 2018 17:21:37 -0600 Subject: [PATCH 133/150] improved conditions for enabling frequency percentage search --- .../CommonAttributePanel.java | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java index 8635694b88..b48cca309a 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java @@ -87,11 +87,15 @@ public final class CommonAttributePanel extends javax.swing.JDialog { this.errorText.setVisible(false); this.setupDataSources(); - if (CommonAttributePanel.isEamDbAvailable()) { + if (CommonAttributePanel.isEamDbAvailableForIntercaseSearch()) { this.setupCases(); - this.enablePercentageOptions(); } else { this.disableIntercaseSearch(); + } + + if(CommonAttributePanel.isEamDbAvailableForPercentageFrequencyCalculations()){ + this.enablePercentageOptions(); + } else { this.disablePercentageOptions(); } @@ -125,7 +129,7 @@ public final class CommonAttributePanel extends javax.swing.JDialog { }); } - private static boolean isEamDbAvailable() { + private static boolean isEamDbAvailableForIntercaseSearch() { try { return EamDb.isEnabled() && EamDb.getInstance() != null @@ -138,6 +142,17 @@ public final class CommonAttributePanel extends javax.swing.JDialog { } return false; } + + private static boolean isEamDbAvailableForPercentageFrequencyCalculations(){ + try { + return EamDb.isEnabled() + && EamDb.getInstance() != null + && EamDb.getInstance().getCases().size() > 0; + } catch (EamDbException ex) { + LOGGER.log(Level.SEVERE, "Unexpected exception while checking for EamDB enabled.", ex); + } + return false; + } private void disableIntercaseSearch() { this.intraCaseRadio.setSelected(true); From 4be4c67de1556c15d3be6dd686885efc58e61413 Mon Sep 17 00:00:00 2001 From: Brian Sweeney Date: Wed, 22 Aug 2018 17:32:55 -0600 Subject: [PATCH 134/150] remove nodes with no children from ui --- .../commonfilesearch/CommonAttributeSearchResults.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResults.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResults.java index 7539419b14..76f96a36b5 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResults.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResults.java @@ -143,7 +143,12 @@ final public class CommonAttributeSearchResults { final List values = valuesToRemove.getValue(); for (CommonAttributeValue value : values){ - this.instanceCountToAttributeValues.get(key).remove(value); + final List instanceCountValue = this.instanceCountToAttributeValues.get(key); + instanceCountValue.remove(value); + + if(instanceCountValue.isEmpty()){ + this.instanceCountToAttributeValues.remove(key); + } } } From a47979ee658f675b88507525237fbd8fcc062698 Mon Sep 17 00:00:00 2001 From: Andrew Ziehl Date: Wed, 22 Aug 2018 17:51:00 -0700 Subject: [PATCH 135/150] Code review cleanup, codeacy issue fixes. --- .../DataContentViewerOtherCases.java | 2 +- .../OtherOccurrenceNodeInstanceData.java | 2 +- .../datamodel/AbstractSqlEamDb.java | 99 ++++++----- .../CorrelationAttributeInstance.java | 154 +++++++++--------- .../datamodel/EamArtifactUtil.java | 11 +- .../centralrepository/datamodel/EamDb.java | 4 +- .../datamodel/SqliteEamDb.java | 4 +- .../ingestmodule/IngestModule.java | 2 +- .../datamodel/CentralRepoDatamodelTest.java | 14 +- 9 files changed, 148 insertions(+), 144 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java index f7991628ea..9da0b577b3 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java @@ -130,7 +130,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi } else if (jmi.equals(addCommentMenuItem)) { try { OtherOccurrenceNodeInstanceData selectedNode = (OtherOccurrenceNodeInstanceData) tableModel.getRow(otherCasesTable.getSelectedRow()); - AddEditCentralRepoCommentAction action = new AddEditCentralRepoCommentAction(selectedNode.createCorrelationAttribute()); + AddEditCentralRepoCommentAction action = new AddEditCentralRepoCommentAction(selectedNode.getCorrelationAttribute()); action.actionPerformed(null); String currentComment = action.getComment(); if (currentComment != null) { diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeInstanceData.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeInstanceData.java index 6eb23455c0..b049613f33 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeInstanceData.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeInstanceData.java @@ -129,7 +129,7 @@ class OtherOccurrenceNodeInstanceData implements OtherOccurrenceNodeData { * Should only be called if isCentralRepoNode() is true. * @return the newly created CorrelationAttribute */ - CorrelationAttributeInstance createCorrelationAttribute() throws EamDbException { + CorrelationAttributeInstance getCorrelationAttribute() throws EamDbException { if (! isCentralRepoNode() ) { throw new EamDbException("Can not create CorrelationAttribute for non central repo node"); } diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java index cd51e59a9d..ac568496b6 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java @@ -88,7 +88,7 @@ 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 @@ -152,7 +152,7 @@ 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 @@ -387,6 +387,7 @@ abstract class AbstractSqlEamDb implements EamDb { return eamCaseResult; } + /** * Retrieves Case details based on Case ID * @@ -504,8 +505,8 @@ 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 @@ -542,12 +543,12 @@ abstract class AbstractSqlEamDb implements EamDb { return eamDataSourceResult; } - + /** * Retrieves Data Source details based on data source ID * - * @param correlationCase the current CorrelationCase used for ensuring - * uniqueness of DataSource + * @param correlationCase the current CorrelationCase used for ensuring + * uniqueness of DataSource * @param dataSourceId the data source ID number * * @return The data source @@ -643,6 +644,15 @@ abstract class AbstractSqlEamDb implements EamDb { + "\nCorrelationArtifact Value: " + eamArtifact.getCorrelationValue()); } + if (eamArtifact.getCorrelationCase() == null) { + throw new EamDbException("CorrelationAttributeInstance case is null"); + } + if (eamArtifact.getCorrelationDataSource() == null) { + throw new EamDbException("CorrelationAttributeInstance data source is null"); + } + if (eamArtifact.getKnownStatus() == null) { + throw new EamDbException("CorrelationAttributeInstance known status is null"); + } Connection conn = connect(); @@ -662,15 +672,6 @@ abstract class AbstractSqlEamDb implements EamDb { preparedStatement = conn.prepareStatement(sql); if (!eamArtifact.getCorrelationValue().isEmpty()) { - if (eamArtifact.getCorrelationCase() == null) { - throw new EamDbException("CorrelationAttributeInstance case is null"); - } - if (eamArtifact.getCorrelationDataSource() == null) { - throw new EamDbException("CorrelationAttributeInstance data source is null"); - } - if (eamArtifact.getKnownStatus() == null) { - throw new EamDbException("CorrelationAttributeInstance known status is null"); - } preparedStatement.setString(1, eamArtifact.getCorrelationCase().getCaseUUID()); preparedStatement.setString(2, eamArtifact.getCorrelationDataSource().getDeviceID()); @@ -754,12 +755,12 @@ abstract class AbstractSqlEamDb implements EamDb { return artifactInstances; } - + /** * 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 @@ -786,9 +787,9 @@ abstract class AbstractSqlEamDb implements EamDb { String sql = "SELECT " + tableName - + ".id, " + + ".id, " + tableName - + ".value," + + ".value," + " 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 " @@ -826,7 +827,7 @@ abstract class AbstractSqlEamDb implements EamDb { * @param value The correlation value * * @return Number of artifact instances having ArtifactType and - * ArtifactValue. + * ArtifactValue. */ @Override public Long getCountArtifactInstancesByTypeValue(CorrelationAttributeInstance.Type aType, String value) throws EamDbException { @@ -955,11 +956,11 @@ 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 { @@ -1006,8 +1007,8 @@ abstract class AbstractSqlEamDb implements EamDb { /** * Adds an eamArtifact to an internal list to be later added to DB. Artifact - can have 1 or more Artifact Instances. Insert will be triggered by a - threshold or a call to commitAttrtibuteInstancesBulk(). + * can have 1 or more Artifact Instances. Insert will be triggered by a + * threshold or a call to commitAttributeInstancesBulk(). * * @param eamArtifact The artifact to add */ @@ -1023,7 +1024,7 @@ abstract class AbstractSqlEamDb implements EamDb { bulkArtifactsCount++; if (bulkArtifactsCount >= bulkArtifactsThreshold) { - commitAttrtibuteInstancesBulk(); + commitAttributeInstancesBulk(); } } } @@ -1037,10 +1038,10 @@ abstract class AbstractSqlEamDb implements EamDb { /** * Executes a bulk insert of the eamArtifacts added from the - addAttributeInstanceBulk() method + * addAttributeInstanceBulk() method */ @Override - public void commitAttrtibuteInstancesBulk() throws EamDbException { + public void commitAttributeInstancesBulk() throws EamDbException { List artifactTypes = getDefinedCorrelationTypes(); Connection conn = connect(); @@ -1068,8 +1069,6 @@ abstract class AbstractSqlEamDb implements EamDb { Collection eamArtifacts = bulkArtifacts.get(type.getDbTableName()); for (CorrelationAttributeInstance eamArtifact : eamArtifacts) { - - if (!eamArtifact.getCorrelationValue().isEmpty()) { if (eamArtifact.getCorrelationCase() == null) { @@ -1088,7 +1087,7 @@ abstract class AbstractSqlEamDb implements EamDb { throw new EamDbException("CorrelationAttributeInstance known status is null for: " + "\n\tCorrelationArtifact ID: " + eamArtifact.getID() + "\n\tCorrelationArtifact Type: " + eamArtifact.getCorrelationType().getDisplayName() - + "\n\tCorrelationArtifact Value: " + eamArtifact.getCorrelationValue() + + "\n\tCorrelationArtifact Value: " + eamArtifact.getCorrelationValue() + "\n\tEam Instance: " + "\n\t\tCaseId: " + eamArtifact.getCorrelationDataSource().getCaseID() + "\n\t\tDeviceID: " + eamArtifact.getCorrelationDataSource().getDeviceID()); @@ -1118,7 +1117,7 @@ abstract class AbstractSqlEamDb implements EamDb { + "\n\t\tFilePath: " + eamArtifact.getFilePath()); } } - + } bulkPs.executeBatch(); @@ -1224,7 +1223,7 @@ abstract class AbstractSqlEamDb implements EamDb { * associated CorrelationAttribute object. * * @param eamArtifact The correlation attribute whose database instance will - * be updated. + * be updated. * * @throws EamDbException */ @@ -1274,11 +1273,11 @@ abstract class AbstractSqlEamDb implements EamDb { * 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 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. + * @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. * @@ -1353,7 +1352,7 @@ abstract class AbstractSqlEamDb implements EamDb { * * @param eamArtifact Artifact containing exactly one (1) ArtifactInstance. * @param knownStatus The status to change the artifact to. Should never be - * KNOWN + * KNOWN */ @Override public void setAttributeInstanceKnownStatus(CorrelationAttributeInstance eamArtifact, TskData.FileKnown knownStatus) throws EamDbException { @@ -1615,7 +1614,7 @@ 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 */ @@ -1855,7 +1854,7 @@ abstract class AbstractSqlEamDb implements EamDb { /** * Process the Artifact instance in the EamDb * - * @param type EamArtifact.Type to search for + * @param type EamArtifact.Type to search for * @param instanceTableCallback callback to process the instance * * @throws EamDbException @@ -1908,8 +1907,8 @@ abstract class AbstractSqlEamDb implements EamDb { if (instanceTableCallback == null) { throw new EamDbException("Callback interface is null"); } - - if(whereClause == null) { + + if (whereClause == null) { throw new EamDbException("Where clause is null"); } @@ -1935,7 +1934,6 @@ abstract class AbstractSqlEamDb implements EamDb { EamDbUtil.closeConnection(conn); } } - @Override public EamOrganization newOrganization(EamOrganization eamOrg) throws EamDbException { @@ -2079,7 +2077,7 @@ abstract class AbstractSqlEamDb implements 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 */ @@ -2282,8 +2280,7 @@ 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 */ @@ -2411,7 +2408,7 @@ 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 @@ -2549,7 +2546,7 @@ 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 */ @@ -2584,7 +2581,7 @@ 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 */ @@ -2688,7 +2685,7 @@ 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 * @@ -2758,7 +2755,7 @@ 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 * diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java index 349faab736..d5beeffad7 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java @@ -49,7 +49,6 @@ public class CorrelationAttributeInstance implements Serializable { private String comment; private TskData.FileKnown knownStatus; - public CorrelationAttributeInstance( String correlationValue, CorrelationAttributeInstance.Type correlationType, @@ -71,28 +70,29 @@ public class CorrelationAttributeInstance implements Serializable { ) throws EamDbException { this(correlationType, correlationValue, -1, eamCase, eamDataSource, filePath, comment, knownStatus); } - + public CorrelationAttributeInstance( - Type filesType, - String md5Hash, - CorrelationCase correlationCase, - CorrelationDataSource fromTSKDataSource, + Type correlationType, + String correlationValue, + CorrelationCase correlationCase, + CorrelationDataSource fromTSKDataSource, String string) throws EamDbException { - this(filesType, md5Hash, -1, correlationCase, fromTSKDataSource, string, "", TskData.FileKnown.UNKNOWN); + this(correlationType, correlationValue, -1, correlationCase, fromTSKDataSource, string, "", TskData.FileKnown.UNKNOWN); } /** * NOTE: Only used for when EamDB is NOT enabled. - * @param aType CorrelationType - * @param value md5 value + * + * @param aType CorrelationAttributeInstance.Type + * @param value correlation value */ - public CorrelationAttributeInstance(Type aType, String value) throws EamDbException { + public CorrelationAttributeInstance(Type aType, String value) throws EamDbException { this(aType, value, -1, null, null, "", "", TskData.FileKnown.UNKNOWN); } CorrelationAttributeInstance( - Type type, - String value, + Type type, + String value, int instanceId, CorrelationCase eamCase, CorrelationDataSource eamDataSource, @@ -103,9 +103,9 @@ public class CorrelationAttributeInstance implements Serializable { if (filePath == null) { throw new EamDbException("file path is null"); } - + if (value == null) { - throw new EamDbException("md5 value is null"); + throw new EamDbException("correlation value is null"); } this.correlationType = type; @@ -119,8 +119,6 @@ public class CorrelationAttributeInstance implements Serializable { this.knownStatus = knownStatus; } - - public Boolean equals(CorrelationAttributeInstance otherInstance) { return ((this.getID() == otherInstance.getID()) && (this.getCorrelationCase().equals(otherInstance.getCorrelationCase())) @@ -139,8 +137,8 @@ public class CorrelationAttributeInstance implements Serializable { + this.getKnownStatus() + this.getComment(); } - - /** + + /** * @return the correlationValue */ public String getCorrelationValue() { @@ -169,12 +167,11 @@ public class CorrelationAttributeInstance implements Serializable { this.correlationType = correlationType; } - /** * Is this a database instance? * * @return True if the instance ID is greater or equal to zero; otherwise - * false. + * false. */ public boolean isDatabaseInstance() { return (ID >= 0); @@ -237,23 +234,24 @@ public class CorrelationAttributeInstance implements Serializable { * as notable and should never be set to KNOWN. * * @param knownStatus Should be BAD if the item is tagged as notable, - * UNKNOWN otherwise + * UNKNOWN otherwise */ public void setKnownStatus(TskData.FileKnown knownStatus) { this.knownStatus = knownStatus; } - - // Type ID's for Default Correlation Types + + // Type ID's for Default Correlation Types public static final int FILES_TYPE_ID = 0; public static final int DOMAIN_TYPE_ID = 1; public static final int EMAIL_TYPE_ID = 2; public static final int PHONE_TYPE_ID = 3; public static final int USBID_TYPE_ID = 4; - - /** + + /** * Load the default correlation types - * - * @throws EamDbException if the Type's dbTableName has invalid characters/format + * + * @throws EamDbException if the Type's dbTableName has invalid + * characters/format */ @Messages({"CorrelationType.FILES.displayName=Files", "CorrelationType.DOMAIN.displayName=Domains", @@ -269,7 +267,8 @@ public class CorrelationAttributeInstance implements Serializable { DEFAULT_CORRELATION_TYPES.add(new CorrelationAttributeInstance.Type(USBID_TYPE_ID, Bundle.CorrelationType_USBID_displayName(), "usb_devices", true, true)); // NON-NLS return DEFAULT_CORRELATION_TYPES; } - public static class Type implements Serializable { + + public static class Type implements Serializable { private int id; private String displayName; @@ -277,19 +276,19 @@ public class CorrelationAttributeInstance implements Serializable { private Boolean supported; private Boolean enabled; private final static String DB_NAMES_REGEX = "[a-z][a-z0-9_]*"; - - /** - * - * @param id Unique ID for this Correlation Type - * @param displayName Name of this type displayed in the UI. - * @param dbTableName Central repository db table where data of this type is stored. - * Must start with a lowercase letter and only contain - * lowercase letters, numbers, and '_' characters. - * @param supported Is this Type currently supported - * @param enabled Is this Type currently enabled. + + /** + * + * @param id Unique ID for this Correlation Type + * @param displayName Name of this type displayed in the UI. + * @param dbTableName Central repository db table where data of this + * type is stored. Must start with a lowercase letter and only contain + * lowercase letters, numbers, and '_' characters. + * @param supported Is this Type currently supported + * @param enabled Is this Type currently enabled. */ public Type(int id, String displayName, String dbTableName, Boolean supported, Boolean enabled) throws EamDbException { - if(dbTableName == null) { + if (dbTableName == null) { throw new EamDbException("dbTableName is null"); } this.id = id; @@ -304,15 +303,15 @@ public class CorrelationAttributeInstance implements Serializable { /** * Constructor for custom types where we do not know the Type ID until - * the row has been entered into the correlation_types table - * in the central repository. - * - * @param displayName Name of this type displayed in the UI. - * @param dbTableName Central repository db table where data of this type is stored - * Must start with a lowercase letter and only contain - * lowercase letters, numbers, and '_' characters. - * @param supported Is this Type currently supported - * @param enabled Is this Type currently enabled. + * the row has been entered into the correlation_types table in the + * central repository. + * + * @param displayName Name of this type displayed in the UI. + * @param dbTableName Central repository db table where data of this + * type is stored Must start with a lowercase letter and only contain + * lowercase letters, numbers, and '_' characters. + * @param supported Is this Type currently supported + * @param enabled Is this Type currently enabled. */ public Type(String displayName, String dbTableName, Boolean supported, Boolean enabled) throws EamDbException { this(-1, displayName, dbTableName, supported, enabled); @@ -362,11 +361,16 @@ public class CorrelationAttributeInstance implements Serializable { @Override public String toString() { StringBuilder str = new StringBuilder(55); - str.append("(id=").append(getId()); - str.append(", displayName=").append(getDisplayName()); - str.append(", dbTableName=").append(getDbTableName()); - str.append(", supported=").append(isSupported().toString()); - str.append(", enabled=").append(isEnabled().toString()); + str.append("(id="); + str.append(getId()); + str.append(", displayName="); + str.append(getDisplayName()); + str.append(", dbTableName="); + str.append(getDbTableName()); + str.append(", supported="); + str.append(isSupported().toString()); + str.append(", enabled="); + str.append(isEnabled().toString()); str.append(")"); return str.toString(); } @@ -436,14 +440,14 @@ public class CorrelationAttributeInstance implements Serializable { } /** - * To support having different database tables for each Type, - * this field provides the prefix/suffix of the table name, - * which allows us to automatically compute the table names - * and index names. - * - * It is the prefix for the instances tables *_instances. (i.e. file_instances) - * It is the suffix for the reference tables reference_*. (i.e. reference_file) - * + * To support having different database tables for each Type, this field + * provides the prefix/suffix of the table name, which allows us to + * automatically compute the table names and index names. + * + * It is the prefix for the instances tables *_instances. (i.e. + * file_instances) It is the suffix for the reference tables + * reference_*. (i.e. reference_file) + * * When custom Types are added in the future, they are already supported * by just giving the desired value for the table name for each custom * Type. Possibly having all custom Types use a common table name. @@ -455,21 +459,23 @@ public class CorrelationAttributeInstance implements Serializable { } /** - * To support having different database tables for each Type, - * this field provides the prefix/suffix of the table name, - * which allows us to automatically compute the table names - * and index names. - * - * It is the prefix for the instances tables *_instances. (i.e. file_instances) - * It is the suffix for the reference tables reference_*. (i.e. reference_file) - * + * To support having different database tables for each Type, this field + * provides the prefix/suffix of the table name, which allows us to + * automatically compute the table names and index names. + * + * It is the prefix for the instances tables *_instances. (i.e. + * file_instances) It is the suffix for the reference tables + * reference_*. (i.e. reference_file) + * * When custom Types are added in the future, they are already supported * by just giving the desired value for the table name for each custom - * Type. Possibly having all custom Types use a common table name. (i.e. custom_instances) - * - * @param dbTableName the dbTableName to set. Must start with lowercase letter - * and can only contain lowercase letters, numbers, and '_' characters. - * + * Type. Possibly having all custom Types use a common table name. (i.e. + * custom_instances) + * + * @param dbTableName the dbTableName to set. Must start with lowercase + * letter and can only contain lowercase letters, numbers, and '_' + * characters. + * * @throws EamDbException if dbTableName contains invalid characters */ public void setDbTableName(String dbTableName) throws EamDbException { diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamArtifactUtil.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamArtifactUtil.java index 02737a5010..48aea257ba 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamArtifactUtil.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamArtifactUtil.java @@ -62,7 +62,6 @@ public class EamArtifactUtil { * * @return List of EamArtifacts */ - //TODO, needs overhaul to comdine 72-85 with the second step public static List makeInstancesFromBlackboardArtifact(BlackboardArtifact bbArtifact, boolean checkEnabled) { @@ -182,10 +181,12 @@ public class EamArtifactUtil { } /** - * Uses the determined type and vallue, then looks up instance details to create proper CorrelationAttributeInstance. + * Uses the determined type and vallue, then looks up instance details to + * create proper CorrelationAttributeInstance. + * * @param bbArtifact the blackboard artifatc * @param correlationType the given type - * @param value the artifact md5 + * @param value the artifact value * @return CorrelationAttributeInstance from details */ private static CorrelationAttributeInstance makeCorrelationAttributeInstanceUsingTypeValue(BlackboardArtifact bbArtifact, CorrelationAttributeInstance.Type correlationType, String value) { @@ -211,7 +212,7 @@ public class EamArtifactUtil { "", TskData.FileKnown.UNKNOWN ); - + } catch (TskCoreException | EamDbException ex) { logger.log(Level.SEVERE, "Error creating artifact instance.", ex); // NON-NLS return null; @@ -240,7 +241,6 @@ public class EamArtifactUtil { return null; } - CorrelationAttributeInstance correlationAttributeInstance; CorrelationAttributeInstance.Type type; CorrelationCase correlationCase; CorrelationDataSource correlationDataSource; @@ -264,6 +264,7 @@ public class EamArtifactUtil { return null; } + CorrelationAttributeInstance correlationAttributeInstance; try { correlationAttributeInstance = EamDb.getInstance().getCorrelationAttributeInstance(type, correlationCase, correlationDataSource, value, filePath); } catch (EamDbException ex) { diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDb.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDb.java index d05cb64ba5..a58daa4b9b 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDb.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDb.java @@ -315,7 +315,7 @@ public interface EamDb { /** * Adds an eamArtifact to an internal list to be later added to DB. Artifact can have 1 or more Artifact Instances. Insert will be triggered by a - threshold or a call to commitAttrtibuteInstancesBulk(). + threshold or a call to commitAttributeInstancesBulk(). * * @param eamArtifact The artifact to add */ @@ -325,7 +325,7 @@ public interface EamDb { * Executes a bulk insert of the eamArtifacts added from the addAttributeInstanceBulk() method */ - void commitAttrtibuteInstancesBulk() throws EamDbException; + void commitAttributeInstancesBulk() throws EamDbException; /** * Executes a bulk insert of the cases diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDb.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDb.java index 47e2870390..d300964b5f 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDb.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDb.java @@ -565,10 +565,10 @@ final class SqliteEamDb extends AbstractSqlEamDb { addAttributeInstanceBulk() method */ @Override - public void commitAttrtibuteInstancesBulk() throws EamDbException { + public void commitAttributeInstancesBulk() throws EamDbException { try { acquireExclusiveLock(); - super.commitAttrtibuteInstancesBulk(); + super.commitAttributeInstancesBulk(); } finally { releaseExclusiveLock(); } diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestModule.java b/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestModule.java index e329eb0182..441380bcc6 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestModule.java @@ -181,7 +181,7 @@ final class IngestModule implements FileIngestModule { return; } try { - dbManager.commitAttrtibuteInstancesBulk(); + dbManager.commitAttributeInstancesBulk(); } catch (EamDbException ex) { logger.log(Level.SEVERE, "Error doing bulk insert of artifacts.", ex); // NON-NLS } 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 6cebfd98c2..102308533b 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 @@ -447,7 +447,7 @@ public class CentralRepoDatamodelTest extends TestCase { /** * Test the methods associated with bulk artifacts (addAttributeInstanceBulk and - commitAttrtibuteInstancesBulk). + commitAttributeInstancesBulk). * First test the normal use case of a large number of valid artifacts getting added. * Next test the error conditions: * - Test preparing artifact with null type @@ -466,7 +466,7 @@ public class CentralRepoDatamodelTest extends TestCase { // - Make a list of artifacts equal to the threshold size // - Call addAttributeInstanceBulk on all of them // - Verify that the bulk threshold number of them were written to the database - // - Call commitAttrtibuteInstancesBulk to insert the remainder + // - Call commitAttributeInstancesBulk to insert the remainder // - Verify that the database now has all the artifacts try { // Make sure there are no artifacts in the database to start @@ -511,8 +511,8 @@ public class CentralRepoDatamodelTest extends TestCase { long count = EamDb.getInstance().getCountArtifactInstancesByCaseDataSource(case1.getCaseUUID(), dataSource1fromCase1.getDeviceID()); assertTrue("Artifact count " + count + " does not match bulkThreshold " + DEFAULT_BULK_THRESHOLD, count == DEFAULT_BULK_THRESHOLD); - // Now call commitAttrtibuteInstancesBulk() to insert the rest of queue - EamDb.getInstance().commitAttrtibuteInstancesBulk(); + // Now call commitAttributeInstancesBulk() to insert the rest of queue + EamDb.getInstance().commitAttributeInstancesBulk(); count = EamDb.getInstance().getCountArtifactInstancesByCaseDataSource(case1.getCaseUUID(), dataSource1fromCase1.getDeviceID()); int expectedCount = list1.size() + list2.size(); assertTrue("Artifact count " + count + " does not match expected count " + expectedCount, count == expectedCount); @@ -535,7 +535,7 @@ public class CentralRepoDatamodelTest extends TestCase { try { CorrelationAttributeInstance attr = new CorrelationAttributeInstance("value", fileType, null, dataSource1fromCase1, "path"); EamDb.getInstance().addAttributeInstanceBulk(attr); - EamDb.getInstance().commitAttrtibuteInstancesBulk(); + EamDb.getInstance().commitAttributeInstancesBulk(); Assert.fail("bulkInsertArtifacts failed to throw exception for null case"); } catch (EamDbException ex) { // This is the expected behavior @@ -545,7 +545,7 @@ public class CentralRepoDatamodelTest extends TestCase { try { CorrelationAttributeInstance attr = new CorrelationAttributeInstance("value", fileType, case1, null, "path"); EamDb.getInstance().addAttributeInstanceBulk(attr); - EamDb.getInstance().commitAttrtibuteInstancesBulk(); + EamDb.getInstance().commitAttributeInstancesBulk(); Assert.fail("prepareBulkArtifact failed to throw exception for null data source"); } catch (EamDbException ex) { // This is the expected behavior @@ -565,7 +565,7 @@ public class CentralRepoDatamodelTest extends TestCase { try { CorrelationAttributeInstance attr = new CorrelationAttributeInstance("value", fileType, case1, dataSource1fromCase1, "path", "comment", null); EamDb.getInstance().addAttributeInstanceBulk(attr); - EamDb.getInstance().commitAttrtibuteInstancesBulk(); + EamDb.getInstance().commitAttributeInstancesBulk(); Assert.fail("prepareBulkArtifact failed to throw exception for null known status"); } catch (EamDbException ex) { // This is the expected behavior From 9ec50de2163878cf48f425d8433f77f4d8f0c001 Mon Sep 17 00:00:00 2001 From: Andrew Ziehl Date: Wed, 22 Aug 2018 18:04:31 -0700 Subject: [PATCH 136/150] Codeacy doesn't like reusing the target variable, or having multiple method calls on one line. Does it like this? --- .../CorrelationAttributeInstance.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java index d5beeffad7..aa8bcf27d0 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java @@ -361,16 +361,16 @@ public class CorrelationAttributeInstance implements Serializable { @Override public String toString() { StringBuilder str = new StringBuilder(55); - str.append("(id="); - str.append(getId()); - str.append(", displayName="); - str.append(getDisplayName()); - str.append(", dbTableName="); - str.append(getDbTableName()); - str.append(", supported="); - str.append(isSupported().toString()); - str.append(", enabled="); - str.append(isEnabled().toString()); + str.append("(id=") + .append(getId()); + str.append(", displayName=") + .append(getDisplayName()); + str.append(", dbTableName=") + .append(getDbTableName()); + str.append(", supported=") + .append(isSupported().toString()); + str.append(", enabled=") + .append(isEnabled().toString()); str.append(")"); return str.toString(); } From e405c3ab98aa115b44eb760ebdbb5f63d4ad17bd Mon Sep 17 00:00:00 2001 From: Andrew Ziehl Date: Thu, 23 Aug 2018 09:05:10 -0700 Subject: [PATCH 137/150] Address codeacy, remove TODOs from code. --- .../CorrelationAttributeInstance.java | 20 +++++++++---------- .../datamodel/CentralRepoDatamodelTest.java | 4 +--- .../commonfilessearch/InterCaseTestUtils.java | 1 - 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java index aa8bcf27d0..1567fec70b 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java @@ -362,16 +362,16 @@ public class CorrelationAttributeInstance implements Serializable { public String toString() { StringBuilder str = new StringBuilder(55); str.append("(id=") - .append(getId()); - str.append(", displayName=") - .append(getDisplayName()); - str.append(", dbTableName=") - .append(getDbTableName()); - str.append(", supported=") - .append(isSupported().toString()); - str.append(", enabled=") - .append(isEnabled().toString()); - str.append(")"); + .append(getId()) + .append(", displayName=") + .append(getDisplayName()) + .append(", dbTableName=") + .append(getDbTableName()) + .append(", supported=") + .append(isSupported().toString()) + .append(", enabled=") + .append(isEnabled().toString()) + .append(")"); return str.toString(); } 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 102308533b..7577000f4b 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 @@ -284,7 +284,7 @@ public class CentralRepoDatamodelTest extends TestCase { } // Try to update artifact with two CorrelationAttributeInstance instances - // TODO, this seems like it is no longer a valid test, already moved fail to the catch since it no longe fails. + // This appears to no longer be a valid test, already moved fail to the catch since it no longe fails. try { CorrelationAttributeInstance attr1 = new CorrelationAttributeInstance("badHash", fileType, case1, dataSource1fromCase1, "badPath", "", TskData.FileKnown.KNOWN); @@ -553,7 +553,6 @@ public class CentralRepoDatamodelTest extends TestCase { // Test preparing artifact with null path // CorrelationAttributeInstance will throw an exception - // TODO, still valid? try { CorrelationAttributeInstance attr = new CorrelationAttributeInstance("value", fileType, case1, dataSource1fromCase1, null); Assert.fail("CorrelationAttributeInstance failed to throw exception for null path"); @@ -995,7 +994,6 @@ public class CentralRepoDatamodelTest extends TestCase { // Test updating a correlation attribute instance comment try { - // TODO CorrelationAttributeInstance correlationAttribute = EamDb.getInstance().getCorrelationAttributeInstance( usbDeviceType, case1, dataSource1fromCase1, devIdValue, devIdPath); assertNotNull("getCorrelationAttributeInstance returned null", correlationAttribute); diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/InterCaseTestUtils.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/InterCaseTestUtils.java index cf6bd36fbc..f456553f84 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/InterCaseTestUtils.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/InterCaseTestUtils.java @@ -304,7 +304,6 @@ class InterCaseTestUtils { } } - //TODO refactor static boolean verifyInstanceExistanceAndCount(CommonAttributeSearchResults searchDomain, String fileName, String dataSource, String crCase, int instanceCount){ int tally = 0; From 7f0ba58eab0ab7c0bb7321d2af9dcb6467384bdc Mon Sep 17 00:00:00 2001 From: Andrew Ziehl Date: Thu, 23 Aug 2018 09:37:17 -0700 Subject: [PATCH 138/150] Reduce NPath complexity --- .../datamodel/AbstractSqlEamDb.java | 149 +++++++++++++----- .../CorrelationAttributeInstance.java | 2 +- 2 files changed, 109 insertions(+), 42 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java index ac568496b6..288363fe95 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java @@ -628,31 +628,7 @@ abstract class AbstractSqlEamDb implements EamDb { */ @Override public void addArtifactInstance(CorrelationAttributeInstance eamArtifact) throws EamDbException { - if (eamArtifact == null) { - throw new EamDbException("CorrelationAttribute is null"); - } - if (eamArtifact.getCorrelationType() == null) { - throw new EamDbException("Correlation type is null"); - } - if (eamArtifact.getCorrelationValue() == null) { - throw new EamDbException("Correlation value is null"); - } - if (eamArtifact.getCorrelationValue().length() >= MAX_VALUE_LENGTH) { - throw new EamDbException("Artifact value too long for central repository." - + "\nCorrelationArtifact ID: " + eamArtifact.getID() - + "\nCorrelationArtifact Type: " + eamArtifact.getCorrelationType().getDisplayName() - + "\nCorrelationArtifact Value: " + eamArtifact.getCorrelationValue()); - - } - if (eamArtifact.getCorrelationCase() == null) { - throw new EamDbException("CorrelationAttributeInstance case is null"); - } - if (eamArtifact.getCorrelationDataSource() == null) { - throw new EamDbException("CorrelationAttributeInstance data source is null"); - } - if (eamArtifact.getKnownStatus() == null) { - throw new EamDbException("CorrelationAttributeInstance known status is null"); - } + checkAddArtifactInstanceNulls(eamArtifact); Connection conn = connect(); @@ -696,6 +672,34 @@ abstract class AbstractSqlEamDb implements EamDb { } } + private void checkAddArtifactInstanceNulls(CorrelationAttributeInstance eamArtifact) throws EamDbException { + if (eamArtifact == null) { + throw new EamDbException("CorrelationAttribute is null"); + } + if (eamArtifact.getCorrelationType() == null) { + throw new EamDbException("Correlation type is null"); + } + if (eamArtifact.getCorrelationValue() == null) { + throw new EamDbException("Correlation value is null"); + } + if (eamArtifact.getCorrelationValue().length() >= MAX_VALUE_LENGTH) { + throw new EamDbException("Artifact value too long for central repository." + + "\nCorrelationArtifact ID: " + eamArtifact.getID() + + "\nCorrelationArtifact Type: " + eamArtifact.getCorrelationType().getDisplayName() + + "\nCorrelationArtifact Value: " + eamArtifact.getCorrelationValue()); + + } + if (eamArtifact.getCorrelationCase() == null) { + throw new EamDbException("CorrelationAttributeInstance case is null"); + } + if (eamArtifact.getCorrelationDataSource() == null) { + throw new EamDbException("CorrelationAttributeInstance data source is null"); + } + if (eamArtifact.getKnownStatus() == null) { + throw new EamDbException("CorrelationAttributeInstance known status is null"); + } + } + /** * Retrieves eamArtifact instances from the database that are associated * with the eamArtifactType and eamArtifactValue of the given eamArtifact. @@ -2460,7 +2464,27 @@ abstract class AbstractSqlEamDb implements EamDb { if (newType == null) { throw new EamDbException("Correlation type is null"); } + int typeId; + if (-1 == newType.getId()) { + typeId = newCorrelationTypeNotKnownId(newType); + } else { + typeId = newCorrelationTypeKnownId(newType); + } + return typeId; + } + + /** + * Helper function which adds a new EamArtifact.Type to the db without an + * id. + * + * @param newType New type to add. + * + * @return ID of this new Correlation Type + * + * @throws EamDbException + */ + public int newCorrelationTypeNotKnownId(CorrelationAttributeInstance.Type newType) throws EamDbException { Connection conn = connect(); PreparedStatement preparedStatement = null; @@ -2470,28 +2494,71 @@ abstract class AbstractSqlEamDb implements EamDb { String insertSql; String querySql; // if we have a known ID, use it, if not (is -1) let the db assign it. - if (-1 == newType.getId()) { - insertSql = "INSERT INTO correlation_types(display_name, db_table_name, supported, enabled) VALUES (?, ?, ?, ?) " + getConflictClause(); - } else { - insertSql = "INSERT INTO correlation_types(id, display_name, db_table_name, supported, enabled) VALUES (?, ?, ?, ?, ?) " + getConflictClause(); - } + insertSql = "INSERT INTO correlation_types(display_name, db_table_name, supported, enabled) VALUES (?, ?, ?, ?) " + getConflictClause(); + querySql = "SELECT * FROM correlation_types WHERE display_name=? AND db_table_name=?"; try { preparedStatement = conn.prepareStatement(insertSql); - if (-1 == newType.getId()) { - preparedStatement.setString(1, newType.getDisplayName()); - preparedStatement.setString(2, newType.getDbTableName()); - preparedStatement.setInt(3, newType.isSupported() ? 1 : 0); - preparedStatement.setInt(4, newType.isEnabled() ? 1 : 0); - } else { - preparedStatement.setInt(1, newType.getId()); - preparedStatement.setString(2, newType.getDisplayName()); - preparedStatement.setString(3, newType.getDbTableName()); - preparedStatement.setInt(4, newType.isSupported() ? 1 : 0); - preparedStatement.setInt(5, newType.isEnabled() ? 1 : 0); + preparedStatement.setString(1, newType.getDisplayName()); + preparedStatement.setString(2, newType.getDbTableName()); + preparedStatement.setInt(3, newType.isSupported() ? 1 : 0); + preparedStatement.setInt(4, newType.isEnabled() ? 1 : 0); + + preparedStatement.executeUpdate(); + + preparedStatementQuery = conn.prepareStatement(querySql); + preparedStatementQuery.setString(1, newType.getDisplayName()); + preparedStatementQuery.setString(2, newType.getDbTableName()); + + resultSet = preparedStatementQuery.executeQuery(); + if (resultSet.next()) { + CorrelationAttributeInstance.Type correlationType = getCorrelationTypeFromResultSet(resultSet); + typeId = correlationType.getId(); } + } catch (SQLException ex) { + throw new EamDbException("Error inserting new correlation type.", ex); // NON-NLS + } finally { + EamDbUtil.closeStatement(preparedStatement); + EamDbUtil.closeStatement(preparedStatementQuery); + EamDbUtil.closeResultSet(resultSet); + EamDbUtil.closeConnection(conn); + } + return typeId; + } + + /** + * Helper function which adds a new EamArtifact.Type to the db. + * + * @param newType New type to add. + * + * @return ID of this new Correlation Type + * + * @throws EamDbException + */ + private int newCorrelationTypeKnownId(CorrelationAttributeInstance.Type newType) throws EamDbException { + Connection conn = connect(); + + PreparedStatement preparedStatement = null; + PreparedStatement preparedStatementQuery = null; + ResultSet resultSet = null; + int typeId = 0; + String insertSql; + String querySql; + // if we have a known ID, use it, if not (is -1) let the db assign it. + insertSql = "INSERT INTO correlation_types(id, display_name, db_table_name, supported, enabled) VALUES (?, ?, ?, ?, ?) " + getConflictClause(); + + querySql = "SELECT * FROM correlation_types WHERE display_name=? AND db_table_name=?"; + + try { + preparedStatement = conn.prepareStatement(insertSql); + + preparedStatement.setInt(1, newType.getId()); + preparedStatement.setString(2, newType.getDisplayName()); + preparedStatement.setString(3, newType.getDbTableName()); + preparedStatement.setInt(4, newType.isSupported() ? 1 : 0); + preparedStatement.setInt(5, newType.isEnabled() ? 1 : 0); preparedStatement.executeUpdate(); diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java index 1567fec70b..eafdcf1d14 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java @@ -371,7 +371,7 @@ public class CorrelationAttributeInstance implements Serializable { .append(isSupported().toString()) .append(", enabled=") .append(isEnabled().toString()) - .append(")"); + .append(')'); return str.toString(); } From 2db51beb697a11eae4e82d2e1b3f5eaf0751ec86 Mon Sep 17 00:00:00 2001 From: Andrew Ziehl Date: Thu, 23 Aug 2018 09:45:58 -0700 Subject: [PATCH 139/150] more codeacy cleanup for CorrelationAttributeInstance --- .../CorrelationAttributeInstance.java | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java index eafdcf1d14..9fa9ab77da 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java @@ -268,9 +268,13 @@ public class CorrelationAttributeInstance implements Serializable { return DEFAULT_CORRELATION_TYPES; } + /** + * Correlation Types which determine which table to query in the CR + */ + @SuppressWarnings("serial") public static class Type implements Serializable { - private int id; + private int typeId; private String displayName; private String dbTableName; private Boolean supported; @@ -287,11 +291,11 @@ public class CorrelationAttributeInstance implements Serializable { * @param supported Is this Type currently supported * @param enabled Is this Type currently enabled. */ - public Type(int id, String displayName, String dbTableName, Boolean supported, Boolean enabled) throws EamDbException { + public Type(int typeId, String displayName, String dbTableName, Boolean supported, Boolean enabled) throws EamDbException { if (dbTableName == null) { throw new EamDbException("dbTableName is null"); } - this.id = id; + this.typeId = typeId; this.displayName = displayName; this.dbTableName = dbTableName; this.supported = supported; @@ -344,7 +348,7 @@ public class CorrelationAttributeInstance implements Serializable { * @return true if it is the same type */ private boolean sameType(CorrelationAttributeInstance.Type that) { - return this.id == that.getId() + return this.typeId == that.getId() && Objects.equals(this.supported, that.isSupported()) && Objects.equals(this.enabled, that.isEnabled()); } @@ -352,7 +356,7 @@ public class CorrelationAttributeInstance implements Serializable { @Override public int hashCode() { int hash = 7; - hash = 67 * hash + Objects.hashCode(this.id); + hash = 67 * hash + Objects.hashCode(this.typeId); hash = 67 * hash + Objects.hashCode(this.supported); hash = 67 * hash + Objects.hashCode(this.enabled); return hash; @@ -376,17 +380,17 @@ public class CorrelationAttributeInstance implements Serializable { } /** - * @return the id + * @return the typeId */ public int getId() { - return id; + return typeId; } /** - * @param id the id to set + * @param id the typeId to set */ - public void setId(int id) { - this.id = id; + public void setId(int typeId) { + this.typeId = typeId; } /** From e70fc3210c592bb4e9359bc8c264d757ea127ad6 Mon Sep 17 00:00:00 2001 From: Brian Sweeney Date: Thu, 23 Aug 2018 10:47:12 -0600 Subject: [PATCH 140/150] assertion api changed slightly --- .../IngestedWithHashAndFileTypeInterCaseTests.java | 2 +- .../sleuthkit/autopsy/commonfilessearch/InterCaseTestUtils.java | 2 +- .../sleuthkit/autopsy/commonfilessearch/IntraCaseTestUtils.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithHashAndFileTypeInterCaseTests.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithHashAndFileTypeInterCaseTests.java index a35a6c7273..a425ecbcc8 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithHashAndFileTypeInterCaseTests.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithHashAndFileTypeInterCaseTests.java @@ -231,7 +231,7 @@ public class IngestedWithHashAndFileTypeInterCaseTests extends NbTestCase { } catch (TskCoreException | NoCurrentCaseException | SQLException | EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); } } } diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/InterCaseTestUtils.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/InterCaseTestUtils.java index 1ecc6ff216..b4f6302d61 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/InterCaseTestUtils.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/InterCaseTestUtils.java @@ -384,7 +384,7 @@ class InterCaseTestUtils { return tally == instanceCount; } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); return false; } } diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IntraCaseTestUtils.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IntraCaseTestUtils.java index 6311543b9d..f2a06d01f1 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IntraCaseTestUtils.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IntraCaseTestUtils.java @@ -218,7 +218,7 @@ class IntraCaseTestUtils { return instanceIdToDataSource; } catch (EamDbException ex) { Exceptions.printStackTrace(ex); - Assert.fail(ex); + Assert.fail(ex.getMessage()); return instanceIdToDataSource; } } From f0ec93e0694e78e5f25c3bc4458b6953f9caad4f Mon Sep 17 00:00:00 2001 From: Andrew Ziehl Date: Thu, 23 Aug 2018 10:02:53 -0700 Subject: [PATCH 141/150] ignore warning on Type --- .../datamodel/CorrelationAttributeInstance.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java index 9fa9ab77da..9d64f7dc72 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java @@ -272,7 +272,7 @@ public class CorrelationAttributeInstance implements Serializable { * Correlation Types which determine which table to query in the CR */ @SuppressWarnings("serial") - public static class Type implements Serializable { + public static class Type implements Serializable { // NOPMD Avoid short class names like Type private int typeId; private String displayName; From 4bd5fc4c9139c1bfc5b75d1ce15e40c29ef740e2 Mon Sep 17 00:00:00 2001 From: Brian Sweeney Date: Thu, 23 Aug 2018 11:58:03 -0600 Subject: [PATCH 142/150] protect against occasional use of unmodifiable collections --- .../CommonAttributeSearchResults.java | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResults.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResults.java index 76f96a36b5..4374e11091 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResults.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResults.java @@ -40,17 +40,6 @@ final public class CommonAttributeSearchResults { private final int percentageThreshold; - /** - * Create a values object which can be handed off to the node factories. - * - * @param values list of CommonAttributeValue indexed by size of - * CommonAttributeValue - */ - CommonAttributeSearchResults(Map> metadata) { - this.instanceCountToAttributeValues = metadata; - this.percentageThreshold = 0; - } - /** * Create a values object which can be handed off to the node factories. * @@ -58,7 +47,8 @@ final public class CommonAttributeSearchResults { * CommonAttributeValue */ CommonAttributeSearchResults(Map> metadata, int percentageThreshold) { - this.instanceCountToAttributeValues = metadata; + //wrap in a new object in case any client code has used an unmodifiable collection + this.instanceCountToAttributeValues = new HashMap<>(metadata); this.percentageThreshold = percentageThreshold; } From 656b4464d4922bc23ffb1d4cd9ad1b07edb90b86 Mon Sep 17 00:00:00 2001 From: Brian Sweeney Date: Thu, 23 Aug 2018 12:59:08 -0600 Subject: [PATCH 143/150] codacy stuff --- .../autopsy/commonfilesearch/CommonAttributePanel.java | 8 ++++---- .../CommonAttributeSearchResultRootNode.java | 1 - .../commonfilesearch/CommonAttributeSearchResults.java | 2 +- .../autopsy/commonfilesearch/UserInputErrorManager.java | 6 +++--- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java index b48cca309a..f9a5c114bc 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java @@ -110,19 +110,19 @@ public final class CommonAttributePanel extends javax.swing.JDialog { } @Override - public void insertUpdate(DocumentEvent e) { + public void insertUpdate(DocumentEvent event) { this.maintainSize(); CommonAttributePanel.this.percentageThresholdChanged(); } @Override - public void removeUpdate(DocumentEvent e) { + public void removeUpdate(DocumentEvent event) { this.maintainSize(); CommonAttributePanel.this.percentageThresholdChanged(); } @Override - public void changedUpdate(DocumentEvent e) { + public void changedUpdate(DocumentEvent event) { this.maintainSize(); CommonAttributePanel.this.percentageThresholdChanged(); } @@ -710,7 +710,7 @@ public final class CommonAttributePanel extends javax.swing.JDialog { try { this.percentageThresholdValue = Integer.parseInt(percentageString); - } catch (Exception e) { + } catch (NumberFormatException exception) { this.percentageThresholdValue = -1; } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResultRootNode.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResultRootNode.java index ef36025d8d..b44e79aa68 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResultRootNode.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResultRootNode.java @@ -24,7 +24,6 @@ import java.util.logging.Logger; import org.openide.nodes.ChildFactory; import org.openide.nodes.Children; import org.openide.nodes.Node; -import org.openide.util.Exceptions; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; import org.sleuthkit.autopsy.datamodel.DisplayableItemNode; diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResults.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResults.java index 4374e11091..82089fead9 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResults.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResults.java @@ -99,7 +99,7 @@ final public class CommonAttributeSearchResults { CorrelationAttribute.Type fileAttributeType = CorrelationAttribute .getDefaultCorrelationTypes() .stream() - .filter(t -> t.getId() == CorrelationAttribute.FILES_TYPE_ID) + .filter(filterType -> filterType.getId() == CorrelationAttribute.FILES_TYPE_ID) .findFirst().get(); EamDb eamDb = EamDb.getInstance(); diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/UserInputErrorManager.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/UserInputErrorManager.java index 11c22a3aec..5b3e806bd4 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/UserInputErrorManager.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/UserInputErrorManager.java @@ -67,7 +67,7 @@ class UserInputErrorManager { * @return true for yes, else false */ boolean anyErrors(){ - return this.currentErrors.values().stream().anyMatch(errorMessage -> errorMessage.getStatus() == true); + return this.currentErrors.values().stream().anyMatch(errorMessage -> errorMessage.isErrorSet() == true); } /** @@ -75,7 +75,7 @@ class UserInputErrorManager { */ List getErrors(){ return this.currentErrors.values().stream() - .filter(errorMessage -> errorMessage.getStatus() == true) + .filter(errorMessage -> errorMessage.isErrorSet() == true) .map(ErrorMessage::getMessage) .collect(Collectors.toList()); } @@ -119,7 +119,7 @@ class UserInputErrorManager { * Return the status (true for error status, false for no error) * @return */ - boolean getStatus(){ + boolean isErrorSet(){ return this.status; } } From f4c253f1ae384a37c46466734d3b94215dc02395 Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Mon, 27 Aug 2018 16:34:50 -0400 Subject: [PATCH 144/150] Removed a duplicate scan of the output directory for each manifest file --- .../autopsy/experimental/autoingest/AutoIngestManager.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java index 8dddd2a3e6..ebf1bda7b8 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java @@ -1431,10 +1431,6 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen sysLogger.log(Level.SEVERE, String.format("Error attempting to set node data for %s", manifest.getFilePath()), ex); } } - Path caseDirectory = PathUtils.findCaseDirectory(rootOutputDirectory, manifest.getCaseName()); - if (null != caseDirectory) { - job.setCaseDirectoryPath(caseDirectory); - } newPendingJobsList.add(job); } From 46bb667bf3dbb8fb99b9fa219dada4aca42c0eb8 Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Mon, 27 Aug 2018 16:52:26 -0400 Subject: [PATCH 145/150] No longer trying to find case output directory for each job while doing an input scan --- .../autoingest/AutoIngestManager.java | 80 +++++++++---------- 1 file changed, 38 insertions(+), 42 deletions(-) diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java index ebf1bda7b8..6239be509b 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java @@ -1398,10 +1398,6 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen AutoIngestJob job; if (nodeData.getVersion() == AutoIngestJobNodeData.getCurrentVersion()) { job = new AutoIngestJob(nodeData); - Path caseDirectory = PathUtils.findCaseDirectory(rootOutputDirectory, manifest.getCaseName()); - if (null != caseDirectory) { - job.setCaseDirectoryPath(caseDirectory); - } } else { job = new AutoIngestJob(manifest); job.setPriority(nodeData.getPriority()); // Retain priority, present in all versions of the node data. @@ -1591,54 +1587,53 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen * @throws InterruptedException */ private void addCompletedJob(Manifest manifest, AutoIngestJobNodeData nodeData) throws CoordinationServiceException, InterruptedException, AutoIngestJobException { - Path caseDirectoryPath = PathUtils.findCaseDirectory(rootOutputDirectory, manifest.getCaseName()); - if (null != caseDirectoryPath) { - AutoIngestJob job; - if (nodeData.getVersion() == AutoIngestJobNodeData.getCurrentVersion()) { - job = new AutoIngestJob(nodeData); - job.setCaseDirectoryPath(caseDirectoryPath); - } else { - /** - * Use the manifest rather than the node data here to create - * a new AutoIngestJob instance because the AutoIngestJob - * constructor that takes a node data object expects the - * node data to have fields that do not exist in earlier - * versions. - */ - job = new AutoIngestJob(manifest); - job.setCaseDirectoryPath(caseDirectoryPath); + Path caseDirectoryPath = nodeData.getCaseDirectoryPath(); + if (!caseDirectoryPath.toFile().exists()) { + sysLogger.log(Level.WARNING, String.format("Job completed for %s, but cannot find case directory, ignoring job", nodeData.getManifestFilePath())); + return; + } - /** - * Update the job with the fields that exist in all versions - * of the nodeData. - */ - job.setCompletedDate(nodeData.getCompletedDate()); - job.setErrorsOccurred(nodeData.getErrorsOccurred()); - job.setPriority(nodeData.getPriority()); - job.setNumberOfCrashes(nodeData.getNumberOfCrashes()); - job.setProcessingStage(AutoIngestJob.Stage.COMPLETED, nodeData.getCompletedDate()); - job.setProcessingStatus(AutoIngestJob.ProcessingStatus.COMPLETED); + AutoIngestJob job; + if (nodeData.getVersion() == AutoIngestJobNodeData.getCurrentVersion()) { + job = new AutoIngestJob(nodeData); + job.setCaseDirectoryPath(caseDirectoryPath); + } else { + /** + * Use the manifest rather than the node data here to create a + * new AutoIngestJob instance because the AutoIngestJob + * constructor that takes a node data object expects the node + * data to have fields that do not exist in earlier versions. + */ + job = new AutoIngestJob(manifest); + job.setCaseDirectoryPath(caseDirectoryPath); - /* + /** + * Update the job with the fields that exist in all versions of + * the nodeData. + */ + job.setCompletedDate(nodeData.getCompletedDate()); + job.setErrorsOccurred(nodeData.getErrorsOccurred()); + job.setPriority(nodeData.getPriority()); + job.setNumberOfCrashes(nodeData.getNumberOfCrashes()); + job.setProcessingStage(AutoIngestJob.Stage.COMPLETED, nodeData.getCompletedDate()); + job.setProcessingStatus(AutoIngestJob.ProcessingStatus.COMPLETED); + + /* * Try to upgrade/update the coordination service manifest * node data for the job. It is possible that two hosts will * both try to obtain the lock to do the upgrade operation * at the same time. If this happens, the host that is * holding the lock will complete the upgrade operation. - */ - try (Lock manifestLock = coordinationService.tryGetExclusiveLock(CoordinationService.CategoryNode.MANIFESTS, manifest.getFilePath().toString())) { - if (null != manifestLock) { - updateCoordinationServiceManifestNode(job); - } - } catch (CoordinationServiceException ex) { - sysLogger.log(Level.SEVERE, String.format("Error attempting to set node data for %s", manifest.getFilePath()), ex); + */ + try (Lock manifestLock = coordinationService.tryGetExclusiveLock(CoordinationService.CategoryNode.MANIFESTS, manifest.getFilePath().toString())) { + if (null != manifestLock) { + updateCoordinationServiceManifestNode(job); } + } catch (CoordinationServiceException ex) { + sysLogger.log(Level.SEVERE, String.format("Error attempting to set node data for %s", manifest.getFilePath()), ex); } - newCompletedJobsList.add(job); - - } else { - sysLogger.log(Level.WARNING, String.format("Job completed for %s, but cannot find case directory, ignoring job", nodeData.getManifestFilePath())); } + newCompletedJobsList.add(job); } /** @@ -2452,6 +2447,7 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen Thread.sleep(AutoIngestUserPreferences.getSecondsToSleepBetweenCases() * 1000); } currentJob.setCaseDirectoryPath(caseDirectoryPath); + updateCoordinationServiceManifestNode(currentJob); // update case directory path Case caseForJob = Case.getCurrentCase(); sysLogger.log(Level.INFO, "Opened case {0} for {1}", new Object[]{caseForJob.getName(), manifest.getFilePath()}); return caseForJob; From b5dcd2bd37862c5d18d5981463d37ea0657c21e8 Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Tue, 28 Aug 2018 10:37:07 -0400 Subject: [PATCH 146/150] Additional logging --- .../autopsy/experimental/autoingest/AutoIngestManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java index 6239be509b..e8500e6ba2 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java @@ -1589,7 +1589,7 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen private void addCompletedJob(Manifest manifest, AutoIngestJobNodeData nodeData) throws CoordinationServiceException, InterruptedException, AutoIngestJobException { Path caseDirectoryPath = nodeData.getCaseDirectoryPath(); if (!caseDirectoryPath.toFile().exists()) { - sysLogger.log(Level.WARNING, String.format("Job completed for %s, but cannot find case directory, ignoring job", nodeData.getManifestFilePath())); + sysLogger.log(Level.WARNING, String.format("Job completed for %s, but cannot find case directory %s, ignoring job", nodeData.getManifestFilePath(), caseDirectoryPath.toString())); return; } From 120b3db105afc799bf5bccc7e8fe05c15cae6d65 Mon Sep 17 00:00:00 2001 From: Brian Sweeney Date: Tue, 28 Aug 2018 11:37:19 -0600 Subject: [PATCH 147/150] comparison was backwards --- .../commonfilesearch/CommonAttributeSearchResults.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResults.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResults.java index e8efefa6f4..388dca1bab 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResults.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResults.java @@ -86,13 +86,13 @@ final public class CommonAttributeSearchResults { * search. * * Remove results which are not found in the portion of available data - * sources described by minimumPercentageThreshold. + sources described by maximumPercentageThreshold. * * @return metadata */ - private Map> getMetadata(int minimumPercentageThreshold) throws EamDbException { + private Map> getMetadata(int maximumPercentageThreshold) throws EamDbException { - if(minimumPercentageThreshold == 0){ + if(maximumPercentageThreshold == 0){ return Collections.unmodifiableMap(this.instanceCountToAttributeValues); } @@ -115,7 +115,7 @@ final public class CommonAttributeSearchResults { int frequencyPercentage = eamDb.getFrequencyPercentage(new CorrelationAttributeInstance(fileAttributeType, value.getValue())); - if(frequencyPercentage < minimumPercentageThreshold){ + if(frequencyPercentage > maximumPercentageThreshold){ if(itemsToRemove.containsKey(key)){ itemsToRemove.get(key).add(value); } else { From c202db7261e24a526969906d783c8b538f84b343 Mon Sep 17 00:00:00 2001 From: Brian Sweeney Date: Tue, 28 Aug 2018 17:49:46 -0600 Subject: [PATCH 148/150] error messages truncated --- .../commonfilesearch/Bundle.properties | 14 +- .../CommonAttributePanel.form | 137 +++++++----- .../CommonAttributePanel.java | 195 ++++++++++-------- .../UserInputErrorManager.java | 2 +- 4 files changed, 206 insertions(+), 142 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/Bundle.properties b/Core/src/org/sleuthkit/autopsy/commonfilesearch/Bundle.properties index 8864d4a73a..b8f2e3e0d7 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/Bundle.properties @@ -9,10 +9,14 @@ IntraCasePanel.withinDataSourceRadioButton.text=At least one match must appear i IntraCasePanel.selectDataSourceComboBox.actionCommand= InterCasePanel.specificCentralRepoCaseRadio.text=Matches must be from the following Central Repo case: InterCasePanel.anyCentralRepoCaseRadio.text=Matches may be from any Central Repo case -CommonAttributePanel.commonFilesSearchLabel2.text=Scope of Search +CommonAttributePanel.jCheckBox1.text=Hide files found in over +CommonAttributePanel.jLabel1.text=% of data sources in central repository. +CommonAttributePanel.percentageThresholdTextTwo.text_1=% of data sources in central repository. +CommonAttributePanel.percentageThresholdTextOne.text=20 +CommonAttributePanel.percentageThresholdCheck.text_1=Hide files found in over CommonAttributePanel.intraCaseRadio.text=Within current case CommonAttributePanel.commonFilesSearchLabel1.text=Find common files to correlate data soures or cases. -CommonAttributePanel.errorText.text=In order to search, you must select a file category. +CommonAttributePanel.errorText.text=In order to search, you must select a file category. CommonAttributePanel.categoriesLabel.text=File Types To Include: CommonAttributePanel.documentsCheckbox.text=Documents CommonAttributePanel.pictureVideoCheckbox.text=Pictures and Videos @@ -23,8 +27,4 @@ CommonAttributePanel.allFileCategoriesRadioButton.text=All file types CommonAttributePanel.cancelButton.actionCommand=Cancel CommonAttributePanel.cancelButton.text=Cancel CommonAttributePanel.searchButton.text=Search -CommonAttributePanel.jCheckBox1.text=Hide files found in over -CommonAttributePanel.jLabel1.text=% of data sources in central repository. -CommonAttributePanel.percentageThreshold.text=20 -CommonAttributePanel.jLabel1.text_1=% of data sources in central repository. -CommonAttributePanel.percentageThresholdCheck.text_1=Hide files found in over +CommonAttributePanel.commonFilesSearchLabel2.text=Scope of Search diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.form b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.form index 329de08aef..20cbb33fe9 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.form +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.form @@ -8,8 +8,11 @@ + + + - + @@ -29,7 +32,7 @@ - + @@ -37,14 +40,15 @@ - + - + - + + @@ -55,45 +59,57 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - @@ -123,16 +139,22 @@ - - + + - - - - + + + + + + + + + + - + @@ -243,6 +265,7 @@ + @@ -310,10 +333,10 @@ - + - + @@ -326,13 +349,33 @@ - + - + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java index f9a5c114bc..1955b17ac9 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java @@ -84,7 +84,7 @@ public final class CommonAttributePanel extends javax.swing.JDialog { initComponents(); this.setLocationRelativeTo(WindowManager.getDefault().getMainWindow()); - this.errorText.setVisible(false); + //this.errorText.setVisible(false); this.setupDataSources(); if (CommonAttributePanel.isEamDbAvailableForIntercaseSearch()) { @@ -101,12 +101,12 @@ public final class CommonAttributePanel extends javax.swing.JDialog { this.errorManager = new UserInputErrorManager(); - this.percentageThreshold.getDocument().addDocumentListener(new DocumentListener(){ + this.percentageThresholdTextOne.getDocument().addDocumentListener(new DocumentListener(){ - private Dimension preferredSize = CommonAttributePanel.this.percentageThreshold.getPreferredSize(); + private Dimension preferredSize = CommonAttributePanel.this.percentageThresholdTextOne.getPreferredSize(); private void maintainSize(){ - CommonAttributePanel.this.percentageThreshold.setSize(preferredSize); + CommonAttributePanel.this.percentageThresholdTextOne.setSize(preferredSize); } @Override @@ -466,10 +466,13 @@ public final class CommonAttributePanel extends javax.swing.JDialog { intraCasePanel = new org.sleuthkit.autopsy.commonfilesearch.IntraCasePanel(); interCasePanel = new org.sleuthkit.autopsy.commonfilesearch.InterCasePanel(); percentageThresholdCheck = new javax.swing.JCheckBox(); - percentageThreshold = new javax.swing.JTextField(); - jLabel1 = new javax.swing.JLabel(); + percentageThresholdTextOne = new javax.swing.JTextField(); + percentageThresholdTextTwo = new javax.swing.JLabel(); + filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 32767)); + filler2 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(32767, 32767)); - setMinimumSize(new java.awt.Dimension(412, 375)); + setMaximumSize(new java.awt.Dimension(450, 375)); + setMinimumSize(new java.awt.Dimension(450, 375)); setResizable(false); addWindowListener(new java.awt.event.WindowAdapter() { public void windowClosed(java.awt.event.WindowEvent evt) { @@ -477,9 +480,10 @@ public final class CommonAttributePanel extends javax.swing.JDialog { } }); - jPanel1.setMaximumSize(new java.awt.Dimension(412, 375)); - jPanel1.setMinimumSize(new java.awt.Dimension(412, 375)); - jPanel1.setPreferredSize(new java.awt.Dimension(412, 375)); + jPanel1.setMaximumSize(new java.awt.Dimension(450, 375)); + jPanel1.setMinimumSize(new java.awt.Dimension(450, 375)); + jPanel1.setPreferredSize(new java.awt.Dimension(450, 375)); + jPanel1.setRequestFocusEnabled(false); org.openide.awt.Mnemonics.setLocalizedText(commonFilesSearchLabel2, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.commonFilesSearchLabel2.text")); // NOI18N commonFilesSearchLabel2.setFocusable(false); @@ -542,6 +546,7 @@ public final class CommonAttributePanel extends javax.swing.JDialog { errorText.setForeground(new java.awt.Color(255, 0, 0)); org.openide.awt.Mnemonics.setLocalizedText(errorText, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.errorText.text")); // NOI18N + errorText.setVerticalAlignment(javax.swing.SwingConstants.TOP); org.openide.awt.Mnemonics.setLocalizedText(commonFilesSearchLabel1, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.commonFilesSearchLabel1.text")); // NOI18N commonFilesSearchLabel1.setFocusable(false); @@ -574,12 +579,12 @@ public final class CommonAttributePanel extends javax.swing.JDialog { } }); - percentageThreshold.setText(org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.percentageThreshold.text")); // NOI18N - percentageThreshold.setMaximumSize(new java.awt.Dimension(40, 24)); - percentageThreshold.setMinimumSize(new java.awt.Dimension(40, 24)); - percentageThreshold.setPreferredSize(new java.awt.Dimension(40, 24)); + percentageThresholdTextOne.setText(org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.percentageThresholdTextOne.text")); // NOI18N + percentageThresholdTextOne.setMaximumSize(new java.awt.Dimension(40, 24)); + percentageThresholdTextOne.setMinimumSize(new java.awt.Dimension(40, 24)); + percentageThresholdTextOne.setPreferredSize(new java.awt.Dimension(40, 24)); - org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.jLabel1.text_1")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(percentageThresholdTextTwo, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.percentageThresholdTextTwo.text_1")); // NOI18N javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1); jPanel1.setLayout(jPanel1Layout); @@ -588,35 +593,43 @@ public final class CommonAttributePanel extends javax.swing.JDialog { .addGroup(jPanel1Layout.createSequentialGroup() .addContainerGap() .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(commonFilesSearchLabel2) - .addComponent(intraCaseRadio) - .addComponent(interCaseRadio) - .addComponent(commonFilesSearchLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(categoriesLabel) - .addComponent(selectedFileCategoriesButton) - .addGroup(jPanel1Layout.createSequentialGroup() - .addGap(29, 29, 29) - .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(documentsCheckbox) - .addComponent(pictureVideoCheckbox))) - .addComponent(allFileCategoriesRadioButton) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup() - .addGap(10, 10, 10) - .addComponent(layoutPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) - .addGroup(jPanel1Layout.createSequentialGroup() - .addComponent(percentageThresholdCheck) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(percentageThreshold, javax.swing.GroupLayout.PREFERRED_SIZE, 40, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jLabel1)) - .addGroup(jPanel1Layout.createSequentialGroup() + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup() .addComponent(searchButton) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(cancelButton) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(errorText))) - .addContainerGap()) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createSequentialGroup() + .addGap(0, 0, Short.MAX_VALUE) + .addComponent(filler1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(80, 80, 80) + .addComponent(filler2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(errorText))) + .addGroup(jPanel1Layout.createSequentialGroup() + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(commonFilesSearchLabel2) + .addComponent(intraCaseRadio) + .addComponent(interCaseRadio) + .addComponent(commonFilesSearchLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(categoriesLabel) + .addComponent(selectedFileCategoriesButton) + .addGroup(jPanel1Layout.createSequentialGroup() + .addGap(29, 29, 29) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(documentsCheckbox) + .addComponent(pictureVideoCheckbox))) + .addComponent(allFileCategoriesRadioButton) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup() + .addGap(10, 10, 10) + .addComponent(layoutPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addGroup(jPanel1Layout.createSequentialGroup() + .addComponent(percentageThresholdCheck) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(percentageThresholdTextOne, javax.swing.GroupLayout.PREFERRED_SIZE, 40, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(percentageThresholdTextTwo))) + .addContainerGap(9, Short.MAX_VALUE)))) ); jPanel1Layout.setVerticalGroup( jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -644,68 +657,72 @@ public final class CommonAttributePanel extends javax.swing.JDialog { .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(percentageThresholdCheck) - .addComponent(percentageThreshold, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(jLabel1)) + .addComponent(percentageThresholdTextOne, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(percentageThresholdTextTwo)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(searchButton) - .addComponent(cancelButton) - .addComponent(errorText)) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(filler2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(filler1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(searchButton) + .addComponent(cancelButton) + .addComponent(errorText, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addContainerGap()) ); getContentPane().add(jPanel1, java.awt.BorderLayout.CENTER); }// //GEN-END:initComponents - private void searchButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_searchButtonActionPerformed - search(); - SwingUtilities.windowForComponent(this).dispose(); - }//GEN-LAST:event_searchButtonActionPerformed - - private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed - SwingUtilities.windowForComponent(this).dispose(); - }//GEN-LAST:event_cancelButtonActionPerformed - - private void allFileCategoriesRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_allFileCategoriesRadioButtonActionPerformed - this.handleFileTypeCheckBoxState(); - }//GEN-LAST:event_allFileCategoriesRadioButtonActionPerformed - - private void selectedFileCategoriesButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_selectedFileCategoriesButtonActionPerformed - this.handleFileTypeCheckBoxState(); - }//GEN-LAST:event_selectedFileCategoriesButtonActionPerformed - - private void pictureVideoCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pictureVideoCheckboxActionPerformed - this.handleFileTypeCheckBoxState(); - }//GEN-LAST:event_pictureVideoCheckboxActionPerformed - - private void documentsCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_documentsCheckboxActionPerformed - this.handleFileTypeCheckBoxState(); - }//GEN-LAST:event_documentsCheckboxActionPerformed - - private void intraCaseRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_intraCaseRadioActionPerformed - ((java.awt.CardLayout) this.layoutPanel.getLayout()).first(this.layoutPanel); - }//GEN-LAST:event_intraCaseRadioActionPerformed - - private void interCaseRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_interCaseRadioActionPerformed - ((java.awt.CardLayout) this.layoutPanel.getLayout()).last(this.layoutPanel); - }//GEN-LAST:event_interCaseRadioActionPerformed - private void formWindowClosed(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowClosed SwingUtilities.windowForComponent(this).dispose(); }//GEN-LAST:event_formWindowClosed private void percentageThresholdCheckActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_percentageThresholdCheckActionPerformed if (this.percentageThresholdCheck.isSelected()) { - this.percentageThreshold.setEnabled(true); + this.percentageThresholdTextOne.setEnabled(true); } else { - this.percentageThreshold.setEnabled(false); + this.percentageThresholdTextOne.setEnabled(false); } this.handleFrequencyPercentageState(); }//GEN-LAST:event_percentageThresholdCheckActionPerformed + private void interCaseRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_interCaseRadioActionPerformed + ((java.awt.CardLayout) this.layoutPanel.getLayout()).last(this.layoutPanel); + }//GEN-LAST:event_interCaseRadioActionPerformed + + private void intraCaseRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_intraCaseRadioActionPerformed + ((java.awt.CardLayout) this.layoutPanel.getLayout()).first(this.layoutPanel); + }//GEN-LAST:event_intraCaseRadioActionPerformed + + private void documentsCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_documentsCheckboxActionPerformed + this.handleFileTypeCheckBoxState(); + }//GEN-LAST:event_documentsCheckboxActionPerformed + + private void pictureVideoCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pictureVideoCheckboxActionPerformed + this.handleFileTypeCheckBoxState(); + }//GEN-LAST:event_pictureVideoCheckboxActionPerformed + + private void selectedFileCategoriesButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_selectedFileCategoriesButtonActionPerformed + this.handleFileTypeCheckBoxState(); + }//GEN-LAST:event_selectedFileCategoriesButtonActionPerformed + + private void allFileCategoriesRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_allFileCategoriesRadioButtonActionPerformed + this.handleFileTypeCheckBoxState(); + }//GEN-LAST:event_allFileCategoriesRadioButtonActionPerformed + + private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed + SwingUtilities.windowForComponent(this).dispose(); + }//GEN-LAST:event_cancelButtonActionPerformed + + private void searchButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_searchButtonActionPerformed + search(); + SwingUtilities.windowForComponent(this).dispose(); + }//GEN-LAST:event_searchButtonActionPerformed + private void percentageThresholdChanged(){ - String percentageString = this.percentageThreshold.getText(); + String percentageString = this.percentageThresholdTextOne.getText(); try { this.percentageThresholdValue = Integer.parseInt(percentageString); @@ -731,15 +748,17 @@ public final class CommonAttributePanel extends javax.swing.JDialog { } private void enablePercentageOptions() { - this.percentageThreshold.setEnabled(true); + this.percentageThresholdTextOne.setEnabled(true); this.percentageThresholdCheck.setEnabled(true); this.percentageThresholdCheck.setSelected(true); + this.percentageThresholdTextTwo.setEnabled(true); } private void disablePercentageOptions() { - this.percentageThreshold.setEnabled(false); + this.percentageThresholdTextOne.setEnabled(false); this.percentageThresholdCheck.setEnabled(false); this.percentageThresholdCheck.setSelected(false); + this.percentageThresholdTextTwo.setEnabled(false); } private void handleFileTypeCheckBoxState() { @@ -791,16 +810,18 @@ public final class CommonAttributePanel extends javax.swing.JDialog { private javax.swing.JCheckBox documentsCheckbox; private javax.swing.JLabel errorText; private javax.swing.ButtonGroup fileTypeFilterButtonGroup; + private javax.swing.Box.Filler filler1; + private javax.swing.Box.Filler filler2; private org.sleuthkit.autopsy.commonfilesearch.InterCasePanel interCasePanel; private javax.swing.JRadioButton interCaseRadio; private javax.swing.ButtonGroup interIntraButtonGroup; private org.sleuthkit.autopsy.commonfilesearch.IntraCasePanel intraCasePanel; private javax.swing.JRadioButton intraCaseRadio; - private javax.swing.JLabel jLabel1; private javax.swing.JPanel jPanel1; private java.awt.Panel layoutPanel; - private javax.swing.JTextField percentageThreshold; private javax.swing.JCheckBox percentageThresholdCheck; + private javax.swing.JTextField percentageThresholdTextOne; + private javax.swing.JLabel percentageThresholdTextTwo; private javax.swing.JCheckBox pictureVideoCheckbox; private javax.swing.JButton searchButton; private javax.swing.JRadioButton selectedFileCategoriesButton; diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/UserInputErrorManager.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/UserInputErrorManager.java index 5b3e806bd4..358891da38 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/UserInputErrorManager.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/UserInputErrorManager.java @@ -45,7 +45,7 @@ class UserInputErrorManager { // and add them to the map. this.currentErrors = new HashMap<>(); - this.currentErrors.put(FREQUENCY_PERCENTAGE_OUT_OF_RANGE_KEY, new ErrorMessage("Frequency percentage must be greater than zero and less than or equal to 100.")); + this.currentErrors.put(FREQUENCY_PERCENTAGE_OUT_OF_RANGE_KEY, new ErrorMessage("Invalid Frequency Percentage: 0 < % < 100.")); this.currentErrors.put(NO_FILE_CATEGORIES_SELECTED_KEY, new ErrorMessage("No file categories are included in the search.")); } From 59efa9bf0e8003abcdcf16b1aa48e4b27f6e8db3 Mon Sep 17 00:00:00 2001 From: Brian Sweeney Date: Tue, 28 Aug 2018 17:53:30 -0600 Subject: [PATCH 149/150] removed dead code --- .../sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java | 1 - 1 file changed, 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java index 1955b17ac9..21f2b72624 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java @@ -84,7 +84,6 @@ public final class CommonAttributePanel extends javax.swing.JDialog { initComponents(); this.setLocationRelativeTo(WindowManager.getDefault().getMainWindow()); - //this.errorText.setVisible(false); this.setupDataSources(); if (CommonAttributePanel.isEamDbAvailableForIntercaseSearch()) { From 333060bebb07eb7cd4d0654dbfba742ba5bbd5dd Mon Sep 17 00:00:00 2001 From: Brian Sweeney Date: Tue, 28 Aug 2018 18:15:35 -0600 Subject: [PATCH 150/150] ui text added to bundles --- .../autopsy/commonfilesearch/UserInputErrorManager.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/UserInputErrorManager.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/UserInputErrorManager.java index 358891da38..3e09e32193 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/UserInputErrorManager.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/UserInputErrorManager.java @@ -23,6 +23,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; +import org.openide.util.NbBundle; /** * Manager for present state of errors on the Common Files Search. @@ -39,14 +40,17 @@ class UserInputErrorManager { * of all known error states, retrieve error messages, and determine if * anything is in an error state. */ + @NbBundle.Messages({ + "UserInputErrorManager.frequency=Invalid Frequency Percentage: 0 < % < 100.", + "UserInputErrorManager.categories=No file categories are included in the search."}) UserInputErrorManager (){ //when new errors are needed for the dialog, define a key and a value // and add them to the map. this.currentErrors = new HashMap<>(); - this.currentErrors.put(FREQUENCY_PERCENTAGE_OUT_OF_RANGE_KEY, new ErrorMessage("Invalid Frequency Percentage: 0 < % < 100.")); - this.currentErrors.put(NO_FILE_CATEGORIES_SELECTED_KEY, new ErrorMessage("No file categories are included in the search.")); + this.currentErrors.put(FREQUENCY_PERCENTAGE_OUT_OF_RANGE_KEY, new ErrorMessage(Bundle.UserInputErrorManager_frequency())); + this.currentErrors.put(NO_FILE_CATEGORIES_SELECTED_KEY, new ErrorMessage(Bundle.UserInputErrorManager_categories())); } /**