From 7f7a1094ac64eac2b198515caa606ad6090a1a7b Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Mon, 4 Mar 2019 15:43:38 -0500 Subject: [PATCH 01/59] 4791 remove text/plain mime type from document results --- .../commonpropertiessearch/AbstractCommonAttributeSearcher.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/AbstractCommonAttributeSearcher.java b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/AbstractCommonAttributeSearcher.java index 00a4a0f653..f3de0ff791 100644 --- a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/AbstractCommonAttributeSearcher.java +++ b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/AbstractCommonAttributeSearcher.java @@ -186,9 +186,9 @@ public abstract class AbstractCommonAttributeSearcher { * when checkType is ONLY_TEXT_FILES. ".doc", ".docx", ".odt", ".xls", * ".xlsx", ".ppt", ".pptx" ".txt", ".rtf", ".log", ".text", ".xml" ".html", * ".htm", ".css", ".js", ".php", ".aspx" ".pdf" + * //ignore text/plain due to large number of results with that type */ static final Set TEXT_FILES_MIME_TYPES = Stream.of( - "text/plain", //NON-NLS "application/rtf", //NON-NLS "application/pdf", //NON-NLS "text/css", //NON-NLS From ba0010f6267cee0f78c0ac3e1a02fcf3f8f59d21 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Mon, 4 Mar 2019 15:44:36 -0500 Subject: [PATCH 02/59] 4791 make default selection for intra-case search to filter by mime type --- .../autopsy/commonpropertiessearch/IntraCasePanel.form | 2 +- .../autopsy/commonpropertiessearch/IntraCasePanel.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/IntraCasePanel.form b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/IntraCasePanel.form index d4a11098bb..e3fedcff6c 100644 --- a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/IntraCasePanel.form +++ b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/IntraCasePanel.form @@ -99,6 +99,7 @@ + @@ -139,7 +140,6 @@ - diff --git a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/IntraCasePanel.java b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/IntraCasePanel.java index d0c7ab1579..b2382fe8a4 100644 --- a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/IntraCasePanel.java +++ b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/IntraCasePanel.java @@ -154,6 +154,7 @@ public final class IntraCasePanel extends javax.swing.JPanel { categoriesLabel.setName(""); // NOI18N buttonGroup.add(selectedFileCategoriesButton); + selectedFileCategoriesButton.setSelected(true); org.openide.awt.Mnemonics.setLocalizedText(selectedFileCategoriesButton, org.openide.util.NbBundle.getMessage(IntraCasePanel.class, "IntraCasePanel.selectedFileCategoriesButton.text")); // NOI18N selectedFileCategoriesButton.setToolTipText(org.openide.util.NbBundle.getMessage(IntraCasePanel.class, "IntraCasePanel.selectedFileCategoriesButton.toolTipText")); // NOI18N selectedFileCategoriesButton.addActionListener(new java.awt.event.ActionListener() { @@ -181,7 +182,6 @@ public final class IntraCasePanel extends javax.swing.JPanel { }); buttonGroup.add(allFileCategoriesRadioButton); - allFileCategoriesRadioButton.setSelected(true); org.openide.awt.Mnemonics.setLocalizedText(allFileCategoriesRadioButton, org.openide.util.NbBundle.getMessage(IntraCasePanel.class, "IntraCasePanel.allFileCategoriesRadioButton.text")); // NOI18N allFileCategoriesRadioButton.setToolTipText(org.openide.util.NbBundle.getMessage(IntraCasePanel.class, "IntraCasePanel.allFileCategoriesRadioButton.toolTipText")); // NOI18N allFileCategoriesRadioButton.addActionListener(new java.awt.event.ActionListener() { From 90c75d635e8b12a42cbf5f8f63a6c41be55014d3 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Mon, 4 Mar 2019 16:24:43 -0500 Subject: [PATCH 03/59] 4791 use name of first image in multi-image data sources --- .../datamodel/utils/DataSourceLoader.java | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/utils/DataSourceLoader.java b/Core/src/org/sleuthkit/autopsy/datamodel/utils/DataSourceLoader.java index 0c1329d9f6..18c762d073 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/utils/DataSourceLoader.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/utils/DataSourceLoader.java @@ -1,16 +1,16 @@ /* - * + * * 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. @@ -30,10 +30,10 @@ import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; /** - * Encapsulates logic required to create a mapping of data sources in the + * Encapsulates logic required to create a mapping of data sources in the * current case to their data source IDs. - * - * Intended to be used within the context of a SwingWorker or other background + * + * Intended to be used within the context of a SwingWorker or other background * thread. */ public class DataSourceLoader { @@ -46,8 +46,7 @@ public class DataSourceLoader { //try block releases resources - exceptions are handled in done() try ( SleuthkitCase.CaseDbQuery query = tskDb.executeQuery(SELECT_DATA_SOURCES_LOGICAL); - ResultSet resultSet = query.getResultSet() - ) { + ResultSet resultSet = query.getResultSet()) { while (resultSet.next()) { Long objectId = resultSet.getLong(1); String dataSourceName = resultSet.getString(2); @@ -56,7 +55,7 @@ public class DataSourceLoader { } } - private void loadImageSources(SleuthkitCase tskDb, Map dataSouceMap) throws SQLException, TskCoreException { + private void loadImageSources(SleuthkitCase tskDb, Map dataSourceMap) throws SQLException, TskCoreException { //try block releases resources - exceptions are handled in done() try ( SleuthkitCase.CaseDbQuery query = tskDb.executeQuery(SELECT_DATA_SOURCES_IMAGE); @@ -64,21 +63,24 @@ public class DataSourceLoader { while (resultSet.next()) { Long objectId = resultSet.getLong(1); - String dataSourceName = resultSet.getString(2); - File image = new File(dataSourceName); - String dataSourceNameTrimmed = image.getName(); - dataSouceMap.put(objectId, dataSourceNameTrimmed); + if (!dataSourceMap.containsKey(objectId)) { + String dataSourceName = resultSet.getString(2); + File image = new File(dataSourceName); + String dataSourceNameTrimmed = image.getName(); + dataSourceMap.put(objectId, dataSourceNameTrimmed); + } } } } /** * Get a map of data source Ids to their string names for the current case. - * + * * @return Map of Long (id) to String (name) + * * @throws NoCurrentCaseException * @throws TskCoreException - * @throws SQLException + * @throws SQLException */ public Map getDataSourceMap() throws NoCurrentCaseException, TskCoreException, SQLException { Map dataSouceMap = new HashMap<>(); From b979217fe3d9861f94bb8d60ec468d0dd84be95b Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Mon, 4 Mar 2019 17:43:09 -0500 Subject: [PATCH 04/59] 4791 Organize by number of data sources instead of instances --- .../AbstractCommonAttributeSearcher.java | 2 +- .../autopsy/commonpropertiessearch/CommonAttributeValue.java | 4 ++-- .../autopsy/commonpropertiessearch/IntraCasePanel.form | 2 -- .../autopsy/commonpropertiessearch/IntraCasePanel.java | 2 -- 4 files changed, 3 insertions(+), 7 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/AbstractCommonAttributeSearcher.java b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/AbstractCommonAttributeSearcher.java index f3de0ff791..8f553dff2a 100644 --- a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/AbstractCommonAttributeSearcher.java +++ b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/AbstractCommonAttributeSearcher.java @@ -135,7 +135,7 @@ public abstract class AbstractCommonAttributeSearcher { Map instanceCollatedCommonFiles = new TreeMap<>(); for (CommonAttributeValue md5Metadata : commonFiles.values()) { - Integer size = md5Metadata.getInstanceCount(); + Integer size = md5Metadata.getDataSources().size(); if (instanceCollatedCommonFiles.containsKey(size)) { instanceCollatedCommonFiles.get(size).addMetadataToList(md5Metadata); diff --git a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeValue.java b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeValue.java index d86eff5a4f..2048c15987 100644 --- a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeValue.java +++ b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeValue.java @@ -54,13 +54,13 @@ final public class CommonAttributeValue { return this.fileInstances.stream().map(AbstractCommonAttributeInstance::getCaseName).collect(Collectors.joining(", ")); } - public String getDataSources() { + public Set getDataSources() { Set sources = new HashSet<>(); for (AbstractCommonAttributeInstance data : this.fileInstances) { sources.add(data.getDataSource()); } - return String.join(", ", sources); + return sources; } void addInstance(AbstractCommonAttributeInstance metadata) { diff --git a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/IntraCasePanel.form b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/IntraCasePanel.form index e3fedcff6c..2d79515463 100644 --- a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/IntraCasePanel.form +++ b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/IntraCasePanel.form @@ -117,7 +117,6 @@ - @@ -129,7 +128,6 @@ - diff --git a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/IntraCasePanel.java b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/IntraCasePanel.java index b2382fe8a4..b513125da7 100644 --- a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/IntraCasePanel.java +++ b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/IntraCasePanel.java @@ -165,7 +165,6 @@ public final class IntraCasePanel extends javax.swing.JPanel { pictureVideoCheckbox.setSelected(true); org.openide.awt.Mnemonics.setLocalizedText(pictureVideoCheckbox, org.openide.util.NbBundle.getMessage(IntraCasePanel.class, "IntraCasePanel.pictureVideoCheckbox.text")); // NOI18N - pictureVideoCheckbox.setEnabled(false); pictureVideoCheckbox.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { pictureVideoCheckboxActionPerformed(evt); @@ -174,7 +173,6 @@ public final class IntraCasePanel extends javax.swing.JPanel { documentsCheckbox.setSelected(true); org.openide.awt.Mnemonics.setLocalizedText(documentsCheckbox, org.openide.util.NbBundle.getMessage(IntraCasePanel.class, "IntraCasePanel.documentsCheckbox.text")); // NOI18N - documentsCheckbox.setEnabled(false); documentsCheckbox.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { documentsCheckboxActionPerformed(evt); From 44eb4d0b0d09328e1f0842d72b22ed66936ef4c4 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Tue, 5 Mar 2019 10:07:37 -0500 Subject: [PATCH 05/59] 4791 rename hash node with token file name --- .../commonpropertiessearch/CommonAttributeValue.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeValue.java b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeValue.java index 2048c15987..310dea3458 100644 --- a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeValue.java +++ b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeValue.java @@ -35,6 +35,7 @@ final public class CommonAttributeValue { private final String value; private final List fileInstances; + private String tokenFileName = null; CommonAttributeValue(String value) { this.value = value; @@ -45,6 +46,10 @@ final public class CommonAttributeValue { return this.value; } + String getTokenFileName(){ + return tokenFileName; + } + /** * concatenate cases this value was seen into a single string * @@ -64,6 +69,9 @@ final public class CommonAttributeValue { } void addInstance(AbstractCommonAttributeInstance metadata) { + if (tokenFileName==null){ + tokenFileName = metadata.getAbstractFile().getName(); + } this.fileInstances.add(metadata); } From 1af5006a0e180965ff74bac093b1a56d4fcaa2be Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Tue, 5 Mar 2019 10:08:04 -0500 Subject: [PATCH 06/59] 4791 rename hash node with token file name --- .../commonpropertiessearch/CommonAttributeValueNode.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeValueNode.java b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeValueNode.java index d149847d78..86bac3dac3 100644 --- a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeValueNode.java +++ b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeValueNode.java @@ -57,7 +57,7 @@ public class CommonAttributeValueNode extends DisplayableItemNode { // @@ We seem to be doing this string concat twice. We also do it in getDataSources() this.dataSources = String.join(", ", data.getDataSources()); this.value = data.getValue(); - this.setDisplayName(String.format(Bundle.CommonAttributeValueNode_CommonAttributeValueNode_format(), this.value)); + this.setDisplayName(data.getTokenFileName()); this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/fileset-icon-16.png"); //NON-NLS } From b6c8936d5568b28fa9eb5eb7a12abe93952c35ec Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Tue, 5 Mar 2019 17:01:55 -0500 Subject: [PATCH 07/59] 4791-fix potential null pointer with token file name --- .../autopsy/commonpropertiessearch/CommonAttributeValue.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeValue.java b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeValue.java index 310dea3458..95bdff19d7 100644 --- a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeValue.java +++ b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeValue.java @@ -69,7 +69,7 @@ final public class CommonAttributeValue { } void addInstance(AbstractCommonAttributeInstance metadata) { - if (tokenFileName==null){ + if (tokenFileName==null && metadata.getAbstractFile() != null){ tokenFileName = metadata.getAbstractFile().getName(); } this.fileInstances.add(metadata); From 88404aa9ee3429e1eb848fbd1ec8627d61a8da1c Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Tue, 5 Mar 2019 18:35:19 -0500 Subject: [PATCH 08/59] 4791 fix sorting of results by count for inter-case search --- .../AbstractCommonAttributeSearcher.java | 4 ++-- .../CommonAttributeCountSearchResults.java | 7 ++++--- .../InterCaseSearchResultsProcessor.java | 9 +++++---- .../IntraCaseCommonAttributeSearcher.java | 3 ++- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/AbstractCommonAttributeSearcher.java b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/AbstractCommonAttributeSearcher.java index 8f553dff2a..047ed4ed4d 100644 --- a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/AbstractCommonAttributeSearcher.java +++ b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/AbstractCommonAttributeSearcher.java @@ -130,9 +130,9 @@ public abstract class AbstractCommonAttributeSearcher { } } - static Map collateMatchesByNumberOfInstances(Map commonFiles) { + static TreeMap collateMatchesByNumberOfInstances(Map commonFiles) { //collate matches by number of matching instances - doing this in sql doesnt seem efficient - Map instanceCollatedCommonFiles = new TreeMap<>(); + TreeMap instanceCollatedCommonFiles = new TreeMap<>(); for (CommonAttributeValue md5Metadata : commonFiles.values()) { Integer size = md5Metadata.getDataSources().size(); diff --git a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeCountSearchResults.java b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeCountSearchResults.java index c1d2dcb481..9eda2b4020 100644 --- a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeCountSearchResults.java +++ b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeCountSearchResults.java @@ -25,6 +25,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.TreeMap; import java.util.logging.Level; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeNormalizationException; @@ -41,7 +42,7 @@ final public class CommonAttributeCountSearchResults { private static final Logger LOGGER = Logger.getLogger(CommonAttributeCountSearchResults.class.getName()); // maps instance count to list of attribute values. - private final Map instanceCountToAttributeValues; + private final TreeMap instanceCountToAttributeValues; private final int percentageThreshold; private final int resultTypeId; @@ -58,7 +59,7 @@ final public class CommonAttributeCountSearchResults { */ CommonAttributeCountSearchResults(Map metadata, int percentageThreshold, CorrelationAttributeInstance.Type resultType) { //wrap in a new object in case any client code has used an unmodifiable collection - this.instanceCountToAttributeValues = new HashMap<>(metadata); + this.instanceCountToAttributeValues = new TreeMap<>(metadata); this.percentageThreshold = percentageThreshold; this.resultTypeId = resultType.getId(); } @@ -73,7 +74,7 @@ final public class CommonAttributeCountSearchResults { */ CommonAttributeCountSearchResults(Map metadata, int percentageThreshold) { //wrap in a new object in case any client code has used an unmodifiable collection - this.instanceCountToAttributeValues = new HashMap<>(metadata); + this.instanceCountToAttributeValues = new TreeMap<>(metadata); this.percentageThreshold = percentageThreshold; this.resultTypeId = CorrelationAttributeInstance.FILES_TYPE_ID; } diff --git a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/InterCaseSearchResultsProcessor.java b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/InterCaseSearchResultsProcessor.java index 52f2cec276..e2dc8e616b 100644 --- a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/InterCaseSearchResultsProcessor.java +++ b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/InterCaseSearchResultsProcessor.java @@ -29,6 +29,7 @@ import java.util.HashSet; import java.util.Set; import java.util.List; import java.util.Map; +import java.util.TreeMap; import java.util.logging.Level; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; @@ -173,7 +174,7 @@ final class InterCaseSearchResultsProcessor { } catch (EamDbException | TskCoreException ex) { LOGGER.log(Level.SEVERE, "Error accessing EamDb processing CaseInstancesTable.", ex); } - return new HashMap<>(); + return new TreeMap<>(); } /** @@ -205,7 +206,7 @@ final class InterCaseSearchResultsProcessor { } catch (EamDbException | TskCoreException ex) { LOGGER.log(Level.SEVERE, "Error accessing EamDb processing CaseInstancesTable.", ex); } - return new HashMap<>(); + return new TreeMap<>(); } /** @@ -248,7 +249,7 @@ final class InterCaseSearchResultsProcessor { */ private class InterCaseByCountCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback, InstanceTableCallback { - private final Map instanceCollatedCommonFiles = new HashMap<>(); + private final TreeMap instanceCollatedCommonFiles = new TreeMap<>(); private final int caseID; private final int targetCase; @@ -311,7 +312,7 @@ final class InterCaseSearchResultsProcessor { } Map getInstanceCollatedCommonFiles() { - return Collections.unmodifiableMap(instanceCollatedCommonFiles); + return Collections.unmodifiableSortedMap(instanceCollatedCommonFiles); } } diff --git a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/IntraCaseCommonAttributeSearcher.java b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/IntraCaseCommonAttributeSearcher.java index f48d23d030..21ac1bb18b 100644 --- a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/IntraCaseCommonAttributeSearcher.java +++ b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/IntraCaseCommonAttributeSearcher.java @@ -26,6 +26,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; +import java.util.TreeMap; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; @@ -137,7 +138,7 @@ public abstract class IntraCaseCommonAttributeSearcher extends AbstractCommonAtt } } - Map instanceCollatedCommonFiles = collateMatchesByNumberOfInstances(commonFiles); + TreeMap instanceCollatedCommonFiles = collateMatchesByNumberOfInstances(commonFiles); return new CommonAttributeCountSearchResults(instanceCollatedCommonFiles, this.frequencyPercentageThreshold); } From 6a0186f4cb0701afd030af3a52715fdccc14347b Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Wed, 6 Mar 2019 13:33:34 -0500 Subject: [PATCH 09/59] 4791 change number of instances to number of data sources in search common property search results --- .../centralrepository/datamodel/CorrelationDataSource.java | 2 +- .../autopsy/commonpropertiessearch/Bundle.properties-MERGED | 2 +- .../autopsy/commonpropertiessearch/InstanceCountNode.java | 2 +- .../InterCaseSearchResultsProcessor.java | 3 ++- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationDataSource.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationDataSource.java index 03175347ca..8b8ed63699 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationDataSource.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationDataSource.java @@ -168,7 +168,7 @@ public class CorrelationDataSource implements Serializable { * * @return the ID or -1 if unknown */ - int getID() { + public int getID() { return dataSourceID; } diff --git a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/Bundle.properties-MERGED index 10370577d7..532d9ed139 100755 --- a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/Bundle.properties-MERGED @@ -60,7 +60,7 @@ CommonFilesSearchResultsViewerTable.noDescText=\ CommonFilesSearchResultsViewerTable.pathColLbl=Parent Path CommonFilesSearchResultsViewerTable.valueColLbl=Value InstanceCountNode.createSheet.noDescription=\ -InstanceCountNode.displayName=Files with %s instances (%s) +InstanceCountNode.displayName=Exists in %s data sources (%s) IntraCasePanel.selectDataSourceComboBox.actionCommand= CommonAttributePanel.jCheckBox1.text=Hide files found in over CommonAttributePanel.jLabel1.text=% of data sources in central repository. diff --git a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/InstanceCountNode.java b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/InstanceCountNode.java index 29f817210d..91929488da 100644 --- a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/InstanceCountNode.java +++ b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/InstanceCountNode.java @@ -54,7 +54,7 @@ public final class InstanceCountNode extends DisplayableItemNode { * @param attributeValues */ @NbBundle.Messages({ - "InstanceCountNode.displayName=Files with %s instances (%s)" + "InstanceCountNode.displayName=Exists in %s data sources (%s)" }) public InstanceCountNode(int instanceCount, CommonAttributeValueList attributeValues) { super(Children.create(new CommonAttributeValueNodeFactory(attributeValues.getMetadataList()), false)); diff --git a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/InterCaseSearchResultsProcessor.java b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/InterCaseSearchResultsProcessor.java index e2dc8e616b..a6dd6833b3 100644 --- a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/InterCaseSearchResultsProcessor.java +++ b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/InterCaseSearchResultsProcessor.java @@ -31,6 +31,7 @@ import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.logging.Level; +import java.util.stream.Collectors; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance.Type; @@ -285,7 +286,7 @@ final class InterCaseSearchResultsProcessor { } else { instances = EamDb.getInstance().getArtifactInstancesByTypeValuesAndCases(correlationType, Arrays.asList(corValue), targetCases); } - int size = instances.size(); + int size = instances.stream().map(instance -> instance.getCorrelationDataSource().getID()).collect(Collectors.toSet()).size(); if (size > 1) { CommonAttributeValue commonAttributeValue = new CommonAttributeValue(corValue); boolean anotherCase = false; From a1bb00a59a1b7d441d32c7136bee28da18fcb115 Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Wed, 6 Mar 2019 16:51:31 -0500 Subject: [PATCH 10/59] First cut at not moving files around --- .../autoingest/AddArchiveTask.java | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AddArchiveTask.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AddArchiveTask.java index e8e38dc743..1eae877690 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AddArchiveTask.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AddArchiveTask.java @@ -122,10 +122,12 @@ class AddArchiveTask implements Runnable { numExtractedFilesRemaining = extractedFiles.size(); } catch (ArchiveUtil.ArchiveExtractionException ex) { // delete extracted contents - logger.log(Level.SEVERE,"Exception while extracting archive contents into {0}. Deleteing the directory", destinationFolder.toString()); + logger.log(Level.SEVERE, "Exception while extracting archive contents into " + destinationFolder.toString() + ". Deleteing the directory", ex); FileUtils.deleteDirectory(destinationFolder.toFile()); throw ex; } + + List unclaimedFiles = new ArrayList<>(extractedFiles); // lookup all AutomatedIngestDataSourceProcessors so that we only do it once. // LocalDisk, LocalFiles, and ArchiveDSP are removed from the list. @@ -159,7 +161,7 @@ class AddArchiveTask implements Runnable { * if we do not move the data sources out of the extracted * contents folder, those data source files will get added twice * and can potentially result in duplicate keyword hits. - */ + Path newFolder = createDirectoryForFile(file, currentCase.getModuleDirectory()); if (newFolder.toString().isEmpty()) { // unable to create directory @@ -170,9 +172,9 @@ class AddArchiveTask implements Runnable { } // Copy it to a different folder - FileUtils.copyFileToDirectory(fileObject, newFolder.toFile()); - Path newFilePath = Paths.get(newFolder.toString(), FilenameUtils.getName(file)); - + //FileUtils.copyFileToDirectory(fileObject, newFolder.toFile()); + //Path newFilePath = Paths.get(newFolder.toString(), FilenameUtils.getName(file)); +*/ // Try each DSP in decreasing order of confidence boolean success = false; for (AutoIngestDataSourceProcessor selectedProcessor : validDataSourceProcessors) { @@ -181,9 +183,9 @@ class AddArchiveTask implements Runnable { synchronized (archiveDspLock) { UUID taskId = UUID.randomUUID(); currentCase.notifyAddingDataSource(taskId); - AutoIngestDataSource internalDataSource = new AutoIngestDataSource(deviceId, newFilePath); + AutoIngestDataSource internalDataSource = new AutoIngestDataSource(deviceId, fileObject.toPath()); DataSourceProcessorCallback internalArchiveDspCallBack = new AddDataSourceCallback(currentCase, internalDataSource, taskId, archiveDspLock); - selectedProcessor.process(deviceId, newFilePath, progressMonitor, internalArchiveDspCallBack); + selectedProcessor.process(deviceId, fileObject.toPath(), progressMonitor, internalArchiveDspCallBack); archiveDspLock.wait(); // at this point we got the content object(s) from the current DSP. @@ -201,6 +203,10 @@ class AddArchiveTask implements Runnable { success = true; newDataSources.addAll(internalDataSource.getContent()); + // this extracted file has been "claimed" by one of the DSPs, + // remove it from the list of Logical Files that will be added later + unclaimedFiles.remove(file); + // update data source info for (Content c:internalDataSource.getContent()) { if (c instanceof DataSource) { @@ -227,7 +233,7 @@ class AddArchiveTask implements Runnable { } } - if (success) { + /*if (success) { // one of the DSPs successfully processed the data source. delete the // copy of the data source in the original extracted archive folder. // otherwise the data source is going to be added again as a logical file. @@ -238,12 +244,12 @@ class AddArchiveTask implements Runnable { // copy of the data source in the temporary folder. the data source is // going to be added as a logical file with the rest of the extracted contents. FileUtils.deleteQuietly(newFolder.toFile()); - } + }*/ } // after all archive contents have been examined (and moved to separate folders if necessary), // add remaining extracted contents as one logical file set - if (numExtractedFilesRemaining > 0) { + if (unclaimedFiles.size() > 0) { progressMonitor.setProgressText(String.format("Adding: %s", destinationFolder.toString())); logger.log(Level.INFO, "Adding directory {0} as logical file set", destinationFolder.toString()); synchronized (archiveDspLock) { @@ -260,7 +266,7 @@ class AddArchiveTask implements Runnable { String archiveFileName = FilenameUtils.getName(archivePath); LocalFilesDSProcessor localFilesDSP = new LocalFilesDSProcessor(); - localFilesDSP.run(deviceId, archiveFileName, pathsList, progressMonitor, internalArchiveDspCallBack); + localFilesDSP.run(deviceId, archiveFileName, unclaimedFiles, progressMonitor, internalArchiveDspCallBack); archiveDspLock.wait(); From 0c8c9a3bd568d05204a5e6e7580ccd650f949a70 Mon Sep 17 00:00:00 2001 From: Raman Date: Thu, 7 Mar 2019 06:25:34 -0500 Subject: [PATCH 11/59] 1183: Mark group as 'unseen' for all examiners --- .../imagegallery/actions/NextUnseenGroup.java | 2 +- .../imagegallery/datamodel/DrawableDB.java | 47 ++++++++++++++----- .../datamodel/grouping/GroupManager.java | 39 +++++++++++---- 3 files changed, 67 insertions(+), 21 deletions(-) diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/NextUnseenGroup.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/NextUnseenGroup.java index c11005045d..d8f6178eb4 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/NextUnseenGroup.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/NextUnseenGroup.java @@ -88,7 +88,7 @@ public class NextUnseenGroup extends Action { if (group.isPresent()) { // NOTE: We need to wait for current group to be marked as seen because the 'advance' // method grabs the top of the unseen list - groupManager.markGroupSeen(group.get(), true) + groupManager.markGroupSeen(group.get()) .addListener(this::advanceToNextUnseenGroup, MoreExecutors.newDirectExecutorService()); return; } diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java index 427c729a8b..7400b12818 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java @@ -1141,23 +1141,22 @@ public final class DrawableDB { } /** - * Record in the DB that the group with the given key has the given seen - * state for the given examiner id. + * Record in the DB that the group with the given key is seen + * by given examiner id. * - * @param groupKey - * @param seen - * @param examinerID + * @param groupKey key identifying the group. + * @param examinerID examiner id. * * @throws TskCoreException */ - public void markGroupSeen(GroupKey groupKey, boolean seen, long examinerID) throws TskCoreException { + public void markGroupSeen(GroupKey groupKey, long examinerID) throws TskCoreException { /* * Check the groupSeenCache to see if the seen status for this group was set recently. - * If recently set to the same value, there's no need to update it + * If recently set to seen, there's no need to update it */ Boolean cachedValue = groupSeenCache.getIfPresent(groupKey); - if (cachedValue != null && cachedValue == seen) { + if (cachedValue != null && cachedValue == true) { return; } @@ -1168,17 +1167,41 @@ public final class DrawableDB { SleuthkitCase.escapeSingleQuotes(groupKey.getValueDisplayName()), groupKey.getAttribute() == DrawableAttribute.PATH ? groupKey.getDataSourceObjId() : 0); - String insertSQL = String.format(" (group_id, examiner_id, seen) VALUES (%s, %d, %d)", innerQuery, examinerID, seen ? 1 : 0); + String insertSQL = String.format(" (group_id, examiner_id, seen) VALUES (%s, %d, %d)", innerQuery, examinerID, 1); if (DbType.POSTGRESQL == tskCase.getDatabaseType()) { - insertSQL += String.format(" ON CONFLICT (group_id, examiner_id) DO UPDATE SET seen = %d", seen ? 1 : 0); + insertSQL += String.format(" ON CONFLICT (group_id, examiner_id) DO UPDATE SET seen = %d", 1); } tskCase.getCaseDbAccessManager().insertOrUpdate(GROUPS_SEEN_TABLENAME, insertSQL); - groupSeenCache.put(groupKey, seen); - + groupSeenCache.put(groupKey, true); } + /** + * Record in the DB that given group is unseen. + * The group is marked unseen for ALL examiners that have seen the group. + * + * @param groupKey key identifying the group. + * + * @throws TskCoreException + */ + public void markGroupUnseen(GroupKey groupKey) throws TskCoreException { + + /* + * Check the groupSeenCache to see if the seen status for this group was set recently. + * If recently set to unseen, there's no need to update it + */ + Boolean cachedValue = groupSeenCache.getIfPresent(groupKey); + if (cachedValue != null && cachedValue == false) { + return; + } + + String updateSQL = String.format(" SET seen = 0 WHERE group_id in ( " + getGroupIdQuery(groupKey) + ")" ); + tskCase.getCaseDbAccessManager().update(GROUPS_SEEN_TABLENAME, updateSQL); + + groupSeenCache.put(groupKey, false); + } + /** * Sets the isAnalysed flag in the groups table for the given group to true. * diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/GroupManager.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/GroupManager.java index 15fdc291e9..508da04d05 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/GroupManager.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/GroupManager.java @@ -264,24 +264,23 @@ public class GroupManager { } /** - * 'Save' the given group as seen in the drawable db. + * Marks the given group as 'seen' by the current examiner, in drawable db. * * @param group The DrawableGroup to mark as seen. - * @param seen The seen state to set for the given group. * * @return A ListenableFuture that encapsulates saving the seen state to the * DB. * * */ - public ListenableFuture markGroupSeen(DrawableGroup group, boolean seen) { + public ListenableFuture markGroupSeen(DrawableGroup group) { return exec.submit(() -> { try { Examiner examiner = controller.getSleuthKitCase().getCurrentExaminer(); - getDrawableDB().markGroupSeen(group.getGroupKey(), seen, examiner.getId()); + getDrawableDB().markGroupSeen(group.getGroupKey(), examiner.getId()); // only update and reshuffle if its new results - if (group.isSeen() != seen) { - group.setSeen(seen); + if (group.isSeen() != true) { + group.setSeen(true); updateUnSeenGroups(group); } } catch (TskCoreException ex) { @@ -290,6 +289,30 @@ public class GroupManager { }); } + /** + * Marks the given group as unseen in the drawable db. + * + * @param group The DrawableGroup. + * + * @return A ListenableFuture that encapsulates saving the seen state to the + * DB. + */ + public ListenableFuture markGroupUnseen(DrawableGroup group) { + return exec.submit(() -> { + try { + + getDrawableDB().markGroupUnseen(group.getGroupKey()); + // only update and reshuffle if its new results + if (group.isSeen() != false) { + group.setSeen(false); + updateUnSeenGroups(group); + } + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, String.format("Error setting group: %s to unseen.", group.getGroupKey().getValue().toString()), ex); //NON-NLS + } + }); + } + /** * Update unseenGroups list accordingly based on the current status of * 'group'. Removes it if it is seen or adds it if it is unseen. @@ -325,7 +348,7 @@ public class GroupManager { // If we're grouping by category, we don't want to remove empty groups. if (group.getFileIDs().isEmpty()) { - markGroupSeen(group, true); + markGroupSeen(group); if (groupKey.getAttribute() != DrawableAttribute.CATEGORY) { if (analyzedGroups.contains(group)) { analyzedGroups.remove(group); @@ -570,7 +593,7 @@ public class GroupManager { // reset the seen status for the group (if it is currently considered analyzed) if (group != null) { - markGroupSeen(group, false); + markGroupUnseen(group); } } From e6ad011e9508488b47794be7fad05e73b9cc5752 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Thu, 7 Mar 2019 13:56:03 -0500 Subject: [PATCH 12/59] 4791 correct count of data sources when duplicate name --- .../AbstractCommonAttributeSearcher.java | 2 +- .../CommonAttributeValue.java | 25 ++++++++++++++++--- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/AbstractCommonAttributeSearcher.java b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/AbstractCommonAttributeSearcher.java index 047ed4ed4d..1880489cc4 100644 --- a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/AbstractCommonAttributeSearcher.java +++ b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/AbstractCommonAttributeSearcher.java @@ -135,7 +135,7 @@ public abstract class AbstractCommonAttributeSearcher { TreeMap instanceCollatedCommonFiles = new TreeMap<>(); for (CommonAttributeValue md5Metadata : commonFiles.values()) { - Integer size = md5Metadata.getDataSources().size(); + Integer size = md5Metadata.getNumberOfDataSourcesInCurrentCase(); if (instanceCollatedCommonFiles.containsKey(size)) { instanceCollatedCommonFiles.get(size).addMetadataToList(md5Metadata); diff --git a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeValue.java b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeValue.java index 95bdff19d7..b2f9085898 100644 --- a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeValue.java +++ b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeValue.java @@ -26,6 +26,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; +import org.sleuthkit.datamodel.AbstractFile; /** * Defines a value that was in the common file search results as well as @@ -46,10 +47,10 @@ final public class CommonAttributeValue { return this.value; } - String getTokenFileName(){ + String getTokenFileName() { return tokenFileName; } - + /** * concatenate cases this value was seen into a single string * @@ -68,8 +69,26 @@ final public class CommonAttributeValue { return sources; } + /** + * Get the number of unique data sources in the current case which the value + * appeared in. + * + * @return the number of unique data sources in the current case which + * contained the value + */ + int getNumberOfDataSourcesInCurrentCase() { + Set dataSourceIds = new HashSet<>(); + for (AbstractCommonAttributeInstance data : this.fileInstances) { + AbstractFile file = data.getAbstractFile(); + if (file != null) { + dataSourceIds.add(file.getDataSourceObjectId()); + } + } + return dataSourceIds.size(); + } + void addInstance(AbstractCommonAttributeInstance metadata) { - if (tokenFileName==null && metadata.getAbstractFile() != null){ + if (tokenFileName == null && metadata.getAbstractFile() != null) { tokenFileName = metadata.getAbstractFile().getName(); } this.fileInstances.add(metadata); From 9dd378c12f5d388b5ce0a88e99d8b1bc3e9abf2e Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Thu, 7 Mar 2019 13:58:35 -0500 Subject: [PATCH 13/59] 4791 make default selection for InterCase search to filter on file type for consistency --- .../autopsy/commonpropertiessearch/InterCasePanel.form | 2 +- .../autopsy/commonpropertiessearch/InterCasePanel.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/InterCasePanel.form b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/InterCasePanel.form index f878323524..93a06ca05c 100644 --- a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/InterCasePanel.form +++ b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/InterCasePanel.form @@ -134,7 +134,6 @@ - @@ -152,6 +151,7 @@ + diff --git a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/InterCasePanel.java b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/InterCasePanel.java index 2c6100a42c..143cea98f1 100644 --- a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/InterCasePanel.java +++ b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/InterCasePanel.java @@ -164,7 +164,6 @@ public final class InterCasePanel extends javax.swing.JPanel { categoriesLabel.setName(""); // NOI18N buttonGroup.add(allFileCategoriesRadioButton); - allFileCategoriesRadioButton.setSelected(true); org.openide.awt.Mnemonics.setLocalizedText(allFileCategoriesRadioButton, org.openide.util.NbBundle.getMessage(InterCasePanel.class, "InterCasePanel.allFileCategoriesRadioButton.text")); // NOI18N allFileCategoriesRadioButton.setToolTipText(org.openide.util.NbBundle.getMessage(InterCasePanel.class, "InterCasePanel.allFileCategoriesRadioButton.toolTipText")); // NOI18N allFileCategoriesRadioButton.setEnabled(false); @@ -175,6 +174,7 @@ public final class InterCasePanel extends javax.swing.JPanel { }); buttonGroup.add(selectedFileCategoriesButton); + selectedFileCategoriesButton.setSelected(true); org.openide.awt.Mnemonics.setLocalizedText(selectedFileCategoriesButton, org.openide.util.NbBundle.getMessage(InterCasePanel.class, "InterCasePanel.selectedFileCategoriesButton.text")); // NOI18N selectedFileCategoriesButton.setToolTipText(org.openide.util.NbBundle.getMessage(InterCasePanel.class, "InterCasePanel.selectedFileCategoriesButton.toolTipText")); // NOI18N selectedFileCategoriesButton.setEnabled(false); From bfe607b02909db4abe42238f3ca749145c4f7c25 Mon Sep 17 00:00:00 2001 From: Raman Date: Thu, 7 Mar 2019 14:23:16 -0500 Subject: [PATCH 14/59] Address review comments. --- .../autopsy/imagegallery/datamodel/DrawableDB.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java index 7400b12818..d25e8e8659 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java @@ -1161,15 +1161,15 @@ public final class DrawableDB { } // query to find the group id from attribute/value - String innerQuery = String.format("( SELECT group_id FROM " + GROUPS_TABLENAME - + " WHERE attribute = \'%s\' AND value = \'%s\' and data_source_obj_id = %d )", + String innerQuery = String.format("( SELECT group_id FROM " + GROUPS_TABLENAME //NON-NLS + + " WHERE attribute = \'%s\' AND value = \'%s\' and data_source_obj_id = %d )", //NON-NLS SleuthkitCase.escapeSingleQuotes(groupKey.getAttribute().attrName.toString()), SleuthkitCase.escapeSingleQuotes(groupKey.getValueDisplayName()), groupKey.getAttribute() == DrawableAttribute.PATH ? groupKey.getDataSourceObjId() : 0); - String insertSQL = String.format(" (group_id, examiner_id, seen) VALUES (%s, %d, %d)", innerQuery, examinerID, 1); + String insertSQL = String.format(" (group_id, examiner_id, seen) VALUES (%s, %d, %d)", innerQuery, examinerID, 1); //NON-NLS if (DbType.POSTGRESQL == tskCase.getDatabaseType()) { - insertSQL += String.format(" ON CONFLICT (group_id, examiner_id) DO UPDATE SET seen = %d", 1); + insertSQL += String.format(" ON CONFLICT (group_id, examiner_id) DO UPDATE SET seen = %d", 1); //NON-NLS } tskCase.getCaseDbAccessManager().insertOrUpdate(GROUPS_SEEN_TABLENAME, insertSQL); @@ -1196,7 +1196,7 @@ public final class DrawableDB { return; } - String updateSQL = String.format(" SET seen = 0 WHERE group_id in ( " + getGroupIdQuery(groupKey) + ")" ); + String updateSQL = String.format(" SET seen = 0 WHERE group_id in ( " + getGroupIdQuery(groupKey) + ")" ); //NON-NLS tskCase.getCaseDbAccessManager().update(GROUPS_SEEN_TABLENAME, updateSQL); groupSeenCache.put(groupKey, false); From 18dec7e5433beef9aafb33fe4d7c5e8affc88ad6 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Thu, 7 Mar 2019 15:54:39 -0500 Subject: [PATCH 15/59] 4791 clean up and copyright update --- .../datamodel/DataSourceUpdateService.java | 2 +- .../CommonAttributeCountSearchResults.java | 2 +- .../commonpropertiessearch/CommonAttributeValue.java | 12 +++++++++++- .../CommonAttributeValueNode.java | 2 +- .../commonpropertiessearch/InstanceCountNode.java | 2 +- .../commonpropertiessearch/InterCasePanel.java | 2 +- .../IntraCaseCommonAttributeSearcher.java | 9 ++++----- .../commonpropertiessearch/IntraCasePanel.java | 2 +- .../autopsy/datamodel/utils/DataSourceLoader.java | 2 +- 9 files changed, 22 insertions(+), 13 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/DataSourceUpdateService.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/DataSourceUpdateService.java index f1e52b03e4..46696bad94 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/DataSourceUpdateService.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/DataSourceUpdateService.java @@ -65,4 +65,4 @@ public class DataSourceUpdateService implements AutopsyService { } } -} \ No newline at end of file +} diff --git a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeCountSearchResults.java b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeCountSearchResults.java index 9eda2b4020..8265a8db25 100644 --- a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeCountSearchResults.java +++ b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeCountSearchResults.java @@ -42,7 +42,7 @@ final public class CommonAttributeCountSearchResults { private static final Logger LOGGER = Logger.getLogger(CommonAttributeCountSearchResults.class.getName()); // maps instance count to list of attribute values. - private final TreeMap instanceCountToAttributeValues; + private final Map instanceCountToAttributeValues; private final int percentageThreshold; private final int resultTypeId; diff --git a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeValue.java b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeValue.java index b2f9085898..ca40854ec9 100644 --- a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeValue.java +++ b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeValue.java @@ -2,7 +2,7 @@ * * Autopsy Forensic Browser * - * Copyright 2018 Basis Technology Corp. + * Copyright 2018-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -47,6 +47,11 @@ final public class CommonAttributeValue { return this.value; } + /** + * Get the file name of the first available instance of this value. + * + * @return the file name of an instance of this file + */ String getTokenFileName() { return tokenFileName; } @@ -60,6 +65,11 @@ final public class CommonAttributeValue { return this.fileInstances.stream().map(AbstractCommonAttributeInstance::getCaseName).collect(Collectors.joining(", ")); } + /** + * Get the set of data sources names this value exists in + * + * @return a set of data source names + */ public Set getDataSources() { Set sources = new HashSet<>(); for (AbstractCommonAttributeInstance data : this.fileInstances) { diff --git a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeValueNode.java b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeValueNode.java index 86bac3dac3..475f33888b 100644 --- a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeValueNode.java +++ b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeValueNode.java @@ -2,7 +2,7 @@ * * Autopsy Forensic Browser * - * Copyright 2018 Basis Technology Corp. + * Copyright 2018-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/InstanceCountNode.java b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/InstanceCountNode.java index 91929488da..5cfe79f58c 100644 --- a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/InstanceCountNode.java +++ b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/InstanceCountNode.java @@ -2,7 +2,7 @@ * * Autopsy Forensic Browser * - * Copyright 2018 Basis Technology Corp. + * Copyright 2018-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/InterCasePanel.java b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/InterCasePanel.java index 143cea98f1..c437bb0a3a 100644 --- a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/InterCasePanel.java +++ b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/InterCasePanel.java @@ -2,7 +2,7 @@ * * Autopsy Forensic Browser * - * Copyright 2018 Basis Technology Corp. + * Copyright 2018-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/IntraCaseCommonAttributeSearcher.java b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/IntraCaseCommonAttributeSearcher.java index 21ac1bb18b..dac83a333e 100644 --- a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/IntraCaseCommonAttributeSearcher.java +++ b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/IntraCaseCommonAttributeSearcher.java @@ -2,7 +2,7 @@ * * Autopsy Forensic Browser * - * Copyright 2018 Basis Technology Corp. + * Copyright 2018-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -26,7 +26,6 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; -import java.util.TreeMap; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; @@ -38,8 +37,8 @@ import org.sleuthkit.datamodel.TskCoreException; /** * * Generates a List when - * findMatchesByCount() is called, which organizes files by md5 to prepare - * to display in viewer. + * findMatchesByCount() is called, which organizes files by md5 to + * prepare to display in viewer. * * This entire thing runs on a background thread where exceptions are handled. */ @@ -138,7 +137,7 @@ public abstract class IntraCaseCommonAttributeSearcher extends AbstractCommonAtt } } - TreeMap instanceCollatedCommonFiles = collateMatchesByNumberOfInstances(commonFiles); + Map instanceCollatedCommonFiles = collateMatchesByNumberOfInstances(commonFiles); return new CommonAttributeCountSearchResults(instanceCollatedCommonFiles, this.frequencyPercentageThreshold); } diff --git a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/IntraCasePanel.java b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/IntraCasePanel.java index b513125da7..69e02eb7a7 100644 --- a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/IntraCasePanel.java +++ b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/IntraCasePanel.java @@ -2,7 +2,7 @@ * * Autopsy Forensic Browser * - * Copyright 2018 Basis Technology Corp. + * Copyright 2018-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/utils/DataSourceLoader.java b/Core/src/org/sleuthkit/autopsy/datamodel/utils/DataSourceLoader.java index 18c762d073..f435efd9bd 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/utils/DataSourceLoader.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/utils/DataSourceLoader.java @@ -2,7 +2,7 @@ * * Autopsy Forensic Browser * - * Copyright 2018 Basis Technology Corp. + * Copyright 2018-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); From 01e84910c86c60eccf7051814ce942566403b188 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Thu, 7 Mar 2019 16:02:11 -0500 Subject: [PATCH 16/59] 4791 undo formating changes to datasourceUpdateService --- .../centralrepository/datamodel/DataSourceUpdateService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/DataSourceUpdateService.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/DataSourceUpdateService.java index 46696bad94..f1e52b03e4 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/DataSourceUpdateService.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/DataSourceUpdateService.java @@ -65,4 +65,4 @@ public class DataSourceUpdateService implements AutopsyService { } } -} +} \ No newline at end of file From 07f23f3780eabf21a3f73f30682eaedba44ede64 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Thu, 7 Mar 2019 16:12:42 -0500 Subject: [PATCH 17/59] 4791 update text for older inter case search option --- .../sleuthkit/autopsy/commonpropertiessearch/Bundle.properties | 2 +- .../autopsy/commonpropertiessearch/Bundle.properties-MERGED | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/Bundle.properties b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/Bundle.properties index cf530cf013..563c433322 100644 --- a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/Bundle.properties @@ -44,5 +44,5 @@ CommonAttributePanel.resultsDisplayLabel.text_2=Display results organized by: CommonAttributePanel.organizeByCaseRadio.text=Case CommonAttributePanel.organizeByCountRadio.text=Number of occurrences CommonAttributePanel.caseResultsRadioButton.text=Case -CommonAttributePanel.countResultsRadioButton.text=Number of occurrences +CommonAttributePanel.countResultsRadioButton.text=Number of data sources CommonAttributePanel.displayResultsLabel.text_2=Display results organized by: diff --git a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/Bundle.properties-MERGED index 532d9ed139..d164529639 100755 --- a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/Bundle.properties-MERGED @@ -101,7 +101,7 @@ CommonAttributePanel.resultsDisplayLabel.text_2=Display results organized by: CommonAttributePanel.organizeByCaseRadio.text=Case CommonAttributePanel.organizeByCountRadio.text=Number of occurrences CommonAttributePanel.caseResultsRadioButton.text=Case -CommonAttributePanel.countResultsRadioButton.text=Number of occurrences +CommonAttributePanel.countResultsRadioButton.text=Number of data sources CommonAttributePanel.displayResultsLabel.text_2=Display results organized by: # {0} - case name # {1} - attr type From e69b3230da7873e553157c8bedd7dae1c7156824 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Thu, 7 Mar 2019 17:35:19 -0500 Subject: [PATCH 18/59] 4791 fix node name for non-file attributes --- .../CommonAttributePanel.java | 2 +- .../CommonAttributeSearchResultRootNode.java | 11 +++++++---- .../CommonAttributeValue.java | 1 - .../CommonAttributeValueNode.java | 12 +++++++++--- .../InstanceCountNode.java | 17 ++++++++++------- 5 files changed, 27 insertions(+), 16 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributePanel.java b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributePanel.java index 72086983de..030b78c76e 100644 --- a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributePanel.java +++ b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributePanel.java @@ -294,7 +294,7 @@ final class CommonAttributePanel extends javax.swing.JDialog implements Observer DataResultTopComponent.createInstance(tabTitle, Bundle.CommonAttributePanel_search_results_pathText(), commonFilesNode, 1); } else { // -3969 - Node commonFilesNode = new CommonAttributeSearchResultRootNode(metadata); + Node commonFilesNode = new CommonAttributeSearchResultRootNode(metadata, interCasePanel.getSelectedCorrelationType()); DataResultFilterNode dataResultFilterNode = new DataResultFilterNode(commonFilesNode, ExplorerManager.find(CommonAttributePanel.this)); TableFilterNode tableFilterWithDescendantsNode = new TableFilterNode(dataResultFilterNode, 3); DataResultViewerTable table = new CommonAttributesSearchResultsViewerTable(); diff --git a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeSearchResultRootNode.java b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeSearchResultRootNode.java index 6576c9219f..27156f97fa 100644 --- a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeSearchResultRootNode.java +++ b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeSearchResultRootNode.java @@ -25,6 +25,7 @@ import org.openide.nodes.ChildFactory; import org.openide.nodes.Children; import org.openide.nodes.Node; import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; import org.sleuthkit.autopsy.datamodel.DisplayableItemNode; import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor; @@ -35,8 +36,8 @@ import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor; */ final public class CommonAttributeSearchResultRootNode extends DisplayableItemNode { - CommonAttributeSearchResultRootNode(CommonAttributeCountSearchResults metadataList) { - super(Children.create(new InstanceCountNodeFactory(metadataList), true)); + CommonAttributeSearchResultRootNode(CommonAttributeCountSearchResults metadataList, CorrelationAttributeInstance.Type type) { + super(Children.create(new InstanceCountNodeFactory(metadataList, type), true)); } CommonAttributeSearchResultRootNode(CommonAttributeCaseSearchResults metadataList) { @@ -73,6 +74,7 @@ final public class CommonAttributeSearchResultRootNode extends DisplayableItemNo private static final Logger LOGGER = Logger.getLogger(InstanceCountNodeFactory.class.getName()); private final CommonAttributeCountSearchResults searchResults; + private final CorrelationAttributeInstance.Type type; /** * Build a factory which converts a @@ -81,8 +83,9 @@ final public class CommonAttributeSearchResultRootNode extends DisplayableItemNo * * @param searchResults */ - InstanceCountNodeFactory(CommonAttributeCountSearchResults searchResults) { + InstanceCountNodeFactory(CommonAttributeCountSearchResults searchResults, CorrelationAttributeInstance.Type type) { this.searchResults = searchResults; + this.type = type; } @Override @@ -94,7 +97,7 @@ final public class CommonAttributeSearchResultRootNode extends DisplayableItemNo @Override protected Node createNodeForKey(Integer instanceCount) { CommonAttributeValueList attributeValues = this.searchResults.getAttributeValuesForInstanceCount(instanceCount); - return new InstanceCountNode(instanceCount, attributeValues); + return new InstanceCountNode(instanceCount, attributeValues, type); } } diff --git a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeValue.java b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeValue.java index ca40854ec9..7895e7a410 100644 --- a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeValue.java +++ b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeValue.java @@ -75,7 +75,6 @@ final public class CommonAttributeValue { for (AbstractCommonAttributeInstance data : this.fileInstances) { sources.add(data.getDataSource()); } - return sources; } diff --git a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeValueNode.java b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeValueNode.java index 475f33888b..ed8c395b5f 100644 --- a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeValueNode.java +++ b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeValueNode.java @@ -25,6 +25,8 @@ import org.openide.nodes.Children; import org.openide.nodes.Node; import org.openide.nodes.Sheet; import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; +import static org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance.FILES_TYPE_ID; import org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode; import org.sleuthkit.autopsy.datamodel.DisplayableItemNode; import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor; @@ -49,15 +51,19 @@ public class CommonAttributeValueNode extends DisplayableItemNode { * * @param data the common feature, and the children */ - public CommonAttributeValueNode(CommonAttributeValue data) { + public CommonAttributeValueNode(CommonAttributeValue data, CorrelationAttributeInstance.Type type) { super(Children.create( new FileInstanceNodeFactory(data), true)); this.commonFileCount = data.getInstanceCount(); this.cases = data.getCases(); - // @@ We seem to be doing this string concat twice. We also do it in getDataSources() this.dataSources = String.join(", ", data.getDataSources()); this.value = data.getValue(); - this.setDisplayName(data.getTokenFileName()); + //if the type is null (indicating intra-case) or files then make the node name the representitive file name + if (type == null || type.getId() == FILES_TYPE_ID) { + this.setDisplayName(data.getTokenFileName()); + } else { + this.setDisplayName(String.format(Bundle.CommonAttributeValueNode_CommonAttributeValueNode_format(), this.value)); + } this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/fileset-icon-16.png"); //NON-NLS } diff --git a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/InstanceCountNode.java b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/InstanceCountNode.java index 5cfe79f58c..973d7711c4 100644 --- a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/InstanceCountNode.java +++ b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/InstanceCountNode.java @@ -28,6 +28,7 @@ import org.openide.nodes.Children; import org.openide.nodes.Node; import org.openide.nodes.Sheet; import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode; @@ -45,6 +46,7 @@ public final class InstanceCountNode extends DisplayableItemNode { final private int instanceCount; final private CommonAttributeValueList attributeValues; + final private CorrelationAttributeInstance.Type type; /** * Create a node with the given number of instances, and the given selection @@ -56,9 +58,9 @@ public final class InstanceCountNode extends DisplayableItemNode { @NbBundle.Messages({ "InstanceCountNode.displayName=Exists in %s data sources (%s)" }) - public InstanceCountNode(int instanceCount, CommonAttributeValueList attributeValues) { - super(Children.create(new CommonAttributeValueNodeFactory(attributeValues.getMetadataList()), false)); - + public InstanceCountNode(int instanceCount, CommonAttributeValueList attributeValues, CorrelationAttributeInstance.Type type) { + super(Children.create(new CommonAttributeValueNodeFactory(attributeValues.getMetadataList(), type), false)); + this.type = type; this.instanceCount = instanceCount; this.attributeValues = attributeValues; @@ -81,7 +83,7 @@ public final class InstanceCountNode extends DisplayableItemNode { */ void createChildren() { attributeValues.displayDelayedMetadata(); - setChildren(Children.create(new CommonAttributeValueNodeFactory(attributeValues.getMetadataList()), false)); + setChildren(Children.create(new CommonAttributeValueNodeFactory(attributeValues.getMetadataList(), type), false)); } /** @@ -146,10 +148,11 @@ public final class InstanceCountNode extends DisplayableItemNode { */ // maps sting version of value to value Object (??) private final Map metadata; + private final CorrelationAttributeInstance.Type type; - CommonAttributeValueNodeFactory(List attributeValues) { + CommonAttributeValueNodeFactory(List attributeValues, CorrelationAttributeInstance.Type type) { this.metadata = new HashMap<>(); - + this.type = type; Iterator iterator = attributeValues.iterator(); while (iterator.hasNext()) { CommonAttributeValue attributeValue = iterator.next(); @@ -167,7 +170,7 @@ public final class InstanceCountNode extends DisplayableItemNode { @Override protected Node createNodeForKey(String attributeValue) { CommonAttributeValue md5Metadata = this.metadata.get(attributeValue); - return new CommonAttributeValueNode(md5Metadata); + return new CommonAttributeValueNode(md5Metadata, type); } } } From 6d3154c207afb441029ed3474671413c0c97162a Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Thu, 7 Mar 2019 17:52:02 -0500 Subject: [PATCH 19/59] Adding unclaimed files to virtual directory --- .../autoingest/AddArchiveTask.java | 37 +++++++++++++++---- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AddArchiveTask.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AddArchiveTask.java index 1eae877690..93c30fdb53 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AddArchiveTask.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AddArchiveTask.java @@ -43,6 +43,9 @@ import org.sleuthkit.autopsy.coreutils.TimeStampUtils; import org.sleuthkit.autopsy.datasourceprocessors.RawDSProcessor; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.DataSource; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TskData; +import org.sleuthkit.datamodel.VirtualDirectory; /* * A runnable that adds an archive data source as well as data sources contained @@ -59,6 +62,7 @@ class AddArchiveTask implements Runnable { private final Object archiveDspLock; private static final String ARCHIVE_EXTRACTOR_MODULE_OUTPUT_DIR = "Archive Extractor"; + private static final String LOGICAL_FILE_VIRTUAL_DIR_NAME = "$AdditionalFiles"; /** * Constructs a runnable task that adds an archive as well as data sources @@ -115,11 +119,9 @@ class AddArchiveTask implements Runnable { // extract contents of ZIP archive into destination folder List extractedFiles = new ArrayList<>(); - int numExtractedFilesRemaining = 0; try { progressMonitor.setProgressText(String.format("Extracting archive contents to: %s", destinationFolder.toString())); extractedFiles = ArchiveUtil.unpackArchiveFile(archivePath, destinationFolder.toString()); - numExtractedFilesRemaining = extractedFiles.size(); } catch (ArchiveUtil.ArchiveExtractionException ex) { // delete extracted contents logger.log(Level.SEVERE, "Exception while extracting archive contents into " + destinationFolder.toString() + ". Deleteing the directory", ex); @@ -134,12 +136,13 @@ class AddArchiveTask implements Runnable { List processorCandidates = getListOfValidDataSourceProcessors(); // do processing + int numValidDataSources = 0; + DataSource defaultDataSource = null; for (String file : extractedFiles) { // we only care about files, skip directories File fileObject = new File(file); if (fileObject.isDirectory()) { - numExtractedFilesRemaining--; continue; } @@ -202,10 +205,11 @@ class AddArchiveTask implements Runnable { // if we are here it means the data source was added successfully success = true; newDataSources.addAll(internalDataSource.getContent()); + numValidDataSources++; // this extracted file has been "claimed" by one of the DSPs, // remove it from the list of Logical Files that will be added later - unclaimedFiles.remove(file); + removeClaimedFiles(file, unclaimedFiles, selectedProcessor); // update data source info for (Content c:internalDataSource.getContent()) { @@ -225,6 +229,8 @@ class AddArchiveTask implements Runnable { String newName = Paths.get(archivePath).getFileName() + "/" + ds.getName(); ds.setDisplayName(newName); currentCase.notifyDataSourceNameChanged(c, newName); + + defaultDataSource = ds; } } @@ -237,7 +243,7 @@ class AddArchiveTask implements Runnable { // one of the DSPs successfully processed the data source. delete the // copy of the data source in the original extracted archive folder. // otherwise the data source is going to be added again as a logical file. - numExtractedFilesRemaining--; + numValidDataSources--; FileUtils.deleteQuietly(fileObject); } else { // none of the DSPs were able to process the data source. delete the @@ -250,7 +256,17 @@ class AddArchiveTask implements Runnable { // after all archive contents have been examined (and moved to separate folders if necessary), // add remaining extracted contents as one logical file set if (unclaimedFiles.size() > 0) { - progressMonitor.setProgressText(String.format("Adding: %s", destinationFolder.toString())); + + // ELTODO investigate if i need to aquire datase lock? + SleuthkitCase caseDatabase = Case.getCurrentCaseThrows().getSleuthkitCase(); + VirtualDirectory additionalFilesDir = caseDatabase.addVirtualDirectory(defaultDataSource.getId(), LOGICAL_FILE_VIRTUAL_DIR_NAME); + + for (String file : unclaimedFiles) { + File fileObject = new File(file); + caseDatabase.addLocalFile(fileObject.getName(), fileObject.getAbsolutePath(), fileObject.length(), 0, 0, 0, 0, fileObject.isFile(), TskData.EncodingType.NONE, additionalFilesDir); + } + + /*progressMonitor.setProgressText(String.format("Adding: %s", destinationFolder.toString())); logger.log(Level.INFO, "Adding directory {0} as logical file set", destinationFolder.toString()); synchronized (archiveDspLock) { UUID taskId = UUID.randomUUID(); @@ -281,7 +297,7 @@ class AddArchiveTask implements Runnable { ds.setAcquisitionDetails(details); } } - } + }*/ } } catch (Exception ex) { criticalErrorOccurred = true; @@ -300,6 +316,13 @@ class AddArchiveTask implements Runnable { callback.done(result, errorMessages, newDataSources); } } + + private void removeClaimedFiles(String file, List unclaimedFiles, AutoIngestDataSourceProcessor selectedProcessor) { + + //if (!(selectedProcessor instanceof ForensicToolReportProcessor)) { + unclaimedFiles.remove(file); + //} + } /** * Get a list of data source processors. LocalFiles, RawDSProcessor, and From 48e1f224c96dc8079e2d2c847af60d2ffb92bbf4 Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Mon, 11 Mar 2019 13:56:38 -0400 Subject: [PATCH 20/59] Moved some of the Experimental utility code to Autopsy Core --- .../AddDataSourceCallback.java | 10 +- .../AutoIngestDataSource.java | 20 +- .../DataSourceProcessorUtility.java | 15 +- .../autoingest/AddArchiveTask.java | 403 ------------------ .../ArchiveExtractorDSProcessor.java | 176 -------- .../autoingest/ArchiveFilePanel.form | 94 ---- .../autoingest/ArchiveFilePanel.java | 289 ------------- .../experimental/autoingest/ArchiveUtil.java | 313 -------------- .../autoingest/AutoIngestManager.java | 5 +- .../experimental/autoingest/Bundle.properties | 4 - .../autoingest/Bundle.properties-MERGED | 10 - 11 files changed, 25 insertions(+), 1314 deletions(-) rename {Experimental/src/org/sleuthkit/autopsy/experimental/autoingest => Core/src/org/sleuthkit/autopsy/datasourceprocessors}/AddDataSourceCallback.java (90%) rename {Experimental/src/org/sleuthkit/autopsy/experimental/autoingest => Core/src/org/sleuthkit/autopsy/datasourceprocessors}/AutoIngestDataSource.java (71%) rename {Experimental/src/org/sleuthkit/autopsy/experimental/autoingest => Core/src/org/sleuthkit/autopsy/datasourceprocessors}/DataSourceProcessorUtility.java (83%) delete mode 100644 Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AddArchiveTask.java delete mode 100644 Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/ArchiveExtractorDSProcessor.java delete mode 100644 Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/ArchiveFilePanel.form delete mode 100644 Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/ArchiveFilePanel.java delete mode 100644 Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/ArchiveUtil.java diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AddDataSourceCallback.java b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/AddDataSourceCallback.java similarity index 90% rename from Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AddDataSourceCallback.java rename to Core/src/org/sleuthkit/autopsy/datasourceprocessors/AddDataSourceCallback.java index fc00149249..3ee02b9026 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AddDataSourceCallback.java +++ b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/AddDataSourceCallback.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.experimental.autoingest; +package org.sleuthkit.autopsy.datasourceprocessors; import java.util.List; import java.util.UUID; @@ -32,7 +32,7 @@ import org.sleuthkit.datamodel.Content; * processor finishes running in its own thread. */ @Immutable -class AddDataSourceCallback extends DataSourceProcessorCallback { +public class AddDataSourceCallback extends DataSourceProcessorCallback { private final Case caseForJob; private final AutoIngestDataSource dataSourceInfo; @@ -48,7 +48,7 @@ class AddDataSourceCallback extends DataSourceProcessorCallback { * @param dataSourceInfo The data source * @param taskId The task id to associate with ingest job events. */ - AddDataSourceCallback(Case caseForJob, AutoIngestDataSource dataSourceInfo, UUID taskId, Object lock) { + public AddDataSourceCallback(Case caseForJob, AutoIngestDataSource dataSourceInfo, UUID taskId, Object lock) { this.caseForJob = caseForJob; this.dataSourceInfo = dataSourceInfo; this.taskId = taskId; @@ -87,7 +87,7 @@ class AddDataSourceCallback extends DataSourceProcessorCallback { * @param result The result code for the processing of the data source. * @param errorMessages Any error messages generated during the processing * of the data source. - * @param dataSourceContent The content produced by processing the data + * @param dataSources The content produced by processing the data * source. */ @Override diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestDataSource.java b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/AutoIngestDataSource.java similarity index 71% rename from Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestDataSource.java rename to Core/src/org/sleuthkit/autopsy/datasourceprocessors/AutoIngestDataSource.java index 89a0d0ad5f..fbf5de11c5 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestDataSource.java +++ b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/AutoIngestDataSource.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.experimental.autoingest; +package org.sleuthkit.autopsy.datasourceprocessors; import java.nio.file.Path; import java.util.ArrayList; @@ -26,7 +26,7 @@ import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback import org.sleuthkit.datamodel.Content; @ThreadSafe -class AutoIngestDataSource { +public class AutoIngestDataSource { private final String deviceId; private final Path path; @@ -34,34 +34,34 @@ class AutoIngestDataSource { private List errorMessages; private List content; - AutoIngestDataSource(String deviceId, Path path) { + public AutoIngestDataSource(String deviceId, Path path) { this.deviceId = deviceId; this.path = path; } - String getDeviceId() { + public String getDeviceId() { return deviceId; } - Path getPath() { + public Path getPath() { return this.path; } - synchronized void setDataSourceProcessorOutput(DataSourceProcessorResult result, List errorMessages, List content) { + public synchronized void setDataSourceProcessorOutput(DataSourceProcessorResult result, List errorMessages, List content) { this.resultCode = result; this.errorMessages = new ArrayList<>(errorMessages); this.content = new ArrayList<>(content); } - synchronized DataSourceProcessorResult getResultDataSourceProcessorResultCode() { + public synchronized DataSourceProcessorResult getResultDataSourceProcessorResultCode() { return resultCode; } - synchronized List getDataSourceProcessorErrorMessages() { + public synchronized List getDataSourceProcessorErrorMessages() { return new ArrayList<>(errorMessages); } - synchronized List getContent() { + public synchronized List getContent() { return new ArrayList<>(content); } diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/DataSourceProcessorUtility.java b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/DataSourceProcessorUtility.java similarity index 83% rename from Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/DataSourceProcessorUtility.java rename to Core/src/org/sleuthkit/autopsy/datasourceprocessors/DataSourceProcessorUtility.java index 4878f7fa7d..8b27ff6c19 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/DataSourceProcessorUtility.java +++ b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/DataSourceProcessorUtility.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.experimental.autoingest; +package org.sleuthkit.autopsy.datasourceprocessors; import java.nio.file.Path; import java.util.Collection; @@ -25,13 +25,12 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; import org.openide.util.Lookup; -import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor; import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException; /** * A utility class to find Data Source Processors */ -class DataSourceProcessorUtility { +public class DataSourceProcessorUtility { private DataSourceProcessorUtility() { } @@ -47,7 +46,7 @@ class DataSourceProcessorUtility { * @throws * org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException */ - static Map getDataSourceProcessorForFile(Path dataSourcePath, Collection processorCandidates) throws AutoIngestDataSourceProcessorException { + public static Map getDataSourceProcessorForFile(Path dataSourcePath, Collection processorCandidates) throws AutoIngestDataSourceProcessorException { Map validDataSourceProcessorsMap = new HashMap<>(); for (AutoIngestDataSourceProcessor processor : processorCandidates) { int confidence = processor.canProcess(dataSourcePath); @@ -74,7 +73,7 @@ class DataSourceProcessorUtility { * @throws * org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException */ - static List getOrderedListOfDataSourceProcessors(Path dataSourcePath) throws AutoIngestDataSourceProcessorException { + public static List getOrderedListOfDataSourceProcessors(Path dataSourcePath) throws AutoIngestDataSourceProcessorException { // lookup all AutomatedIngestDataSourceProcessors Collection processorCandidates = Lookup.getDefault().lookupAll(AutoIngestDataSourceProcessor.class); return getOrderedListOfDataSourceProcessors(dataSourcePath, processorCandidates); @@ -96,7 +95,7 @@ class DataSourceProcessorUtility { * @throws * org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException */ - static List getOrderedListOfDataSourceProcessors(Path dataSourcePath, Collection processorCandidates) throws AutoIngestDataSourceProcessorException { + public static List getOrderedListOfDataSourceProcessors(Path dataSourcePath, Collection processorCandidates) throws AutoIngestDataSourceProcessorException { Map validDataSourceProcessorsMap = getDataSourceProcessorForFile(dataSourcePath, processorCandidates); return orderDataSourceProcessorsByConfidence(validDataSourceProcessorsMap); } @@ -110,7 +109,7 @@ class DataSourceProcessorUtility { * the data source along with their confidence score * @return Ordered list of data source processors */ - static List orderDataSourceProcessorsByConfidence(Map validDataSourceProcessorsMap) { + public static List orderDataSourceProcessorsByConfidence(Map validDataSourceProcessorsMap) { List validDataSourceProcessors = validDataSourceProcessorsMap.entrySet().stream() .sorted(Map.Entry.comparingByValue().reversed()) .map(Map.Entry::getKey) diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AddArchiveTask.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AddArchiveTask.java deleted file mode 100644 index 93c30fdb53..0000000000 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AddArchiveTask.java +++ /dev/null @@ -1,403 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2011-2018 Basis Technology Corp. - * Contact: carrier sleuthkit org - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sleuthkit.autopsy.experimental.autoingest; - -import java.io.File; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.UUID; -import java.util.logging.Level; -import java.util.stream.Collectors; -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.FilenameUtils; -import org.openide.util.Lookup; -import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.casemodule.LocalFilesDSProcessor; -import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback; -import static org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS; -import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor; -import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor; -import org.sleuthkit.autopsy.coreutils.TimeStampUtils; -import org.sleuthkit.autopsy.datasourceprocessors.RawDSProcessor; -import org.sleuthkit.datamodel.Content; -import org.sleuthkit.datamodel.DataSource; -import org.sleuthkit.datamodel.SleuthkitCase; -import org.sleuthkit.datamodel.TskData; -import org.sleuthkit.datamodel.VirtualDirectory; - -/* - * A runnable that adds an archive data source as well as data sources contained - * in the archive to the case database. - */ -class AddArchiveTask implements Runnable { - - private final Logger logger = Logger.getLogger(AddArchiveTask.class.getName()); - private final String deviceId; - private final String archivePath; - private final DataSourceProcessorProgressMonitor progressMonitor; - private final DataSourceProcessorCallback callback; - private boolean criticalErrorOccurred; - private final Object archiveDspLock; - - private static final String ARCHIVE_EXTRACTOR_MODULE_OUTPUT_DIR = "Archive Extractor"; - private static final String LOGICAL_FILE_VIRTUAL_DIR_NAME = "$AdditionalFiles"; - - /** - * Constructs a runnable task that adds an archive as well as data sources - * contained in the archive to the case database. - * - * @param deviceId An ASCII-printable identifier for the device - * associated with the data source that is intended - * to be unique across multiple cases (e.g., a UUID). - * @param archivePath Path to the archive file. - * @param progressMonitor Progress monitor to report progress during - * processing. - * @param callback Callback to call when processing is done. - */ - AddArchiveTask(String deviceId, String archivePath, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { - this.deviceId = deviceId; - this.archivePath = archivePath; - this.callback = callback; - this.progressMonitor = progressMonitor; - this.archiveDspLock = new Object(); - } - - /** - * Adds the archive to the case database. - */ - @Override - public void run() { - progressMonitor.setIndeterminate(true); - List errorMessages = new ArrayList<>(); - List newDataSources = new ArrayList<>(); - DataSourceProcessorCallback.DataSourceProcessorResult result; - if (!ArchiveUtil.isArchive(Paths.get(archivePath))) { - criticalErrorOccurred = true; - logger.log(Level.SEVERE, String.format("Input data source is not a valid datasource: %s", archivePath)); //NON-NLS - errorMessages.add("Input data source is not a valid datasource: " + archivePath); - result = DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS; - callback.done(result, errorMessages, newDataSources); - } - - logger.log(Level.INFO, "Using Archive Extractor DSP to process archive {0} ", archivePath); - - // extract the archive and pass the extracted folder as input - try { - Case currentCase = Case.getCurrentCaseThrows(); - - // create folder to extract archive to - Path destinationFolder = createDirectoryForFile(archivePath, currentCase.getModuleDirectory()); - if (destinationFolder.toString().isEmpty()) { - // unable to create directory - criticalErrorOccurred = true; - errorMessages.add(String.format("Unable to create directory {0} to extract archive {1} ", new Object[]{destinationFolder.toString(), archivePath})); - logger.log(Level.SEVERE, "Unable to create directory {0} to extract archive {1} ", new Object[]{destinationFolder.toString(), archivePath}); - return; - } - - // extract contents of ZIP archive into destination folder - List extractedFiles = new ArrayList<>(); - try { - progressMonitor.setProgressText(String.format("Extracting archive contents to: %s", destinationFolder.toString())); - extractedFiles = ArchiveUtil.unpackArchiveFile(archivePath, destinationFolder.toString()); - } catch (ArchiveUtil.ArchiveExtractionException ex) { - // delete extracted contents - logger.log(Level.SEVERE, "Exception while extracting archive contents into " + destinationFolder.toString() + ". Deleteing the directory", ex); - FileUtils.deleteDirectory(destinationFolder.toFile()); - throw ex; - } - - List unclaimedFiles = new ArrayList<>(extractedFiles); - - // lookup all AutomatedIngestDataSourceProcessors so that we only do it once. - // LocalDisk, LocalFiles, and ArchiveDSP are removed from the list. - List processorCandidates = getListOfValidDataSourceProcessors(); - - // do processing - int numValidDataSources = 0; - DataSource defaultDataSource = null; - for (String file : extractedFiles) { - - // we only care about files, skip directories - File fileObject = new File(file); - if (fileObject.isDirectory()) { - continue; - } - - // identify all "valid" DSPs that can process this file - List validDataSourceProcessors = getDataSourceProcessorsForFile(Paths.get(file), errorMessages, processorCandidates); - if (validDataSourceProcessors.isEmpty()) { - continue; - } - - // identified a "valid" data source within the archive - progressMonitor.setProgressText(String.format("Adding: %s", file)); - - /* - * NOTE: we have to move the valid data sources to a separate - * folder and then add the data source from that folder. This is - * necessary because after all valid data sources have been - * identified, we are going to add the remaining extracted - * contents of the archive as a single logical file set. Hence, - * if we do not move the data sources out of the extracted - * contents folder, those data source files will get added twice - * and can potentially result in duplicate keyword hits. - - Path newFolder = createDirectoryForFile(file, currentCase.getModuleDirectory()); - if (newFolder.toString().isEmpty()) { - // unable to create directory - criticalErrorOccurred = true; - errorMessages.add(String.format("Unable to create directory {0} to extract content of archive {1} ", new Object[]{newFolder.toString(), archivePath})); - logger.log(Level.SEVERE, "Unable to create directory {0} to extract content of archive {1} ", new Object[]{newFolder.toString(), archivePath}); - return; - } - - // Copy it to a different folder - //FileUtils.copyFileToDirectory(fileObject, newFolder.toFile()); - //Path newFilePath = Paths.get(newFolder.toString(), FilenameUtils.getName(file)); -*/ - // Try each DSP in decreasing order of confidence - boolean success = false; - for (AutoIngestDataSourceProcessor selectedProcessor : validDataSourceProcessors) { - - logger.log(Level.INFO, "Using {0} to process extracted file {1} ", new Object[]{selectedProcessor.getDataSourceType(), file}); - synchronized (archiveDspLock) { - UUID taskId = UUID.randomUUID(); - currentCase.notifyAddingDataSource(taskId); - AutoIngestDataSource internalDataSource = new AutoIngestDataSource(deviceId, fileObject.toPath()); - DataSourceProcessorCallback internalArchiveDspCallBack = new AddDataSourceCallback(currentCase, internalDataSource, taskId, archiveDspLock); - selectedProcessor.process(deviceId, fileObject.toPath(), progressMonitor, internalArchiveDspCallBack); - archiveDspLock.wait(); - - // at this point we got the content object(s) from the current DSP. - // check whether the data source was processed successfully - if ((internalDataSource.getResultDataSourceProcessorResultCode() == CRITICAL_ERRORS) - || internalDataSource.getContent().isEmpty()) { - // move onto the the next DSP that can process this data source - for (String errorMessage : internalDataSource.getDataSourceProcessorErrorMessages()) { - logger.log(Level.SEVERE, "Data source processor {0} was unable to process {1}: {2}", new Object[]{selectedProcessor.getDataSourceType(), internalDataSource.getPath(), errorMessage}); - } - continue; - } - - // if we are here it means the data source was added successfully - success = true; - newDataSources.addAll(internalDataSource.getContent()); - numValidDataSources++; - - // this extracted file has been "claimed" by one of the DSPs, - // remove it from the list of Logical Files that will be added later - removeClaimedFiles(file, unclaimedFiles, selectedProcessor); - - // update data source info - for (Content c:internalDataSource.getContent()) { - if (c instanceof DataSource) { - DataSource ds = (DataSource) c; - - // Read existing aquisition details and update them - String details = "Extracted from archive: " + archivePath.toString(); - String existingDetails = ds.getAcquisitionDetails(); - if (existingDetails != null && !existingDetails.isEmpty()) { - ds.setAcquisitionDetails(existingDetails + System.getProperty("line.separator") + details); - } else { - ds.setAcquisitionDetails(details); - } - - // Update the names for all new data sources to be the root archive plus the name of the data source - String newName = Paths.get(archivePath).getFileName() + "/" + ds.getName(); - ds.setDisplayName(newName); - currentCase.notifyDataSourceNameChanged(c, newName); - - defaultDataSource = ds; - } - } - - // skip all other DSPs for this data source - break; - } - } - - /*if (success) { - // one of the DSPs successfully processed the data source. delete the - // copy of the data source in the original extracted archive folder. - // otherwise the data source is going to be added again as a logical file. - numValidDataSources--; - FileUtils.deleteQuietly(fileObject); - } else { - // none of the DSPs were able to process the data source. delete the - // copy of the data source in the temporary folder. the data source is - // going to be added as a logical file with the rest of the extracted contents. - FileUtils.deleteQuietly(newFolder.toFile()); - }*/ - } - - // after all archive contents have been examined (and moved to separate folders if necessary), - // add remaining extracted contents as one logical file set - if (unclaimedFiles.size() > 0) { - - // ELTODO investigate if i need to aquire datase lock? - SleuthkitCase caseDatabase = Case.getCurrentCaseThrows().getSleuthkitCase(); - VirtualDirectory additionalFilesDir = caseDatabase.addVirtualDirectory(defaultDataSource.getId(), LOGICAL_FILE_VIRTUAL_DIR_NAME); - - for (String file : unclaimedFiles) { - File fileObject = new File(file); - caseDatabase.addLocalFile(fileObject.getName(), fileObject.getAbsolutePath(), fileObject.length(), 0, 0, 0, 0, fileObject.isFile(), TskData.EncodingType.NONE, additionalFilesDir); - } - - /*progressMonitor.setProgressText(String.format("Adding: %s", destinationFolder.toString())); - logger.log(Level.INFO, "Adding directory {0} as logical file set", destinationFolder.toString()); - synchronized (archiveDspLock) { - UUID taskId = UUID.randomUUID(); - currentCase.notifyAddingDataSource(taskId); - AutoIngestDataSource internalDataSource = new AutoIngestDataSource(deviceId, destinationFolder); - DataSourceProcessorCallback internalArchiveDspCallBack = new AddDataSourceCallback(currentCase, internalDataSource, taskId, archiveDspLock); - - // folder where archive was extracted to - List pathsList = new ArrayList<>(); - pathsList.add(destinationFolder.toString()); - - // use archive file name as the name of the logical file set - String archiveFileName = FilenameUtils.getName(archivePath); - - LocalFilesDSProcessor localFilesDSP = new LocalFilesDSProcessor(); - localFilesDSP.run(deviceId, archiveFileName, unclaimedFiles, progressMonitor, internalArchiveDspCallBack); - - archiveDspLock.wait(); - - // at this point we got the content object(s) from the current DSP. - newDataSources.addAll(internalDataSource.getContent()); - - for (Content c : internalDataSource.getContent()) { - if (c instanceof DataSource) { - DataSource ds = (DataSource) c; - // This is a new data source so just write the aquisition details - String details = "Extracted from archive: " + archivePath.toString(); - ds.setAcquisitionDetails(details); - } - } - }*/ - } - } catch (Exception ex) { - criticalErrorOccurred = true; - errorMessages.add(ex.getMessage()); - logger.log(Level.SEVERE, String.format("Critical error occurred while extracting archive %s", archivePath), ex); //NON-NLS - } finally { - logger.log(Level.INFO, "Finished processing of archive {0}", archivePath); - progressMonitor.setProgress(100); - if (criticalErrorOccurred) { - result = DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS; - } else if (!errorMessages.isEmpty()) { - result = DataSourceProcessorCallback.DataSourceProcessorResult.NONCRITICAL_ERRORS; - } else { - result = DataSourceProcessorCallback.DataSourceProcessorResult.NO_ERRORS; - } - callback.done(result, errorMessages, newDataSources); - } - } - - private void removeClaimedFiles(String file, List unclaimedFiles, AutoIngestDataSourceProcessor selectedProcessor) { - - //if (!(selectedProcessor instanceof ForensicToolReportProcessor)) { - unclaimedFiles.remove(file); - //} - } - - /** - * Get a list of data source processors. LocalFiles, RawDSProcessor, and - * ArchiveDSP are removed from the list. - * - * @return List of data source processors - */ - private List getListOfValidDataSourceProcessors() { - - Collection processorCandidates = Lookup.getDefault().lookupAll(AutoIngestDataSourceProcessor.class); - - List validDataSourceProcessors = processorCandidates.stream().collect(Collectors.toList()); - - for (Iterator iterator = validDataSourceProcessors.iterator(); iterator.hasNext();) { - AutoIngestDataSourceProcessor selectedProcessor = iterator.next(); - - // skip local files, only looking for "valid" data sources. - // also skip RawDSP as we don't want to add random "bin" and "raw" files that may be inside archive - // as individual data sources. - // also skip nested archive files, those will be ingested as logical files and extracted during ingest - if ((selectedProcessor instanceof LocalFilesDSProcessor) - || (selectedProcessor instanceof RawDSProcessor) - || (selectedProcessor instanceof ArchiveExtractorDSProcessor)) { - iterator.remove(); - } - } - - return validDataSourceProcessors; - } - - /** - * Get a list of data source processors that can process the data source of - * interest. The list is sorted by confidence in decreasing order. - * - * @param dataSourcePath Full path to the data source - * @param errorMessages List for error messages - * @param errorMessages List of AutoIngestDataSourceProcessor to try - * - * @return Ordered list of applicable DSPs - */ - private List getDataSourceProcessorsForFile(Path dataSourcePath, List errorMessages, - List processorCandidates) { - - // Get an ordered list of data source processors to try - List validDataSourceProcessorsForFile = Collections.emptyList(); - try { - validDataSourceProcessorsForFile = DataSourceProcessorUtility.getOrderedListOfDataSourceProcessors(dataSourcePath, processorCandidates); - } catch (AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException ex) { - criticalErrorOccurred = true; - errorMessages.add(ex.getMessage()); - logger.log(Level.SEVERE, String.format("Critical error occurred while extracting archive %s", archivePath), ex); //NON-NLS - return Collections.emptyList(); - } - return validDataSourceProcessorsForFile; - } - - /** - * Create a directory in ModuleOutput folder based on input file name. A - * time stamp is appended to the directory name. - * - * @param fileName File name - * @param baseDirectory Base directory. Typically the case output directory. - * - * @return Full path to the new directory - */ - private Path createDirectoryForFile(String fileName, String baseDirectory) { - // get file name without full path or extension - String fileNameNoExt = FilenameUtils.getBaseName(fileName); - - // create folder to extract archive to - Path newFolder = Paths.get(baseDirectory, ARCHIVE_EXTRACTOR_MODULE_OUTPUT_DIR, fileNameNoExt + "_" + TimeStampUtils.createTimeStamp()); - if (newFolder.toFile().mkdirs() == false) { - // unable to create directory - return Paths.get(""); - } - return newFolder; - } -} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/ArchiveExtractorDSProcessor.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/ArchiveExtractorDSProcessor.java deleted file mode 100644 index a30fbf0c75..0000000000 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/ArchiveExtractorDSProcessor.java +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2011-2017 Basis Technology Corp. - * Contact: carrier sleuthkit org - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sleuthkit.autopsy.experimental.autoingest; - -import com.google.common.util.concurrent.ThreadFactoryBuilder; -import java.nio.file.Path; -import java.util.UUID; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import javax.swing.JPanel; -import org.openide.util.NbBundle; -import org.openide.util.lookup.ServiceProvider; -import org.openide.util.lookup.ServiceProviders; -import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback; -import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor; -import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor; - -/** - * A data source processor that handles archive files. Implements the - * DataSourceProcessor service provider interface to allow integration with the - * add data source wizard. It also provides a run method overload to allow it to - * be used independently of the wizard. - */ -@ServiceProviders(value={ - @ServiceProvider(service=AutoIngestDataSourceProcessor.class)} -) -@NbBundle.Messages({ - "ArchiveDSP.dsType.text=Archive file"}) -public class ArchiveExtractorDSProcessor implements AutoIngestDataSourceProcessor { - - private final static String DATA_SOURCE_TYPE = Bundle.ArchiveDSP_dsType_text(); - - private final ArchiveFilePanel configPanel; - private String deviceId; - private String archivePath; - private boolean setDataSourceOptionsCalled; - - private final ExecutorService jobProcessingExecutor; - private static final String ARCHIVE_DSP_THREAD_NAME = "Archive-DSP-%d"; - private AddArchiveTask addArchiveTask; - - /** - * Constructs an archive data source processor that - * implements the DataSourceProcessor service provider interface to allow - * integration with the add data source wizard. It also provides a run - * method overload to allow it to be used independently of the wizard. - */ - public ArchiveExtractorDSProcessor() { - configPanel = ArchiveFilePanel.createInstance(ArchiveExtractorDSProcessor.class.getName(), ArchiveUtil.getArchiveFilters()); - jobProcessingExecutor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat(ARCHIVE_DSP_THREAD_NAME).build()); - } - - @Override - public int canProcess(Path dataSourcePath) throws AutoIngestDataSourceProcessorException { - // check whether this is an archive - if (ArchiveUtil.isArchive(dataSourcePath)){ - // return "high confidence" value - return 100; - } - return 0; - } - - @Override - public void process(String deviceId, Path dataSourcePath, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callBack) { - run(deviceId, dataSourcePath.toString(), progressMonitor, callBack); - } - - @Override - public String getDataSourceType() { - return DATA_SOURCE_TYPE; - } - - /** - * Gets the panel that allows a user to select a data source and do any - * configuration required by the data source. The panel is less than 544 - * pixels wide and less than 173 pixels high. - * - * @return A selection and configuration panel for this data source - * processor. - */ - @Override - public JPanel getPanel() { - configPanel.readSettings(); - configPanel.select(); - return configPanel; - } - - /** - * Indicates whether the settings in the selection and configuration panel - * are valid and complete. - * - * @return True if the settings are valid and complete and the processor is - * ready to have its run method called, false otherwise. - */ - @Override - public boolean isPanelValid() { - return configPanel.validatePanel(); - } - - /** - * Adds a data source to the case database using a background task in a - * separate thread and the settings provided by the selection and - * configuration panel. Returns as soon as the background task is started. - * The background task uses a callback object to signal task completion and - * return results. - * - * This method should not be called unless isPanelValid returns true. - * - * @param progressMonitor Progress monitor that will be used by the - * background task to report progress. - * @param callback Callback that will be used by the background task - * to return results. - */ - @Override - public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { - if (!setDataSourceOptionsCalled) { - configPanel.storeSettings(); - deviceId = UUID.randomUUID().toString(); - archivePath = configPanel.getContentPaths(); - } - run(deviceId, archivePath, progressMonitor, callback); - } - - /** - * Adds a data source to the case database using a background task in a - * separate thread and the given settings instead of those provided by the - * selection and configuration panel. Returns as soon as the background task - * is started and uses the callback object to signal task completion and - * return results. - * - * @param deviceId An ASCII-printable identifier for the device - * associated with the data source that is - * intended to be unique across multiple cases - * (e.g., a UUID). - * @param archivePath Path to the archive file. - * @param progressMonitor Progress monitor for reporting progress - * during processing. - * @param callback Callback to call when processing is done. - */ - public void run(String deviceId, String archivePath, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { - addArchiveTask = new AddArchiveTask(deviceId, archivePath, progressMonitor, callback); - jobProcessingExecutor.submit(addArchiveTask); - } - - /** - * This DSP is a service to AutoIngestDataSourceProcessor only. Hence it is - * only used by AIM. AIM currently doesn't support DSP cancellation. - */ - @Override - public void cancel() { - } - - @Override - public void reset() { - deviceId = null; - archivePath = null; - configPanel.reset(); - setDataSourceOptionsCalled = false; - } -} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/ArchiveFilePanel.form b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/ArchiveFilePanel.form deleted file mode 100644 index af9347df0c..0000000000 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/ArchiveFilePanel.form +++ /dev/null @@ -1,94 +0,0 @@ - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/ArchiveFilePanel.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/ArchiveFilePanel.java deleted file mode 100644 index cbe39cac1b..0000000000 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/ArchiveFilePanel.java +++ /dev/null @@ -1,289 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2011-2018 Basis Technology Corp. - * Contact: carrier sleuthkit org - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sleuthkit.autopsy.experimental.autoingest; - -import java.io.File; -import java.util.List; -import java.util.logging.Level; -import javax.swing.JFileChooser; -import javax.swing.JPanel; -import javax.swing.event.DocumentEvent; -import javax.swing.event.DocumentListener; -import javax.swing.filechooser.FileFilter; -import org.apache.commons.lang3.StringUtils; -import org.openide.util.NbBundle; -import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; -import static org.sleuthkit.autopsy.experimental.autoingest.Bundle.*; -import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; -import org.sleuthkit.autopsy.coreutils.DriveUtils; -import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; -import org.sleuthkit.autopsy.coreutils.ModuleSettings; -import org.sleuthkit.autopsy.coreutils.PathValidator; - -/** - * Panel for adding an archive file which is supported by 7zip library (e.g. - * "zip", "rar", "arj", "7z", "7zip", "gzip, etc). Allows the user to select a - * file. - */ -@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives -class ArchiveFilePanel extends JPanel implements DocumentListener { - - private static final Logger logger = Logger.getLogger(ArchiveFilePanel.class.getName()); - private static final String PROP_LAST_ARCHIVE_PATH = "LBL_LastImage_PATH"; //NON-NLS - - private final JFileChooser fileChooser = new JFileChooser(); - - /** - * Externally supplied name is used to store settings - */ - private final String contextName; - - /** - * Creates new form ArchiveFilePanel - * - * @param context A string context name used to read/store last - * used settings. - * @param fileChooserFilters A list of filters to be used with the - * FileChooser. - */ - private ArchiveFilePanel(String context, List fileChooserFilters) { - this.contextName = context; - initComponents(); - - errorLabel.setVisible(false); - - fileChooser.setDragEnabled(false); - fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); - fileChooser.setMultiSelectionEnabled(false); - fileChooserFilters.forEach(fileChooser::addChoosableFileFilter); - if (fileChooserFilters.isEmpty() == false) { - fileChooser.setFileFilter(fileChooserFilters.get(0)); - } - } - - /** - * Creates and returns an instance of a ArchiveFilePanel. - * - * @param context A string context name used to read/store last - * used settings. - * @param fileChooserFilters A list of filters to be used with the - * FileChooser. - * - * @return instance of the ArchiveFilePanel - */ - public static synchronized ArchiveFilePanel createInstance(String context, List fileChooserFilters) { - ArchiveFilePanel instance = new ArchiveFilePanel(context, fileChooserFilters); - // post-constructor initialization of listener support without leaking references of uninitialized objects - instance.pathTextField.getDocument().addDocumentListener(instance); - return instance; - } - - /** - * This method is called from within the constructor to initialize the form. - * WARNING: Do NOT modify this code. The content of this method is always - * regenerated by the Form Editor. - */ - // //GEN-BEGIN:initComponents - private void initComponents() { - - pathLabel = new javax.swing.JLabel(); - browseButton = new javax.swing.JButton(); - pathTextField = new javax.swing.JTextField(); - errorLabel = new javax.swing.JLabel(); - - setMinimumSize(new java.awt.Dimension(0, 65)); - setPreferredSize(new java.awt.Dimension(403, 65)); - - org.openide.awt.Mnemonics.setLocalizedText(pathLabel, org.openide.util.NbBundle.getMessage(ArchiveFilePanel.class, "ArchiveFilePanel.pathLabel.text")); // NOI18N - - org.openide.awt.Mnemonics.setLocalizedText(browseButton, org.openide.util.NbBundle.getMessage(ArchiveFilePanel.class, "ArchiveFilePanel.browseButton.text")); // NOI18N - browseButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - browseButtonActionPerformed(evt); - } - }); - - pathTextField.setText(org.openide.util.NbBundle.getMessage(ArchiveFilePanel.class, "ArchiveFilePanel.pathTextField.text")); // NOI18N - - errorLabel.setForeground(new java.awt.Color(255, 0, 0)); - org.openide.awt.Mnemonics.setLocalizedText(errorLabel, org.openide.util.NbBundle.getMessage(ArchiveFilePanel.class, "ArchiveFilePanel.errorLabel.text")); // NOI18N - - javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); - this.setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(pathTextField) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(browseButton) - .addGap(2, 2, 2)) - .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(pathLabel) - .addComponent(errorLabel)) - .addGap(0, 277, Short.MAX_VALUE)) - ); - layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(pathLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(browseButton) - .addComponent(pathTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addGap(3, 3, 3) - .addComponent(errorLabel) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - ); - }// //GEN-END:initComponents - - private void browseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_browseButtonActionPerformed - String oldText = getContentPaths(); - // set the current directory of the FileChooser if the ArchivePath Field is valid - File currentDir = new File(oldText); - if (currentDir.exists()) { - fileChooser.setCurrentDirectory(currentDir); - } - - if (fileChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { - String path = fileChooser.getSelectedFile().getPath(); - setContentPath(path); - } - - updateHelper(); - }//GEN-LAST:event_browseButtonActionPerformed - - // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JButton browseButton; - private javax.swing.JLabel errorLabel; - private javax.swing.JLabel pathLabel; - private javax.swing.JTextField pathTextField; - // End of variables declaration//GEN-END:variables - - /** - * Get the path of the user selected archive. - * - * @return the archive path - */ - public String getContentPaths() { - return pathTextField.getText(); - } - - /** - * Set the path of the archive file. - * - * @param s path of the archive file - */ - public void setContentPath(String s) { - pathTextField.setText(s); - } - - public void reset() { - //reset the UI elements to default - pathTextField.setText(null); - } - - /** - * Should we enable the next button of the wizard? - * - * @return true if a proper archive has been selected, false otherwise - */ - @NbBundle.Messages({"DataSourceOnCDriveError.text=Warning: Path to multi-user data source is on \"C:\" drive", - "DataSourceOnCDriveError.noOpenCase.errMsg=Warning: Exception while getting open case." - }) - public boolean validatePanel() { - errorLabel.setVisible(false); - String path = getContentPaths(); - if (StringUtils.isBlank(path)) { - return false; - } - - // display warning if there is one (but don't disable "next" button) - try { - if (false == PathValidator.isValidForMultiUserCase(path, Case.getCurrentCaseThrows().getCaseType())) { - errorLabel.setVisible(true); - errorLabel.setText(Bundle.DataSourceOnCDriveError_text()); - } - } catch (NoCurrentCaseException ex) { - errorLabel.setVisible(true); - errorLabel.setText(Bundle.DataSourceOnCDriveError_noOpenCase_errMsg()); - } - - return new File(path).isFile() - || DriveUtils.isPhysicalDrive(path) - || DriveUtils.isPartition(path); - } - - public void storeSettings() { - String archivePathName = getContentPaths(); - if (null != archivePathName) { - String archivePath = archivePathName.substring(0, archivePathName.lastIndexOf(File.separator) + 1); - ModuleSettings.setConfigSetting(contextName, PROP_LAST_ARCHIVE_PATH, archivePath); - } - } - - public void readSettings() { - String lastArchivePath = ModuleSettings.getConfigSetting(contextName, PROP_LAST_ARCHIVE_PATH); - if (StringUtils.isNotBlank(lastArchivePath)) { - setContentPath(lastArchivePath); - } - } - - @Override - public void insertUpdate(DocumentEvent e) { - updateHelper(); - } - - @Override - public void removeUpdate(DocumentEvent e) { - updateHelper(); - } - - @Override - public void changedUpdate(DocumentEvent e) { - updateHelper(); - } - - /** - * Update functions are called by the pathTextField which has this set as - * it's DocumentEventListener. Each update function fires a property change - * to be caught by the parent panel. - * - */ - @NbBundle.Messages({"ArchiveFilePanel.moduleErr=Module Error", - "ArchiveFilePanel.moduleErr.msg=A module caused an error listening to ArchiveFilePanel updates." - + " See log to determine which module. Some data could be incomplete.\n"}) - private void updateHelper() { - try { - firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), false, true); - } catch (Exception e) { - logger.log(Level.SEVERE, "ArchiveFilePanel listener threw exception", e); //NON-NLS - MessageNotifyUtil.Notify.error(ArchiveFilePanel_moduleErr(), ArchiveFilePanel_moduleErr_msg()); - } - } - - /** - * Set the focus to the pathTextField. - */ - public void select() { - pathTextField.requestFocusInWindow(); - } -} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/ArchiveUtil.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/ArchiveUtil.java deleted file mode 100644 index c22dfbd4ee..0000000000 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/ArchiveUtil.java +++ /dev/null @@ -1,313 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2015 Basis Technology Corp. - * Contact: carrier sleuthkit org - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sleuthkit.autopsy.experimental.autoingest; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.io.RandomAccessFile; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import javax.swing.filechooser.FileFilter; -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.impl.RandomAccessFileInStream; -import net.sf.sevenzipjbinding.simple.ISimpleInArchive; -import net.sf.sevenzipjbinding.simple.ISimpleInArchiveItem; -import org.openide.util.NbBundle; -import org.sleuthkit.autopsy.casemodule.GeneralFilter; -import org.sleuthkit.autopsy.coreutils.FileUtil; - -/** - * Set of utilities that handles archive file extraction. Uses 7zip library. - */ -final class ArchiveUtil { - - private static final String[] SUPPORTED_EXTENSIONS = {"zip", "rar", "arj", "7z", "7zip", "gzip", "gz", "bzip2", "tar", "tgz",}; // NON-NLS - private static final List ARCHIVE_EXTS = Arrays.asList(".zip", ".rar", ".arj", ".7z", ".7zip", ".gzip", ".gz", ".bzip2", ".tar", ".tgz"); //NON-NLS - @NbBundle.Messages("GeneralFilter.archiveDesc.text=Archive Files (.zip, .rar, .arj, .7z, .7zip, .gzip, .gz, .bzip2, .tar, .tgz)") - private static final String ARCHIVE_DESC = Bundle.GeneralFilter_archiveDesc_text(); - private static final GeneralFilter SEVEN_ZIP_FILTER = new GeneralFilter(ARCHIVE_EXTS, ARCHIVE_DESC); - private static final List ARCHIVE_FILTERS = new ArrayList<>(); - static { - ARCHIVE_FILTERS.add(SEVEN_ZIP_FILTER); - } - - private ArchiveUtil() { - } - - static List getArchiveFilters() { - return ARCHIVE_FILTERS; - } - - static boolean isArchive(Path dataSourcePath) { - String fileName = dataSourcePath.getFileName().toString(); - // check whether it's a zip archive file that can be extracted - return isAcceptedByFiler(new File(fileName), ARCHIVE_FILTERS); - } - - private static boolean isAcceptedByFiler(File file, List filters) { - for (FileFilter filter : filters) { - if (filter.accept(file)) { - return true; - } - } - return false; - } - - /** - * Enum of mime types which support archive extraction - */ - private enum SupportedArchiveExtractionFormats { - - ZIP("application/zip"), //NON-NLS - SEVENZ("application/x-7z-compressed"), //NON-NLS - GZIP("application/gzip"), //NON-NLS - XGZIP("application/x-gzip"), //NON-NLS - XBZIP2("application/x-bzip2"), //NON-NLS - XTAR("application/x-tar"), //NON-NLS - XGTAR("application/x-gtar"), - XRAR("application/x-rar-compressed"); //NON-NLS - - private final String mimeType; - - SupportedArchiveExtractionFormats(final String mimeType) { - this.mimeType = mimeType; - } - - @Override - public String toString() { - return this.mimeType; - } - } - - /** - * Exception thrown when archive handling resulted in an error - */ - static class ArchiveExtractionException extends Exception { - - private static final long serialVersionUID = 1L; - - ArchiveExtractionException(String message) { - super(message); - } - - ArchiveExtractionException(String message, Throwable cause) { - super(message, cause); - } - } - - /** - * This method returns array of supported file extensions. - * - * @return String array of supported file extensions. - */ - static String[] getSupportedArchiveTypes(){ - return SUPPORTED_EXTENSIONS; - } - - /** - * This method returns true if the MIME type is currently supported. Else it - * returns false. - * - * @param mimeType File mime type - * - * @return This method returns true if the file format is currently - * supported. Else it returns false. - */ - static boolean isExtractionSupportedByMimeType(String mimeType) { - for (SupportedArchiveExtractionFormats s : SupportedArchiveExtractionFormats.values()) { - if (s.toString().equals(mimeType)) { - return true; - } - } - return false; - } - - /** - * This method returns true if the file extension is currently supported. - * Else it returns false. Attempt extension based detection in case Apache - * Tika based detection fails. - * - * @param extension File extension - * - * @return This method returns true if the file format is currently - * supported. Else it returns false. - */ - static boolean isExtractionSupportedByFileExtension(String extension) { - // attempt extension matching - for (String supportedExtension : SUPPORTED_EXTENSIONS) { - if (extension.equals(supportedExtension)) { - return true; - } - } - return false; - } - - /** - * Returns a list of file names contained within an archive. - * - * @param archiveFilePath Full path to the archive file - * - * @return List of file names contained within archive - * - * @throws - * ArchiveExtractionException - */ - static List getListOfFilesWithinArchive(String archiveFilePath) throws ArchiveExtractionException { - if (!SevenZip.isInitializedSuccessfully() && (SevenZip.getLastInitializationException() == null)) { - try { - SevenZip.initSevenZipFromPlatformJAR(); - } catch (SevenZipNativeInitializationException ex) { - throw new ArchiveExtractionException("AutoIngestDashboard_bnPause_paused", ex); - } - } - List files = new ArrayList<>(); - ISevenZipInArchive inArchive = null; - try { - RandomAccessFile randomAccessFile = new RandomAccessFile(new File(archiveFilePath), "r"); - inArchive = SevenZip.openInArchive(null, new RandomAccessFileInStream(randomAccessFile)); - final ISimpleInArchive simpleInArchive = inArchive.getSimpleInterface(); - for (ISimpleInArchiveItem item : simpleInArchive.getArchiveItems()) { - files.add(item.getPath()); - } - } catch (Exception ex) { - throw new ArchiveExtractionException("Exception while reading archive contents", ex); - } finally { - if (inArchive != null) { - try { - inArchive.close(); - } catch (SevenZipException ex) { - throw new ArchiveExtractionException("Exception while closing the archive", ex); - } - } - } - return files; - } - - /** - * Extracts contents of an archive file into a directory. - * - * @param archiveFilePath Full path to archive. - * @param destinationFolder Path to directory where results will be - * extracted to. - * - * @return List of file names contained within archive - * @throws - * ArchiveExtractionException - */ - static List unpackArchiveFile(String archiveFilePath, String destinationFolder) throws ArchiveExtractionException { - if (!SevenZip.isInitializedSuccessfully() && (SevenZip.getLastInitializationException() == null)) { - try { - SevenZip.initSevenZipFromPlatformJAR(); - } catch (SevenZipNativeInitializationException ex) { - throw new ArchiveExtractionException("Unable to initialize 7Zip libraries", ex); - } - } - List files = new ArrayList<>(); - ISevenZipInArchive inArchive = null; - try { - RandomAccessFile randomAccessFile = new RandomAccessFile(new File(archiveFilePath), "r"); - inArchive = SevenZip.openInArchive(null, new RandomAccessFileInStream(randomAccessFile)); - final ISimpleInArchive simpleInArchive = inArchive.getSimpleInterface(); - - for (ISimpleInArchiveItem entry : simpleInArchive.getArchiveItems()) { - String entryPathInArchive = entry.getPath(); - Path fullPath = Paths.get(destinationFolder, FileUtil.escapeFileName(entryPathInArchive)); // remove illegal characters from file name - File destFile = new File(fullPath.toString()); - File destinationParent = destFile.getParentFile(); - destinationParent.mkdirs(); - if (!entry.isFolder()) { - UnpackStream unpackStream = null; - try { - Long size = entry.getSize(); - unpackStream = new UnpackStream(destFile.toString(), size); - entry.extractSlow(unpackStream); - } catch (Exception ex) { - throw new ArchiveExtractionException("Exception while unpacking archive contents", ex); - } finally { - if (unpackStream != null) { - unpackStream.close(); - } - } - } - // keep track of extracted files - files.add(fullPath.toString()); - } - } catch (Exception ex) { - throw new ArchiveExtractionException("Exception while unpacking archive contents", ex); - } finally { - try { - if (inArchive != null) { - inArchive.close(); - } - } catch (SevenZipException ex) { - throw new ArchiveExtractionException("Exception while closing the archive", ex); - } - } - return files; - } - - /** - * Stream used to unpack an archive to local file - */ - private static class UnpackStream implements ISequentialOutStream { - - private OutputStream output; - private String destFilePath; - - UnpackStream(String destFilePath, long size) throws ArchiveExtractionException { - this.destFilePath = destFilePath; - try { - output = new FileOutputStream(destFilePath); - } catch (IOException ex) { - throw new ArchiveExtractionException("Exception while unpacking archive contents", ex); - } - - } - - @Override - public int write(byte[] bytes) throws SevenZipException { - try { - output.write(bytes); - } catch (IOException ex) { - throw new SevenZipException("Error writing unpacked file to " + destFilePath, ex); - } - return bytes.length; - } - - public void close() throws ArchiveExtractionException { - if (output != null) { - try { - output.flush(); - output.close(); - } catch (IOException ex) { - throw new ArchiveExtractionException("Exception while closing the archive", ex); - } - } - } - } -} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java index 55332cc03c..cf0e5c615d 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java @@ -32,7 +32,6 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.attribute.BasicFileAttributes; -import java.sql.SQLException; import java.time.Duration; import java.time.Instant; import java.util.ArrayList; @@ -70,7 +69,6 @@ import org.sleuthkit.autopsy.coordinationservice.CoordinationService.Lock; import org.sleuthkit.autopsy.core.RuntimeProperties; import org.sleuthkit.autopsy.core.ServicesMonitor; import org.sleuthkit.autopsy.core.ServicesMonitor.ServicesMonitorException; -import org.sleuthkit.autopsy.core.UserPreferencesException; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback.DataSourceProcessorResult; import static org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS; @@ -92,6 +90,9 @@ import org.sleuthkit.autopsy.experimental.configuration.SharedConfiguration; import org.sleuthkit.autopsy.experimental.configuration.SharedConfiguration.SharedConfigurationException; import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor; import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException; +import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSource; +import org.sleuthkit.autopsy.datasourceprocessors.AddDataSourceCallback; +import org.sleuthkit.autopsy.datasourceprocessors.DataSourceProcessorUtility; import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestJob.AutoIngestJobException; import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestNodeControlEvent.ControlEventType; import org.sleuthkit.autopsy.ingest.IngestJob; diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties index 4d2ade1b0a..0f074ca11c 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties @@ -219,10 +219,6 @@ AutoIngestMetricsDialog.reportTextArea.text= AutoIngestMetricsDialog.metricsButton.text=Generate Metrics Report AutoIngestMetricsDialog.closeButton.text=Close AutoIngestMetricsDialog.datePicker.toolTipText=Choose a date -ArchiveFilePanel.pathLabel.text=Browse for an archive file: -ArchiveFilePanel.browseButton.text=Browse -ArchiveFilePanel.pathTextField.text= -ArchiveFilePanel.errorLabel.text=Error Label AutoIngestMetricsDialog.startingDataLabel.text=Starting Date: AutoIngestControlPanel.bnDeprioritizeCase.text=Deprioritize Case AutoIngestControlPanel.bnDeprioritizeJob.text=Deprioritize Job diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties-MERGED b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties-MERGED index c10ac5581f..a7336e26a6 100755 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties-MERGED +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties-MERGED @@ -8,9 +8,6 @@ AinStatusNode.status.shuttingdown=Shutting Down AinStatusNode.status.startingup=Starting Up AinStatusNode.status.title=Status AinStatusNode.status.unknown=Unknown -ArchiveDSP.dsType.text=Archive file -ArchiveFilePanel.moduleErr=Module Error -ArchiveFilePanel.moduleErr.msg=A module caused an error listening to ArchiveFilePanel updates. See log to determine which module. Some data could be incomplete.\n AutoIngestAdminActions.cancelJobAction.title=Cancel Job AutoIngestAdminActions.cancelModuleAction.title=Cancel Module AutoIngestAdminActions.deleteCaseAction.error=Failed to delete case. @@ -170,12 +167,9 @@ CTL_AutoIngestDashboardOpenAction=Auto Ingest Dashboard CTL_AutoIngestDashboardTopComponent=Auto Ingest Jobs CTL_CasesDashboardAction=Multi-User Cases Dashboard CTL_CasesDashboardTopComponent=Cases -DataSourceOnCDriveError.noOpenCase.errMsg=Warning: Exception while getting open case. -DataSourceOnCDriveError.text=Warning: Path to multi-user data source is on "C:" drive DeleteCaseInputDirectoriesAction.menuItemText=Delete Input Directories DeleteCasesAction.menuItemText=Delete Case and Jobs DeleteCasesForReprocessingAction.menuItemText=Delete for Reprocessing -GeneralFilter.archiveDesc.text=Archive Files (.zip, .rar, .arj, .7z, .7zip, .gzip, .gz, .bzip2, .tar, .tgz) HINT_CasesDashboardTopComponent=This is an adminstrative dashboard for multi-user cases OpenAutoIngestLogAction.deletedLogErrorMsg=The case auto ingest log has been deleted. OpenAutoIngestLogAction.logOpenFailedErrorMsg=Failed to open case auto ingest log. See application log for details. @@ -377,10 +371,6 @@ AutoIngestMetricsDialog.reportTextArea.text= AutoIngestMetricsDialog.metricsButton.text=Generate Metrics Report AutoIngestMetricsDialog.closeButton.text=Close AutoIngestMetricsDialog.datePicker.toolTipText=Choose a date -ArchiveFilePanel.pathLabel.text=Browse for an archive file: -ArchiveFilePanel.browseButton.text=Browse -ArchiveFilePanel.pathTextField.text= -ArchiveFilePanel.errorLabel.text=Error Label AutoIngestMetricsDialog.startingDataLabel.text=Starting Date: AutoIngestControlPanel.bnDeprioritizeCase.text=Deprioritize Case AutoIngestControlPanel.bnDeprioritizeJob.text=Deprioritize Job From 81cd78d695ddbde7a71b515d8fa421cdc7af1e7a Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Thu, 14 Mar 2019 10:12:55 -0400 Subject: [PATCH 21/59] Removed code duplication --- .../AddDataSourceCallback.java | 96 -------------- .../CommandLineIngestManager.java | 13 +- .../autopsy/commandlineingest/DataSource.java | 66 ---------- .../DataSourceProcessorUtility.java | 121 ------------------ 4 files changed, 8 insertions(+), 288 deletions(-) delete mode 100755 Core/src/org/sleuthkit/autopsy/commandlineingest/AddDataSourceCallback.java delete mode 100755 Core/src/org/sleuthkit/autopsy/commandlineingest/DataSource.java delete mode 100755 Core/src/org/sleuthkit/autopsy/commandlineingest/DataSourceProcessorUtility.java diff --git a/Core/src/org/sleuthkit/autopsy/commandlineingest/AddDataSourceCallback.java b/Core/src/org/sleuthkit/autopsy/commandlineingest/AddDataSourceCallback.java deleted file mode 100755 index b4a000b0f3..0000000000 --- a/Core/src/org/sleuthkit/autopsy/commandlineingest/AddDataSourceCallback.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2019-2019 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.commandlineingest; - -import java.util.List; -import java.util.UUID; -import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback; -import org.sleuthkit.datamodel.Content; - - -/** - * A "callback" that collects the results of running a data source processor on - * a data source and unblocks the job processing thread when the data source - * processor finishes running in its own thread. - */ -class AddDataSourceCallback extends DataSourceProcessorCallback { - - private final Case caseForJob; - private final DataSource dataSourceInfo; - private final UUID taskId; - private final Object lock; - - /** - * Constructs a "callback" that collects the results of running a data - * source processor on a data source and unblocks the job processing thread - * when the data source processor finishes running in its own thread. - * - * @param caseForJob The case for the current job. - * @param dataSourceInfo The data source - * @param taskId The task id to associate with ingest job events. - */ - AddDataSourceCallback(Case caseForJob, DataSource dataSourceInfo, UUID taskId, Object lock) { - this.caseForJob = caseForJob; - this.dataSourceInfo = dataSourceInfo; - this.taskId = taskId; - this.lock = lock; - } - - /** - * Called by the data source processor when it finishes running in its own - * thread. - * - * @param result The result code for the processing of the data source. - * @param errorMessages Any error messages generated during the processing - * of the data source. - * @param dataSourceContent The content produced by processing the data - * source. - */ - @Override - public void done(DataSourceProcessorCallback.DataSourceProcessorResult result, List errorMessages, List dataSourceContent) { - if (!dataSourceContent.isEmpty()) { - caseForJob.notifyDataSourceAdded(dataSourceContent.get(0), taskId); - } else { - caseForJob.notifyFailedAddingDataSource(taskId); - } - dataSourceInfo.setDataSourceProcessorOutput(result, errorMessages, dataSourceContent); - dataSourceContent.addAll(dataSourceContent); - synchronized (lock) { - lock.notifyAll(); - } - } - - /** - * Called by the data source processor when it finishes running in its own - * thread, if that thread is the AWT (Abstract Window Toolkit) event - * dispatch thread (EDT). - * - * @param result The result code for the processing of the data source. - * @param errorMessages Any error messages generated during the processing - * of the data source. - * @param dataSourceContent The content produced by processing the data - * source. - */ - @Override - public void doneEDT(DataSourceProcessorCallback.DataSourceProcessorResult result, List errorMessages, List dataSources) { - done(result, errorMessages, dataSources); - } - -} diff --git a/Core/src/org/sleuthkit/autopsy/commandlineingest/CommandLineIngestManager.java b/Core/src/org/sleuthkit/autopsy/commandlineingest/CommandLineIngestManager.java index 13b76a5eca..8039aa0479 100755 --- a/Core/src/org/sleuthkit/autopsy/commandlineingest/CommandLineIngestManager.java +++ b/Core/src/org/sleuthkit/autopsy/commandlineingest/CommandLineIngestManager.java @@ -43,6 +43,9 @@ import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgress import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.TimeStampUtils; import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor; +import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSource; +import org.sleuthkit.autopsy.datasourceprocessors.AddDataSourceCallback; +import org.sleuthkit.autopsy.datasourceprocessors.DataSourceProcessorUtility; import org.sleuthkit.autopsy.events.AutopsyEvent; import org.sleuthkit.autopsy.ingest.IngestJob; import org.sleuthkit.autopsy.ingest.IngestJobSettings; @@ -177,7 +180,7 @@ public class CommandLineIngestManager { return; } - DataSource dataSource = new DataSource("", Paths.get(dataSourcePath)); + AutoIngestDataSource dataSource = new AutoIngestDataSource("", Paths.get(dataSourcePath)); try { // run data source processor runDataSourceProcessor(caseForJob, dataSource); @@ -228,7 +231,7 @@ public class CommandLineIngestManager { * @param dataSource DataSource object * @return object ID */ - private Long getDataSourceId(DataSource dataSource) { + private Long getDataSourceId(AutoIngestDataSource dataSource) { Content content = dataSource.getContent().get(0); return content.getId(); } @@ -271,7 +274,7 @@ public class CommandLineIngestManager { * task is interrupted while blocked, i.e., if auto ingest is shutting * down. */ - private void runDataSourceProcessor(Case caseForJob, DataSource dataSource) throws InterruptedException, AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException { + private void runDataSourceProcessor(Case caseForJob, AutoIngestDataSource dataSource) throws InterruptedException, AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException { LOGGER.log(Level.INFO, "Adding data source {0} ", dataSource.getPath().toString()); @@ -329,7 +332,7 @@ public class CommandLineIngestManager { * * @param dataSource The data source. */ - private void logDataSourceProcessorResult(DataSource dataSource) { + private void logDataSourceProcessorResult(AutoIngestDataSource dataSource) { DataSourceProcessorCallback.DataSourceProcessorResult resultCode = dataSource.getResultDataSourceProcessorResultCode(); if (null != resultCode) { @@ -376,7 +379,7 @@ public class CommandLineIngestManager { * task is interrupted while blocked, i.e., if auto ingest is shutting * down. */ - private void analyze(DataSource dataSource) throws AnalysisStartupException, InterruptedException { + private void analyze(AutoIngestDataSource dataSource) throws AnalysisStartupException, InterruptedException { LOGGER.log(Level.INFO, "Starting ingest modules analysis for {0} ", dataSource.getPath()); IngestJobEventListener ingestJobEventListener = new IngestJobEventListener(); diff --git a/Core/src/org/sleuthkit/autopsy/commandlineingest/DataSource.java b/Core/src/org/sleuthkit/autopsy/commandlineingest/DataSource.java deleted file mode 100755 index 527a4e57f3..0000000000 --- a/Core/src/org/sleuthkit/autopsy/commandlineingest/DataSource.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2019-2019 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.commandlineingest; - -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; -import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback.DataSourceProcessorResult; -import org.sleuthkit.datamodel.Content; - -class DataSource { - - private final String deviceId; - private final Path path; - private DataSourceProcessorResult resultCode; - private List errorMessages; - private List content; - - DataSource(String deviceId, Path path) { - this.deviceId = deviceId; - this.path = path; - } - - String getDeviceId() { - return deviceId; - } - - Path getPath() { - return this.path; - } - - synchronized void setDataSourceProcessorOutput(DataSourceProcessorResult result, List errorMessages, List content) { - this.resultCode = result; - this.errorMessages = new ArrayList<>(errorMessages); - this.content = new ArrayList<>(content); - } - - synchronized DataSourceProcessorResult getResultDataSourceProcessorResultCode() { - return resultCode; - } - - synchronized List getDataSourceProcessorErrorMessages() { - return new ArrayList<>(errorMessages); - } - - synchronized List getContent() { - return new ArrayList<>(content); - } - -} diff --git a/Core/src/org/sleuthkit/autopsy/commandlineingest/DataSourceProcessorUtility.java b/Core/src/org/sleuthkit/autopsy/commandlineingest/DataSourceProcessorUtility.java deleted file mode 100755 index d5df4cef3d..0000000000 --- a/Core/src/org/sleuthkit/autopsy/commandlineingest/DataSourceProcessorUtility.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2019-2019 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.commandlineingest; - -import java.nio.file.Path; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; -import org.openide.util.Lookup; -import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor; -import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException; - -/** - * A utility class to find Data Source Processors - */ -final class DataSourceProcessorUtility { - - private DataSourceProcessorUtility() { - } - - /** - * A utility method to find all Data Source Processors (DSP) that are able - * to process the input data source. Only the DSPs that implement - * AutoIngestDataSourceProcessor interface are used. - * - * @param dataSourcePath Full path to the data source - * @return Hash map of all DSPs that can process the data source along with - * their confidence score - * @throws - * org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException - */ - static Map getDataSourceProcessorForFile(Path dataSourcePath, Collection processorCandidates) throws AutoIngestDataSourceProcessorException { - Map validDataSourceProcessorsMap = new HashMap<>(); - for (AutoIngestDataSourceProcessor processor : processorCandidates) { - int confidence = processor.canProcess(dataSourcePath); - if (confidence > 0) { - validDataSourceProcessorsMap.put(processor, confidence); - } - } - - return validDataSourceProcessorsMap; - } - - /** - * A utility method to find all Data Source Processors (DSP) that are able - * to process the input data source. Only the DSPs that implement - * AutoIngestDataSourceProcessor interface are used. Returns ordered list of - * data source processors. DSPs are ordered in descending order from highest - * confidence to lowest. - * - * @param dataSourcePath Full path to the data source - * - * @return Ordered list of data source processors. DSPs are ordered in - * descending order from highest confidence to lowest. - * - * @throws - * org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException - */ - static List getOrderedListOfDataSourceProcessors(Path dataSourcePath) throws AutoIngestDataSourceProcessorException { - // lookup all AutomatedIngestDataSourceProcessors - Collection processorCandidates = Lookup.getDefault().lookupAll(AutoIngestDataSourceProcessor.class); - return getOrderedListOfDataSourceProcessors(dataSourcePath, processorCandidates); - } - - /** - * A utility method to find all Data Source Processors (DSP) that are able - * to process the input data source. Only the DSPs that implement - * AutoIngestDataSourceProcessor interface are used. Returns ordered list of - * data source processors. DSPs are ordered in descending order from highest - * confidence to lowest. - * - * @param dataSourcePath Full path to the data source - * @param processorCandidates Collection of AutoIngestDataSourceProcessor objects to use - * - * @return Ordered list of data source processors. DSPs are ordered in - * descending order from highest confidence to lowest. - * - * @throws - * org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException - */ - static List getOrderedListOfDataSourceProcessors(Path dataSourcePath, Collection processorCandidates) throws AutoIngestDataSourceProcessorException { - Map validDataSourceProcessorsMap = getDataSourceProcessorForFile(dataSourcePath, processorCandidates); - return orderDataSourceProcessorsByConfidence(validDataSourceProcessorsMap); - } - - - /** - * A utility method to get an ordered list of data source processors. DSPs - * are ordered in descending order from highest confidence to lowest. - * - * @param validDataSourceProcessorsMap Hash map of all DSPs that can process - * the data source along with their confidence score - * @return Ordered list of data source processors - */ - static List orderDataSourceProcessorsByConfidence(Map validDataSourceProcessorsMap) { - List validDataSourceProcessors = validDataSourceProcessorsMap.entrySet().stream() - .sorted(Map.Entry.comparingByValue().reversed()) - .map(Map.Entry::getKey) - .collect(Collectors.toList()); - - return validDataSourceProcessors; - } -} From 114c8a913a9007a33c6dea42e267ff683ecadc17 Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Thu, 14 Mar 2019 12:04:31 -0400 Subject: [PATCH 22/59] Inital commit for communications snapshot report support --- .../autopsy/communications/Bundle.properties | 1 + .../communications/Bundle.properties-MERGED | 5 + .../communications/VisualizationPanel.form | 30 ++- .../communications/VisualizationPanel.java | 163 ++++++++++++- .../snapshot/CommSnapShotReportWriter.java | 126 ++++++++++ .../snapshot/comm_snapshot_template.html | 27 +++ .../sleuthkit/autopsy/report/images/image.png | Bin 0 -> 516 bytes .../uisnapshot/UiSnapShotReportWriter.java | 226 ++++++++++++++++++ .../autopsy/report/uisnapshot/index.css | 19 ++ .../report/uisnapshot/index_template.html | 14 ++ .../autopsy/report/uisnapshot/navigation.html | 16 ++ .../autopsy/report/uisnapshot/summary.css | 14 ++ .../report/uisnapshot/summary_template.html | 55 +++++ 13 files changed, 687 insertions(+), 9 deletions(-) create mode 100755 Core/src/org/sleuthkit/autopsy/communications/snapshot/CommSnapShotReportWriter.java create mode 100755 Core/src/org/sleuthkit/autopsy/communications/snapshot/comm_snapshot_template.html create mode 100755 Core/src/org/sleuthkit/autopsy/report/images/image.png create mode 100755 Core/src/org/sleuthkit/autopsy/report/uisnapshot/UiSnapShotReportWriter.java create mode 100755 Core/src/org/sleuthkit/autopsy/report/uisnapshot/index.css create mode 100755 Core/src/org/sleuthkit/autopsy/report/uisnapshot/index_template.html create mode 100755 Core/src/org/sleuthkit/autopsy/report/uisnapshot/navigation.html create mode 100755 Core/src/org/sleuthkit/autopsy/report/uisnapshot/summary.css create mode 100755 Core/src/org/sleuthkit/autopsy/report/uisnapshot/summary_template.html diff --git a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties index 358971eaa8..6665c2e958 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties @@ -38,3 +38,4 @@ VisualizationPanel.organicLayoutButton.text=Organic VisualizationPanel.fastOrganicLayoutButton.text=Fast Organic VisualizationPanel.hierarchyLayoutButton.text=Hierarchical VisualizationPanel.clearVizButton.text_1=Clear Viz. +VisualizationPanel.snapshotButton.text_1=Snapshot Report diff --git a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties-MERGED index 381ec7b337..949d7876e1 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties-MERGED @@ -32,6 +32,8 @@ ResetAndPinAccountsAction.singularText=Visualize Only Selected Account UnpinAccountsAction.pluralText=Remove Selected Accounts UnpinAccountsAction.singularText=Remove Selected Account VisalizationPanel.paintingError=Problem painting visualization. +VisualizationPane_fileName_prompt=Enter name for the Communications Snapshot Report: +VisualizationPane_reportName=Communications Snapshot VisualizationPanel.cancelButton.text=Cancel VisualizationPanel.computingLayout=Computing Layout VisualizationPanel.jButton1.text=Fast Organic @@ -65,3 +67,6 @@ VisualizationPanel.organicLayoutButton.text=Organic VisualizationPanel.fastOrganicLayoutButton.text=Fast Organic VisualizationPanel.hierarchyLayoutButton.text=Hierarchical VisualizationPanel.clearVizButton.text_1=Clear Viz. +VisualizationPanel.snapshotButton.text_1=Snapshot Report +VisualizationPanel_action_dialogs_title=Communications +VisualizationPanel_action_name_text=Snapshot Report diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form index 49b86ae014..4e0a73a6c7 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form @@ -11,7 +11,7 @@ - + @@ -49,9 +49,9 @@ - + - + @@ -120,6 +120,10 @@ + + + + @@ -143,6 +147,8 @@ + + @@ -310,6 +316,24 @@ + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 8f5e6e5c94..a3c0fad868 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -28,6 +28,7 @@ import com.mxgraph.model.mxCell; import com.mxgraph.model.mxICell; import com.mxgraph.swing.handler.mxRubberband; import com.mxgraph.swing.mxGraphComponent; +import com.mxgraph.util.mxCellRenderer; import com.mxgraph.util.mxEvent; import com.mxgraph.util.mxEventObject; import com.mxgraph.util.mxEventSource; @@ -41,19 +42,28 @@ import com.mxgraph.view.mxGraphView; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Cursor; +import java.awt.Desktop; import java.awt.Dimension; import java.awt.Font; import java.awt.Frame; import java.awt.Graphics; +import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseWheelEvent; +import java.awt.image.BufferedImage; import java.beans.PropertyChangeEvent; import java.beans.PropertyVetoException; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.text.DecimalFormat; +import java.text.SimpleDateFormat; import java.util.Arrays; +import java.util.Date; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; @@ -75,14 +85,17 @@ import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JMenuItem; +import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JSplitPane; import javax.swing.JTextArea; +import javax.swing.JTextField; import javax.swing.JToolBar; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; +import org.apache.commons.lang3.StringUtils; import org.controlsfx.control.Notifications; import org.jdesktop.layout.GroupLayout; import org.jdesktop.layout.LayoutStyle; @@ -92,8 +105,11 @@ import org.openide.nodes.Node; import org.openide.util.Lookup; import org.openide.util.NbBundle; import org.openide.util.lookup.ProxyLookup; +import org.openide.windows.WindowManager; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.communications.snapshot.CommSnapShotReportWriter; +import org.sleuthkit.autopsy.coreutils.FileUtil; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.progress.ModalDialogProgressIndicator; @@ -101,7 +117,6 @@ import org.sleuthkit.datamodel.CommunicationsFilter; import org.sleuthkit.datamodel.CommunicationsManager; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.TskCoreException; - /** * A panel that goes in the Visualize tab of the Communications Visualization * Tool. Hosts an JGraphX mxGraphComponent that implements the communications @@ -172,7 +187,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider public void paint(Graphics graphics) { try { super.paint(graphics); - } catch (NullPointerException ex) { //NOPMD + } catch (NullPointerException ex) { //NOPMD /* We can't find the underlying cause of the NPE in * jgraphx, but it doesn't seem to cause any * noticeable problems, so we are just logging it @@ -387,6 +402,8 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider zoomLabel = new JLabel(); clearVizButton = new JButton(); jSeparator2 = new JToolBar.Separator(); + snapshotButton = new JButton(); + jSeparator3 = new JToolBar.Separator(); notificationsJFXPanel = new JFXPanel(); setLayout(new BorderLayout()); @@ -406,9 +423,9 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider placeHolderPanel.setLayout(placeHolderPanelLayout); placeHolderPanelLayout.setHorizontalGroup(placeHolderPanelLayout.createParallelGroup(GroupLayout.LEADING) .add(placeHolderPanelLayout.createSequentialGroup() - .addContainerGap(71, Short.MAX_VALUE) + .addContainerGap(268, Short.MAX_VALUE) .add(jTextArea1, GroupLayout.PREFERRED_SIZE, 424, GroupLayout.PREFERRED_SIZE) - .addContainerGap(248, Short.MAX_VALUE)) + .addContainerGap(445, Short.MAX_VALUE)) ); placeHolderPanelLayout.setVerticalGroup(placeHolderPanelLayout.createParallelGroup(GroupLayout.LEADING) .add(placeHolderPanelLayout.createSequentialGroup() @@ -505,6 +522,16 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider jSeparator2.setOrientation(SwingConstants.VERTICAL); + snapshotButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/report/images/image.png"))); // NOI18N + snapshotButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.snapshotButton.text_1")); // NOI18N + snapshotButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + snapshotButtonActionPerformed(evt); + } + }); + + jSeparator3.setOrientation(SwingConstants.VERTICAL); + GroupLayout toolbarLayout = new GroupLayout(toolbar); toolbar.setLayout(toolbarLayout); toolbarLayout.setHorizontalGroup(toolbarLayout.createParallelGroup(GroupLayout.LEADING) @@ -537,6 +564,10 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider .add(zoomActualButton, GroupLayout.PREFERRED_SIZE, 33, GroupLayout.PREFERRED_SIZE) .addPreferredGap(LayoutStyle.RELATED) .add(fitZoomButton, GroupLayout.PREFERRED_SIZE, 32, GroupLayout.PREFERRED_SIZE) + .addPreferredGap(LayoutStyle.RELATED) + .add(jSeparator3, GroupLayout.PREFERRED_SIZE, 10, GroupLayout.PREFERRED_SIZE) + .addPreferredGap(LayoutStyle.RELATED) + .add(snapshotButton) .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); toolbarLayout.setVerticalGroup(toolbarLayout.createParallelGroup(GroupLayout.LEADING) @@ -556,7 +587,9 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider .add(jLabel2) .add(zoomLabel) .add(clearVizButton) - .add(jSeparator2, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .add(jSeparator2, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .add(snapshotButton) + .add(jSeparator3, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .add(3, 3, 3)) ); @@ -648,6 +681,14 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider setCursor(Cursor.getDefaultCursor()); }//GEN-LAST:event_clearVizButtonActionPerformed + private void snapshotButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_snapshotButtonActionPerformed + try{ + handleSnapshotEvent(); + } catch (NoCurrentCaseException | IOException ex) { + // TODO something here + } + }//GEN-LAST:event_snapshotButtonActionPerformed + private void fitGraph() { graphComponent.zoomTo(1, true); mxPoint translate = graph.getView().getTranslate(); @@ -674,7 +715,115 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graphComponent.zoom((heightFactor + widthFactor) / 2.0); } + + /** + * Handle the ActionPerformed event from the Snapshot button. + * + * @throws NoCurrentCaseException + * @throws IOException + */ + @NbBundle.Messages({ + "VisualizationPanel_action_dialogs_title=Communications", + "VisualizationPanel_action_name_text=Snapshot Report", + "VisualizationPane_fileName_prompt=Enter name for the Communications Snapshot Report:", + "VisualizationPane_reportName=Communications Snapshot",}) + private void handleSnapshotEvent() throws NoCurrentCaseException, IOException { + Case currentCase = Case.getCurrentCaseThrows(); + Date generationDate = new Date(); + final String defaultReportName = FileUtil.escapeFileName(currentCase.getDisplayName() + " " + new SimpleDateFormat("MMddyyyyHHmmss").format(generationDate)); //NON_NLS + + final JTextField text = new JTextField(50); + final JPanel panel = new JPanel(new GridLayout(2, 1)); + panel.add(new JLabel(Bundle.VisualizationPane_fileName_prompt())); + panel.add(text); + + text.setText(defaultReportName); + + int result = JOptionPane.showConfirmDialog(graphComponent, panel, + Bundle.VisualizationPanel_action_dialogs_title(), JOptionPane.OK_CANCEL_OPTION); + + List filters = currentFilter.getAndFilters(); + + if (result == JOptionPane.OK_OPTION) { + String enteredReportName = text.getText(); + String reportName = StringUtils.defaultIfBlank(enteredReportName, defaultReportName); + Path reportPath = Paths.get(currentCase.getReportDirectory(), reportName); + if (!Files.exists(reportPath)) { + createReport(currentCase, reportName); + } else { + String message = String.format("Overwrite existing report?\n%s", reportName); + result = JOptionPane.showConfirmDialog(graphComponent, message, + Bundle.VisualizationPanel_action_dialogs_title(), JOptionPane.OK_CANCEL_OPTION); + + if (result == JOptionPane.OK_OPTION) { + FileUtil.deleteFileDir(reportPath.toFile()); + createReport(currentCase, reportName); + } + } + } + } + + /** + * Create the Snapshot Report. + * + * @param currentCase The current case + * @param reportName User selected name for the report + * + * @throws IOException + */ + @NbBundle.Messages({ + "VisualizationPane_DisplayName=Open Report", + "VisualizationPane_NoAssociatedEditorMessage=There is no associated editor for reports of this type or the associated application failed to launch.", + "VisualizationPane_MessageBoxTitle=Open Report Failure", + "VisualizationPane_NoOpenInEditorSupportMessage=This platform (operating system) does not support opening a file in an editor this way.", + "VisualizationPane_MissingReportFileMessage=The report file no longer exists.", + "VisualizationPane_ReportFileOpenPermissionDeniedMessage=Permission to open the report file was denied.", + "VisualizationPane_Report_Success=Report Successfully create at:\n{0}", + "VisualizationPane_Report_OK_Button=OK", + "VisualizationPane_Open_Report=Open Report",}) + private void createReport(Case currentCase, String reportName) throws IOException { + + // Create the report. + Path reportFolderPath = Paths.get(currentCase.getReportDirectory(), reportName, Bundle.VisualizationPane_reportName()); //NON_NLS + BufferedImage image = mxCellRenderer.createBufferedImage(graph, null, graph.getView().getScale(), Color.WHITE, true, null); + Path reportPath = (new CommSnapShotReportWriter(currentCase, reportFolderPath, reportName, new Date(), image, currentFilter)).writeReport(); + + // Report success to the user and offer to open the report. + String message = Bundle.VisualizationPane_Report_Success(reportPath.toAbsolutePath()); + String[] buttons = {Bundle.VisualizationPane_Report_OK_Button(), + Bundle.VisualizationPane_Open_Report()}; + + int result = JOptionPane.showOptionDialog(graphComponent, message, + Bundle.VisualizationPanel_action_dialogs_title(), + JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, + null, buttons, buttons[1]); + if (result == JOptionPane.NO_OPTION) { + try { + Desktop.getDesktop().open(reportPath.toFile()); + } catch (IOException ex) { + JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), + Bundle.VisualizationPane_NoAssociatedEditorMessage(), + Bundle.VisualizationPane_MessageBoxTitle(), + JOptionPane.ERROR_MESSAGE); + } catch (UnsupportedOperationException ex) { + JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), + Bundle.VisualizationPane_NoOpenInEditorSupportMessage(), + Bundle.VisualizationPane_MessageBoxTitle(), + JOptionPane.ERROR_MESSAGE); + } catch (IllegalArgumentException ex) { + JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), + Bundle.VisualizationPane_MissingReportFileMessage(), + Bundle.VisualizationPane_MessageBoxTitle(), + JOptionPane.ERROR_MESSAGE); + } catch (SecurityException ex) { + JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), + Bundle.VisualizationPane_ReportFileOpenPermissionDeniedMessage(), + Bundle.VisualizationPane_MessageBoxTitle(), + JOptionPane.ERROR_MESSAGE); + } + } + } // Variables declaration - do not modify//GEN-BEGIN:variables private JPanel borderLayoutPanel; @@ -687,10 +836,12 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private JLabel jLabel2; private JToolBar.Separator jSeparator1; private JToolBar.Separator jSeparator2; + private JToolBar.Separator jSeparator3; private JTextArea jTextArea1; private JFXPanel notificationsJFXPanel; private JButton organicLayoutButton; private JPanel placeHolderPanel; + private JButton snapshotButton; private JSplitPane splitPane; private JPanel toolbar; private JButton zoomActualButton; @@ -1001,4 +1152,4 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider lockedVertexModel.lock(selectedVertices); } } -} +} \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/communications/snapshot/CommSnapShotReportWriter.java b/Core/src/org/sleuthkit/autopsy/communications/snapshot/CommSnapShotReportWriter.java new file mode 100755 index 0000000000..becea9ebcd --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/snapshot/CommSnapShotReportWriter.java @@ -0,0 +1,126 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.communications.snapshot; + +import java.util.List; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.nio.file.Path; +import java.text.SimpleDateFormat; +import java.time.Instant; +import java.util.Date; +import java.util.HashMap; +import java.util.Set; +import javax.imageio.ImageIO; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.report.uisnapshot.UiSnapShotReportWriter; +import org.sleuthkit.datamodel.Account; +import org.sleuthkit.datamodel.CommunicationsFilter; +import org.sleuthkit.datamodel.CommunicationsFilter.AccountTypeFilter; +import org.sleuthkit.datamodel.CommunicationsFilter.DateRangeFilter; +import org.sleuthkit.datamodel.CommunicationsFilter.SubFilter; + +/** + * Generate and write the Communication snapshot report to disk. + */ +public class CommSnapShotReportWriter extends UiSnapShotReportWriter { + + private final BufferedImage image; + private final CommunicationsFilter filter; + + /** + * Constructor + * + * @param currentCase The Case to write a report for. + * @param reportFolderPath The Path to the folder that will contain the + * report. + * @param reportName The name of the report. + * @param generationDate The generation Date of the report. + * @param snapshot A snapshot of the view to include in the report. + */ + public CommSnapShotReportWriter(Case currentCase, Path reportFolderPath, String reportName, Date generationDate, BufferedImage snapshot, CommunicationsFilter filter) { + + super(currentCase, reportFolderPath, reportName, generationDate); + + this.image = snapshot; + this.filter = filter; + + } + + /** + * Generate and write the html page that shows the snapshot and the state of + * the CommunicationFilters + * + * @throws IOException If there is a problem writing the html file to disk. + */ + @Override + protected void writeSnapShotHTMLFile() throws IOException { + SimpleDateFormat formatter = new SimpleDateFormat("MMMMM dd, yyyy"); + + ImageIO.write(image, "png", getReportFolderPath().resolve("snapshot.png").toFile()); //NON-NLS + + //make a map of context objects to resolve template paramaters against + HashMap snapShotContext = new HashMap<>(); + snapShotContext.put("reportTitle", getReportName()); //NON-NLS + + List filters = filter.getAndFilters(); + + for (SubFilter f : filters) { + if (f.getClass().getName().equals(DateRangeFilter.class.getName())) { + long startDate = ((DateRangeFilter) f).getStartDate(); + long endDate = ((DateRangeFilter) f).getEndDate(); + + if (startDate > 0) { + + snapShotContext.put("startTime", formatter.format(new Date((Instant.ofEpochSecond(startDate)).toEpochMilli()))); //NON-NLS + } + + if (endDate > 0) { + snapShotContext.put("endTime", formatter.format(new Date((Instant.ofEpochSecond(endDate)).toEpochMilli()))); //NON-NLS + } + } else if (f.getClass().getName().equals(AccountTypeFilter.class.getName())) { + snapShotContext.put("emailSelected", "checked"); //NON-NLS + + Set types = ((AccountTypeFilter) f).getAccountTypes(); + for (Account.Type type : types) { + if (type == Account.Type.DEVICE) { + snapShotContext.put("deviceSelected", "checked"); //NON-NLS + } else if (type == Account.Type.PHONE) { + snapShotContext.put("phoneSelected", "checked"); //NON-NLS + } else if (type == Account.Type.EMAIL) { + snapShotContext.put("emailSelected", "checked"); //NON-NLS + } else if (type == Account.Type.FACEBOOK) { + snapShotContext.put("facebookSelected", "checked"); //NON-NLS + } else if (type == Account.Type.TWITTER) { + snapShotContext.put("twitterSelected", "checked"); //NON-NLS + } else if (type == Account.Type.INSTAGRAM) { + snapShotContext.put("instagramSelected", "checked"); //NON-NLS + } else if (type == Account.Type.WHATSAPP) { + snapShotContext.put("whatsAppSelected", "checked"); //NON-NLS + } else if (type == Account.Type.WEBSITE) { + snapShotContext.put("websiteSelected", "checked"); //NON-NLS + } + } + } + } + + fillTemplateAndWrite("/org/sleuthkit/autopsy/communications/snapshot/comm_snapshot_template.html", "Snapshot", snapShotContext, getReportFolderPath().resolve("snapshot.html")); //NON-NLS + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/communications/snapshot/comm_snapshot_template.html b/Core/src/org/sleuthkit/autopsy/communications/snapshot/comm_snapshot_template.html new file mode 100755 index 0000000000..9b5640c3c1 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/snapshot/comm_snapshot_template.html @@ -0,0 +1,27 @@ + + + Communications Snapshot: {{reportTitle}} + + + + +
+ Timeline Snapshot + + + + + + + + + + + + + +
FiltersDate Range
Start:{{startTime}}
End:{{endTime}}
Account Types
Device:
Phone: +
Email:
Facebook:
Twitter
Instagram
WhatsApp
MessagingApp
Website
+
+ + \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/report/images/image.png b/Core/src/org/sleuthkit/autopsy/report/images/image.png new file mode 100755 index 0000000000000000000000000000000000000000..fc3c393caa3bc4371d12d0c67ffd6d333ecf1d8e GIT binary patch literal 516 zcmV+f0{i`mP)0oSgT$J*kO*Aq9I~CW*s{G*(t$KS{OS+#aO%?udUme<*TTEO`Fr@r_QT zk=#}u-n~>Vm!+9S1PE{@3<)G~CPb<$Za;W?3+O}|+q)?*Pn355=}S(XIZmEANjZci zf5 zj<%@MX^bD1^BwlS^+AD|$dm-1wial0hwPI;CDM?Y9SXW#@w-UF0SQ8OgplRTleOB2 zUjkDS|0U9pI|lSN*EvXUa~*UIclJdZ#)Npbwh9>YT?Z;=B8|l&^t~P~om?<5Lre$+ z;%`P>SL7`djY#8Y9$wv9dv|3p) 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.report.uisnapshot; + +import com.github.mustachejava.DefaultMustacheFactory; +import com.github.mustachejava.Mustache; +import com.github.mustachejava.MustacheFactory; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Writer; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashMap; +import org.apache.commons.lang3.StringUtils; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.ingest.IngestManager; +import org.sleuthkit.autopsy.report.ReportBranding; + +/** + * Generate and write the Timeline snapshot report to disk. + */ +public abstract class UiSnapShotReportWriter { + + /** + * mustache.java template factory. + */ + private final static MustacheFactory mf = new DefaultMustacheFactory(); + + private final Case currentCase; + private final Path reportFolderPath; + private final String reportName; + private final ReportBranding reportBranding; + + private Date generationDate; + + /** + * Constructor + * + * @param currentCase The Case to write a report for. + * @param reportFolderPath The Path to the folder that will contain the + * report. + * @param reportName The name of the report. + * @param generationDate The generation Date of the report. + * @param snapshot A snapshot of the view to include in the + * report. + */ + protected UiSnapShotReportWriter(Case currentCase, Path reportFolderPath, String reportName, Date generationDate) { + this.currentCase = currentCase; + this.reportFolderPath = reportFolderPath; + this.reportName = reportName; + this.generationDate = generationDate; + + this.reportBranding = new ReportBranding(); + } + + /** + * Generate and write the report to disk. + * + * @return The Path to the "main file" of the report. This is the file that + * Autopsy shows in the results view when the Reports Node is + * selected in the DirectoryTree. + * + * @throws IOException If there is a problem writing the report. + */ + public Path writeReport() throws IOException { + //ensure directory exists + Files.createDirectories(reportFolderPath); + + copyResources(); + + writeSummaryHTML(); + writeSnapShotHTMLFile(); + return writeIndexHTML(); + } + + protected String getReportName(){ + return reportName; + } + + protected Path getReportFolderPath(){ + return reportFolderPath; + } + + /** + * Generate and write the html page that shows the snapshot and the state of + * any filters. + * + * @throws IOException If there is a problem writing the html file to disk. + */ + protected abstract void writeSnapShotHTMLFile() throws IOException ; + + /** + * Generate and write the main html page with frames for navigation on the + * left and content on the right. + * + * @return The Path of the written html file. + * + * @throws IOException If there is a problem writing the html file to disk. + */ + private Path writeIndexHTML() throws IOException { + //make a map of context objects to resolve template paramaters against + HashMap indexContext = new HashMap<>(); + indexContext.put("reportBranding", reportBranding); //NON-NLS + indexContext.put("reportName", reportName); //NON-NLS + Path reportIndexFile = reportFolderPath.resolve("index.html"); //NON-NLS + + fillTemplateAndWrite("/org/sleuthkit/autopsy/report/uisnapshot/index_template.html", "Index", indexContext, reportIndexFile); //NON-NLS + return reportIndexFile; + } + + /** + * * Generate and write the summary of the current case for this report. + * + * @throws IOException If there is a problem writing the html file to disk. + */ + private void writeSummaryHTML() throws IOException { + //make a map of context objects to resolve template paramaters against + HashMap summaryContext = new HashMap<>(); + summaryContext.put("reportName", reportName); //NON-NLS + summaryContext.put("reportBranding", reportBranding); //NON-NLS + summaryContext.put("generationDateTime", new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(generationDate)); //NON-NLS + summaryContext.put("ingestRunning", IngestManager.getInstance().isIngestRunning()); //NON-NLS + summaryContext.put("currentCase", currentCase); //NON-NLS + String agencyLogo = "agency_logo.png"; //default name for agency logo. + if (StringUtils.isNotBlank(reportBranding.getAgencyLogoPath())){ + agencyLogo = Paths.get(reportBranding.getAgencyLogoPath()).getFileName().toString(); + } + summaryContext.put("agencyLogoFileName", agencyLogo); + fillTemplateAndWrite("/org/sleuthkit/autopsy/report/uisnapshot/summary_template.html", "Summary", summaryContext, reportFolderPath.resolve("summary.html")); //NON-NLS + } + + /** + * Fill in the mustache template at the given location using the values from + * the given context object and save it to the given outPutFile. + * + * @param templateLocation The location of the template. suitible for use + * with Class.getResourceAsStream + * @param templateName The name of the tempalte. (Used by mustache to + * cache templates?) + * @param context The contect to use to fill in the template + * values. + * @param outPutFile The filled in tempalte will be saced at this + * Path. + * + * @throws IOException If there is a problem saving the filled in template + * to disk. + */ + protected void fillTemplateAndWrite(final String templateLocation, final String templateName, Object context, final Path outPutFile) throws IOException { + + Mustache summaryMustache = mf.compile(new InputStreamReader(UiSnapShotReportWriter.class.getResourceAsStream(templateLocation)), templateName); + try (Writer writer = Files.newBufferedWriter(outPutFile, Charset.forName("UTF-8"))) { //NON-NLS + summaryMustache.execute(writer, context); + } + } + + /** + * Copy static resources (static html, css, images, etc) to the reports + * folder. + * + * @throws IOException If there is a problem copying the resources. + */ + private void copyResources() throws IOException { + + //pull generator and agency logos from branding + String generatorLogoPath = reportBranding.getGeneratorLogoPath(); + if (StringUtils.isNotBlank(generatorLogoPath)) { + Files.copy(Files.newInputStream(Paths.get(generatorLogoPath)), reportFolderPath.resolve("generator_logo.png")); //NON-NLS + } + String agencyLogoPath = reportBranding.getAgencyLogoPath(); + if (StringUtils.isNotBlank(agencyLogoPath)) { + Files.copy(Files.newInputStream(Paths.get(agencyLogoPath)), reportFolderPath.resolve(Paths.get(reportBranding.getAgencyLogoPath()).getFileName())); //NON-NLS + } + + //copy navigation html + try (InputStream navStream = UiSnapShotReportWriter.class.getResourceAsStream("/org/sleuthkit/autopsy/report/uisnapshot/navigation.html")) { //NON-NLS + Files.copy(navStream, reportFolderPath.resolve("nav.html")); //NON-NLS + } + //copy favicon + if (StringUtils.isBlank(agencyLogoPath)) { + // use default Autopsy icon if custom icon is not set + try (InputStream faviconStream = UiSnapShotReportWriter.class.getResourceAsStream("/org/sleuthkit/autopsy/report/images/favicon.ico")) { //NON-NLS + Files.copy(faviconStream, reportFolderPath.resolve("favicon.ico")); //NON-NLS + } + } else { + Files.copy(Files.newInputStream(Paths.get(agencyLogoPath)), reportFolderPath.resolve("favicon.ico")); //NON-NLS + } + + //copy report summary icon + try (InputStream summaryStream = UiSnapShotReportWriter.class.getResourceAsStream("/org/sleuthkit/autopsy/report/images/summary.png")) { //NON-NLS + Files.copy(summaryStream, reportFolderPath.resolve("summary.png")); //NON-NLS + } + //copy snapshot icon + try (InputStream snapshotIconStream = UiSnapShotReportWriter.class.getResourceAsStream("/org/sleuthkit/autopsy/report/images/image.png")) { //NON-NLS + Files.copy(snapshotIconStream, reportFolderPath.resolve("snapshot_icon.png")); //NON-NLS + } + //copy main report css + try (InputStream resource = UiSnapShotReportWriter.class.getResourceAsStream("/org/sleuthkit/autopsy/report/uisnapshot/index.css")) { //NON-NLS + Files.copy(resource, reportFolderPath.resolve("index.css")); //NON-NLS + } + //copy summary css + try (InputStream resource = UiSnapShotReportWriter.class.getResourceAsStream("/org/sleuthkit/autopsy/report/uisnapshot/summary.css")) { //NON-NLS + Files.copy(resource, reportFolderPath.resolve("summary.css")); //NON-NLS + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/report/uisnapshot/index.css b/Core/src/org/sleuthkit/autopsy/report/uisnapshot/index.css new file mode 100755 index 0000000000..cbb4947792 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/report/uisnapshot/index.css @@ -0,0 +1,19 @@ +body {margin: 0px; padding: 0px; background: #FFFFFF; font: 13px/20px Arial, Helvetica, sans-serif; color: #535353;} +#snapshot{max-width:800; max-height:600} +#content {padding: 30px;} +#header {width:100%; padding: 10px; line-height: 25px; background: #07A; color: #FFF; font-size: 20px;} +h1 {font-size: 20px; font-weight: normal; color: #07A; padding: 0 0 7px 0; margin-top: 25px; border-bottom: 1px solid #D6D6D6;} +h2 {font-size: 20px; font-weight: bolder; color: #07A;} +h3 {font-size: 16px; color: #07A;} +h4 {background: #07A; color: #FFF; font-size: 16px; margin: 0 0 0 25px; padding: 0; padding-left: 15px;} +ul.nav {list-style-type: none; line-height: 35px; padding: 0px; margin-left: 15px;} +ul li a {font-size: 14px; color: #444; text-decoration: none; padding-left: 25px;} +ul li a:hover {text-decoration: underline;} +p {margin: 0 0 20px 0;} +table {white-space:nowrap; min-width: 800px; padding: 2; margin: 0; border-collapse: collapse; border-bottom: 2px solid #e5e5e5;} +.keyword_list table {margin: 0 0 25px 25px; border-bottom: 2px solid #dedede;} +table th {white-space:nowrap; display: table-cell; text-align: center; padding: 2px 4px; background: #e5e5e5; color: #777; font-size: 11px; text-shadow: #e9f9fd 0 1px 0; border-top: 1px solid #dedede; border-bottom: 2px solid #e5e5e5;} +table .left_align_cell{display: table-cell; padding: 2px 4px; font: 13px/20px Arial, Helvetica, sans-serif; min-width: 125px; overflow: auto; text-align: left; } +table .right_align_cell{display: table-cell; padding: 2px 4px; font: 13px/20px Arial, Helvetica, sans-serif; min-width: 125px; overflow: auto; text-align: right; } +table td {white-space:nowrap; display: table-cell; padding: 2px 3px; font: 13px/20px Arial, Helvetica, sans-serif; min-width: 125px; overflow: auto; text-align:left; } +table tr:nth-child(even) td {background: #f3f3f3;} \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/report/uisnapshot/index_template.html b/Core/src/org/sleuthkit/autopsy/report/uisnapshot/index_template.html new file mode 100755 index 0000000000..91a9e37698 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/report/uisnapshot/index_template.html @@ -0,0 +1,14 @@ + + + {{reportBranding.getReportTitle}} for {{reportName}} + + + + + + + Your browser is not compatible with our frame setup.<br /> + Please see <a href="nav.html">the navigation page</a> for links,<br /> + and <a href="summary.html">the summary page</a> for a case summary. + + diff --git a/Core/src/org/sleuthkit/autopsy/report/uisnapshot/navigation.html b/Core/src/org/sleuthkit/autopsy/report/uisnapshot/navigation.html new file mode 100755 index 0000000000..bfcf061e34 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/report/uisnapshot/navigation.html @@ -0,0 +1,16 @@ + + + Report Navigation + + + + +
+

Report Navigation

+ +
+ + \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/report/uisnapshot/summary.css b/Core/src/org/sleuthkit/autopsy/report/uisnapshot/summary.css new file mode 100755 index 0000000000..489c19079a --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/report/uisnapshot/summary.css @@ -0,0 +1,14 @@ +body { padding: 0px; margin: 0px; font: 13px/20px Arial, Helvetica, sans-serif; color: #535353; } +#wrapper { width: 90%; margin: 0px auto; margin-top: 35px; } +h1 { color: #07A; font-size: 36px; line-height: 42px; font-weight: normal; margin: 0px; border-bottom: 1px solid #81B9DB; } +h1 span { color: #F00; display: block; font-size: 16px; font-weight: bold; line-height: 22px;} +h2 { padding: 0 0 3px 0; margin: 0px; color: #07A; font-weight: normal; border-bottom: 1px dotted #81B9DB; } +table td { padding-right: 25px; } +p.subheadding { padding: 0px; margin: 0px; font-size: 11px; color: #B5B5B5; } +.title { width: 660px; margin-bottom: 50px; } +.left { float: left; width: 250px; margin-top: 20px; text-align: center; } +.left img { max-width: 250px; max-height: 250px; min-width: 200px; min-height: 200px; } +.right { float: right; width: 385px; margin-top: 25px; font-size: 14px; } +.clear { clear: both; } +.info p { padding: 3px 10px; background: #e5e5e5; color: #777; font-size: 12px; font-weight: bold; text-shadow: #e9f9fd 0 1px 0; border-top: 1px solid #dedede; border-bottom: 2px solid #dedede; } +.info table { margin: 0 25px 20px 25px; } diff --git a/Core/src/org/sleuthkit/autopsy/report/uisnapshot/summary_template.html b/Core/src/org/sleuthkit/autopsy/report/uisnapshot/summary_template.html new file mode 100755 index 0000000000..50aa65ff8a --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/report/uisnapshot/summary_template.html @@ -0,0 +1,55 @@ + + + Case Summary + + + + + +
+

{{reportBranding.getReportTitle}}: {{reportName}}{{#ingestRunning}}Warning, this report was run before ingest services completed!{{/ingestRunning}}

+ +

Report generated on {{generationDateTime}}

+
+ {{#reportBranding.getAgencyLogoPath}} +
+ +
+
+ {{/reportBranding.getAgencyLogoPath}} + {{^reportBranding.getAgencyLogoPath}} +
+ {{/reportBranding.getAgencyLogoPath}} + + + + + +
Case:{{currentCase.getName}}
Case Number:{{currentCase.getCaseNumber}}{{^currentCase.getCaseNumber}}No case number{{/currentCase.getCaseNumber}}
Examiner:{{currentCase.getExaminer}}{{^currentCase.getExaminer}}No examiner{{/currentCase.getExaminer}}
Number of Images:{{currentCase.getDataSources.size}}
+
+
+
+

Image Information:

+ {{#currentCase.getDataSources}} +

{{getName}}

+ {{#getTimeZone}} + + + {{#getPaths}} + + {{/getPaths}} +
Timezone:{{getTimeZone}}
Path:{{toString}}
+ {{/getTimeZone}} + {{/currentCase.getDataSources}} +
+ {{#reportBranding.getGeneratorLogoPath}} +
+ +
+ {{/reportBranding.getGeneratorLogoPath}} +
+ {{#reportBranding.getReportFooter}} +

{{toString}}

+ {{/reportBranding.getReportFooter}} +
+ \ No newline at end of file From 07460ea7b4f695a66d4bc14eff44916663b712ec Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Thu, 14 Mar 2019 12:54:27 -0400 Subject: [PATCH 23/59] Modified timeline snapshot report to use common parent class, removed no longer needed snapshot report files --- .../communications/Bundle.properties-MERGED | 10 + .../communications/VisualizationPanel.java | 1 + .../snapshot/SnapShotReportWriter.java | 183 ++---------------- .../autopsy/timeline/snapshot/index.css | 19 -- .../timeline/snapshot/index_template.html | 14 -- .../autopsy/timeline/snapshot/navigation.html | 16 -- .../autopsy/timeline/snapshot/summary.css | 14 -- .../timeline/snapshot/summary_template.html | 55 ------ 8 files changed, 22 insertions(+), 290 deletions(-) delete mode 100644 Core/src/org/sleuthkit/autopsy/timeline/snapshot/index.css delete mode 100644 Core/src/org/sleuthkit/autopsy/timeline/snapshot/index_template.html delete mode 100644 Core/src/org/sleuthkit/autopsy/timeline/snapshot/navigation.html delete mode 100644 Core/src/org/sleuthkit/autopsy/timeline/snapshot/summary.css delete mode 100644 Core/src/org/sleuthkit/autopsy/timeline/snapshot/summary_template.html diff --git a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties-MERGED index 949d7876e1..f88a5ffd81 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties-MERGED @@ -32,7 +32,17 @@ ResetAndPinAccountsAction.singularText=Visualize Only Selected Account UnpinAccountsAction.pluralText=Remove Selected Accounts UnpinAccountsAction.singularText=Remove Selected Account VisalizationPanel.paintingError=Problem painting visualization. +VisualizationPane_DisplayName=Open Report VisualizationPane_fileName_prompt=Enter name for the Communications Snapshot Report: +VisualizationPane_MessageBoxTitle=Open Report Failure +VisualizationPane_MissingReportFileMessage=The report file no longer exists. +VisualizationPane_NoAssociatedEditorMessage=There is no associated editor for reports of this type or the associated application failed to launch. +VisualizationPane_NoOpenInEditorSupportMessage=This platform (operating system) does not support opening a file in an editor this way. +VisualizationPane_Open_Report=Open Report +VisualizationPane_Report_OK_Button=OK +# {0} - report path +VisualizationPane_Report_Success=Report Successfully create at:\n{0} +VisualizationPane_ReportFileOpenPermissionDeniedMessage=Permission to open the report file was denied. VisualizationPane_reportName=Communications Snapshot VisualizationPanel.cancelButton.text=Cancel VisualizationPanel.computingLayout=Computing Layout diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index a3c0fad868..20f52937ef 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -779,6 +779,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider "VisualizationPane_NoOpenInEditorSupportMessage=This platform (operating system) does not support opening a file in an editor this way.", "VisualizationPane_MissingReportFileMessage=The report file no longer exists.", "VisualizationPane_ReportFileOpenPermissionDeniedMessage=Permission to open the report file was denied.", + "# {0} - report path", "VisualizationPane_Report_Success=Report Successfully create at:\n{0}", "VisualizationPane_Report_OK_Button=OK", "VisualizationPane_Open_Report=Open Report",}) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/snapshot/SnapShotReportWriter.java b/Core/src/org/sleuthkit/autopsy/timeline/snapshot/SnapShotReportWriter.java index a9bce0acb8..961f357daa 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/snapshot/SnapShotReportWriter.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/snapshot/SnapShotReportWriter.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2016 Basis Technology Corp. + * Copyright 2016 - 2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,46 +18,23 @@ */ package org.sleuthkit.autopsy.timeline.snapshot; -import com.github.mustachejava.DefaultMustacheFactory; -import com.github.mustachejava.Mustache; -import com.github.mustachejava.MustacheFactory; import java.awt.image.BufferedImage; import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Writer; -import java.nio.charset.Charset; -import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; -import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import javax.imageio.ImageIO; -import org.apache.commons.lang3.StringUtils; import org.joda.time.format.DateTimeFormat; import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.ingest.IngestManager; -import org.sleuthkit.autopsy.report.ReportBranding; +import org.sleuthkit.autopsy.report.uisnapshot.UiSnapShotReportWriter; import org.sleuthkit.autopsy.timeline.zooming.ZoomParams; /** * Generate and write the Timeline snapshot report to disk. */ -public class SnapShotReportWriter { - - /** - * mustache.java template factory. - */ - private final static MustacheFactory mf = new DefaultMustacheFactory(); - - private final Case currentCase; - private final Path reportFolderPath; - private final String reportName; - private final ReportBranding reportBranding; +public class SnapShotReportWriter extends UiSnapShotReportWriter{ private final ZoomParams zoomParams; - private final Date generationDate; private final BufferedImage image; /** @@ -74,37 +51,9 @@ public class SnapShotReportWriter { * report. */ public SnapShotReportWriter(Case currentCase, Path reportFolderPath, String reportName, ZoomParams zoomParams, Date generationDate, BufferedImage snapshot) { - this.currentCase = currentCase; - this.reportFolderPath = reportFolderPath; - this.reportName = reportName; + super(currentCase, reportFolderPath, reportName, generationDate); this.zoomParams = zoomParams; - this.generationDate = generationDate; this.image = snapshot; - - this.reportBranding = new ReportBranding(); - } - - /** - * Generate and write the report to disk. - * - * @return The Path to the "main file" of the report. This is the file that - * Autopsy shows in the results view when the Reports Node is - * selected in the DirectoryTree. - * - * @throws IOException If there is a problem writing the report. - */ - public Path writeReport() throws IOException { - //ensure directory exists - Files.createDirectories(reportFolderPath); - - //save the snapshot in the report directory - ImageIO.write(image, "png", reportFolderPath.resolve("snapshot.png").toFile()); //NON-NLS - - copyResources(); - - writeSummaryHTML(); - writeSnapShotHTMLFile(); - return writeIndexHTML(); } /** @@ -113,128 +62,18 @@ public class SnapShotReportWriter { * * @throws IOException If there is a problem writing the html file to disk. */ - private void writeSnapShotHTMLFile() throws IOException { + @Override + protected void writeSnapShotHTMLFile() throws IOException { + //save the snapshot in the report directory + ImageIO.write(image, "png", getReportFolderPath().resolve("snapshot.png").toFile()); //NON-NLS + //make a map of context objects to resolve template paramaters against HashMap snapShotContext = new HashMap<>(); - snapShotContext.put("reportTitle", reportName); //NON-NLS + snapShotContext.put("reportTitle", getReportName()); //NON-NLS snapShotContext.put("startTime", zoomParams.getTimeRange().getStart().toString(DateTimeFormat.fullDateTime())); //NON-NLS snapShotContext.put("endTime", zoomParams.getTimeRange().getEnd().toString(DateTimeFormat.fullDateTime())); //NON-NLS snapShotContext.put("zoomParams", zoomParams); //NON-NLS - fillTemplateAndWrite("/org/sleuthkit/autopsy/timeline/snapshot/snapshot_template.html", "Snapshot", snapShotContext, reportFolderPath.resolve("snapshot.html")); //NON-NLS - } - - /** - * Generate and write the main html page with frames for navigation on the - * left and content on the right. - * - * @return The Path of the written html file. - * - * @throws IOException If there is a problem writing the html file to disk. - */ - private Path writeIndexHTML() throws IOException { - //make a map of context objects to resolve template paramaters against - HashMap indexContext = new HashMap<>(); - indexContext.put("reportBranding", reportBranding); //NON-NLS - indexContext.put("reportName", reportName); //NON-NLS - Path reportIndexFile = reportFolderPath.resolve("index.html"); //NON-NLS - - fillTemplateAndWrite("/org/sleuthkit/autopsy/timeline/snapshot/index_template.html", "Index", indexContext, reportIndexFile); //NON-NLS - return reportIndexFile; - } - - /** - * * Generate and write the summary of the current case for this report. - * - * @throws IOException If there is a problem writing the html file to disk. - */ - private void writeSummaryHTML() throws IOException { - //make a map of context objects to resolve template paramaters against - HashMap summaryContext = new HashMap<>(); - summaryContext.put("reportName", reportName); //NON-NLS - summaryContext.put("reportBranding", reportBranding); //NON-NLS - summaryContext.put("generationDateTime", new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(generationDate)); //NON-NLS - summaryContext.put("ingestRunning", IngestManager.getInstance().isIngestRunning()); //NON-NLS - summaryContext.put("currentCase", currentCase); //NON-NLS - String agencyLogo = "agency_logo.png"; //default name for agency logo. - if (StringUtils.isNotBlank(reportBranding.getAgencyLogoPath())){ - agencyLogo = Paths.get(reportBranding.getAgencyLogoPath()).getFileName().toString(); - } - summaryContext.put("agencyLogoFileName", agencyLogo); - fillTemplateAndWrite("/org/sleuthkit/autopsy/timeline/snapshot/summary_template.html", "Summary", summaryContext, reportFolderPath.resolve("summary.html")); //NON-NLS - } - - /** - * Fill in the mustache template at the given location using the values from - * the given context object and save it to the given outPutFile. - * - * @param templateLocation The location of the template. suitible for use - * with Class.getResourceAsStream - * @param templateName The name of the tempalte. (Used by mustache to - * cache templates?) - * @param context The contect to use to fill in the template - * values. - * @param outPutFile The filled in tempalte will be saced at this - * Path. - * - * @throws IOException If there is a problem saving the filled in template - * to disk. - */ - private void fillTemplateAndWrite(final String templateLocation, final String templateName, Object context, final Path outPutFile) throws IOException { - - Mustache summaryMustache = mf.compile(new InputStreamReader(SnapShotReportWriter.class.getResourceAsStream(templateLocation)), templateName); - try (Writer writer = Files.newBufferedWriter(outPutFile, Charset.forName("UTF-8"))) { //NON-NLS - summaryMustache.execute(writer, context); - } - } - - /** - * Copy static resources (static html, css, images, etc) to the reports - * folder. - * - * @throws IOException If there is a problem copying the resources. - */ - private void copyResources() throws IOException { - - //pull generator and agency logos from branding - String generatorLogoPath = reportBranding.getGeneratorLogoPath(); - if (StringUtils.isNotBlank(generatorLogoPath)) { - Files.copy(Files.newInputStream(Paths.get(generatorLogoPath)), reportFolderPath.resolve("generator_logo.png")); //NON-NLS - } - String agencyLogoPath = reportBranding.getAgencyLogoPath(); - if (StringUtils.isNotBlank(agencyLogoPath)) { - Files.copy(Files.newInputStream(Paths.get(agencyLogoPath)), reportFolderPath.resolve(Paths.get(reportBranding.getAgencyLogoPath()).getFileName())); //NON-NLS - } - - //copy navigation html - try (InputStream navStream = SnapShotReportWriter.class.getResourceAsStream("/org/sleuthkit/autopsy/timeline/snapshot/navigation.html")) { //NON-NLS - Files.copy(navStream, reportFolderPath.resolve("nav.html")); //NON-NLS - } - //copy favicon - if (StringUtils.isBlank(agencyLogoPath)) { - // use default Autopsy icon if custom icon is not set - try (InputStream faviconStream = SnapShotReportWriter.class.getResourceAsStream("/org/sleuthkit/autopsy/report/images/favicon.ico")) { //NON-NLS - Files.copy(faviconStream, reportFolderPath.resolve("favicon.ico")); //NON-NLS - } - } else { - Files.copy(Files.newInputStream(Paths.get(agencyLogoPath)), reportFolderPath.resolve("favicon.ico")); //NON-NLS - } - - //copy report summary icon - try (InputStream summaryStream = SnapShotReportWriter.class.getResourceAsStream("/org/sleuthkit/autopsy/report/images/summary.png")) { //NON-NLS - Files.copy(summaryStream, reportFolderPath.resolve("summary.png")); //NON-NLS - } - //copy snapshot icon - try (InputStream snapshotIconStream = SnapShotReportWriter.class.getResourceAsStream("/org/sleuthkit/autopsy/timeline/images/image.png")) { //NON-NLS - Files.copy(snapshotIconStream, reportFolderPath.resolve("snapshot_icon.png")); //NON-NLS - } - //copy main report css - try (InputStream resource = SnapShotReportWriter.class.getResourceAsStream("/org/sleuthkit/autopsy/timeline/snapshot/index.css")) { //NON-NLS - Files.copy(resource, reportFolderPath.resolve("index.css")); //NON-NLS - } - //copy summary css - try (InputStream resource = SnapShotReportWriter.class.getResourceAsStream("/org/sleuthkit/autopsy/timeline/snapshot/summary.css")) { //NON-NLS - Files.copy(resource, reportFolderPath.resolve("summary.css")); //NON-NLS - } + fillTemplateAndWrite("/org/sleuthkit/autopsy/timeline/snapshot/snapshot_template.html", "Snapshot", snapShotContext, getReportFolderPath().resolve("snapshot.html")); //NON-NLS } } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/snapshot/index.css b/Core/src/org/sleuthkit/autopsy/timeline/snapshot/index.css deleted file mode 100644 index cbb4947792..0000000000 --- a/Core/src/org/sleuthkit/autopsy/timeline/snapshot/index.css +++ /dev/null @@ -1,19 +0,0 @@ -body {margin: 0px; padding: 0px; background: #FFFFFF; font: 13px/20px Arial, Helvetica, sans-serif; color: #535353;} -#snapshot{max-width:800; max-height:600} -#content {padding: 30px;} -#header {width:100%; padding: 10px; line-height: 25px; background: #07A; color: #FFF; font-size: 20px;} -h1 {font-size: 20px; font-weight: normal; color: #07A; padding: 0 0 7px 0; margin-top: 25px; border-bottom: 1px solid #D6D6D6;} -h2 {font-size: 20px; font-weight: bolder; color: #07A;} -h3 {font-size: 16px; color: #07A;} -h4 {background: #07A; color: #FFF; font-size: 16px; margin: 0 0 0 25px; padding: 0; padding-left: 15px;} -ul.nav {list-style-type: none; line-height: 35px; padding: 0px; margin-left: 15px;} -ul li a {font-size: 14px; color: #444; text-decoration: none; padding-left: 25px;} -ul li a:hover {text-decoration: underline;} -p {margin: 0 0 20px 0;} -table {white-space:nowrap; min-width: 800px; padding: 2; margin: 0; border-collapse: collapse; border-bottom: 2px solid #e5e5e5;} -.keyword_list table {margin: 0 0 25px 25px; border-bottom: 2px solid #dedede;} -table th {white-space:nowrap; display: table-cell; text-align: center; padding: 2px 4px; background: #e5e5e5; color: #777; font-size: 11px; text-shadow: #e9f9fd 0 1px 0; border-top: 1px solid #dedede; border-bottom: 2px solid #e5e5e5;} -table .left_align_cell{display: table-cell; padding: 2px 4px; font: 13px/20px Arial, Helvetica, sans-serif; min-width: 125px; overflow: auto; text-align: left; } -table .right_align_cell{display: table-cell; padding: 2px 4px; font: 13px/20px Arial, Helvetica, sans-serif; min-width: 125px; overflow: auto; text-align: right; } -table td {white-space:nowrap; display: table-cell; padding: 2px 3px; font: 13px/20px Arial, Helvetica, sans-serif; min-width: 125px; overflow: auto; text-align:left; } -table tr:nth-child(even) td {background: #f3f3f3;} \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/timeline/snapshot/index_template.html b/Core/src/org/sleuthkit/autopsy/timeline/snapshot/index_template.html deleted file mode 100644 index 91a9e37698..0000000000 --- a/Core/src/org/sleuthkit/autopsy/timeline/snapshot/index_template.html +++ /dev/null @@ -1,14 +0,0 @@ - - - {{reportBranding.getReportTitle}} for {{reportName}} - - - - - - - Your browser is not compatible with our frame setup.<br /> - Please see <a href="nav.html">the navigation page</a> for links,<br /> - and <a href="summary.html">the summary page</a> for a case summary. - - diff --git a/Core/src/org/sleuthkit/autopsy/timeline/snapshot/navigation.html b/Core/src/org/sleuthkit/autopsy/timeline/snapshot/navigation.html deleted file mode 100644 index 02b6705cfb..0000000000 --- a/Core/src/org/sleuthkit/autopsy/timeline/snapshot/navigation.html +++ /dev/null @@ -1,16 +0,0 @@ - - - Report Navigation - - - - -
-

Report Navigation

- -
- - \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/timeline/snapshot/summary.css b/Core/src/org/sleuthkit/autopsy/timeline/snapshot/summary.css deleted file mode 100644 index 489c19079a..0000000000 --- a/Core/src/org/sleuthkit/autopsy/timeline/snapshot/summary.css +++ /dev/null @@ -1,14 +0,0 @@ -body { padding: 0px; margin: 0px; font: 13px/20px Arial, Helvetica, sans-serif; color: #535353; } -#wrapper { width: 90%; margin: 0px auto; margin-top: 35px; } -h1 { color: #07A; font-size: 36px; line-height: 42px; font-weight: normal; margin: 0px; border-bottom: 1px solid #81B9DB; } -h1 span { color: #F00; display: block; font-size: 16px; font-weight: bold; line-height: 22px;} -h2 { padding: 0 0 3px 0; margin: 0px; color: #07A; font-weight: normal; border-bottom: 1px dotted #81B9DB; } -table td { padding-right: 25px; } -p.subheadding { padding: 0px; margin: 0px; font-size: 11px; color: #B5B5B5; } -.title { width: 660px; margin-bottom: 50px; } -.left { float: left; width: 250px; margin-top: 20px; text-align: center; } -.left img { max-width: 250px; max-height: 250px; min-width: 200px; min-height: 200px; } -.right { float: right; width: 385px; margin-top: 25px; font-size: 14px; } -.clear { clear: both; } -.info p { padding: 3px 10px; background: #e5e5e5; color: #777; font-size: 12px; font-weight: bold; text-shadow: #e9f9fd 0 1px 0; border-top: 1px solid #dedede; border-bottom: 2px solid #dedede; } -.info table { margin: 0 25px 20px 25px; } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/snapshot/summary_template.html b/Core/src/org/sleuthkit/autopsy/timeline/snapshot/summary_template.html deleted file mode 100644 index 2d1e37a2a8..0000000000 --- a/Core/src/org/sleuthkit/autopsy/timeline/snapshot/summary_template.html +++ /dev/null @@ -1,55 +0,0 @@ - - - Case Summary - - - - - -
-

{{reportBranding.getReportTitle}}: {{reportName}}{{#ingestRunning}}Warning, this report was run before ingest services completed!{{/ingestRunning}}

- -

Timeline Report generated on {{generationDateTime}}

-
- {{#reportBranding.getAgencyLogoPath}} -
- -
-
- {{/reportBranding.getAgencyLogoPath}} - {{^reportBranding.getAgencyLogoPath}} -
- {{/reportBranding.getAgencyLogoPath}} - - - - - -
Case:{{currentCase.getName}}
Case Number:{{currentCase.getCaseNumber}}{{^currentCase.getCaseNumber}}No case number{{/currentCase.getCaseNumber}}
Examiner:{{currentCase.getExaminer}}{{^currentCase.getExaminer}}No examiner{{/currentCase.getExaminer}}
Number of Images:{{currentCase.getDataSources.size}}
-
-
-
-

Image Information:

- {{#currentCase.getDataSources}} -

{{getName}}

- {{#getTimeZone}} - - - {{#getPaths}} - - {{/getPaths}} -
Timezone:{{getTimeZone}}
Path:{{toString}}
- {{/getTimeZone}} - {{/currentCase.getDataSources}} -
- {{#reportBranding.getGeneratorLogoPath}} -
- -
- {{/reportBranding.getGeneratorLogoPath}} -
- {{#reportBranding.getReportFooter}} -

{{toString}}

- {{/reportBranding.getReportFooter}} -
- \ No newline at end of file From b8fb023f44f8b9074ea901986ca343836f35c2d2 Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Fri, 15 Mar 2019 11:13:02 -0400 Subject: [PATCH 24/59] Updated based on review comments --- .../communications/Bundle.properties-MERGED | 6 +++ .../communications/VisualizationPanel.java | 48 +++++++++++++------ .../snapshot/comm_snapshot_template.html | 28 +++++------ .../uisnapshot/UiSnapShotReportWriter.java | 46 ++++++++---------- .../keywordsearch/Bundle.properties-MERGED | 3 +- .../recentactivity/Bundle.properties-MERGED | 2 + 6 files changed, 78 insertions(+), 55 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties-MERGED index f88a5ffd81..a4be4813c9 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties-MERGED @@ -32,6 +32,9 @@ ResetAndPinAccountsAction.singularText=Visualize Only Selected Account UnpinAccountsAction.pluralText=Remove Selected Accounts UnpinAccountsAction.singularText=Remove Selected Account VisalizationPanel.paintingError=Problem painting visualization. +# {0} - default name +VisualizationPane_accept_defaultName=Press OK to accept default report name: {0} +VisualizationPane_blank_report_title=Blank Report Name VisualizationPane_DisplayName=Open Report VisualizationPane_fileName_prompt=Enter name for the Communications Snapshot Report: VisualizationPane_MessageBoxTitle=Open Report Failure @@ -39,6 +42,8 @@ VisualizationPane_MissingReportFileMessage=The report file no longer exists. VisualizationPane_NoAssociatedEditorMessage=There is no associated editor for reports of this type or the associated application failed to launch. VisualizationPane_NoOpenInEditorSupportMessage=This platform (operating system) does not support opening a file in an editor this way. VisualizationPane_Open_Report=Open Report +# {0} - report name +VisualizationPane_overrite_exiting=Overwrite existing report?\n{0} VisualizationPane_Report_OK_Button=OK # {0} - report path VisualizationPane_Report_Success=Report Successfully create at:\n{0} @@ -80,3 +85,4 @@ VisualizationPanel.clearVizButton.text_1=Clear Viz. VisualizationPanel.snapshotButton.text_1=Snapshot Report VisualizationPanel_action_dialogs_title=Communications VisualizationPanel_action_name_text=Snapshot Report +VisualizationPanel_snapshot_report_failure=Snapshot report not created. An error occurred during creation. diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 20f52937ef..f8ff5aaf69 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -681,11 +681,19 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider setCursor(Cursor.getDefaultCursor()); }//GEN-LAST:event_clearVizButtonActionPerformed + @NbBundle.Messages({ + "VisualizationPanel_snapshot_report_failure=Snapshot report not created. An error occurred during creation." + }) private void snapshotButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_snapshotButtonActionPerformed - try{ + try { handleSnapshotEvent(); } catch (NoCurrentCaseException | IOException ex) { - // TODO something here + logger.log(Level.SEVERE, "Unable to create communications snapsot report", ex); //NON-NLS + + Platform.runLater(() + -> Notifications.create().owner(notificationsJFXPanel.getScene().getWindow()) + .text(Bundle.VisualizationPanel_snapshot_report_failure()) + .showWarning()); } }//GEN-LAST:event_snapshotButtonActionPerformed @@ -726,7 +734,13 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider "VisualizationPanel_action_dialogs_title=Communications", "VisualizationPanel_action_name_text=Snapshot Report", "VisualizationPane_fileName_prompt=Enter name for the Communications Snapshot Report:", - "VisualizationPane_reportName=Communications Snapshot",}) + "VisualizationPane_reportName=Communications Snapshot", + "# {0} - default name", + "VisualizationPane_accept_defaultName=Press OK to accept default report name: {0}", + "VisualizationPane_blank_report_title=Blank Report Name", + "# {0} - report name", + "VisualizationPane_overrite_exiting=Overwrite existing report?\n{0}" + }) private void handleSnapshotEvent() throws NoCurrentCaseException, IOException { Case currentCase = Case.getCurrentCaseThrows(); Date generationDate = new Date(); @@ -743,23 +757,28 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider int result = JOptionPane.showConfirmDialog(graphComponent, panel, Bundle.VisualizationPanel_action_dialogs_title(), JOptionPane.OK_CANCEL_OPTION); - List filters = currentFilter.getAndFilters(); - if (result == JOptionPane.OK_OPTION) { String enteredReportName = text.getText(); + + if(enteredReportName.isEmpty()){ + result = JOptionPane.showConfirmDialog(graphComponent, Bundle.VisualizationPane_accept_defaultName(defaultReportName), Bundle.VisualizationPane_blank_report_title(), JOptionPane.OK_CANCEL_OPTION); + if(result != JOptionPane.OK_OPTION) { + return; + } + } + String reportName = StringUtils.defaultIfBlank(enteredReportName, defaultReportName); Path reportPath = Paths.get(currentCase.getReportDirectory(), reportName); - if (!Files.exists(reportPath)) { - createReport(currentCase, reportName); - } else { - String message = String.format("Overwrite existing report?\n%s", reportName); - result = JOptionPane.showConfirmDialog(graphComponent, message, + if (Files.exists(reportPath)) { + result = JOptionPane.showConfirmDialog(graphComponent, Bundle.VisualizationPane_overrite_exiting(reportName), Bundle.VisualizationPanel_action_dialogs_title(), JOptionPane.OK_CANCEL_OPTION); if (result == JOptionPane.OK_OPTION) { FileUtil.deleteFileDir(reportPath.toFile()); createReport(currentCase, reportName); } + } else { + createReport(currentCase, reportName); } } } @@ -788,18 +807,17 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider // Create the report. Path reportFolderPath = Paths.get(currentCase.getReportDirectory(), reportName, Bundle.VisualizationPane_reportName()); //NON_NLS BufferedImage image = mxCellRenderer.createBufferedImage(graph, null, graph.getView().getScale(), Color.WHITE, true, null); - Path reportPath = (new CommSnapShotReportWriter(currentCase, reportFolderPath, reportName, new Date(), image, currentFilter)).writeReport(); + Path reportPath = new CommSnapShotReportWriter(currentCase, reportFolderPath, reportName, new Date(), image, currentFilter).writeReport(); // Report success to the user and offer to open the report. String message = Bundle.VisualizationPane_Report_Success(reportPath.toAbsolutePath()); - String[] buttons = {Bundle.VisualizationPane_Report_OK_Button(), - Bundle.VisualizationPane_Open_Report()}; + String[] buttons = {Bundle.VisualizationPane_Open_Report(), Bundle.VisualizationPane_Report_OK_Button()}; int result = JOptionPane.showOptionDialog(graphComponent, message, Bundle.VisualizationPanel_action_dialogs_title(), - JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, + JOptionPane.OK_CANCEL_OPTION, JOptionPane.INFORMATION_MESSAGE, null, buttons, buttons[1]); - if (result == JOptionPane.NO_OPTION) { + if (result == JOptionPane.YES_NO_OPTION) { try { Desktop.getDesktop().open(reportPath.toFile()); } catch (IOException ex) { diff --git a/Core/src/org/sleuthkit/autopsy/communications/snapshot/comm_snapshot_template.html b/Core/src/org/sleuthkit/autopsy/communications/snapshot/comm_snapshot_template.html index 9b5640c3c1..3800cae9fd 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/snapshot/comm_snapshot_template.html +++ b/Core/src/org/sleuthkit/autopsy/communications/snapshot/comm_snapshot_template.html @@ -6,21 +6,21 @@
- Timeline Snapshot + Snapshot - - - - - - - - - - - - + + + + + + + + + + + +
FiltersDate Range
Start:{{startTime}}
End:{{endTime}}
Account Types
Device:
Phone: -
Email:
Facebook:
Twitter
Instagram
WhatsApp
MessagingApp
Website
Date Range
Start:{{startTime}}
End:{{endTime}}
Account Types
Device:
Phone: +
Email:
Facebook:
Twitter
Instagram
WhatsApp
MessagingApp
Website
diff --git a/Core/src/org/sleuthkit/autopsy/report/uisnapshot/UiSnapShotReportWriter.java b/Core/src/org/sleuthkit/autopsy/report/uisnapshot/UiSnapShotReportWriter.java index a4588aec7d..6f6c81a67b 100755 --- a/Core/src/org/sleuthkit/autopsy/report/uisnapshot/UiSnapShotReportWriter.java +++ b/Core/src/org/sleuthkit/autopsy/report/uisnapshot/UiSnapShotReportWriter.java @@ -38,7 +38,7 @@ import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.report.ReportBranding; /** - * Generate and write the Timeline snapshot report to disk. + * Generate and write the snapshot report to disk. */ public abstract class UiSnapShotReportWriter { @@ -191,36 +191,32 @@ public abstract class UiSnapShotReportWriter { if (StringUtils.isNotBlank(agencyLogoPath)) { Files.copy(Files.newInputStream(Paths.get(agencyLogoPath)), reportFolderPath.resolve(Paths.get(reportBranding.getAgencyLogoPath()).getFileName())); //NON-NLS } - - //copy navigation html - try (InputStream navStream = UiSnapShotReportWriter.class.getResourceAsStream("/org/sleuthkit/autopsy/report/uisnapshot/navigation.html")) { //NON-NLS - Files.copy(navStream, reportFolderPath.resolve("nav.html")); //NON-NLS - } + //copy favicon if (StringUtils.isBlank(agencyLogoPath)) { - // use default Autopsy icon if custom icon is not set - try (InputStream faviconStream = UiSnapShotReportWriter.class.getResourceAsStream("/org/sleuthkit/autopsy/report/images/favicon.ico")) { //NON-NLS - Files.copy(faviconStream, reportFolderPath.resolve("favicon.ico")); //NON-NLS - } + copyInternalResource("/org/sleuthkit/autopsy/report/images/favicon.ico", "favicon.ico"); } else { Files.copy(Files.newInputStream(Paths.get(agencyLogoPath)), reportFolderPath.resolve("favicon.ico")); //NON-NLS } - //copy report summary icon - try (InputStream summaryStream = UiSnapShotReportWriter.class.getResourceAsStream("/org/sleuthkit/autopsy/report/images/summary.png")) { //NON-NLS - Files.copy(summaryStream, reportFolderPath.resolve("summary.png")); //NON-NLS - } - //copy snapshot icon - try (InputStream snapshotIconStream = UiSnapShotReportWriter.class.getResourceAsStream("/org/sleuthkit/autopsy/report/images/image.png")) { //NON-NLS - Files.copy(snapshotIconStream, reportFolderPath.resolve("snapshot_icon.png")); //NON-NLS - } - //copy main report css - try (InputStream resource = UiSnapShotReportWriter.class.getResourceAsStream("/org/sleuthkit/autopsy/report/uisnapshot/index.css")) { //NON-NLS - Files.copy(resource, reportFolderPath.resolve("index.css")); //NON-NLS - } - //copy summary css - try (InputStream resource = UiSnapShotReportWriter.class.getResourceAsStream("/org/sleuthkit/autopsy/report/uisnapshot/summary.css")) { //NON-NLS - Files.copy(resource, reportFolderPath.resolve("summary.css")); //NON-NLS + copyInternalResource("/org/sleuthkit/autopsy/report/uisnapshot/navigation.html", "nav.html"); + copyInternalResource("/org/sleuthkit/autopsy/report/images/summary.png", "summary.png"); + copyInternalResource("/org/sleuthkit/autopsy/report/images/image.png", "snapshot_icon.png"); + copyInternalResource("/org/sleuthkit/autopsy/report/uisnapshot/index.css", "index.css"); + copyInternalResource("/org/sleuthkit/autopsy/report/uisnapshot/summary.css", "summary.css"); + } + + /** + * Copies internal resource to the report folder. + * + * @param internalPath Location in jar of the image + * @param fileName Name to give resource in new location + * + * @throws IOException + */ + private void copyInternalResource(String internalPath, String fileName) throws IOException{ + try (InputStream resource = UiSnapShotReportWriter.class.getResourceAsStream(internalPath)) { //NON-NLS + Files.copy(resource, reportFolderPath.resolve(fileName)); //NON-NLS } } } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties-MERGED b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties-MERGED index f64794953f..6f95dfc82f 100755 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties-MERGED +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties-MERGED @@ -34,7 +34,8 @@ KeywordSearchIngestModule.startupMessage.failedToGetIndexSchema=Failed to get sc KeywordSearchResultFactory.createNodeForKey.noResultsFound.text=No results found. KeywordSearchResultFactory.query.exception.msg=Could not perform the query OpenIDE-Module-Display-Category=Ingest Module -OpenIDE-Module-Long-Description=Keyword Search ingest module.\n\nThe module indexes files found in the disk image at ingest time.\nIt then periodically runs the search on the indexed files using one or more keyword lists (containing pure words and/or regular expressions) and posts results.\n\n\The module also contains additional tools integrated in the main GUI, such as keyword list configuration, keyword search bar in the top-right corner, extracted text viewer and search results viewer showing highlighted keywords found. + +OpenIDE-Module-Long-Description=Keyword Search ingest module.\n\nThe module indexes files found in the disk image at ingest time.\nIt then periodically runs the search on the indexed files using one or more keyword lists (containing pure words and/or regular expressions) and posts results.\n\nThe module also contains additional tools integrated in the main GUI, such as keyword list configuration, keyword search bar in the top-right corner, extracted text viewer and search results viewer showing highlighted keywords found. OpenIDE-Module-Name=KeywordSearch OptionsCategory_Name_KeywordSearchOptions=Keyword Search OptionsCategory_Keywords_KeywordSearchOptions=Keyword Search diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED index 521b871e47..282fa33b81 100755 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED @@ -2,6 +2,7 @@ cannotBuildXmlParser=Unable to build XML parser: cannotLoadSEUQA=Unable to load Search Engine URL Query Analyzer settings file, SEUQAMappings.xml: cannotParseXml=Unable to parse XML file: ChromeCacheExtractor.moduleName=ChromeCacheExtractor +# {0} - OS name DataSourceUsageAnalyzer.customVolume.label=OS Drive ({0}) DataSourceUsageAnalyzer.parentModuleName=Recent Activity Extract.indexError.message=Failed to index artifact for keyword search. @@ -180,6 +181,7 @@ RecentDocumentsByLnk.parentModuleName.noSpace=RecentActivity RecentDocumentsByLnk.parentModuleName=Recent Activity RegRipperFullNotFound=Full version RegRipper executable not found. RegRipperNotFound=Autopsy RegRipper executable not found. +# {0} - file name SearchEngineURLQueryAnalyzer.init.exception.msg=Unable to find {0}. SearchEngineURLQueryAnalyzer.moduleName.text=Search Engine SearchEngineURLQueryAnalyzer.engineName.none=NONE From e80f9d5d4d09becbbebfda7bd38d1b79419e7eb0 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Fri, 15 Mar 2019 15:47:58 -0400 Subject: [PATCH 25/59] 4791 ensure null correlation type is used for intra case search --- .../commonpropertiessearch/CommonAttributePanel.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributePanel.java b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributePanel.java index 030b78c76e..a9c9ba1762 100644 --- a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributePanel.java +++ b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributePanel.java @@ -294,7 +294,11 @@ final class CommonAttributePanel extends javax.swing.JDialog implements Observer DataResultTopComponent.createInstance(tabTitle, Bundle.CommonAttributePanel_search_results_pathText(), commonFilesNode, 1); } else { // -3969 - Node commonFilesNode = new CommonAttributeSearchResultRootNode(metadata, interCasePanel.getSelectedCorrelationType()); + CorrelationAttributeInstance.Type correlationType = null; + if (interCaseRadio.isSelected()){ + correlationType = interCasePanel.getSelectedCorrelationType(); + } + Node commonFilesNode = new CommonAttributeSearchResultRootNode(metadata, correlationType); DataResultFilterNode dataResultFilterNode = new DataResultFilterNode(commonFilesNode, ExplorerManager.find(CommonAttributePanel.this)); TableFilterNode tableFilterWithDescendantsNode = new TableFilterNode(dataResultFilterNode, 3); DataResultViewerTable table = new CommonAttributesSearchResultsViewerTable(); From 2dc4351c21c152b5d1b3a8d8dd07c5d3fd75b38c Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Fri, 15 Mar 2019 16:57:00 -0400 Subject: [PATCH 26/59] 3956 change is supported method for hex and string viewer to lookup content using DataContentViewerUtility --- .../autopsy/corecomponents/DataContentViewerHex.java | 10 +++------- .../corecomponents/DataContentViewerString.java | 4 ++-- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerHex.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerHex.java index 601d74293d..a092186318 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerHex.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerHex.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2018 Basis Technology Corp. + * Copyright 2011-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -605,12 +605,8 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont if (node == null) { return false; } - Content content = node.getLookup().lookup(Content.class); - if (content != null && content.getSize() > 0) { - return true; - } - - return false; + Content content = DataContentViewerUtility.getDefaultContent(node); + return content != null && content.getSize() > 0; } @Override diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerString.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerString.java index bc0980c58c..919be9856c 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerString.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerString.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2018 Basis Technology Corp. + * Copyright 2011-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -498,7 +498,7 @@ public class DataContentViewerString extends javax.swing.JPanel implements DataC if (node == null) { return false; } - Content content = node.getLookup().lookup(Content.class); + Content content = DataContentViewerUtility.getDefaultContent(node); return (content != null && content.getSize() > 0); } From 86d2747c7a8c2038a05cc0a54395bb44a772cdfc Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Fri, 15 Mar 2019 17:02:30 -0400 Subject: [PATCH 27/59] 3956 clean up netbeans suggestions and remove unused method --- .../DataContentViewerString.java | 33 +++++-------------- 1 file changed, 8 insertions(+), 25 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerString.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerString.java index 919be9856c..a5a94031e9 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerString.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerString.java @@ -18,12 +18,12 @@ */ package org.sleuthkit.autopsy.corecomponents; -import java.awt.*; +import java.awt.Component; +import java.awt.Cursor; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.List; import java.util.logging.Level; - import org.openide.util.NbBundle; import org.sleuthkit.autopsy.coreutils.Logger; import javax.swing.JMenuItem; @@ -36,7 +36,7 @@ import org.sleuthkit.autopsy.coreutils.StringExtract.StringExtractResult; import org.sleuthkit.autopsy.coreutils.StringExtract.StringExtractUnicodeTable.SCRIPT; import org.sleuthkit.autopsy.datamodel.StringContent; import org.sleuthkit.datamodel.Content; -import org.sleuthkit.datamodel.TskException; +import org.sleuthkit.datamodel.TskCoreException; /** * Viewer displays strings extracted from contents. @@ -264,7 +264,7 @@ public class DataContentViewerString extends javax.swing.JPanel implements DataC private void prevPageButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_prevPageButtonActionPerformed //@@@ this is part of the code dealing with the data viewer. could be copied/removed to implement the scrollbar currentOffset -= PAGE_LENGTH; - currentPage = currentPage - 1; + currentPage -= 1; currentPageLabel.setText(Integer.toString(currentPage)); setDataView(dataSource, currentOffset); }//GEN-LAST:event_prevPageButtonActionPerformed @@ -272,7 +272,7 @@ public class DataContentViewerString extends javax.swing.JPanel implements DataC private void nextPageButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_nextPageButtonActionPerformed //@@@ this is part of the code dealing with the data viewer. could be copied/removed to implement the scrollbar currentOffset += PAGE_LENGTH; - currentPage = currentPage + 1; + currentPage += 1; currentPageLabel.setText(Integer.toString(currentPage)); setDataView(dataSource, currentOffset); }//GEN-LAST:event_nextPageButtonActionPerformed @@ -348,18 +348,15 @@ public class DataContentViewerString extends javax.swing.JPanel implements DataC int bytesRead = 0; // set the data on the bottom and show it - String text = ""; + if (dataSource.getSize() > 0) { try { bytesRead = dataSource.read(data, offset, PAGE_LENGTH); // read the data - } catch (TskException ex) { - text = NbBundle.getMessage(this.getClass(), - "DataContentViewerString.setDataView.errorText", currentOffset, - currentOffset + PAGE_LENGTH); + } catch (TskCoreException ex) { logger.log(Level.WARNING, "Error while trying to show the String content.", ex); //NON-NLS } } - + String text; if (bytesRead > 0) { //text = DataConversion.getString(data, bytesRead, 4); final SCRIPT selScript = (SCRIPT) languageCombo.getSelectedItem(); @@ -511,18 +508,4 @@ public class DataContentViewerString extends javax.swing.JPanel implements DataC public Component getComponent() { return this; } - - - /* - * Show the right click menu only if evt is the correct mouse event - */ - private void maybeShowPopup(java.awt.event.MouseEvent evt) { - if (evt.isPopupTrigger()) { - rightClickMenu.setLocation(evt.getLocationOnScreen()); - rightClickMenu.setVisible(true); - copyMenuItem.setEnabled(outputViewPane.getSelectedText() != null); - } else { - rightClickMenu.setVisible(false); - } - } } From 42f6950a977f1807c2c7f781cb6ae41b39aa9849 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Fri, 15 Mar 2019 17:05:23 -0400 Subject: [PATCH 28/59] 3956 clean up netbeans suggestions for Hex Viewer --- .../corecomponents/DataContentViewerHex.java | 67 +++++++++---------- 1 file changed, 33 insertions(+), 34 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerHex.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerHex.java index a092186318..0753f18905 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerHex.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerHex.java @@ -18,7 +18,8 @@ */ package org.sleuthkit.autopsy.corecomponents; -import java.awt.*; +import java.awt.Component; +import java.awt.Cursor; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; @@ -26,7 +27,6 @@ import java.io.IOException; import java.nio.file.Paths; import java.util.logging.Level; import org.sleuthkit.autopsy.coreutils.PlatformUtil; - import org.openide.util.NbBundle; import org.sleuthkit.autopsy.coreutils.Logger; import javax.swing.JMenuItem; @@ -46,7 +46,7 @@ import org.sleuthkit.autopsy.coreutils.FileUtil; import org.sleuthkit.autopsy.datamodel.ContentUtils; import org.sleuthkit.autopsy.datamodel.DataConversion; import org.sleuthkit.datamodel.Content; -import org.sleuthkit.datamodel.TskException; +import org.sleuthkit.datamodel.TskCoreException; /** * Hex view of file contents. @@ -55,8 +55,8 @@ import org.sleuthkit.datamodel.TskException; @ServiceProvider(service = DataContentViewer.class, position = 1) public class DataContentViewerHex extends javax.swing.JPanel implements DataContentViewer { - private static final long pageLength = 16384; - private final byte[] data = new byte[(int) pageLength]; + private static final long PAGE_LENGTH = 16384; + private final byte[] data = new byte[(int) PAGE_LENGTH]; private static int currentPage = 1; private int totalPages; private Content dataSource; @@ -291,8 +291,7 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont private void goToPageTextFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_goToPageTextFieldActionPerformed String pageNumberStr = goToPageTextField.getText(); - int pageNumber = 0; - + int pageNumber; try { pageNumber = Integer.parseInt(pageNumberStr); } catch (NumberFormatException ex) { @@ -330,7 +329,7 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont Utilities.getRowEnd(outputTextArea, outputTextArea.getCaretPosition())) .toString(); // NOTE: This needs to change if the outputFormat of outputTextArea changes. - String hexForUserSelectedLine = userSelectedLine.substring(0, userSelectedLine.indexOf(":")); + String hexForUserSelectedLine = userSelectedLine.substring(0, userSelectedLine.indexOf(':')); return Long.decode(hexForUserSelectedLine) + userInput; } catch (BadLocationException | StringIndexOutOfBoundsException | NumberFormatException ex) { @@ -361,19 +360,20 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont }//GEN-LAST:event_goToOffsetTextFieldActionPerformed @NbBundle.Messages({"DataContentViewerHex.launchError=Unable to launch HxD Editor. " - + "Please specify the HxD install location in Tools -> Options -> External Viewer", - "DataContentViewerHex.copyingFile=Copying file to open in HxD..."}) + + "Please specify the HxD install location in Tools -> Options -> External Viewer", + "DataContentViewerHex.copyingFile=Copying file to open in HxD..."}) private void launchHxDButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_launchHxDButtonActionPerformed new BackgroundFileCopyTask().execute(); }//GEN-LAST:event_launchHxDButtonActionPerformed /** - * Performs the file copying and process launching in a SwingWorker so that the - * UI is not blocked when opening large files. + * Performs the file copying and process launching in a SwingWorker so that + * the UI is not blocked when opening large files. */ private class BackgroundFileCopyTask extends SwingWorker { + private boolean wasCancelled = false; - + @Override public Void doInBackground() throws InterruptedException { ProgressHandle progress = ProgressHandle.createHandle(DataContentViewerHex_copyingFile(), () -> { @@ -382,31 +382,31 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont wasCancelled = true; return true; }); - + try { File HxDExecutable = new File(UserPreferences.getExternalHexEditorPath()); - if(!HxDExecutable.exists() || !HxDExecutable.canExecute()) { + if (!HxDExecutable.exists() || !HxDExecutable.canExecute()) { JOptionPane.showMessageDialog(null, DataContentViewerHex_launchError()); return null; } - + String tempDirectory = Case.getCurrentCaseThrows().getTempDirectory(); File tempFile = Paths.get(tempDirectory, FileUtil.escapeFileName(dataSource.getId() + dataSource.getName())).toFile(); - + progress.start(100); ContentUtils.writeToFile(dataSource, tempFile, progress, this, true); - - if(wasCancelled) { + + if (wasCancelled) { tempFile.delete(); progress.finish(); return null; } - + try { ProcessBuilder launchHxDExecutable = new ProcessBuilder(); - launchHxDExecutable.command(String.format("\"%s\" \"%s\"", - HxDExecutable.getAbsolutePath(), + launchHxDExecutable.command(String.format("\"%s\" \"%s\"", + HxDExecutable.getAbsolutePath(), tempFile.getAbsolutePath())); launchHxDExecutable.start(); } catch (IOException ex) { @@ -418,14 +418,13 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont logger.log(Level.SEVERE, "Unable to copy file into temp directory", ex); JOptionPane.showMessageDialog(null, DataContentViewerHex_launchError()); } - + progress.finish(); return null; } } - - - + + // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JMenuItem copyMenuItem; private javax.swing.JLabel currentPageLabel; @@ -461,7 +460,7 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont return; } currentPage = page; - long offset = (currentPage - 1) * pageLength; + long offset = (currentPage - 1) * PAGE_LENGTH; setDataView(offset); goToOffsetTextField.setText(Long.toString(offset)); } @@ -475,7 +474,7 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont if (this.dataSource == null) { return; } - currentPage = (int) (offset / pageLength) + 1; + currentPage = (int) (offset / PAGE_LENGTH) + 1; setDataView(offset); goToPageTextField.setText(Integer.toString(currentPage)); } @@ -489,10 +488,10 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont int bytesRead = 0; if (dataSource.getSize() > 0) { try { - bytesRead = dataSource.read(data, offset, pageLength); // read the data - } catch (TskException ex) { + bytesRead = dataSource.read(data, offset, PAGE_LENGTH); // read the data + } catch (TskCoreException ex) { errorText = NbBundle.getMessage(this.getClass(), "DataContentViewerHex.setDataView.errorText", offset, - offset + pageLength); + offset + PAGE_LENGTH); logger.log(Level.WARNING, "Error while trying to show the hex content.", ex); //NON-NLS } } @@ -500,7 +499,7 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont // set the data on the bottom and show it if (bytesRead <= 0) { errorText = NbBundle.getMessage(this.getClass(), "DataContentViewerHex.setDataView.errorText", offset, - offset + pageLength); + offset + PAGE_LENGTH); } // disable or enable the next button @@ -521,7 +520,7 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont // set the output view if (errorText == null) { - int showLength = bytesRead < pageLength ? bytesRead : (int) pageLength; + int showLength = bytesRead < PAGE_LENGTH ? bytesRead : (int) PAGE_LENGTH; outputTextArea.setText(DataConversion.byteArrayToHex(data, showLength, offset)); } else { outputTextArea.setText(errorText); @@ -547,7 +546,7 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont dataSource = content; totalPages = 0; if (dataSource.getSize() > 0) { - totalPages = Math.round((dataSource.getSize() - 1) / pageLength) + 1; + totalPages = Math.round((dataSource.getSize() - 1) / PAGE_LENGTH) + 1; } totalPageLabel.setText(Integer.toString(totalPages)); From 734883521949c8edb7f058f6682daf0edf0c6d6c Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Mon, 18 Mar 2019 10:44:23 -0400 Subject: [PATCH 29/59] Added report to case report list --- .../autopsy/communications/Bundle.properties-MERGED | 1 + .../autopsy/communications/VisualizationPanel.java | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties-MERGED index a4be4813c9..6a2a238915 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties-MERGED @@ -85,4 +85,5 @@ VisualizationPanel.clearVizButton.text_1=Clear Viz. VisualizationPanel.snapshotButton.text_1=Snapshot Report VisualizationPanel_action_dialogs_title=Communications VisualizationPanel_action_name_text=Snapshot Report +VisualizationPanel_module_name=Communications VisualizationPanel_snapshot_report_failure=Snapshot report not created. An error occurred during creation. diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index f8ff5aaf69..625b241d32 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -694,6 +694,8 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider -> Notifications.create().owner(notificationsJFXPanel.getScene().getWindow()) .text(Bundle.VisualizationPanel_snapshot_report_failure()) .showWarning()); + } catch( TskCoreException ex) { + logger.log(Level.WARNING, "Unable to add report to currenct case", ex); //NON-NLS } }//GEN-LAST:event_snapshotButtonActionPerformed @@ -732,6 +734,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider */ @NbBundle.Messages({ "VisualizationPanel_action_dialogs_title=Communications", + "VisualizationPanel_module_name=Communications", "VisualizationPanel_action_name_text=Snapshot Report", "VisualizationPane_fileName_prompt=Enter name for the Communications Snapshot Report:", "VisualizationPane_reportName=Communications Snapshot", @@ -741,7 +744,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider "# {0} - report name", "VisualizationPane_overrite_exiting=Overwrite existing report?\n{0}" }) - private void handleSnapshotEvent() throws NoCurrentCaseException, IOException { + private void handleSnapshotEvent() throws NoCurrentCaseException, IOException, TskCoreException { Case currentCase = Case.getCurrentCaseThrows(); Date generationDate = new Date(); @@ -779,6 +782,8 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } } else { createReport(currentCase, reportName); + currentCase.addReport(reportPath.toString(), Bundle.VisualizationPanel_module_name(), reportName); + } } } From c13e3100167d4e8e42b5e8b800b08c1b7d052c65 Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Mon, 18 Mar 2019 16:47:36 -0400 Subject: [PATCH 30/59] Added devices to the report table and made account type list more dynamic --- .../snapshot/CommSnapShotReportWriter.java | 99 ++++++++++++++----- .../snapshot/comm_snapshot_template.html | 12 +-- .../uisnapshot/UiSnapShotReportWriter.java | 23 ++++- 3 files changed, 98 insertions(+), 36 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/snapshot/CommSnapShotReportWriter.java b/Core/src/org/sleuthkit/autopsy/communications/snapshot/CommSnapShotReportWriter.java index becea9ebcd..8024415d63 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/snapshot/CommSnapShotReportWriter.java +++ b/Core/src/org/sleuthkit/autopsy/communications/snapshot/CommSnapShotReportWriter.java @@ -24,6 +24,8 @@ import java.io.IOException; import java.nio.file.Path; import java.text.SimpleDateFormat; import java.time.Instant; +import java.util.ArrayList; +import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.Set; @@ -34,7 +36,11 @@ import org.sleuthkit.datamodel.Account; import org.sleuthkit.datamodel.CommunicationsFilter; import org.sleuthkit.datamodel.CommunicationsFilter.AccountTypeFilter; import org.sleuthkit.datamodel.CommunicationsFilter.DateRangeFilter; +import org.sleuthkit.datamodel.CommunicationsFilter.DeviceFilter; import org.sleuthkit.datamodel.CommunicationsFilter.SubFilter; +import org.sleuthkit.datamodel.DataSource; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TskCoreException; /** * Generate and write the Communication snapshot report to disk. @@ -71,7 +77,7 @@ public class CommSnapShotReportWriter extends UiSnapShotReportWriter { */ @Override protected void writeSnapShotHTMLFile() throws IOException { - SimpleDateFormat formatter = new SimpleDateFormat("MMMMM dd, yyyy"); + SimpleDateFormat formatter = new SimpleDateFormat("MMMMM dd, yyyy"); //NON-NLS ImageIO.write(image, "png", getReportFolderPath().resolve("snapshot.png").toFile()); //NON-NLS @@ -81,10 +87,10 @@ public class CommSnapShotReportWriter extends UiSnapShotReportWriter { List filters = filter.getAndFilters(); - for (SubFilter f : filters) { - if (f.getClass().getName().equals(DateRangeFilter.class.getName())) { - long startDate = ((DateRangeFilter) f).getStartDate(); - long endDate = ((DateRangeFilter) f).getEndDate(); + for (SubFilter filter : filters) { + if (filter.getClass().getName().equals(DateRangeFilter.class.getName())) { + long startDate = ((DateRangeFilter) filter).getStartDate(); + long endDate = ((DateRangeFilter) filter).getEndDate(); if (startDate > 0) { @@ -94,33 +100,76 @@ public class CommSnapShotReportWriter extends UiSnapShotReportWriter { if (endDate > 0) { snapShotContext.put("endTime", formatter.format(new Date((Instant.ofEpochSecond(endDate)).toEpochMilli()))); //NON-NLS } - } else if (f.getClass().getName().equals(AccountTypeFilter.class.getName())) { - snapShotContext.put("emailSelected", "checked"); //NON-NLS + } else if (filter.getClass().getName().equals(AccountTypeFilter.class.getName())) { - Set types = ((AccountTypeFilter) f).getAccountTypes(); - for (Account.Type type : types) { - if (type == Account.Type.DEVICE) { - snapShotContext.put("deviceSelected", "checked"); //NON-NLS - } else if (type == Account.Type.PHONE) { - snapShotContext.put("phoneSelected", "checked"); //NON-NLS - } else if (type == Account.Type.EMAIL) { - snapShotContext.put("emailSelected", "checked"); //NON-NLS - } else if (type == Account.Type.FACEBOOK) { - snapShotContext.put("facebookSelected", "checked"); //NON-NLS - } else if (type == Account.Type.TWITTER) { - snapShotContext.put("twitterSelected", "checked"); //NON-NLS - } else if (type == Account.Type.INSTAGRAM) { - snapShotContext.put("instagramSelected", "checked"); //NON-NLS - } else if (type == Account.Type.WHATSAPP) { - snapShotContext.put("whatsAppSelected", "checked"); //NON-NLS - } else if (type == Account.Type.WEBSITE) { - snapShotContext.put("websiteSelected", "checked"); //NON-NLS + Set selectedAccounts = ((AccountTypeFilter) filter).getAccountTypes(); + ArrayList fullAccountList = new ArrayList<>(); + for (Account.Type type : Account.Type.PREDEFINED_ACCOUNT_TYPES) { + if (type == Account.Type.CREDIT_CARD) { + continue; } + + fullAccountList.add(new ReportWriterHelper(type.getDisplayName(), selectedAccounts.contains(type))); } + + snapShotContext.put("accounts", fullAccountList); + } else if (filter.getClass().getName().equals(DeviceFilter.class.getName())) { + Collection ids = ((DeviceFilter) filter).getDevices(); + ArrayList list = new ArrayList<>(); + try { + final SleuthkitCase sleuthkitCase = getCurrentCase().getSleuthkitCase(); + for (DataSource dataSource : sleuthkitCase.getDataSources()) { + boolean selected = ids.contains(dataSource.getDeviceId()); + String dsName = sleuthkitCase.getContentById(dataSource.getId()).getName(); + list.add(new ReportWriterHelper(dsName, selected)); + } + } catch (TskCoreException ex) { + + } + + snapShotContext.put("devices", list); } } fillTemplateAndWrite("/org/sleuthkit/autopsy/communications/snapshot/comm_snapshot_template.html", "Snapshot", snapShotContext, getReportFolderPath().resolve("snapshot.html")); //NON-NLS } + /** + * Helper class for use with the html template + */ + private final class ReportWriterHelper { + + private final String label; + private final boolean selected; + + /** + * Helper class for use with the html template. + * + * @param label Display label + * @param selected Boolean selected state + */ + ReportWriterHelper(String label, boolean selected) { + this.label = label; + this.selected = selected; + } + + /** + * Returns the display label + * + * @return The display label + */ + public String getLabel(){ + return label; + } + + /** + * Returns the selection state + * + * @return The selection state + */ + public boolean isSelected(){ + return selected; + } + } + } diff --git a/Core/src/org/sleuthkit/autopsy/communications/snapshot/comm_snapshot_template.html b/Core/src/org/sleuthkit/autopsy/communications/snapshot/comm_snapshot_template.html index 3800cae9fd..078f6fc978 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/snapshot/comm_snapshot_template.html +++ b/Core/src/org/sleuthkit/autopsy/communications/snapshot/comm_snapshot_template.html @@ -11,16 +11,10 @@ Date Range Start:{{startTime}} End:{{endTime}} + Devices: + {{#devices}}{{label}}{{/devices}} Account Types - Device: - Phone: - Email: - Facebook: - Twitter - Instagram - WhatsApp - MessagingApp - Website + {{#accounts}}{{label}}{{/accounts}}
diff --git a/Core/src/org/sleuthkit/autopsy/report/uisnapshot/UiSnapShotReportWriter.java b/Core/src/org/sleuthkit/autopsy/report/uisnapshot/UiSnapShotReportWriter.java index 6f6c81a67b..1017e2e743 100755 --- a/Core/src/org/sleuthkit/autopsy/report/uisnapshot/UiSnapShotReportWriter.java +++ b/Core/src/org/sleuthkit/autopsy/report/uisnapshot/UiSnapShotReportWriter.java @@ -94,13 +94,32 @@ public abstract class UiSnapShotReportWriter { return writeIndexHTML(); } - protected String getReportName(){ + /** + * Get the name for the report. + * + * @return Returns the reportName + */ + protected String getReportName() { return reportName; } - protected Path getReportFolderPath(){ + /** + * Get the folder path for the report. + * + * @return Report folder path + */ + protected Path getReportFolderPath() { return reportFolderPath; } + + /** + * Get the case for this report. + * + * @return Current case object + */ + protected Case getCurrentCase() { + return currentCase; + } /** * Generate and write the html page that shows the snapshot and the state of From 6875c94050afcdee54df42c09b765a6b5ded102a Mon Sep 17 00:00:00 2001 From: Ann Priestman Date: Tue, 19 Mar 2019 07:28:52 -0400 Subject: [PATCH 31/59] Get max database IDs --- .../autopsy/report/Bundle.properties-MERGED | 1 + .../report/CreatePortableCaseModule.java | 51 +++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/Core/src/org/sleuthkit/autopsy/report/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/report/Bundle.properties-MERGED index 68a553dd27..3111ffbe6d 100755 --- a/Core/src/org/sleuthkit/autopsy/report/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/report/Bundle.properties-MERGED @@ -5,6 +5,7 @@ CreatePortableCaseModule.createCase.caseDirExists=Case folder {0} already exists CreatePortableCaseModule.createCase.errorCreatingCase=Error creating case # {0} - folder CreatePortableCaseModule.createCase.errorCreatingFolder=Error creating folder {0} +CreatePortableCaseModule.createCase.errorStoringMaxIds=Error storing maximum database IDs CreatePortableCaseModule.generateReport.caseClosed=Current case has been closed # {0} - tag name CreatePortableCaseModule.generateReport.copyingArtifacts=Copying artifacts tagged as {0}... diff --git a/Core/src/org/sleuthkit/autopsy/report/CreatePortableCaseModule.java b/Core/src/org/sleuthkit/autopsy/report/CreatePortableCaseModule.java index 98805e420e..609928d98f 100644 --- a/Core/src/org/sleuthkit/autopsy/report/CreatePortableCaseModule.java +++ b/Core/src/org/sleuthkit/autopsy/report/CreatePortableCaseModule.java @@ -24,6 +24,8 @@ import java.util.logging.Level; import java.io.File; import java.io.IOException; import java.nio.file.Paths; +import java.sql.ResultSet; +import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -41,6 +43,7 @@ import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifactTag; import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.CaseDbAccessManager; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.ContentTag; import org.sleuthkit.datamodel.FileSystem; @@ -290,6 +293,7 @@ public class CreatePortableCaseModule implements GeneralReportModule { "CreatePortableCaseModule.createCase.errorCreatingCase=Error creating case", "# {0} - folder", "CreatePortableCaseModule.createCase.errorCreatingFolder=Error creating folder {0}", + "CreatePortableCaseModule.createCase.errorStoringMaxIds=Error storing maximum database IDs", }) private void createCase(File outputDir, ReportProgressPanel progressPanel) { @@ -312,6 +316,20 @@ public class CreatePortableCaseModule implements GeneralReportModule { return; } + // Store the highest IDs + try { + currentCase.getSleuthkitCase().getCaseDbAccessManager() + .select("max(obj_id) as max_id from tsk_objects", new StoreMaxIdCallback("tsk_objects")); + currentCase.getSleuthkitCase().getCaseDbAccessManager() + .select("max(tag_id) as max_id from content_tags", new StoreMaxIdCallback("content_tags")); + currentCase.getSleuthkitCase().getCaseDbAccessManager() + .select("max(tag_id) as max_id from blackboard_artifact_tags", new StoreMaxIdCallback("blackboard_artifact_tags")); + } catch (TskCoreException ex) { + handleError("Error storing maximum database IDs", + Bundle.CreatePortableCaseModule_createCase_errorStoringMaxIds(), ex, progressPanel); + return; + } + // Create the base folder for the copied files copiedFilesFolder = Paths.get(caseFolder.toString(), FILE_FOLDER_NAME).toFile(); if (! copiedFilesFolder.mkdir()) { @@ -689,4 +707,37 @@ public class CreatePortableCaseModule implements GeneralReportModule { configPanel = new CreatePortableCasePanel(); return configPanel; } + + class StoreMaxIdCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback { + + private final String tableName; + + StoreMaxIdCallback(String tableName) { + this.tableName = tableName; + } + + @Override + public void process(ResultSet rs) { + + try { + while (rs.next()) { + try { + Long maxId = rs.getLong("max_id"); + String nameStr = "PORTABLE_CASE_" + tableName + "_MAX_ID"; + nameStr = nameStr.toUpperCase(); + String query = "INSERT INTO tsk_db_info_extended (name, value) VALUES ('" + nameStr + "', '" + maxId + "')"; + currentCase.getSleuthkitCase().getCaseDbAccessManager().insert("tsk_db_info_extended", query); + + } catch (SQLException ex) { + logger.log(Level.WARNING, "Unable to get maximum ID from result set", ex); + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Unable to save maximum ID from result set", ex); + } + + } + } catch (SQLException ex) { + logger.log(Level.WARNING, "Failed to get maximum ID from result set", ex); + } + } + } } From e3199919dd4a8e0c94ab386720d841e749df78c3 Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Tue, 19 Mar 2019 08:00:01 -0400 Subject: [PATCH 32/59] Updated to check for white space report name. --- .../sleuthkit/autopsy/communications/Bundle.properties-MERGED | 2 +- .../sleuthkit/autopsy/communications/VisualizationPanel.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties-MERGED index 6a2a238915..e4daded09b 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties-MERGED @@ -33,7 +33,7 @@ UnpinAccountsAction.pluralText=Remove Selected Accounts UnpinAccountsAction.singularText=Remove Selected Account VisalizationPanel.paintingError=Problem painting visualization. # {0} - default name -VisualizationPane_accept_defaultName=Press OK to accept default report name: {0} +VisualizationPane_accept_defaultName=Report name was empty. Press OK to accept default report name: {0} VisualizationPane_blank_report_title=Blank Report Name VisualizationPane_DisplayName=Open Report VisualizationPane_fileName_prompt=Enter name for the Communications Snapshot Report: diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 625b241d32..f3d4f95bb2 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -739,7 +739,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider "VisualizationPane_fileName_prompt=Enter name for the Communications Snapshot Report:", "VisualizationPane_reportName=Communications Snapshot", "# {0} - default name", - "VisualizationPane_accept_defaultName=Press OK to accept default report name: {0}", + "VisualizationPane_accept_defaultName=Report name was empty. Press OK to accept default report name: {0}", "VisualizationPane_blank_report_title=Blank Report Name", "# {0} - report name", "VisualizationPane_overrite_exiting=Overwrite existing report?\n{0}" @@ -763,7 +763,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider if (result == JOptionPane.OK_OPTION) { String enteredReportName = text.getText(); - if(enteredReportName.isEmpty()){ + if(enteredReportName.trim().isEmpty()){ result = JOptionPane.showConfirmDialog(graphComponent, Bundle.VisualizationPane_accept_defaultName(defaultReportName), Bundle.VisualizationPane_blank_report_title(), JOptionPane.OK_CANCEL_OPTION); if(result != JOptionPane.OK_OPTION) { return; From fd688a21c57bf30310329cf67ca5b65df528480c Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Tue, 19 Mar 2019 09:16:27 -0400 Subject: [PATCH 33/59] Changed the case list panel on the left hand side --- .../keywordsearch/multicase/Bundle.properties | 4 +- .../multicase/Bundle.properties-MERGED | 4 +- .../MultiCaseKeywordSearchPanel.form | 130 ++++-------- .../MultiCaseKeywordSearchPanel.java | 185 ++++++------------ 4 files changed, 96 insertions(+), 227 deletions(-) diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/multicase/Bundle.properties b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/multicase/Bundle.properties index 9ca1203be4..97b54b454f 100755 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/multicase/Bundle.properties +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/multicase/Bundle.properties @@ -11,13 +11,11 @@ MultiCaseKeywordSearchPanel.keywordTextField.text_1= MultiCaseKeywordSearchPanel.toolDescriptionTextArea.text=Perform a keyword search on the selected cases. The case can be opened to examine the results more closely. MultiCaseKeywordSearchPanel.casesLabel.text_1=Cases MultiCaseKeywordSearchPanel.resultsLabel.text=Results -MultiCaseKeywordSearchPanel.uncheckButton.text=Uncheck All -MultiCaseKeywordSearchPanel.checkButton.text=Check All MultiCaseKeywordSearchPanel.searchButton.text=Search MultiCaseKeywordSearchPanel.viewErrorsButton.text=View Errors MultiCaseKeywordSearchPanel.warningLabel.text= MultiCaseKeywordSearchPanel.exportButton.text=Export Results MultiCaseKeywordSearchPanel.cancelButton.text=Cancel MultiCaseKeywordSearchPanel.resultsCountLabel.text= -MultiCaseKeywordSearchPanel.pickCasesButton.text_1=Pick Cases +MultiCaseKeywordSearchPanel.pickCasesButton.text_1=Add Cases SelectMultiUserCasesPanel.refreshButton.text=Refresh diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/multicase/Bundle.properties-MERGED b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/multicase/Bundle.properties-MERGED index d10f80b39a..a42701dd53 100755 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/multicase/Bundle.properties-MERGED +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/multicase/Bundle.properties-MERGED @@ -92,13 +92,11 @@ MultiCaseKeywordSearchPanel.keywordTextField.text_1= MultiCaseKeywordSearchPanel.toolDescriptionTextArea.text=Perform a keyword search on the selected cases. The case can be opened to examine the results more closely. MultiCaseKeywordSearchPanel.casesLabel.text_1=Cases MultiCaseKeywordSearchPanel.resultsLabel.text=Results -MultiCaseKeywordSearchPanel.uncheckButton.text=Uncheck All -MultiCaseKeywordSearchPanel.checkButton.text=Check All MultiCaseKeywordSearchPanel.searchButton.text=Search MultiCaseKeywordSearchPanel.viewErrorsButton.text=View Errors MultiCaseKeywordSearchPanel.warningLabel.text= MultiCaseKeywordSearchPanel.exportButton.text=Export Results MultiCaseKeywordSearchPanel.cancelButton.text=Cancel MultiCaseKeywordSearchPanel.resultsCountLabel.text= -MultiCaseKeywordSearchPanel.pickCasesButton.text_1=Pick Cases +MultiCaseKeywordSearchPanel.pickCasesButton.text_1=Add Cases SelectMultiUserCasesPanel.refreshButton.text=Refresh diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/multicase/MultiCaseKeywordSearchPanel.form b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/multicase/MultiCaseKeywordSearchPanel.form index f348e17108..ee2bd31d85 100755 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/multicase/MultiCaseKeywordSearchPanel.form +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/multicase/MultiCaseKeywordSearchPanel.form @@ -39,29 +39,22 @@ - + - + - - - - - - - - - - - - + + + + + - + @@ -72,7 +65,7 @@ - + @@ -88,7 +81,7 @@ - + @@ -122,25 +115,21 @@ - - + + - - - - + + + + - - - - - + @@ -232,31 +221,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - @@ -271,47 +235,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -428,5 +351,24 @@ + + + + + + + + + + + + + + + + + + + diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/multicase/MultiCaseKeywordSearchPanel.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/multicase/MultiCaseKeywordSearchPanel.java index b55a1e426d..07383604dc 100755 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/multicase/MultiCaseKeywordSearchPanel.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/multicase/MultiCaseKeywordSearchPanel.java @@ -30,6 +30,7 @@ import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; @@ -38,11 +39,14 @@ import java.util.Map; import java.util.logging.Level; import java.util.stream.Collectors; import javax.swing.AbstractButton; +import javax.swing.DefaultListModel; +import javax.swing.DefaultListSelectionModel; import javax.swing.JCheckBox; import javax.swing.JFileChooser; import javax.swing.table.TableColumn; import javax.swing.JOptionPane; import javax.swing.JTable; +import javax.swing.ListModel; import javax.swing.ListSelectionModel; import javax.swing.SwingUtilities; import javax.swing.filechooser.FileNameExtensionFilter; @@ -117,6 +121,14 @@ final class MultiCaseKeywordSearchPanel extends javax.swing.JPanel implements Ex outline.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); caseNameToCaseDataMap = new HashMap<>(); setColumnWidths(); + + //Disable selection in JList + caseSelectionList.setSelectionModel(new DefaultListSelectionModel() { + @Override + public void setSelectionInterval(int index0, int index1) { + super.setSelectionInterval(-1, -1); + } + }); } /** @@ -237,30 +249,27 @@ final class MultiCaseKeywordSearchPanel extends javax.swing.JPanel implements Ex * Get the list of cases from the Multi user case browser */ private void populateCasesList(List selectedNodes) { - Collection disabledCases = getCases(false); - casesPanel.removeAll(); - casesPanel.revalidate(); - casesPanel.repaint(); + caseSelectionList.removeAll(); + caseSelectionList.revalidate(); + caseSelectionList.repaint(); caseNameToCaseDataMap.clear(); - int casePanelWidth = casesPanel.getPreferredSize().width; - int heightOfAllRows = 0; - for (CaseNodeData data : selectedNodes) { - //select all new cases and cases which were previously selected + DefaultListModel listModel = new DefaultListModel<>(); + Collections.sort(selectedNodes, (CaseNodeData o1, CaseNodeData o2) -> { + return o1.getName().toLowerCase() + .compareTo(o2.getName().toLowerCase()); + }); + + for (int i = 0; i < selectedNodes.size(); i++) { + CaseNodeData data = selectedNodes.get(i); String multiUserCaseName = data.getName(); + listModel.addElement(multiUserCaseName); + /** + * Map out the name to CaseNodeData so we can retrieve it later for + * search. + */ caseNameToCaseDataMap.put(multiUserCaseName, data); - boolean isSelected = true; - if (disabledCases.contains(multiUserCaseName)) { - isSelected = false; - } - JCheckBox caseCheckBox = new JCheckBox(multiUserCaseName, isSelected); - caseCheckBox.setBackground(Color.white); - if (casePanelWidth < caseCheckBox.getPreferredSize().width) { - casePanelWidth = caseCheckBox.getPreferredSize().width; - } - heightOfAllRows += caseCheckBox.getPreferredSize().height; - casesPanel.add(caseCheckBox); } - casesPanel.setPreferredSize(new Dimension(casePanelWidth, heightOfAllRows)); + caseSelectionList.setModel(listModel); } @Override @@ -283,12 +292,8 @@ final class MultiCaseKeywordSearchPanel extends javax.swing.JPanel implements Ex keywordTextField = new javax.swing.JTextField(); exactRadioButton = new javax.swing.JRadioButton(); regexRadioButton = new javax.swing.JRadioButton(); - casesScrollPane = new javax.swing.JScrollPane(); - casesPanel = new javax.swing.JPanel(); casesLabel = new javax.swing.JLabel(); resultsLabel = new javax.swing.JLabel(); - uncheckButton = new javax.swing.JButton(); - checkButton = new javax.swing.JButton(); toolDescriptionScrollPane = new javax.swing.JScrollPane(); toolDescriptionTextArea = new javax.swing.JTextArea(); resultsScrollPane = new javax.swing.JScrollPane(); @@ -299,6 +304,8 @@ final class MultiCaseKeywordSearchPanel extends javax.swing.JPanel implements Ex resultsCountLabel = new javax.swing.JLabel(); viewErrorsButton = new javax.swing.JButton(); pickCasesButton = new javax.swing.JButton(); + jScrollPane1 = new javax.swing.JScrollPane(); + caseSelectionList = new javax.swing.JList<>(); setName(""); // NOI18N setOpaque(false); @@ -335,38 +342,10 @@ final class MultiCaseKeywordSearchPanel extends javax.swing.JPanel implements Ex searchTypeGroup.add(regexRadioButton); org.openide.awt.Mnemonics.setLocalizedText(regexRadioButton, org.openide.util.NbBundle.getMessage(MultiCaseKeywordSearchPanel.class, "MultiCaseKeywordSearchPanel.regexRadioButton.text_1")); // NOI18N - casesScrollPane.setPreferredSize(new java.awt.Dimension(174, 281)); - - casesPanel.setBackground(new java.awt.Color(255, 255, 255)); - casesPanel.setPreferredSize(new java.awt.Dimension(152, 197)); - casesPanel.setLayout(new javax.swing.BoxLayout(casesPanel, javax.swing.BoxLayout.Y_AXIS)); - casesScrollPane.setViewportView(casesPanel); - org.openide.awt.Mnemonics.setLocalizedText(casesLabel, org.openide.util.NbBundle.getMessage(MultiCaseKeywordSearchPanel.class, "MultiCaseKeywordSearchPanel.casesLabel.text_1")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(resultsLabel, org.openide.util.NbBundle.getMessage(MultiCaseKeywordSearchPanel.class, "MultiCaseKeywordSearchPanel.resultsLabel.text")); // NOI18N - org.openide.awt.Mnemonics.setLocalizedText(uncheckButton, org.openide.util.NbBundle.getMessage(MultiCaseKeywordSearchPanel.class, "MultiCaseKeywordSearchPanel.uncheckButton.text")); // NOI18N - uncheckButton.setMargin(new java.awt.Insets(2, 6, 2, 6)); - uncheckButton.setMaximumSize(new java.awt.Dimension(84, 23)); - uncheckButton.setMinimumSize(new java.awt.Dimension(84, 23)); - uncheckButton.setPreferredSize(new java.awt.Dimension(84, 23)); - uncheckButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - uncheckButtonActionPerformed(evt); - } - }); - - org.openide.awt.Mnemonics.setLocalizedText(checkButton, org.openide.util.NbBundle.getMessage(MultiCaseKeywordSearchPanel.class, "MultiCaseKeywordSearchPanel.checkButton.text")); // NOI18N - checkButton.setMaximumSize(new java.awt.Dimension(84, 23)); - checkButton.setMinimumSize(new java.awt.Dimension(84, 23)); - checkButton.setPreferredSize(new java.awt.Dimension(84, 23)); - checkButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - checkButtonActionPerformed(evt); - } - }); - toolDescriptionTextArea.setEditable(false); toolDescriptionTextArea.setBackground(new java.awt.Color(240, 240, 240)); toolDescriptionTextArea.setColumns(20); @@ -423,6 +402,8 @@ final class MultiCaseKeywordSearchPanel extends javax.swing.JPanel implements Ex } }); + jScrollPane1.setViewportView(caseSelectionList); + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( @@ -438,22 +419,17 @@ final class MultiCaseKeywordSearchPanel extends javax.swing.JPanel implements Ex .addComponent(substringRadioButton) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(regexRadioButton)) - .addComponent(keywordTextField, javax.swing.GroupLayout.DEFAULT_SIZE, 679, Short.MAX_VALUE)) + .addComponent(keywordTextField, javax.swing.GroupLayout.DEFAULT_SIZE, 591, Short.MAX_VALUE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(toolDescriptionScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 295, javax.swing.GroupLayout.PREFERRED_SIZE)) .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) .addComponent(casesLabel) - .addComponent(casesScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) - .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup() - .addComponent(searchButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(pickCasesButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup() - .addComponent(uncheckButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(checkButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))) + .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 174, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGroup(layout.createSequentialGroup() + .addComponent(pickCasesButton, javax.swing.GroupLayout.PREFERRED_SIZE, 84, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(searchButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() @@ -464,7 +440,7 @@ final class MultiCaseKeywordSearchPanel extends javax.swing.JPanel implements Ex .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(viewErrorsButton) - .addComponent(warningLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 695, Short.MAX_VALUE)) + .addComponent(warningLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 607, Short.MAX_VALUE)) .addGap(14, 14, 14) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) .addComponent(exportButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) @@ -473,12 +449,9 @@ final class MultiCaseKeywordSearchPanel extends javax.swing.JPanel implements Ex .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addGap(196, 196, 196) - .addComponent(searchProgressBar, javax.swing.GroupLayout.DEFAULT_SIZE, 769, Short.MAX_VALUE) + .addComponent(searchProgressBar, javax.swing.GroupLayout.DEFAULT_SIZE, 608, Short.MAX_VALUE) .addGap(108, 108, 108))) ); - - layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {checkButton, uncheckButton}); - layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() @@ -501,21 +474,18 @@ final class MultiCaseKeywordSearchPanel extends javax.swing.JPanel implements Ex .addComponent(resultsCountLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 14, javax.swing.GroupLayout.PREFERRED_SIZE))) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(resultsScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(casesScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addComponent(resultsScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 281, Short.MAX_VALUE) + .addComponent(jScrollPane1)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(uncheckButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(checkButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addComponent(warningLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 15, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(exportButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(exportButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(pickCasesButton) + .addComponent(searchButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(searchButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(viewErrorsButton) - .addComponent(pickCasesButton)) + .addComponent(viewErrorsButton) .addComponent(cancelButton)) .addContainerGap()) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -536,7 +506,7 @@ final class MultiCaseKeywordSearchPanel extends javax.swing.JPanel implements Ex */ private void searchButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_searchButtonActionPerformed if (null == searchThread) { - Collection cases = getCases(true); + Collection cases = getCases(); String searchString = keywordTextField.getText(); if (cases.isEmpty()) { warningLabel.setText(Bundle.MultiCaseKeywordSearchPanel_warningText_noCases()); @@ -566,21 +536,16 @@ final class MultiCaseKeywordSearchPanel extends javax.swing.JPanel implements Ex }//GEN-LAST:event_searchButtonActionPerformed /** - * Get the cases which match the selected status specified by isSelected. - * - * @param isSelected true to get selected cases false to get unselected - * cases + * Get the case names from the Case List * * @return cases the cases that match the selected status of isSelected */ - private Collection getCases(boolean isSelected) { + private Collection getCases() { Collection cases = new HashSet<>(); - for (Component comp : casesPanel.getComponents()) { - if (comp instanceof JCheckBox) { - if (((AbstractButton) comp).isSelected() == isSelected) { - cases.add(((AbstractButton) comp).getText()); - } - } + ListModel listModel = caseSelectionList.getModel(); + for(int i = 0; i < listModel.getSize(); i++) { + String caseName = (String) listModel.getElementAt(i); + cases.add(caseName); } return cases; } @@ -637,24 +602,6 @@ final class MultiCaseKeywordSearchPanel extends javax.swing.JPanel implements Ex resultsScrollPane.setPreferredSize(new Dimension(outline.getPreferredSize().width, resultsScrollPane.getPreferredSize().height)); } - /** - * Un-select all check boxes in the cases list - * - * @param evt ignored - */ - private void uncheckButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_uncheckButtonActionPerformed - allCheckboxesSetSelected(false); - }//GEN-LAST:event_uncheckButtonActionPerformed - - /** - * Select all check boxes in the cases list - * - * @param evt ignored - */ - private void checkButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_checkButtonActionPerformed - allCheckboxesSetSelected(true); - }//GEN-LAST:event_checkButtonActionPerformed - /** * Cancel the current multi-case search which is being performed. * @@ -811,20 +758,6 @@ final class MultiCaseKeywordSearchPanel extends javax.swing.JPanel implements Ex } } - /** - * Set the selected status of all checkboxes. - * - * @param selected true if all checkboxes should be selected, false if no - * check boxes should be selected. - */ - private void allCheckboxesSetSelected(boolean selected) { - for (Component comp : casesPanel.getComponents()) { - if (comp instanceof JCheckBox) { - ((AbstractButton) comp).setSelected(selected); - } - } - } - /** * Ask the user if they want to continue their search while this window is * closed. Cancels the current search if they select no. @@ -849,12 +782,11 @@ final class MultiCaseKeywordSearchPanel extends javax.swing.JPanel implements Ex // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton cancelButton; + private javax.swing.JList caseSelectionList; private javax.swing.JLabel casesLabel; - private javax.swing.JPanel casesPanel; - private javax.swing.JScrollPane casesScrollPane; - private javax.swing.JButton checkButton; private javax.swing.JRadioButton exactRadioButton; private javax.swing.JButton exportButton; + private javax.swing.JScrollPane jScrollPane1; private javax.swing.JTextField keywordTextField; private javax.swing.JButton pickCasesButton; private javax.swing.JRadioButton regexRadioButton; @@ -867,7 +799,6 @@ final class MultiCaseKeywordSearchPanel extends javax.swing.JPanel implements Ex private javax.swing.JRadioButton substringRadioButton; private javax.swing.JScrollPane toolDescriptionScrollPane; private javax.swing.JTextArea toolDescriptionTextArea; - private javax.swing.JButton uncheckButton; private javax.swing.JButton viewErrorsButton; private javax.swing.JLabel warningLabel; // End of variables declaration//GEN-END:variables From b7a95b978731979e5ede584118e2091974f8819b Mon Sep 17 00:00:00 2001 From: Ann Priestman Date: Tue, 19 Mar 2019 09:42:24 -0400 Subject: [PATCH 34/59] Change to storing max IDs in new table. Close the portable case when done. --- .../report/CreatePortableCaseModule.java | 40 +++++++++++++------ 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/report/CreatePortableCaseModule.java b/Core/src/org/sleuthkit/autopsy/report/CreatePortableCaseModule.java index 609928d98f..e485c971f7 100644 --- a/Core/src/org/sleuthkit/autopsy/report/CreatePortableCaseModule.java +++ b/Core/src/org/sleuthkit/autopsy/report/CreatePortableCaseModule.java @@ -66,6 +66,7 @@ public class CreatePortableCaseModule implements GeneralReportModule { private static final Logger logger = Logger.getLogger(CreatePortableCaseModule.class.getName()); private static final String FILE_FOLDER_NAME = "PortableCaseFiles"; private static final String UNKNOWN_FILE_TYPE_FOLDER = "Other"; + private static final String PORTABLE_CASE_TABLE_NAME = "portable_case_data"; private CreatePortableCasePanel configPanel; // These are the types for the exported file subfolders @@ -318,12 +319,7 @@ public class CreatePortableCaseModule implements GeneralReportModule { // Store the highest IDs try { - currentCase.getSleuthkitCase().getCaseDbAccessManager() - .select("max(obj_id) as max_id from tsk_objects", new StoreMaxIdCallback("tsk_objects")); - currentCase.getSleuthkitCase().getCaseDbAccessManager() - .select("max(tag_id) as max_id from content_tags", new StoreMaxIdCallback("content_tags")); - currentCase.getSleuthkitCase().getCaseDbAccessManager() - .select("max(tag_id) as max_id from blackboard_artifact_tags", new StoreMaxIdCallback("blackboard_artifact_tags")); + saveHighestIds(); } catch (TskCoreException ex) { handleError("Error storing maximum database IDs", Bundle.CreatePortableCaseModule_createCase_errorStoringMaxIds(), ex, progressPanel); @@ -356,6 +352,27 @@ public class CreatePortableCaseModule implements GeneralReportModule { } + /** + * Save the current highest IDs to the portable case. + * + * @throws TskCoreException + */ + private void saveHighestIds() throws TskCoreException { + + CaseDbAccessManager currentCaseDbManager = currentCase.getSleuthkitCase().getCaseDbAccessManager(); + + String tableSchema = "( id INTEGER PRIMARY KEY, " + + " name TEXT UNIQUE NOT NULL," + + " value TEXT NOT NULL )"; + + portableSkCase.getCaseDbAccessManager().createTable(PORTABLE_CASE_TABLE_NAME, tableSchema); + + currentCaseDbManager.select("max(obj_id) as max_id from tsk_objects", new StoreMaxIdCallback("tsk_objects")); + currentCaseDbManager.select("max(tag_id) as max_id from content_tags", new StoreMaxIdCallback("content_tags")); + currentCaseDbManager.select("max(tag_id) as max_id from blackboard_artifact_tags", new StoreMaxIdCallback("blackboard_artifact_tags")); + currentCaseDbManager.select("max(examiner_id) as max_id from tsk_examiners", new StoreMaxIdCallback("tsk_examiners")); + } + /** * Add all files with a given tag to the portable case. * @@ -694,8 +711,7 @@ public class CreatePortableCaseModule implements GeneralReportModule { oldTagNameToNewTagName.clear(); currentCase = null; if (portableSkCase != null) { - // We want to close the database connections here but it is currently not possible. JIRA-4736 - portableSkCase = null; + portableSkCase.close(); } caseFolder = null; copiedFilesFolder = null; @@ -708,7 +724,7 @@ public class CreatePortableCaseModule implements GeneralReportModule { return configPanel; } - class StoreMaxIdCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback { + private class StoreMaxIdCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback { private final String tableName; @@ -723,10 +739,8 @@ public class CreatePortableCaseModule implements GeneralReportModule { while (rs.next()) { try { Long maxId = rs.getLong("max_id"); - String nameStr = "PORTABLE_CASE_" + tableName + "_MAX_ID"; - nameStr = nameStr.toUpperCase(); - String query = "INSERT INTO tsk_db_info_extended (name, value) VALUES ('" + nameStr + "', '" + maxId + "')"; - currentCase.getSleuthkitCase().getCaseDbAccessManager().insert("tsk_db_info_extended", query); + String query = " (name, value) VALUES ('" + tableName + "', '" + maxId + "')"; + portableSkCase.getCaseDbAccessManager().insert(PORTABLE_CASE_TABLE_NAME, query); } catch (SQLException ex) { logger.log(Level.WARNING, "Unable to get maximum ID from result set", ex); From cc1924faca7e91bbacc2b139b2204612f1dd4b43 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Tue, 19 Mar 2019 09:44:40 -0400 Subject: [PATCH 35/59] Removed some warnings --- .../multicase/Bundle.properties-MERGED | 33 ------------------- .../MultiCaseKeywordSearchPanel.java | 6 ++-- .../multicase/SelectMultiUserCasesDialog.java | 2 +- 3 files changed, 4 insertions(+), 37 deletions(-) diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/multicase/Bundle.properties-MERGED b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/multicase/Bundle.properties-MERGED index a42701dd53..9736f90467 100755 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/multicase/Bundle.properties-MERGED +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/multicase/Bundle.properties-MERGED @@ -29,55 +29,22 @@ MultiCaseKeywordSearchPanel.warningText.noCases=At least one case must be select MultiCaseKeywordSearchTopComponent.exceptionMessage.failedToCreatePanel=Failed to create Multi-case Keyword Search panel. MultiCaseKeywordSearchTopComponent.name.text=Multi-case Keyword Search MultiCaseSearcher.exceptionMessage.cancelledMessage=Search cancelled -# {0} - connection info -# {1} - case name -# {2} - case directory MultiCaseSearcher.exceptionMessage.errorLoadingCore=Error connecting to Solr server and loading core (URL: {0}) for case {1} in {2} -# {0} - PostgreSQL server host -# {1} - PostgreSQL server port -# {2} - case database name -# {3} - case directory MultiCaseSearcher.exceptionMessage.errorOpeningCaseDatabase=Error connecting to PostgreSQL server (Host/Port: [{0}:{1}] and opening case database {2} for case at {3} -# {0} - case directory MultiCaseSearcher.exceptionMessage.failedToFindCaseMetadata=Failed to find case metadata file in {0} -# {0} - case_name MultiCaseSearcher.exceptionMessage.failedToGetCaseDatabaseConnectionInfo=Failed to get case database connection info for case {0} -# {0} - case directory path MultiCaseSearcher.exceptionMessage.failedToGetCaseDirReadlock=Failed to obtain read lock for case directory at {0} -# {0} - case directory MultiCaseSearcher.exceptionMessage.failedToParseCaseMetadata=Failed to parse case file metadata in {0} -# {0} - Solr document id -# {1} - case database name -# {2} - case directory MultiCaseSearcher.exceptionMessage.hitProcessingError=Failed to query case database for processing of Solr object id {0} of case {1} in {2} -# {0} - file name -# {1} - case directory MultiCaseSearcher.exceptionMessage.missingSolrPropertiesFile=Missing {0} file in {1} -# {0} - file name -# {1} - case directory MultiCaseSearcher.exceptionMessage.solrPropertiesFileParseError=Error parsing {0} file in {1} -# {0} - query -# {1} - case_name MultiCaseSearcher.exceptionMessage.solrQueryError=Failed to execute query "{0}" on case {1} -# {0} - case name -# {1} - case counter -# {2} - total cases MultiCaseSearcher.progressMessage.acquiringSharedLockForCase=Acquiring shared lock for "{0}" ({1} of {2} case(s)) MultiCaseSearcher.progressMessage.creatingSolrQuery=Creating search query for Solr server -# {0} - case name -# {1} - case counter -# {2} - total cases MultiCaseSearcher.progressMessage.executingSolrQueryForCase=Getting keyword hits for "{0}" ({1} of {2} case(s)) MultiCaseSearcher.progressMessage.findingCases=Finding selected cases -# {0} - case name -# {1} - case counter -# {2} - total cases MultiCaseSearcher.progressMessage.loadingSolrCoreForCase=Loading Solr core for "{0}" ({1} of {2} case(s)) -# {0} - case name -# {1} - case counter -# {2} - total cases MultiCaseSearcher.progressMessage.openingCaseDbForCase=Opening case database for "{0}" ({1} of {2} case(s)) -# {0} - total cases MultiCaseSearcher.progressMessage.startingCaseSearches=Searching {0} case(s) SelectMultiUserCasesPanel.selectAllButton.text=Select All SelectMultiUserCasesPanel.deselectAllButton.text=Deselect All diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/multicase/MultiCaseKeywordSearchPanel.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/multicase/MultiCaseKeywordSearchPanel.java index 07383604dc..bc197c16a0 100755 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/multicase/MultiCaseKeywordSearchPanel.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/multicase/MultiCaseKeywordSearchPanel.java @@ -542,9 +542,9 @@ final class MultiCaseKeywordSearchPanel extends javax.swing.JPanel implements Ex */ private Collection getCases() { Collection cases = new HashSet<>(); - ListModel listModel = caseSelectionList.getModel(); + ListModel listModel = caseSelectionList.getModel(); for(int i = 0; i < listModel.getSize(); i++) { - String caseName = (String) listModel.getElementAt(i); + String caseName = listModel.getElementAt(i); cases.add(caseName); } return cases; @@ -665,10 +665,10 @@ final class MultiCaseKeywordSearchPanel extends javax.swing.JPanel implements Ex }//GEN-LAST:event_viewErrorsButtonActionPerformed private void pickCasesButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pickCasesButtonActionPerformed + caseSelectionDialog.setVisible(true); if (currentConfirmedSelections != null) { caseSelectionDialog.setNodeSelections(currentConfirmedSelections); } - caseSelectionDialog.setVisible(true); }//GEN-LAST:event_pickCasesButtonActionPerformed diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/multicase/SelectMultiUserCasesDialog.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/multicase/SelectMultiUserCasesDialog.java index 28b583e563..75d449c932 100755 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/multicase/SelectMultiUserCasesDialog.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/multicase/SelectMultiUserCasesDialog.java @@ -64,7 +64,7 @@ class SelectMultiUserCasesDialog extends javax.swing.JDialog { try { multiUserCasesPanel.setSelections(selections); } catch (PropertyVetoException ex) { - + //Do-nothing } } From 46729e8ded81b2941f6158e9f310571743793e80 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Tue, 19 Mar 2019 09:47:04 -0400 Subject: [PATCH 36/59] Added bundle comments back in --- .../multicase/Bundle.properties-MERGED | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/multicase/Bundle.properties-MERGED b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/multicase/Bundle.properties-MERGED index 9736f90467..a42701dd53 100755 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/multicase/Bundle.properties-MERGED +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/multicase/Bundle.properties-MERGED @@ -29,22 +29,55 @@ MultiCaseKeywordSearchPanel.warningText.noCases=At least one case must be select MultiCaseKeywordSearchTopComponent.exceptionMessage.failedToCreatePanel=Failed to create Multi-case Keyword Search panel. MultiCaseKeywordSearchTopComponent.name.text=Multi-case Keyword Search MultiCaseSearcher.exceptionMessage.cancelledMessage=Search cancelled +# {0} - connection info +# {1} - case name +# {2} - case directory MultiCaseSearcher.exceptionMessage.errorLoadingCore=Error connecting to Solr server and loading core (URL: {0}) for case {1} in {2} +# {0} - PostgreSQL server host +# {1} - PostgreSQL server port +# {2} - case database name +# {3} - case directory MultiCaseSearcher.exceptionMessage.errorOpeningCaseDatabase=Error connecting to PostgreSQL server (Host/Port: [{0}:{1}] and opening case database {2} for case at {3} +# {0} - case directory MultiCaseSearcher.exceptionMessage.failedToFindCaseMetadata=Failed to find case metadata file in {0} +# {0} - case_name MultiCaseSearcher.exceptionMessage.failedToGetCaseDatabaseConnectionInfo=Failed to get case database connection info for case {0} +# {0} - case directory path MultiCaseSearcher.exceptionMessage.failedToGetCaseDirReadlock=Failed to obtain read lock for case directory at {0} +# {0} - case directory MultiCaseSearcher.exceptionMessage.failedToParseCaseMetadata=Failed to parse case file metadata in {0} +# {0} - Solr document id +# {1} - case database name +# {2} - case directory MultiCaseSearcher.exceptionMessage.hitProcessingError=Failed to query case database for processing of Solr object id {0} of case {1} in {2} +# {0} - file name +# {1} - case directory MultiCaseSearcher.exceptionMessage.missingSolrPropertiesFile=Missing {0} file in {1} +# {0} - file name +# {1} - case directory MultiCaseSearcher.exceptionMessage.solrPropertiesFileParseError=Error parsing {0} file in {1} +# {0} - query +# {1} - case_name MultiCaseSearcher.exceptionMessage.solrQueryError=Failed to execute query "{0}" on case {1} +# {0} - case name +# {1} - case counter +# {2} - total cases MultiCaseSearcher.progressMessage.acquiringSharedLockForCase=Acquiring shared lock for "{0}" ({1} of {2} case(s)) MultiCaseSearcher.progressMessage.creatingSolrQuery=Creating search query for Solr server +# {0} - case name +# {1} - case counter +# {2} - total cases MultiCaseSearcher.progressMessage.executingSolrQueryForCase=Getting keyword hits for "{0}" ({1} of {2} case(s)) MultiCaseSearcher.progressMessage.findingCases=Finding selected cases +# {0} - case name +# {1} - case counter +# {2} - total cases MultiCaseSearcher.progressMessage.loadingSolrCoreForCase=Loading Solr core for "{0}" ({1} of {2} case(s)) +# {0} - case name +# {1} - case counter +# {2} - total cases MultiCaseSearcher.progressMessage.openingCaseDbForCase=Opening case database for "{0}" ({1} of {2} case(s)) +# {0} - total cases MultiCaseSearcher.progressMessage.startingCaseSearches=Searching {0} case(s) SelectMultiUserCasesPanel.selectAllButton.text=Select All SelectMultiUserCasesPanel.deselectAllButton.text=Deselect All From e7f0e87c8dddb4ee554553c89d04b115aca7241e Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Tue, 19 Mar 2019 09:55:57 -0400 Subject: [PATCH 37/59] Switched from using getClass.getName to instanceof --- .../communications/snapshot/CommSnapShotReportWriter.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/snapshot/CommSnapShotReportWriter.java b/Core/src/org/sleuthkit/autopsy/communications/snapshot/CommSnapShotReportWriter.java index 8024415d63..7b4dee6d19 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/snapshot/CommSnapShotReportWriter.java +++ b/Core/src/org/sleuthkit/autopsy/communications/snapshot/CommSnapShotReportWriter.java @@ -88,7 +88,7 @@ public class CommSnapShotReportWriter extends UiSnapShotReportWriter { List filters = filter.getAndFilters(); for (SubFilter filter : filters) { - if (filter.getClass().getName().equals(DateRangeFilter.class.getName())) { + if (filter instanceof DateRangeFilter) { long startDate = ((DateRangeFilter) filter).getStartDate(); long endDate = ((DateRangeFilter) filter).getEndDate(); @@ -100,7 +100,7 @@ public class CommSnapShotReportWriter extends UiSnapShotReportWriter { if (endDate > 0) { snapShotContext.put("endTime", formatter.format(new Date((Instant.ofEpochSecond(endDate)).toEpochMilli()))); //NON-NLS } - } else if (filter.getClass().getName().equals(AccountTypeFilter.class.getName())) { + } else if (filter instanceof AccountTypeFilter) { Set selectedAccounts = ((AccountTypeFilter) filter).getAccountTypes(); ArrayList fullAccountList = new ArrayList<>(); @@ -113,7 +113,7 @@ public class CommSnapShotReportWriter extends UiSnapShotReportWriter { } snapShotContext.put("accounts", fullAccountList); - } else if (filter.getClass().getName().equals(DeviceFilter.class.getName())) { + } else if (filter instanceof DeviceFilter) { Collection ids = ((DeviceFilter) filter).getDevices(); ArrayList list = new ArrayList<>(); try { From 8c64e63277324e69dde7880abf2034131483dfb6 Mon Sep 17 00:00:00 2001 From: Ann Priestman Date: Tue, 19 Mar 2019 10:05:00 -0400 Subject: [PATCH 38/59] Name changes --- .../autopsy/report/CreatePortableCaseModule.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/report/CreatePortableCaseModule.java b/Core/src/org/sleuthkit/autopsy/report/CreatePortableCaseModule.java index e485c971f7..3ce5c3a369 100644 --- a/Core/src/org/sleuthkit/autopsy/report/CreatePortableCaseModule.java +++ b/Core/src/org/sleuthkit/autopsy/report/CreatePortableCaseModule.java @@ -66,7 +66,7 @@ public class CreatePortableCaseModule implements GeneralReportModule { private static final Logger logger = Logger.getLogger(CreatePortableCaseModule.class.getName()); private static final String FILE_FOLDER_NAME = "PortableCaseFiles"; private static final String UNKNOWN_FILE_TYPE_FOLDER = "Other"; - private static final String PORTABLE_CASE_TABLE_NAME = "portable_case_data"; + private static final String MAX_ID_TABLE_NAME = "portable_case_max_ids"; private CreatePortableCasePanel configPanel; // These are the types for the exported file subfolders @@ -361,11 +361,10 @@ public class CreatePortableCaseModule implements GeneralReportModule { CaseDbAccessManager currentCaseDbManager = currentCase.getSleuthkitCase().getCaseDbAccessManager(); - String tableSchema = "( id INTEGER PRIMARY KEY, " - + " name TEXT UNIQUE NOT NULL," - + " value TEXT NOT NULL )"; + String tableSchema = "( table_name TEXT PRIMARY KEY, " + + " max_id TEXT)"; - portableSkCase.getCaseDbAccessManager().createTable(PORTABLE_CASE_TABLE_NAME, tableSchema); + portableSkCase.getCaseDbAccessManager().createTable(MAX_ID_TABLE_NAME, tableSchema); currentCaseDbManager.select("max(obj_id) as max_id from tsk_objects", new StoreMaxIdCallback("tsk_objects")); currentCaseDbManager.select("max(tag_id) as max_id from content_tags", new StoreMaxIdCallback("content_tags")); @@ -739,8 +738,8 @@ public class CreatePortableCaseModule implements GeneralReportModule { while (rs.next()) { try { Long maxId = rs.getLong("max_id"); - String query = " (name, value) VALUES ('" + tableName + "', '" + maxId + "')"; - portableSkCase.getCaseDbAccessManager().insert(PORTABLE_CASE_TABLE_NAME, query); + String query = " (table_name, max_id) VALUES ('" + tableName + "', '" + maxId + "')"; + portableSkCase.getCaseDbAccessManager().insert(MAX_ID_TABLE_NAME, query); } catch (SQLException ex) { logger.log(Level.WARNING, "Unable to get maximum ID from result set", ex); From 8f158dad12f6c88abb98ee378580ff73ad3f4629 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Tue, 19 Mar 2019 12:28:18 -0400 Subject: [PATCH 39/59] 4791 change token file name to be most common file name --- .../CommonAttributeValue.java | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeValue.java b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeValue.java index 7895e7a410..c4fe25f14b 100644 --- a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeValue.java +++ b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeValue.java @@ -22,8 +22,10 @@ package org.sleuthkit.autopsy.commonpropertiessearch; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import org.sleuthkit.datamodel.AbstractFile; @@ -36,7 +38,7 @@ final public class CommonAttributeValue { private final String value; private final List fileInstances; - private String tokenFileName = null; + private final Map fileNames = new HashMap<>(); CommonAttributeValue(String value) { this.value = value; @@ -53,6 +55,14 @@ final public class CommonAttributeValue { * @return the file name of an instance of this file */ String getTokenFileName() { + String tokenFileName = null; + int maxValue = 0; + for (String key : fileNames.keySet()){ + if (fileNames.get(key) > maxValue){ + maxValue = fileNames.get(key); + tokenFileName = key; + } + } return tokenFileName; } @@ -97,8 +107,10 @@ final public class CommonAttributeValue { } void addInstance(AbstractCommonAttributeInstance metadata) { - if (tokenFileName == null && metadata.getAbstractFile() != null) { - tokenFileName = metadata.getAbstractFile().getName(); + if (metadata.getAbstractFile() != null) { + Integer currentValue = fileNames.get(metadata.getAbstractFile().getName()); + currentValue = currentValue == null ? 1 : currentValue+1; + fileNames.put(metadata.getAbstractFile().getName(), currentValue); } this.fileInstances.add(metadata); } From 9c8aaeca4adc7c2a576e85062613235f0cc04e83 Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Tue, 19 Mar 2019 13:58:19 -0400 Subject: [PATCH 40/59] Fixed a bug in CASE-UCO report module initialization --- Core/src/org/sleuthkit/autopsy/core/layer.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/core/layer.xml b/Core/src/org/sleuthkit/autopsy/core/layer.xml index c25f37199e..02ea2b02cb 100644 --- a/Core/src/org/sleuthkit/autopsy/core/layer.xml +++ b/Core/src/org/sleuthkit/autopsy/core/layer.xml @@ -342,9 +342,9 @@ - + - + + @@ -289,7 +289,7 @@ - + From 81b2938fd36f6da7a9d42cc4b552077aeafcb582 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Thu, 21 Mar 2019 08:32:12 -0400 Subject: [PATCH 49/59] Revert "4841: RecentActivity not making progress on large image" --- .../recentactivity/Bundle.properties-MERGED | 2 - .../autopsy/recentactivity/Chrome.java | 4 +- .../recentactivity/ChromeCacheExtractor.java | 85 ++++++------------- 3 files changed, 26 insertions(+), 65 deletions(-) diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED index 8c51d5e2a6..d909db9e71 100755 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED @@ -2,7 +2,6 @@ cannotBuildXmlParser=Unable to build XML parser: cannotLoadSEUQA=Unable to load Search Engine URL Query Analyzer settings file, SEUQAMappings.xml: cannotParseXml=Unable to parse XML file: ChromeCacheExtractor.moduleName=ChromeCacheExtractor -ChromeCacheExtractor.progressMsg={0}: Extracting cache entry {1} of {2} entries from {3} DataSourceUsage_AndroidMedia=Android Media Card DataSourceUsage_FlashDrive=Flash Drive # {0} - OS name @@ -135,7 +134,6 @@ Progress_Message_Analyze_Registry=Analyzing Registry Files Progress_Message_Analyze_Usage=Data Sources Usage Analysis Progress_Message_Chrome_AutoFill=Chrome Auto Fill Progress_Message_Chrome_Bookmarks=Chrome Bookmarks -Progress_Message_Chrome_Cache=Chrome Cache Progress_Message_Chrome_Cookies=Chrome Cookies Progress_Message_Chrome_Downloads=Chrome Downloads Progress_Message_Chrome_FormHistory=Chrome Form History diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chrome.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chrome.java index 5006ee27b5..23a6f3ab22 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chrome.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chrome.java @@ -93,7 +93,6 @@ class Chrome extends Extract { "Progress_Message_Chrome_FormHistory=Chrome Form History", "Progress_Message_Chrome_AutoFill=Chrome Auto Fill", "Progress_Message_Chrome_Logins=Chrome Logins", - "Progress_Message_Chrome_Cache=Chrome Cache", }) Chrome() { @@ -124,8 +123,7 @@ class Chrome extends Extract { progressBar.progress(Bundle.Progress_Message_Chrome_Downloads()); this.getDownload(); - progressBar.progress(Bundle.Progress_Message_Chrome_Cache()); - ChromeCacheExtractor chromeCacheExtractor = new ChromeCacheExtractor(dataSource, context, progressBar); + ChromeCacheExtractor chromeCacheExtractor = new ChromeCacheExtractor(dataSource, context); chromeCacheExtractor.getCaches(); } diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ChromeCacheExtractor.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ChromeCacheExtractor.java index c9ae1b674c..1f7eb0580e 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ChromeCacheExtractor.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ChromeCacheExtractor.java @@ -44,7 +44,6 @@ import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.services.FileManager; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datamodel.ContentUtils; -import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress; import org.sleuthkit.autopsy.ingest.IngestJobContext; import org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException; import org.sleuthkit.autopsy.ingest.IngestServices; @@ -56,6 +55,7 @@ import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.DerivedFile; +import org.sleuthkit.datamodel.ReadContentInputStream; import org.sleuthkit.datamodel.TimeUtilities; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; @@ -93,17 +93,12 @@ final class ChromeCacheExtractor { private final Content dataSource; private final IngestJobContext context; - private final DataSourceIngestModuleProgress progressBar; private final IngestServices services = IngestServices.getInstance(); private Case currentCase; private FileManager fileManager; - // A file table to cache copies of index and data_n files. private final Map filesTable = new HashMap<>(); - // A file table to cache the f_* files. - private final Map externalFilesTable = new HashMap<>(); - /** * Encapsulates abstract file for a cache file as well as a temp file copy * that can be accessed as a random access file. @@ -132,14 +127,12 @@ final class ChromeCacheExtractor { } @NbBundle.Messages({ - "ChromeCacheExtractor.moduleName=ChromeCacheExtractor", - "ChromeCacheExtractor.progressMsg={0}: Extracting cache entry {1} of {2} entries from {3}" + "ChromeCacheExtractor.moduleName=ChromeCacheExtractor" }) - ChromeCacheExtractor(Content dataSource, IngestJobContext context, DataSourceIngestModuleProgress progressBar ) { + ChromeCacheExtractor(Content dataSource, IngestJobContext context ) { moduleName = Bundle.ChromeCacheExtractor_moduleName(); this.dataSource = dataSource; this.context = context; - this.progressBar = progressBar; } @@ -178,7 +171,6 @@ final class ChromeCacheExtractor { void subInit(String cachePath) throws IngestModuleException { filesTable.clear(); - externalFilesTable.clear(); String cacheAbsOutputFolderName = this.getAbsOutputFolderName() + cachePath; File outDir = new File(cacheAbsOutputFolderName); @@ -292,9 +284,6 @@ final class ChromeCacheExtractor { return; } } - - // find all f_* files in a single query. - findExternalFiles(cachePath); } catch (TskCoreException | IngestModuleException ex) { String msg = "Failed to find cache files in path " + cachePath; //NON-NLS @@ -317,10 +306,8 @@ final class ChromeCacheExtractor { // Process each address in the table for (int i = 0; i < indexHdr.getTableLen(); i++) { CacheAddress addr = new CacheAddress(indexFileROBuffer.getInt() & UINT32_MASK, cachePath); + if (addr.isInitialized()) { - progressBar.progress( NbBundle.getMessage(this.getClass(), - "ChromeCacheExtractor.progressMsg", - moduleName, i, indexHdr.getTableLen(), cachePath) ); try { List addedFiles = this.getCacheEntry(addr, sourceArtifacts, webCacheArtifacts); derivedFiles.addAll(addedFiles); @@ -425,10 +412,13 @@ final class ChromeCacheExtractor { moduleName, dataFile.get().getUniquePath())); - webCacheArtifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID, - moduleName, dataFile.get().getId())); + long pathID = Util.findID(dataSource, dataFile.get().getUniquePath()); + if (pathID != -1) { + webCacheArtifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID, + moduleName, pathID)); + } - webCacheArtifacts.add(webCacheArtifact); + webCacheArtifacts.add(webCacheArtifact); } if (isBrotliCompressed) { @@ -469,10 +459,12 @@ final class ChromeCacheExtractor { webCacheArtifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH, moduleName, derivedFile.getUniquePath())); - - webCacheArtifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID, - moduleName, derivedFile.getId())); - + long pathID = Util.findID(dataSource, derivedFile.getUniquePath()); + if (pathID != -1) { + webCacheArtifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID, + moduleName, pathID)); + } + webCacheArtifacts.add(webCacheArtifact); } @@ -493,37 +485,13 @@ final class ChromeCacheExtractor { } /** - * Finds all the f_* files in the specified path, and fills them in the - * effFilesTable, so that subsequent searches are fast. - * - * @param cachePath path under which to look for. - * - * @throws TskCoreException - */ - private void findExternalFiles(String cachePath) throws TskCoreException { - - List effFiles = fileManager.findFiles(dataSource, "f_%", cachePath); //NON-NLS - for (AbstractFile abstractFile : effFiles ) { - this.externalFilesTable.put(cachePath + abstractFile.getName(), abstractFile); - } - } - /** - * Finds abstract file for cache file with a specified name. - * First checks in the file tables. + * Finds abstract file for cache file with a specified name * * @param cacheFileName - * @return Optional abstract file + * @return Opt * @throws TskCoreException */ Optional findCacheFile(String cacheFileName, String cachePath) throws TskCoreException { - - String fileTableKey = cachePath + cacheFileName; - if (cacheFileName.startsWith("f_") && externalFilesTable.containsKey(fileTableKey)) { - return Optional.of(externalFilesTable.get(fileTableKey)); - } - if (filesTable.containsKey(fileTableKey)) { - return Optional.of(filesTable.get(fileTableKey).getAbstractFile()); - } List cacheFiles = fileManager.findFiles(dataSource, cacheFileName, cachePath); //NON-NLS if (!cacheFiles.isEmpty()) { @@ -946,11 +914,9 @@ final class ChromeCacheExtractor { return; } - // Don't extract data from external files. + cacheFileCopy = getCacheFileCopy(address.getFilename(), address.getCachePath()).get(); if (!address.isInExternalFile() ) { - cacheFileCopy = getCacheFileCopy(address.getFilename(), address.getCachePath()).get(); - this.data = new byte [length]; ByteBuffer buf = cacheFileCopy.getByteBuffer(); int dataOffset = DATAFILE_HDR_SIZE + address.getStartBlock() * address.getBlockSize(); @@ -986,8 +952,8 @@ final class ChromeCacheExtractor { i++; } - // http headers are terminated by 0x00 0x00 - if (i == data.length || data[i+1] == 0) { + // hhtp headers are terminated by 0x00 0x00 + if (data[i+1] == 0) { done = true; } @@ -999,11 +965,10 @@ final class ChromeCacheExtractor { httpResponse = headerLine; } else { int nPos = headerLine.indexOf(':'); - if (nPos > 0 ) { - String key = headerLine.substring(0, nPos); - String val= headerLine.substring(nPos+1); - httpHeaders.put(key.toLowerCase(), val); - } + String key = headerLine.substring(0, nPos); + String val= headerLine.substring(nPos+1); + + httpHeaders.put(key.toLowerCase(), val); } i++; From 4947c739bc3073e01628485078151fda0b82f5e7 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Thu, 21 Mar 2019 11:51:54 -0400 Subject: [PATCH 50/59] Revert "Revert "4841: RecentActivity not making progress on large image"" --- .../recentactivity/Bundle.properties-MERGED | 2 + .../autopsy/recentactivity/Chrome.java | 4 +- .../recentactivity/ChromeCacheExtractor.java | 85 +++++++++++++------ 3 files changed, 65 insertions(+), 26 deletions(-) diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED index d909db9e71..8c51d5e2a6 100755 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED @@ -2,6 +2,7 @@ cannotBuildXmlParser=Unable to build XML parser: cannotLoadSEUQA=Unable to load Search Engine URL Query Analyzer settings file, SEUQAMappings.xml: cannotParseXml=Unable to parse XML file: ChromeCacheExtractor.moduleName=ChromeCacheExtractor +ChromeCacheExtractor.progressMsg={0}: Extracting cache entry {1} of {2} entries from {3} DataSourceUsage_AndroidMedia=Android Media Card DataSourceUsage_FlashDrive=Flash Drive # {0} - OS name @@ -134,6 +135,7 @@ Progress_Message_Analyze_Registry=Analyzing Registry Files Progress_Message_Analyze_Usage=Data Sources Usage Analysis Progress_Message_Chrome_AutoFill=Chrome Auto Fill Progress_Message_Chrome_Bookmarks=Chrome Bookmarks +Progress_Message_Chrome_Cache=Chrome Cache Progress_Message_Chrome_Cookies=Chrome Cookies Progress_Message_Chrome_Downloads=Chrome Downloads Progress_Message_Chrome_FormHistory=Chrome Form History diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chrome.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chrome.java index 23a6f3ab22..5006ee27b5 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chrome.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chrome.java @@ -93,6 +93,7 @@ class Chrome extends Extract { "Progress_Message_Chrome_FormHistory=Chrome Form History", "Progress_Message_Chrome_AutoFill=Chrome Auto Fill", "Progress_Message_Chrome_Logins=Chrome Logins", + "Progress_Message_Chrome_Cache=Chrome Cache", }) Chrome() { @@ -123,7 +124,8 @@ class Chrome extends Extract { progressBar.progress(Bundle.Progress_Message_Chrome_Downloads()); this.getDownload(); - ChromeCacheExtractor chromeCacheExtractor = new ChromeCacheExtractor(dataSource, context); + progressBar.progress(Bundle.Progress_Message_Chrome_Cache()); + ChromeCacheExtractor chromeCacheExtractor = new ChromeCacheExtractor(dataSource, context, progressBar); chromeCacheExtractor.getCaches(); } diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ChromeCacheExtractor.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ChromeCacheExtractor.java index 1f7eb0580e..c9ae1b674c 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ChromeCacheExtractor.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ChromeCacheExtractor.java @@ -44,6 +44,7 @@ import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.services.FileManager; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datamodel.ContentUtils; +import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress; import org.sleuthkit.autopsy.ingest.IngestJobContext; import org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException; import org.sleuthkit.autopsy.ingest.IngestServices; @@ -55,7 +56,6 @@ import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.DerivedFile; -import org.sleuthkit.datamodel.ReadContentInputStream; import org.sleuthkit.datamodel.TimeUtilities; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; @@ -93,12 +93,17 @@ final class ChromeCacheExtractor { private final Content dataSource; private final IngestJobContext context; + private final DataSourceIngestModuleProgress progressBar; private final IngestServices services = IngestServices.getInstance(); private Case currentCase; private FileManager fileManager; + // A file table to cache copies of index and data_n files. private final Map filesTable = new HashMap<>(); + // A file table to cache the f_* files. + private final Map externalFilesTable = new HashMap<>(); + /** * Encapsulates abstract file for a cache file as well as a temp file copy * that can be accessed as a random access file. @@ -127,12 +132,14 @@ final class ChromeCacheExtractor { } @NbBundle.Messages({ - "ChromeCacheExtractor.moduleName=ChromeCacheExtractor" + "ChromeCacheExtractor.moduleName=ChromeCacheExtractor", + "ChromeCacheExtractor.progressMsg={0}: Extracting cache entry {1} of {2} entries from {3}" }) - ChromeCacheExtractor(Content dataSource, IngestJobContext context ) { + ChromeCacheExtractor(Content dataSource, IngestJobContext context, DataSourceIngestModuleProgress progressBar ) { moduleName = Bundle.ChromeCacheExtractor_moduleName(); this.dataSource = dataSource; this.context = context; + this.progressBar = progressBar; } @@ -171,6 +178,7 @@ final class ChromeCacheExtractor { void subInit(String cachePath) throws IngestModuleException { filesTable.clear(); + externalFilesTable.clear(); String cacheAbsOutputFolderName = this.getAbsOutputFolderName() + cachePath; File outDir = new File(cacheAbsOutputFolderName); @@ -284,6 +292,9 @@ final class ChromeCacheExtractor { return; } } + + // find all f_* files in a single query. + findExternalFiles(cachePath); } catch (TskCoreException | IngestModuleException ex) { String msg = "Failed to find cache files in path " + cachePath; //NON-NLS @@ -306,8 +317,10 @@ final class ChromeCacheExtractor { // Process each address in the table for (int i = 0; i < indexHdr.getTableLen(); i++) { CacheAddress addr = new CacheAddress(indexFileROBuffer.getInt() & UINT32_MASK, cachePath); - if (addr.isInitialized()) { + progressBar.progress( NbBundle.getMessage(this.getClass(), + "ChromeCacheExtractor.progressMsg", + moduleName, i, indexHdr.getTableLen(), cachePath) ); try { List addedFiles = this.getCacheEntry(addr, sourceArtifacts, webCacheArtifacts); derivedFiles.addAll(addedFiles); @@ -412,13 +425,10 @@ final class ChromeCacheExtractor { moduleName, dataFile.get().getUniquePath())); - long pathID = Util.findID(dataSource, dataFile.get().getUniquePath()); - if (pathID != -1) { - webCacheArtifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID, - moduleName, pathID)); - } + webCacheArtifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID, + moduleName, dataFile.get().getId())); - webCacheArtifacts.add(webCacheArtifact); + webCacheArtifacts.add(webCacheArtifact); } if (isBrotliCompressed) { @@ -459,12 +469,10 @@ final class ChromeCacheExtractor { webCacheArtifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH, moduleName, derivedFile.getUniquePath())); - long pathID = Util.findID(dataSource, derivedFile.getUniquePath()); - if (pathID != -1) { - webCacheArtifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID, - moduleName, pathID)); - } - + + webCacheArtifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID, + moduleName, derivedFile.getId())); + webCacheArtifacts.add(webCacheArtifact); } @@ -485,13 +493,37 @@ final class ChromeCacheExtractor { } /** - * Finds abstract file for cache file with a specified name + * Finds all the f_* files in the specified path, and fills them in the + * effFilesTable, so that subsequent searches are fast. + * + * @param cachePath path under which to look for. + * + * @throws TskCoreException + */ + private void findExternalFiles(String cachePath) throws TskCoreException { + + List effFiles = fileManager.findFiles(dataSource, "f_%", cachePath); //NON-NLS + for (AbstractFile abstractFile : effFiles ) { + this.externalFilesTable.put(cachePath + abstractFile.getName(), abstractFile); + } + } + /** + * Finds abstract file for cache file with a specified name. + * First checks in the file tables. * * @param cacheFileName - * @return Opt + * @return Optional abstract file * @throws TskCoreException */ Optional findCacheFile(String cacheFileName, String cachePath) throws TskCoreException { + + String fileTableKey = cachePath + cacheFileName; + if (cacheFileName.startsWith("f_") && externalFilesTable.containsKey(fileTableKey)) { + return Optional.of(externalFilesTable.get(fileTableKey)); + } + if (filesTable.containsKey(fileTableKey)) { + return Optional.of(filesTable.get(fileTableKey).getAbstractFile()); + } List cacheFiles = fileManager.findFiles(dataSource, cacheFileName, cachePath); //NON-NLS if (!cacheFiles.isEmpty()) { @@ -914,9 +946,11 @@ final class ChromeCacheExtractor { return; } - cacheFileCopy = getCacheFileCopy(address.getFilename(), address.getCachePath()).get(); + // Don't extract data from external files. if (!address.isInExternalFile() ) { + cacheFileCopy = getCacheFileCopy(address.getFilename(), address.getCachePath()).get(); + this.data = new byte [length]; ByteBuffer buf = cacheFileCopy.getByteBuffer(); int dataOffset = DATAFILE_HDR_SIZE + address.getStartBlock() * address.getBlockSize(); @@ -952,8 +986,8 @@ final class ChromeCacheExtractor { i++; } - // hhtp headers are terminated by 0x00 0x00 - if (data[i+1] == 0) { + // http headers are terminated by 0x00 0x00 + if (i == data.length || data[i+1] == 0) { done = true; } @@ -965,10 +999,11 @@ final class ChromeCacheExtractor { httpResponse = headerLine; } else { int nPos = headerLine.indexOf(':'); - String key = headerLine.substring(0, nPos); - String val= headerLine.substring(nPos+1); - - httpHeaders.put(key.toLowerCase(), val); + if (nPos > 0 ) { + String key = headerLine.substring(0, nPos); + String val= headerLine.substring(nPos+1); + httpHeaders.put(key.toLowerCase(), val); + } } i++; From e61f372720870be10f4283954d5146fd636715b6 Mon Sep 17 00:00:00 2001 From: Ann Priestman Date: Thu, 21 Mar 2019 13:21:19 -0400 Subject: [PATCH 51/59] Clear out the artifact caches and set the case to null. --- .../sleuthkit/autopsy/report/CreatePortableCaseModule.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/report/CreatePortableCaseModule.java b/Core/src/org/sleuthkit/autopsy/report/CreatePortableCaseModule.java index 3ce5c3a369..b0db1d0b46 100644 --- a/Core/src/org/sleuthkit/autopsy/report/CreatePortableCaseModule.java +++ b/Core/src/org/sleuthkit/autopsy/report/CreatePortableCaseModule.java @@ -708,14 +708,18 @@ public class CreatePortableCaseModule implements GeneralReportModule { oldIdToNewContent.clear(); newIdToContent.clear(); oldTagNameToNewTagName.clear(); + oldArtTypeIdToNewArtTypeId.clear(); + oldAttrTypeIdToNewAttrType.clear(); + oldArtifactIdToNewArtifact.clear(); + currentCase = null; if (portableSkCase != null) { portableSkCase.close(); + portableSkCase = null; } caseFolder = null; copiedFilesFolder = null; } - @Override public JPanel getConfigurationPanel() { From b8c02c6cd37de3f532885e8f9c6807483acc42dd Mon Sep 17 00:00:00 2001 From: Brian Carrier Date: Thu, 21 Mar 2019 16:01:27 -0400 Subject: [PATCH 52/59] added comments, renamed. No logic changes except moving of an 'if' --- .../recentactivity/ChromeCacheExtractor.java | 291 ++++++++++-------- 1 file changed, 158 insertions(+), 133 deletions(-) diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ChromeCacheExtractor.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ChromeCacheExtractor.java index c9ae1b674c..d4b731259d 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ChromeCacheExtractor.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ChromeCacheExtractor.java @@ -75,7 +75,7 @@ import org.sleuthkit.datamodel.TskException; */ final class ChromeCacheExtractor { - private final static String DEFAULT_CACHE_STR = "default/cache"; //NON-NLS + private final static String DEFAULT_CACHE_PATH_STR = "default/cache"; //NON-NLS private final static String BROTLI_MIMETYPE ="application/x-brotli"; //NON-NLS private final static long UINT32_MASK = 0xFFFFFFFFl; @@ -99,7 +99,7 @@ final class ChromeCacheExtractor { private FileManager fileManager; // A file table to cache copies of index and data_n files. - private final Map filesTable = new HashMap<>(); + private final Map fileCopyCache = new HashMap<>(); // A file table to cache the f_* files. private final Map externalFilesTable = new HashMap<>(); @@ -148,7 +148,7 @@ final class ChromeCacheExtractor { * * @throws IngestModuleException */ - void moduleInit() throws IngestModuleException { + private void moduleInit() throws IngestModuleException { try { currentCase = Case.getCurrentCaseThrows(); @@ -175,9 +175,9 @@ final class ChromeCacheExtractor { * * @throws org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException */ - void subInit(String cachePath) throws IngestModuleException { + private void resetForNewFolder(String cachePath) throws IngestModuleException { - filesTable.clear(); + fileCopyCache.clear(); externalFilesTable.clear(); String cacheAbsOutputFolderName = this.getAbsOutputFolderName() + cachePath; @@ -199,9 +199,9 @@ final class ChromeCacheExtractor { * Removes any temp copies of cache files created during extraction. * */ - void cleanup () { + private void cleanup () { - for (Entry entry : this.filesTable.entrySet()) { + for (Entry entry : this.fileCopyCache.entrySet()) { Path tempFilePath = Paths.get(RAImageIngestModule.getRATempPath(currentCase, moduleName), entry.getKey() ); try { entry.getValue().getFileCopy().getChannel().close(); @@ -236,7 +236,7 @@ final class ChromeCacheExtractor { } /** - * Extracts the data from Chrome caches + * Extracts the data from Chrome caches . Main entry point for the analysis * * A data source may have multiple Chrome user profiles and caches. * @@ -251,14 +251,14 @@ final class ChromeCacheExtractor { return; } - // Find all possible caches + // Find and process the cache folders. There could be one per user List indexFiles; try { - indexFiles = findCacheFiles("index"); //NON-NLS + indexFiles = findCacheIndexFiles(); - // Get each of the caches + // Process each of the caches for (AbstractFile indexFile: indexFiles) { - getCache(indexFile); + processCacheIndexFile(indexFile); } } catch (TskCoreException ex) { @@ -268,24 +268,27 @@ final class ChromeCacheExtractor { } /** - * Extracts the cache for the specified cache index file. + * Processes a user's cache and creates corresponding artifacts and derived files. * - * @param cacheIndexFile + * @param cacheIndexFile Cache index file for a given user */ - void getCache(AbstractFile indexAbstractFile) { + private void processCacheIndexFile(AbstractFile indexAbstractFile) { String cachePath = indexAbstractFile.getParentPath(); - Optional indexFile; + Optional indexFileCopy; try { - subInit(cachePath); + resetForNewFolder(cachePath); - indexFile = this.getCacheFileCopy(indexAbstractFile.getName(), cachePath); - if (!indexFile.isPresent()) { + // @@@ This is little ineffecient because we later in this call search for the AbstractFile that we currently have + indexFileCopy = this.getCacheFileCopy(indexAbstractFile.getName(), cachePath); + if (!indexFileCopy.isPresent()) { String msg = String.format("Failed to find copy cache index file %s", indexAbstractFile.getUniquePath()); logger.log(Level.SEVERE, msg); return; } + + // load the data files. We do this now to load them into the cache for (int i = 0; i < 4; i ++) { Optional dataFile = findAndCopyCacheFile(String.format("data_%1d",i), cachePath ); if (!dataFile.isPresent()) { @@ -293,7 +296,7 @@ final class ChromeCacheExtractor { } } - // find all f_* files in a single query. + // find all f_* files in a single query and load them into the cache findExternalFiles(cachePath); } catch (TskCoreException | IngestModuleException ex) { @@ -302,13 +305,15 @@ final class ChromeCacheExtractor { return; } + + // parse the index file logger.log(Level.INFO, "{0}- Now reading Cache index file from path {1}", new Object[]{moduleName, cachePath }); //NON-NLS List derivedFiles = new ArrayList<>(); Collection sourceArtifacts = new ArrayList<>(); Collection webCacheArtifacts = new ArrayList<>(); - ByteBuffer indexFileROBuffer = indexFile.get().getByteBuffer(); + ByteBuffer indexFileROBuffer = indexFileCopy.get().getByteBuffer(); IndexFileHeader indexHdr = new IndexFileHeader(indexFileROBuffer); // seek past the header @@ -322,7 +327,7 @@ final class ChromeCacheExtractor { "ChromeCacheExtractor.progressMsg", moduleName, i, indexHdr.getTableLen(), cachePath) ); try { - List addedFiles = this.getCacheEntry(addr, sourceArtifacts, webCacheArtifacts); + List addedFiles = this.processCacheEntry(addr, sourceArtifacts, webCacheArtifacts); derivedFiles.addAll(addedFiles); } catch (TskCoreException | IngestModuleException ex) { @@ -354,15 +359,16 @@ final class ChromeCacheExtractor { * * @return Optional derived file, is a derived file is added for the given entry */ - List getCacheEntry(CacheAddress cacheEntryAddress, Collection sourceArtifacts, Collection webCacheArtifacts ) throws TskCoreException, IngestModuleException { + private List processCacheEntry(CacheAddress cacheEntryAddress, Collection sourceArtifacts, Collection webCacheArtifacts ) throws TskCoreException, IngestModuleException { List derivedFiles = new ArrayList<>(); - String cacheEntryFileName = cacheEntryAddress.getFilename(); + // get the path to the corresponding data_X file + String dataFileName = cacheEntryAddress.getFilename(); String cachePath = cacheEntryAddress.getCachePath(); - Optional cacheEntryFile = this.getCacheFileCopy(cacheEntryFileName, cachePath); + Optional cacheEntryFile = this.getCacheFileCopy(dataFileName, cachePath); if (!cacheEntryFile.isPresent()) { String msg = String.format("Failed to get cache entry at address %s", cacheEntryAddress); //NON-NLS throw new IngestModuleException(msg); @@ -388,104 +394,113 @@ final class ChromeCacheExtractor { // Only process the first payload data segment in each entry // first data segement has the HTTP headers, 2nd is the payload - for (int j = 1; j < dataEntries.size() && j < 2; j++) { - CacheData data = dataEntries.get(j); - String dataFilename = data.getAddress().getFilename(); - Optional dataFile = this.findCacheFile(dataFilename, cachePath); + CacheData dataSegment = dataEntries.get(1); + + // name of the file that was downloaded and cached (or data_X if it was saved into there) + String cachedFileName = dataSegment.getAddress().getFilename(); + Optional cachedFileAbstractFile = this.findCacheFile(cachedFileName, cachePath); + if (!cachedFileAbstractFile.isPresent()) { + logger.log(Level.SEVERE, "Error finding file: " + cachePath + "/" + cachedFileName); //NON-NLS + return derivedFiles; + } - boolean isBrotliCompressed = false; - if (data.getType() != CacheDataTypeEnum.HTTP_HEADER && cacheEntry.isBrotliCompressed() ) { - isBrotliCompressed = true; - } + + + boolean isBrotliCompressed = false; + if (dataSegment.getType() != CacheDataTypeEnum.HTTP_HEADER && cacheEntry.isBrotliCompressed() ) { + isBrotliCompressed = true; + } - Collection sourceArtifactAttributes = new ArrayList<>(); - sourceArtifactAttributes.add(urlAttr); - sourceArtifactAttributes.add(createTimeAttr); + Collection sourceArtifactAttributes = new ArrayList<>(); + sourceArtifactAttributes.add(urlAttr); + sourceArtifactAttributes.add(createTimeAttr); - Collection webCacheAttributes = new ArrayList<>(); - webCacheAttributes.add(urlAttr); - webCacheAttributes.add(createTimeAttr); - webCacheAttributes.add(hhtpHeaderAttr); - - if (dataFile.isPresent()) { - if (data.isInExternalFile() ) { - try { - BlackboardArtifact sourceArtifact = dataFile.get().newArtifact(ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE); - if (sourceArtifact != null) { - sourceArtifact.addAttributes(sourceArtifactAttributes); - sourceArtifacts.add(sourceArtifact); - } - - BlackboardArtifact webCacheArtifact = cacheEntryFile.get().getAbstractFile().newArtifact(ARTIFACT_TYPE.TSK_WEB_CACHE); - if (webCacheArtifact != null) { - webCacheArtifact.addAttributes(webCacheAttributes); - - // Add path of f_* file as attribute - webCacheArtifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH, - moduleName, - dataFile.get().getUniquePath())); - - webCacheArtifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID, - moduleName, dataFile.get().getId())); - - webCacheArtifacts.add(webCacheArtifact); - } - - if (isBrotliCompressed) { - dataFile.get().setMIMEType(BROTLI_MIMETYPE); - dataFile.get().save(); - } - } catch (TskException ex) { - logger.log(Level.SEVERE, "Error while trying to add an artifact", ex); //NON-NLS - } - } else { + Collection webCacheAttributes = new ArrayList<>(); + webCacheAttributes.add(urlAttr); + webCacheAttributes.add(createTimeAttr); + webCacheAttributes.add(hhtpHeaderAttr); - // Data segments in "data_x" files are saved in individual files and added as derived files - String filename = data.save(); - String relPathname = getRelOutputFolderName() + data.getAddress().getCachePath() + filename; - try { - DerivedFile derivedFile = fileManager.addDerivedFile(filename, relPathname, - data.getDataLength(), - cacheEntry.getCreationTime(), cacheEntry.getCreationTime(), cacheEntry.getCreationTime(), cacheEntry.getCreationTime(), // TBD - true, - dataFile.get(), - "", - moduleName, - VERSION_NUMBER, - "", - TskData.EncodingType.NONE); - - BlackboardArtifact sourceArtifact = derivedFile.newArtifact(ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE); - if (sourceArtifact != null) { - sourceArtifact.addAttributes(sourceArtifactAttributes); - sourceArtifacts.add(sourceArtifact); - } - - BlackboardArtifact webCacheArtifact = cacheEntryFile.get().getAbstractFile().newArtifact(ARTIFACT_TYPE.TSK_WEB_CACHE); - if (webCacheArtifact != null) { - webCacheArtifact.addAttributes(webCacheAttributes); - - // Add path of derived file as attribute - webCacheArtifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH, - moduleName, - derivedFile.getUniquePath())); - - webCacheArtifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID, - moduleName, derivedFile.getId())); - - webCacheArtifacts.add(webCacheArtifact); - } - - if (isBrotliCompressed) { - derivedFile.setMIMEType(BROTLI_MIMETYPE); - derivedFile.save(); - } - - derivedFiles.add(derivedFile); - } catch (TskException ex) { - logger.log(Level.SEVERE, "Error while trying to add an artifact", ex); //NON-NLS - } + + + // add artifacts to the f_XXX file + if (dataSegment.isInExternalFile() ) { + try { + BlackboardArtifact sourceArtifact = cachedFileAbstractFile.get().newArtifact(ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE); + if (sourceArtifact != null) { + sourceArtifact.addAttributes(sourceArtifactAttributes); + sourceArtifacts.add(sourceArtifact); } + + BlackboardArtifact webCacheArtifact = cacheEntryFile.get().getAbstractFile().newArtifact(ARTIFACT_TYPE.TSK_WEB_CACHE); + if (webCacheArtifact != null) { + webCacheArtifact.addAttributes(webCacheAttributes); + + // Add path of f_* file as attribute + webCacheArtifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH, + moduleName, + cachedFileAbstractFile.get().getUniquePath())); + + webCacheArtifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID, + moduleName, cachedFileAbstractFile.get().getId())); + + webCacheArtifacts.add(webCacheArtifact); + } + + if (isBrotliCompressed) { + cachedFileAbstractFile.get().setMIMEType(BROTLI_MIMETYPE); + cachedFileAbstractFile.get().save(); + } + } catch (TskException ex) { + logger.log(Level.SEVERE, "Error while trying to add an artifact", ex); //NON-NLS + } + } + // extract the embedded data to a derived file and create artifacts + else { + + // Data segments in "data_x" files are saved in individual files and added as derived files + String filename = dataSegment.save(); + String relPathname = getRelOutputFolderName() + dataSegment.getAddress().getCachePath() + filename; + try { + DerivedFile derivedFile = fileManager.addDerivedFile(filename, relPathname, + dataSegment.getDataLength(), + cacheEntry.getCreationTime(), cacheEntry.getCreationTime(), cacheEntry.getCreationTime(), cacheEntry.getCreationTime(), // TBD + true, + cachedFileAbstractFile.get(), + "", + moduleName, + VERSION_NUMBER, + "", + TskData.EncodingType.NONE); + + BlackboardArtifact sourceArtifact = derivedFile.newArtifact(ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE); + if (sourceArtifact != null) { + sourceArtifact.addAttributes(sourceArtifactAttributes); + sourceArtifacts.add(sourceArtifact); + } + + BlackboardArtifact webCacheArtifact = cacheEntryFile.get().getAbstractFile().newArtifact(ARTIFACT_TYPE.TSK_WEB_CACHE); + if (webCacheArtifact != null) { + webCacheArtifact.addAttributes(webCacheAttributes); + + // Add path of derived file as attribute + webCacheArtifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH, + moduleName, + derivedFile.getUniquePath())); + + webCacheArtifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID, + moduleName, derivedFile.getId())); + + webCacheArtifacts.add(webCacheArtifact); + } + + if (isBrotliCompressed) { + derivedFile.setMIMEType(BROTLI_MIMETYPE); + derivedFile.save(); + } + + derivedFiles.add(derivedFile); + } catch (TskException ex) { + logger.log(Level.SEVERE, "Error while trying to add an artifact", ex); //NON-NLS } } @@ -515,20 +530,22 @@ final class ChromeCacheExtractor { * @return Optional abstract file * @throws TskCoreException */ - Optional findCacheFile(String cacheFileName, String cachePath) throws TskCoreException { + private Optional findCacheFile(String cacheFileName, String cachePath) throws TskCoreException { + // see if it is cached String fileTableKey = cachePath + cacheFileName; if (cacheFileName.startsWith("f_") && externalFilesTable.containsKey(fileTableKey)) { return Optional.of(externalFilesTable.get(fileTableKey)); } - if (filesTable.containsKey(fileTableKey)) { - return Optional.of(filesTable.get(fileTableKey).getAbstractFile()); + if (fileCopyCache.containsKey(fileTableKey)) { + return Optional.of(fileCopyCache.get(fileTableKey).getAbstractFile()); } + List cacheFiles = fileManager.findFiles(dataSource, cacheFileName, cachePath); //NON-NLS if (!cacheFiles.isEmpty()) { for (AbstractFile abstractFile: cacheFiles ) { - if (abstractFile.getUniquePath().trim().endsWith(DEFAULT_CACHE_STR)) { + if (abstractFile.getUniquePath().trim().endsWith(DEFAULT_CACHE_PATH_STR)) { return Optional.of(abstractFile); } } @@ -541,29 +558,29 @@ final class ChromeCacheExtractor { /** * Finds abstract file(s) for a cache file with the specified name. * - * @param cacheFileName * @return list of abstract files matching the specified file name * @throws TskCoreException */ - List findCacheFiles(String cacheFileName) throws TskCoreException { - return fileManager.findFiles(dataSource, cacheFileName, DEFAULT_CACHE_STR); //NON-NLS + private List findCacheIndexFiles() throws TskCoreException { + return fileManager.findFiles(dataSource, "index", DEFAULT_CACHE_PATH_STR); //NON-NLS } /** * Returns CacheFileCopy for the specified file from the file table. * Find the file and creates a copy if it isn't already in the table. - * - * @param cacheFileName + * + * @param cacheFileName Name of file + * @param cachePath Parent path of file * @return CacheFileCopy * @throws TskCoreException */ - Optional getCacheFileCopy(String cacheFileName, String cachePath) throws TskCoreException, IngestModuleException { + private Optional getCacheFileCopy(String cacheFileName, String cachePath) throws TskCoreException, IngestModuleException { - // Check if the file is already in the table + // Check if the file is already in the cache String fileTableKey = cachePath + cacheFileName; - if (filesTable.containsKey(fileTableKey)) { - return Optional.of(filesTable.get(fileTableKey)); + if (fileCopyCache.containsKey(fileTableKey)) { + return Optional.of(fileCopyCache.get(fileTableKey)); } return findAndCopyCacheFile(cacheFileName, cachePath); @@ -576,13 +593,17 @@ final class ChromeCacheExtractor { * @return Cache file copy * @throws TskCoreException */ - Optional findAndCopyCacheFile(String cacheFileName, String cachePath) throws TskCoreException, IngestModuleException { + private Optional findAndCopyCacheFile(String cacheFileName, String cachePath) throws TskCoreException, IngestModuleException { Optional cacheFileOptional = findCacheFile(cacheFileName, cachePath); if (!cacheFileOptional.isPresent()) { return Optional.empty(); } + + // write the file to disk so that we can have a memory-mapped ByteBuffer + // @@@ NOTE: I"m not sure this is needed. These files are small enough and we could probably just load them into + // a byte[] for ByteBuffer. AbstractFile cacheFile = cacheFileOptional.get(); RandomAccessFile randomAccessFile = null; String tempFilePathname = RAImageIngestModule.getRATempPath(currentCase, moduleName) + cachePath + cacheFile.getName(); //NON-NLS @@ -599,7 +620,7 @@ final class ChromeCacheExtractor { CacheFileCopy cacheFileCopy = new CacheFileCopy(cacheFile, randomAccessFile, cacheFileROBuf ); if (!cacheFileName.startsWith("f_")) { - filesTable.put(cachePath + cacheFileName, cacheFileCopy); + fileCopyCache.put(cachePath + cacheFileName, cacheFileCopy); } return Optional.of(cacheFileCopy); @@ -783,6 +804,10 @@ final class ChromeCacheExtractor { return fileType; } + /** + * Name where cached file is stored. Either a data or f_ file. + * @return + */ String getFilename() { return fileName; } From 92a8190056bf3adb7d5d32b4bca01c0f9fd417f8 Mon Sep 17 00:00:00 2001 From: Brian Carrier Date: Thu, 21 Mar 2019 16:16:31 -0400 Subject: [PATCH 53/59] reduce pmd warnings --- ruleset.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ruleset.xml b/ruleset.xml index 3d08ec338c..1db2da660a 100644 --- a/ruleset.xml +++ b/ruleset.xml @@ -94,7 +94,7 @@ --> - + - + From 6371e1a2619089bb3c2e1f9f8cb3cd7155c26f5e Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Fri, 22 Mar 2019 00:14:50 -0400 Subject: [PATCH 55/59] Document ChromeCacheExtractor Message parameters to remove errors --- .../autopsy/recentactivity/Bundle.properties-MERGED | 8 ++++++-- .../autopsy/recentactivity/ChromeCacheExtractor.java | 4 ++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED index 8c51d5e2a6..66df1c457f 100755 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED @@ -2,6 +2,10 @@ cannotBuildXmlParser=Unable to build XML parser: cannotLoadSEUQA=Unable to load Search Engine URL Query Analyzer settings file, SEUQAMappings.xml: cannotParseXml=Unable to parse XML file: ChromeCacheExtractor.moduleName=ChromeCacheExtractor +# {0} - module name +# {1} - row number +# {2} - table length +# {3} - cache path ChromeCacheExtractor.progressMsg={0}: Extracting cache entry {1} of {2} entries from {3} DataSourceUsage_AndroidMedia=Android Media Card DataSourceUsage_FlashDrive=Flash Drive @@ -60,7 +64,7 @@ ExtractZone_progress_Msg=Extracting :Zone.Identifer files ExtractZone_Restricted=Restricted Sites Zone ExtractZone_Trusted=Trusted Sites Zone OpenIDE-Module-Display-Category=Ingest Module -OpenIDE-Module-Long-Description=Recent Activity ingest module.\n\nThe module extracts useful information about the recent user activity on the disk image being ingested, such as:\n\n- Recently open documents,\n- Web acitivity (sites visited, stored cookies, bookmarked sites, search engine queries, file downloads),\n- Recently attached devices,\n- Installed programs.\n\nThe module currently supports Windows only disk images.\nThe plugin is also fully functional when deployed on Windows version of Autopsy. +OpenIDE-Module-Long-Description=Recent Activity ingest module.\n\n\The module extracts useful information about the recent user activity on the disk image being ingested, such as:\n\n- Recently open documents,\n- Web acitivity (sites visited, stored cookies, bookmarked sites, search engine queries, file downloads),\n- Recently attached devices,\n- Installed programs.\n\n\The module currently supports Windows only disk images.\n\The plugin is also fully functional when deployed on Windows version of Autopsy. OpenIDE-Module-Name=RecentActivity OpenIDE-Module-Short-Description=Recent Activity finder ingest module Chrome.moduleName=Chrome @@ -190,7 +194,7 @@ SearchEngineURLQueryAnalyzer.init.exception.msg=Unable to find {0}. SearchEngineURLQueryAnalyzer.moduleName.text=Search Engine SearchEngineURLQueryAnalyzer.engineName.none=NONE SearchEngineURLQueryAnalyzer.domainSubStr.none=NONE -SearchEngineURLQueryAnalyzer.toString=Name: {0}\nDomain Substring: {1}\ncount: {2}\nSplit Tokens: \n{3} +SearchEngineURLQueryAnalyzer.toString=Name: {0}\nDomain Substring: {1}\n\count: {2}\nSplit Tokens: \n{3} SearchEngineURLQueryAnalyzer.parentModuleName.noSpace=RecentActivity SearchEngineURLQueryAnalyzer.parentModuleName=Recent Activity UsbDeviceIdMapper.parseAndLookup.text=Product: {0} diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ChromeCacheExtractor.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ChromeCacheExtractor.java index c9ae1b674c..7cf801efb1 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ChromeCacheExtractor.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ChromeCacheExtractor.java @@ -133,6 +133,10 @@ final class ChromeCacheExtractor { @NbBundle.Messages({ "ChromeCacheExtractor.moduleName=ChromeCacheExtractor", + "# {0} - module name", + "# {1} - row number", + "# {2} - table length", + "# {3} - cache path", "ChromeCacheExtractor.progressMsg={0}: Extracting cache entry {1} of {2} entries from {3}" }) ChromeCacheExtractor(Content dataSource, IngestJobContext context, DataSourceIngestModuleProgress progressBar ) { From 4f1248cc87b2801f154a2cb0d0aa95dcc1b7bf25 Mon Sep 17 00:00:00 2001 From: Brian Carrier Date: Fri, 22 Mar 2019 10:29:18 -0400 Subject: [PATCH 56/59] check number of segments first --- .../recentactivity/ChromeCacheExtractor.java | 39 +++++++++---------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ChromeCacheExtractor.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ChromeCacheExtractor.java index d4b731259d..042ea7f1d2 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ChromeCacheExtractor.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ChromeCacheExtractor.java @@ -377,40 +377,40 @@ final class ChromeCacheExtractor { // Get the cache entry and its data segments CacheEntry cacheEntry = new CacheEntry(cacheEntryAddress, cacheEntryFile.get() ); + List dataEntries = cacheEntry.getData(); - - BlackboardAttribute urlAttr = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_URL, - moduleName, - ((cacheEntry.getKey() != null) ? cacheEntry.getKey() : "")); - - BlackboardAttribute createTimeAttr = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED, - moduleName, - cacheEntry.getCreationTime()); - - BlackboardAttribute hhtpHeaderAttr = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_HEADERS, - moduleName, - cacheEntry.getHTTPHeaders()); - - // Only process the first payload data segment in each entry // first data segement has the HTTP headers, 2nd is the payload + if (dataEntries.size() < 2) { + return derivedFiles; + } CacheData dataSegment = dataEntries.get(1); - + + // name of the file that was downloaded and cached (or data_X if it was saved into there) String cachedFileName = dataSegment.getAddress().getFilename(); Optional cachedFileAbstractFile = this.findCacheFile(cachedFileName, cachePath); if (!cachedFileAbstractFile.isPresent()) { logger.log(Level.SEVERE, "Error finding file: " + cachePath + "/" + cachedFileName); //NON-NLS return derivedFiles; - } - - + } boolean isBrotliCompressed = false; if (dataSegment.getType() != CacheDataTypeEnum.HTTP_HEADER && cacheEntry.isBrotliCompressed() ) { isBrotliCompressed = true; } + // setup some attributes for later use + BlackboardAttribute urlAttr = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_URL, + moduleName, + ((cacheEntry.getKey() != null) ? cacheEntry.getKey() : "")); + BlackboardAttribute createTimeAttr = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED, + moduleName, + cacheEntry.getCreationTime()); + BlackboardAttribute httpHeaderAttr = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_HEADERS, + moduleName, + cacheEntry.getHTTPHeaders()); + Collection sourceArtifactAttributes = new ArrayList<>(); sourceArtifactAttributes.add(urlAttr); sourceArtifactAttributes.add(createTimeAttr); @@ -418,10 +418,9 @@ final class ChromeCacheExtractor { Collection webCacheAttributes = new ArrayList<>(); webCacheAttributes.add(urlAttr); webCacheAttributes.add(createTimeAttr); - webCacheAttributes.add(hhtpHeaderAttr); + webCacheAttributes.add(httpHeaderAttr); - // add artifacts to the f_XXX file if (dataSegment.isInExternalFile() ) { try { From b477b5f0b8586726bf77e6939c4f3f50a7f37a47 Mon Sep 17 00:00:00 2001 From: rcordovano Date: Fri, 22 Mar 2019 14:01:37 -0400 Subject: [PATCH 57/59] Update Autopsy version numbers --- docs/doxygen-user/Doxyfile | 4 ++-- docs/doxygen/Doxyfile | 4 ++-- nbproject/project.properties | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/doxygen-user/Doxyfile b/docs/doxygen-user/Doxyfile index b0498793db..6f16153151 100644 --- a/docs/doxygen-user/Doxyfile +++ b/docs/doxygen-user/Doxyfile @@ -38,7 +38,7 @@ PROJECT_NAME = "Autopsy User Documentation" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 4.10.0 +PROJECT_NUMBER = 4.11.0 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a @@ -1025,7 +1025,7 @@ GENERATE_HTML = YES # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_OUTPUT = 4.10.0 +HTML_OUTPUT = 4.11.0 # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # generated HTML page (for example: .htm, .php, .asp). diff --git a/docs/doxygen/Doxyfile b/docs/doxygen/Doxyfile index 18b54cf41c..a7463ac441 100644 --- a/docs/doxygen/Doxyfile +++ b/docs/doxygen/Doxyfile @@ -38,7 +38,7 @@ PROJECT_NAME = "Autopsy" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 4.10.0 +PROJECT_NUMBER = 4.11.0 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears a the top of each page and should give viewer a @@ -1063,7 +1063,7 @@ GENERATE_HTML = YES # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_OUTPUT = api-docs/4.10.0/ +HTML_OUTPUT = api-docs/4.11.0/ # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # generated HTML page (for example: .htm, .php, .asp). diff --git a/nbproject/project.properties b/nbproject/project.properties index 51982671cc..dbc771a543 100644 --- a/nbproject/project.properties +++ b/nbproject/project.properties @@ -4,7 +4,7 @@ app.title=Autopsy ### lowercase version of above app.name=${branding.token} ### if left unset, version will default to today's date -app.version=4.10.0 +app.version=4.11.0 ### build.type must be one of: DEVELOPMENT, RELEASE #build.type=RELEASE build.type=DEVELOPMENT From 61411d178339df89f994754e231a575900e59b8e Mon Sep 17 00:00:00 2001 From: rcordovano Date: Fri, 22 Mar 2019 14:19:13 -0400 Subject: [PATCH 58/59] Update TSK version to 4.6.6 --- Core/nbproject/project.properties | 2 +- Core/nbproject/project.xml | 4 ++-- TSKVersion.xml | 2 +- unix_setup.sh | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) mode change 100755 => 100644 unix_setup.sh diff --git a/Core/nbproject/project.properties b/Core/nbproject/project.properties index 6c88edea66..19b4a67a42 100644 --- a/Core/nbproject/project.properties +++ b/Core/nbproject/project.properties @@ -61,7 +61,7 @@ file.reference.cxf-rt-transports-http-3.0.16.jar=release/modules/ext/cxf-rt-tran file.reference.fontbox-2.0.8.jar=release/modules/ext/fontbox-2.0.8.jar file.reference.pdfbox-2.0.8.jar=release/modules/ext/pdfbox-2.0.8.jar file.reference.pdfbox-tools-2.0.8.jar=release/modules/ext/pdfbox-tools-2.0.8.jar -file.reference.sleuthkit-postgresql-4.6.5.jar=release/modules/ext/sleuthkit-postgresql-4.6.5.jar +file.reference.sleuthkit-postgresql-4.6.6.jar=release/modules/ext/sleuthkit-postgresql-4.6.6.jar file.reference.tagsoup-1.2.1.jar=release/modules/ext/tagsoup-1.2.1.jar file.reference.tika-core-1.17.jar=release/modules/ext/tika-core-1.17.jar file.reference.tika-parsers-1.17.jar=release/modules/ext/tika-parsers-1.17.jar diff --git a/Core/nbproject/project.xml b/Core/nbproject/project.xml index eee0ae059f..e91c3e7ff9 100644 --- a/Core/nbproject/project.xml +++ b/Core/nbproject/project.xml @@ -450,8 +450,8 @@ release/modules/ext/jhighlight-1.0.2.jar - ext/sleuthkit-postgresql-4.6.5.jar - release/modules/ext/sleuthkit-postgresql-4.6.5.jar + ext/sleuthkit-postgresql-4.6.6.jar + release/modules/ext/sleuthkit-postgresql-4.6.6.jar ext/jempbox-1.8.13.jar diff --git a/TSKVersion.xml b/TSKVersion.xml index 78a21e6779..3c03006088 100644 --- a/TSKVersion.xml +++ b/TSKVersion.xml @@ -1,3 +1,3 @@ - + diff --git a/unix_setup.sh b/unix_setup.sh old mode 100755 new mode 100644 index 8e7c9a94c5..0b252b2598 --- a/unix_setup.sh +++ b/unix_setup.sh @@ -5,7 +5,7 @@ # NOTE: update_sleuthkit_version.pl updates this value and relies # on it keeping the same name and whitespace. Don't change it. -TSK_VERSION=4.6.5 +TSK_VERSION=4.6.6 # In the beginning... From d49a718ee372462225e2cd2a8677b4f29671c93f Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Mon, 25 Mar 2019 11:47:31 -0400 Subject: [PATCH 59/59] 4787 remove an unused references to gstreamer-1.5 we now relly on gst-java-core-0.9.3 --- CoreLibs/nbproject/project.properties | 1 - 1 file changed, 1 deletion(-) diff --git a/CoreLibs/nbproject/project.properties b/CoreLibs/nbproject/project.properties index 647666e3cd..c71cf02060 100644 --- a/CoreLibs/nbproject/project.properties +++ b/CoreLibs/nbproject/project.properties @@ -23,7 +23,6 @@ file.reference.controlsfx-8.40.11.jar=release/modules/ext/controlsfx-8.40.11.jar file.reference.dom4j-1.6.1.jar=release/modules/ext/dom4j-1.6.1.jar file.reference.geronimo-jms_1.1_spec-1.0.jar=release/modules/ext/geronimo-jms_1.1_spec-1.0.jar file.reference.gson-2.8.1.jar=release/modules/ext/gson-2.8.1.jar -file.reference.gstreamer-java-1.5.jar=release/modules/ext/gstreamer-java-1.5.jar file.reference.gst1-java-core-0.9.3.jar=release/modules/ext/gst1-java-core-0.9.3.jar file.reference.jna-3.4.0.jar=release/modules/ext/jna-3.4.0.jar file.reference.guava-19.0.jar=release/modules/ext/guava-19.0.jar