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/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/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/CommandLineIngestSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/commandlineingest/CommandLineIngestSettingsPanel.java
index 20811d0fb8..5d3a0787ce 100755
--- a/Core/src/org/sleuthkit/autopsy/commandlineingest/CommandLineIngestSettingsPanel.java
+++ b/Core/src/org/sleuthkit/autopsy/commandlineingest/CommandLineIngestSettingsPanel.java
@@ -193,7 +193,12 @@ public class CommandLineIngestSettingsPanel extends javax.swing.JPanel {
if (outputPath.isEmpty()) {
jLabelInvalidResultsFolder.setVisible(true);
jLabelInvalidResultsFolder.setText(NbBundle.getMessage(CommandLineIngestSettingsPanel.class, "CommandLineIngestSettingsPanel.ResultsDirectoryUnspecified"));
- return false;
+ /*
+ NOTE: JIRA-4850: Returning false disables OK and Apply buttons for the entire
+ Tools->Options bar until the path is set. It was decided to only validate
+ the path if the path is set.
+ */
+ return true;
}
if (!isFolderPathValid(outputPath)) {
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/commonpropertiessearch/AbstractCommonAttributeSearcher.java b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/AbstractCommonAttributeSearcher.java
index 00a4a0f653..1880489cc4 100644
--- a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/AbstractCommonAttributeSearcher.java
+++ b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/AbstractCommonAttributeSearcher.java
@@ -130,12 +130,12 @@ 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.getInstanceCount();
+ Integer size = md5Metadata.getNumberOfDataSourcesInCurrentCase();
if (instanceCollatedCommonFiles.containsKey(size)) {
instanceCollatedCommonFiles.get(size).addMetadataToList(md5Metadata);
@@ -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
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 10370577d7..d164529639 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.
@@ -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
diff --git a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeCountSearchResults.java b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeCountSearchResults.java
index c1d2dcb481..8265a8db25 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;
@@ -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/CommonAttributePanel.java b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributePanel.java
index 72086983de..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);
+ 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();
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 d86eff5a4f..c4fe25f14b 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");
@@ -22,10 +22,13 @@ 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;
/**
* Defines a value that was in the common file search results as well as
@@ -35,6 +38,7 @@ final public class CommonAttributeValue {
private final String value;
private final List fileInstances;
+ private final Map fileNames = new HashMap<>();
CommonAttributeValue(String value) {
this.value = value;
@@ -45,6 +49,23 @@ 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() {
+ String tokenFileName = null;
+ int maxValue = 0;
+ for (String key : fileNames.keySet()){
+ if (fileNames.get(key) > maxValue){
+ maxValue = fileNames.get(key);
+ tokenFileName = key;
+ }
+ }
+ return tokenFileName;
+ }
+
/**
* concatenate cases this value was seen into a single string
*
@@ -54,16 +75,43 @@ final public class CommonAttributeValue {
return this.fileInstances.stream().map(AbstractCommonAttributeInstance::getCaseName).collect(Collectors.joining(", "));
}
- public String getDataSources() {
+ /**
+ * 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) {
sources.add(data.getDataSource());
}
+ return sources;
+ }
- return String.join(", ", 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 (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);
}
diff --git a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeValueNode.java b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeValueNode.java
index d149847d78..ed8c395b5f 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");
@@ -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(String.format(Bundle.CommonAttributeValueNode_CommonAttributeValueNode_format(), this.value));
+ //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 29f817210d..973d7711c4 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");
@@ -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
@@ -54,11 +56,11 @@ 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));
-
+ 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);
}
}
}
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..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");
@@ -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);
diff --git a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/InterCaseSearchResultsProcessor.java b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/InterCaseSearchResultsProcessor.java
index 52f2cec276..a6dd6833b3 100644
--- a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/InterCaseSearchResultsProcessor.java
+++ b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/InterCaseSearchResultsProcessor.java
@@ -29,7 +29,9 @@ 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 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;
@@ -173,7 +175,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 +207,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 +250,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;
@@ -284,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;
@@ -311,7 +313,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..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");
@@ -37,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.
*/
diff --git a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/IntraCasePanel.form b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/IntraCasePanel.form
index d4a11098bb..2d79515463 100644
--- a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/IntraCasePanel.form
+++ b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/IntraCasePanel.form
@@ -99,6 +99,7 @@
+
@@ -116,7 +117,6 @@
-
@@ -128,7 +128,6 @@
-
@@ -139,7 +138,6 @@
-
diff --git a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/IntraCasePanel.java b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/IntraCasePanel.java
index d0c7ab1579..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");
@@ -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() {
@@ -164,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);
@@ -173,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);
@@ -181,7 +180,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() {
diff --git a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties
index 1d610a5e82..128de511de 100644
--- a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties
+++ b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties
@@ -32,7 +32,16 @@ VisualizationPanel.zoomInButton.toolTipText=Zoom in
VisualizationPanel.zoomInButton.text=
VisualizationPanel.zoomOutButton.toolTipText=Zoom out
VisualizationPanel.zoomOutButton.text=
+<<<<<<< HEAD
VisualizationPanel.fastOrganicLayoutButton.text=Redraw
VisualizationPanel.clearVizButton.text_1=Clear
VisualizationPanel.backButton.text_1=Undo
VisualizationPanel.forwardButton.text=Redo
+=======
+VisualizationPanel.circleLayoutButton.text=Circle
+VisualizationPanel.organicLayoutButton.text=Organic
+VisualizationPanel.fastOrganicLayoutButton.text=Redraw
+VisualizationPanel.hierarchyLayoutButton.text=Hierarchical
+VisualizationPanel.clearVizButton.text_1=Clear Viz.
+VisualizationPanel.snapshotButton.text_1=Snapshot Report
+>>>>>>> develop
diff --git a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties-MERGED
index f3d5aba759..b62e471df2 100755
--- a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties-MERGED
+++ b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties-MERGED
@@ -32,6 +32,23 @@ 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=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:
+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
+# {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}
+VisualizationPane_ReportFileOpenPermissionDeniedMessage=Permission to open the report file was denied.
+VisualizationPane_reportName=Communications Snapshot
VisualizationPanel.cancelButton.text=Cancel
VisualizationPanel.computingLayout=Computing Layout
VisualizationPanel.jButton1.text=Fast Organic
@@ -59,7 +76,20 @@ VisualizationPanel.zoomInButton.toolTipText=Zoom in
VisualizationPanel.zoomInButton.text=
VisualizationPanel.zoomOutButton.toolTipText=Zoom out
VisualizationPanel.zoomOutButton.text=
+<<<<<<< HEAD
VisualizationPanel.fastOrganicLayoutButton.text=Redraw
VisualizationPanel.clearVizButton.text_1=Clear
VisualizationPanel.backButton.text_1=Undo
VisualizationPanel.forwardButton.text=Redo
+=======
+VisualizationPanel.circleLayoutButton.text=Circle
+VisualizationPanel.organicLayoutButton.text=Organic
+VisualizationPanel.fastOrganicLayoutButton.text=Redraw
+VisualizationPanel.hierarchyLayoutButton.text=Hierarchical
+VisualizationPanel.clearVizButton.text_1=Clear Viz.
+VisualizationPanel.snapshotButton.text_1=Snapshot Report
+>>>>>>> develop
+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.form b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form
index 910ff48a3e..c50fcfe761 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 @@
-
+
-
+
@@ -114,7 +114,11 @@
-
+
+
+
+
+
@@ -134,6 +138,8 @@
+
+
@@ -279,6 +285,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java
index 4233fcb866..b8a5fe5325 100644
--- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java
+++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java
@@ -29,6 +29,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,20 +42,30 @@ import com.mxgraph.view.mxGraph;
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.PropertyChangeListener;
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;
@@ -76,14 +87,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;
@@ -93,8 +107,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;
@@ -104,7 +121,6 @@ import org.sleuthkit.datamodel.Content;
import static org.sleuthkit.datamodel.Relationship.Type.CALL_LOG;
import static org.sleuthkit.datamodel.Relationship.Type.MESSAGE;
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
@@ -392,6 +408,8 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
jSeparator2 = new JToolBar.Separator();
backButton = new JButton();
forwardButton = new JButton();
+ snapshotButton = new JButton();
+ jSeparator3 = new JToolBar.Separator();
notificationsJFXPanel = new JFXPanel();
setLayout(new BorderLayout());
@@ -411,9 +429,9 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
placeHolderPanel.setLayout(placeHolderPanelLayout);
placeHolderPanelLayout.setHorizontalGroup(placeHolderPanelLayout.createParallelGroup(GroupLayout.LEADING)
.add(placeHolderPanelLayout.createSequentialGroup()
- .addContainerGap(280, Short.MAX_VALUE)
+ .addContainerGap(143, Short.MAX_VALUE)
.add(jTextArea1, GroupLayout.PREFERRED_SIZE, 424, GroupLayout.PREFERRED_SIZE)
- .addContainerGap(455, Short.MAX_VALUE))
+ .addContainerGap(317, Short.MAX_VALUE))
);
placeHolderPanelLayout.setVerticalGroup(placeHolderPanelLayout.createParallelGroup(GroupLayout.LEADING)
.add(placeHolderPanelLayout.createSequentialGroup()
@@ -505,6 +523,16 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
}
});
+ 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)
@@ -531,7 +559,11 @@ 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)
- .addContainerGap(157, Short.MAX_VALUE))
+ .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)
.add(toolbarLayout.createSequentialGroup()
@@ -547,7 +579,9 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
.add(clearVizButton)
.add(jSeparator2, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.add(backButton)
- .add(forwardButton))
+ .add(forwardButton)
+ .add(snapshotButton)
+ .add(jSeparator3, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.add(3, 3, 3))
);
@@ -652,8 +686,8 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
if(newState == null) {
return;
}
-
- // If the zoom was changed, only change the zoom.
+
+ // If the zoom was changed, only change the zoom.
if(newState.isZoomChange()) {
graph.getView().setScale(newState.getZoomValue());
return;
@@ -678,13 +712,13 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
newState.getCommunicationsFilters().forEach(filter -> {
currentFilter.addAndFilter(filter);
});
-
+
rebuildGraph();
// Updates the display
graph.getModel().endUpdate();
-
- fitGraph();
-
+
+ fitGraph();
+
}
private void setStateButtonsEnabled() {
@@ -692,6 +726,24 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
forwardButton.setEnabled(stateManager.canAdvance());
}
+ @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 {
+ handleSnapshotEvent();
+ } catch (NoCurrentCaseException | IOException ex) {
+ 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());
+ } catch( TskCoreException ex) {
+ logger.log(Level.WARNING, "Unable to add report to currenct case", ex); //NON-NLS
+ }
+ }//GEN-LAST:event_snapshotButtonActionPerformed
+
private void fitGraph() {
graphComponent.zoomTo(1, true);
mxPoint translate = graph.getView().getTranslate();
@@ -719,6 +771,128 @@ 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_module_name=Communications",
+ "VisualizationPanel_action_name_text=Snapshot Report",
+ "VisualizationPane_fileName_prompt=Enter name for the Communications Snapshot Report:",
+ "VisualizationPane_reportName=Communications Snapshot",
+ "# {0} - default name",
+ "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}"
+ })
+ private void handleSnapshotEvent() throws NoCurrentCaseException, IOException, TskCoreException {
+ 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);
+
+ if (result == JOptionPane.OK_OPTION) {
+ String enteredReportName = text.getText();
+
+ 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;
+ }
+ }
+
+ String reportName = StringUtils.defaultIfBlank(enteredReportName, defaultReportName);
+ Path reportPath = Paths.get(currentCase.getReportDirectory(), reportName);
+ 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);
+ currentCase.addReport(reportPath.toString(), Bundle.VisualizationPanel_module_name(), 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.",
+ "# {0} - report path",
+ "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_Open_Report(), Bundle.VisualizationPane_Report_OK_Button()};
+
+ int result = JOptionPane.showOptionDialog(graphComponent, message,
+ Bundle.VisualizationPanel_action_dialogs_title(),
+ JOptionPane.OK_CANCEL_OPTION, JOptionPane.INFORMATION_MESSAGE,
+ null, buttons, buttons[1]);
+ if (result == JOptionPane.YES_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 JButton backButton;
@@ -729,9 +903,11 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
private JButton forwardButton;
private JLabel jLabel2;
private JToolBar.Separator jSeparator2;
+ private JToolBar.Separator jSeparator3;
private JTextArea jTextArea1;
private JFXPanel notificationsJFXPanel;
private JPanel placeHolderPanel;
+ private JButton snapshotButton;
private JSplitPane splitPane;
private JPanel toolbar;
private JButton zoomActualButton;
@@ -1042,4 +1218,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..7b4dee6d19
--- /dev/null
+++ b/Core/src/org/sleuthkit/autopsy/communications/snapshot/CommSnapShotReportWriter.java
@@ -0,0 +1,175 @@
+/*
+ * 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.ArrayList;
+import java.util.Collection;
+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.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.
+ */
+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"); //NON-NLS
+
+ 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 filter : filters) {
+ if (filter instanceof DateRangeFilter) {
+ long startDate = ((DateRangeFilter) filter).getStartDate();
+ long endDate = ((DateRangeFilter) filter).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 (filter instanceof AccountTypeFilter) {
+
+ 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 instanceof DeviceFilter) {
+ 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
new file mode 100755
index 0000000000..078f6fc978
--- /dev/null
+++ b/Core/src/org/sleuthkit/autopsy/communications/snapshot/comm_snapshot_template.html
@@ -0,0 +1,21 @@
+
+
+ Communications Snapshot: {{reportTitle}}
+
+
+
+
+
+

+
+
+
+
\ No newline at end of file
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 @@
-
+
-
+
-
+
-
+
@@ -258,8 +258,8 @@
-
-
+
+
@@ -289,7 +289,7 @@
-
+
diff --git a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/VcardParser.java b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/VcardParser.java
index 3d2abda74e..d1f5f25777 100755
--- a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/VcardParser.java
+++ b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/VcardParser.java
@@ -165,8 +165,35 @@ final class VcardParser {
List accountInstances = new ArrayList<>();
extractPhotos(vcard, abstractFile);
-
- ThunderbirdMboxFileIngestModule.addArtifactAttribute(vcard.getFormattedName().getValue(), BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME_PERSON, attributes);
+
+ String name = "";
+ if (vcard.getFormattedName() != null) {
+ name = vcard.getFormattedName().getValue();
+ } else {
+ if (vcard.getStructuredName() != null) {
+ // Attempt to put the name together if there was no formatted version
+ for (String prefix:vcard.getStructuredName().getPrefixes()) {
+ name += prefix + " ";
+ }
+ if (vcard.getStructuredName().getGiven() != null) {
+ name += vcard.getStructuredName().getGiven() + " ";
+ }
+ if (vcard.getStructuredName().getFamily() != null) {
+ name += vcard.getStructuredName().getFamily() + " ";
+ }
+ for (String suffix:vcard.getStructuredName().getSuffixes()) {
+ name += suffix + " ";
+ }
+ if (! vcard.getStructuredName().getAdditionalNames().isEmpty()) {
+ name += "(";
+ for (String addName:vcard.getStructuredName().getAdditionalNames()) {
+ name += addName + " ";
+ }
+ name += ")";
+ }
+ }
+ }
+ ThunderbirdMboxFileIngestModule.addArtifactAttribute(name, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME_PERSON, attributes);
for (Telephone telephone : vcard.getTelephoneNumbers()) {
addPhoneAttributes(telephone, abstractFile, attributes);
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...