merged in develop

This commit is contained in:
Kelly Kelly 2019-03-25 19:34:41 -04:00
commit f73b255ce8
80 changed files with 1461 additions and 2358 deletions

View File

@ -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.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-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.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.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-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 file.reference.tika-parsers-1.17.jar=release/modules/ext/tika-parsers-1.17.jar

View File

@ -450,8 +450,8 @@
<binary-origin>release/modules/ext/jhighlight-1.0.2.jar</binary-origin> <binary-origin>release/modules/ext/jhighlight-1.0.2.jar</binary-origin>
</class-path-extension> </class-path-extension>
<class-path-extension> <class-path-extension>
<runtime-relative-path>ext/sleuthkit-postgresql-4.6.5.jar</runtime-relative-path> <runtime-relative-path>ext/sleuthkit-postgresql-4.6.6.jar</runtime-relative-path>
<binary-origin>release/modules/ext/sleuthkit-postgresql-4.6.5.jar</binary-origin> <binary-origin>release/modules/ext/sleuthkit-postgresql-4.6.6.jar</binary-origin>
</class-path-extension> </class-path-extension>
<class-path-extension> <class-path-extension>
<runtime-relative-path>ext/jempbox-1.8.13.jar</runtime-relative-path> <runtime-relative-path>ext/jempbox-1.8.13.jar</runtime-relative-path>

View File

@ -168,7 +168,7 @@ public class CorrelationDataSource implements Serializable {
* *
* @return the ID or -1 if unknown * @return the ID or -1 if unknown
*/ */
int getID() { public int getID() {
return dataSourceID; return dataSourceID;
} }

View File

@ -1,96 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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<String> errorMessages, List<Content> 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<String> errorMessages, List<Content> dataSources) {
done(result, errorMessages, dataSources);
}
}

View File

@ -43,6 +43,9 @@ import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgress
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.TimeStampUtils; import org.sleuthkit.autopsy.coreutils.TimeStampUtils;
import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor; 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.events.AutopsyEvent;
import org.sleuthkit.autopsy.ingest.IngestJob; import org.sleuthkit.autopsy.ingest.IngestJob;
import org.sleuthkit.autopsy.ingest.IngestJobSettings; import org.sleuthkit.autopsy.ingest.IngestJobSettings;
@ -177,7 +180,7 @@ public class CommandLineIngestManager {
return; return;
} }
DataSource dataSource = new DataSource("", Paths.get(dataSourcePath)); AutoIngestDataSource dataSource = new AutoIngestDataSource("", Paths.get(dataSourcePath));
try { try {
// run data source processor // run data source processor
runDataSourceProcessor(caseForJob, dataSource); runDataSourceProcessor(caseForJob, dataSource);
@ -228,7 +231,7 @@ public class CommandLineIngestManager {
* @param dataSource DataSource object * @param dataSource DataSource object
* @return object ID * @return object ID
*/ */
private Long getDataSourceId(DataSource dataSource) { private Long getDataSourceId(AutoIngestDataSource dataSource) {
Content content = dataSource.getContent().get(0); Content content = dataSource.getContent().get(0);
return content.getId(); return content.getId();
} }
@ -271,7 +274,7 @@ public class CommandLineIngestManager {
* task is interrupted while blocked, i.e., if auto ingest is shutting * task is interrupted while blocked, i.e., if auto ingest is shutting
* down. * 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()); LOGGER.log(Level.INFO, "Adding data source {0} ", dataSource.getPath().toString());
@ -329,7 +332,7 @@ public class CommandLineIngestManager {
* *
* @param dataSource The data source. * @param dataSource The data source.
*/ */
private void logDataSourceProcessorResult(DataSource dataSource) { private void logDataSourceProcessorResult(AutoIngestDataSource dataSource) {
DataSourceProcessorCallback.DataSourceProcessorResult resultCode = dataSource.getResultDataSourceProcessorResultCode(); DataSourceProcessorCallback.DataSourceProcessorResult resultCode = dataSource.getResultDataSourceProcessorResultCode();
if (null != resultCode) { if (null != resultCode) {
@ -376,7 +379,7 @@ public class CommandLineIngestManager {
* task is interrupted while blocked, i.e., if auto ingest is shutting * task is interrupted while blocked, i.e., if auto ingest is shutting
* down. * 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()); LOGGER.log(Level.INFO, "Starting ingest modules analysis for {0} ", dataSource.getPath());
IngestJobEventListener ingestJobEventListener = new IngestJobEventListener(); IngestJobEventListener ingestJobEventListener = new IngestJobEventListener();

View File

@ -193,7 +193,12 @@ public class CommandLineIngestSettingsPanel extends javax.swing.JPanel {
if (outputPath.isEmpty()) { if (outputPath.isEmpty()) {
jLabelInvalidResultsFolder.setVisible(true); jLabelInvalidResultsFolder.setVisible(true);
jLabelInvalidResultsFolder.setText(NbBundle.getMessage(CommandLineIngestSettingsPanel.class, "CommandLineIngestSettingsPanel.ResultsDirectoryUnspecified")); 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)) { if (!isFolderPathValid(outputPath)) {

View File

@ -1,66 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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<String> errorMessages;
private List<Content> 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<String> errorMessages, List<Content> content) {
this.resultCode = result;
this.errorMessages = new ArrayList<>(errorMessages);
this.content = new ArrayList<>(content);
}
synchronized DataSourceProcessorResult getResultDataSourceProcessorResultCode() {
return resultCode;
}
synchronized List<String> getDataSourceProcessorErrorMessages() {
return new ArrayList<>(errorMessages);
}
synchronized List<Content> getContent() {
return new ArrayList<>(content);
}
}

View File

@ -130,12 +130,12 @@ public abstract class AbstractCommonAttributeSearcher {
} }
} }
static Map<Integer, CommonAttributeValueList> collateMatchesByNumberOfInstances(Map<String, CommonAttributeValue> commonFiles) { static TreeMap<Integer, CommonAttributeValueList> collateMatchesByNumberOfInstances(Map<String, CommonAttributeValue> commonFiles) {
//collate matches by number of matching instances - doing this in sql doesnt seem efficient //collate matches by number of matching instances - doing this in sql doesnt seem efficient
Map<Integer, CommonAttributeValueList> instanceCollatedCommonFiles = new TreeMap<>(); TreeMap<Integer, CommonAttributeValueList> instanceCollatedCommonFiles = new TreeMap<>();
for (CommonAttributeValue md5Metadata : commonFiles.values()) { for (CommonAttributeValue md5Metadata : commonFiles.values()) {
Integer size = md5Metadata.getInstanceCount(); Integer size = md5Metadata.getNumberOfDataSourcesInCurrentCase();
if (instanceCollatedCommonFiles.containsKey(size)) { if (instanceCollatedCommonFiles.containsKey(size)) {
instanceCollatedCommonFiles.get(size).addMetadataToList(md5Metadata); instanceCollatedCommonFiles.get(size).addMetadataToList(md5Metadata);
@ -186,9 +186,9 @@ public abstract class AbstractCommonAttributeSearcher {
* when checkType is ONLY_TEXT_FILES. ".doc", ".docx", ".odt", ".xls", * when checkType is ONLY_TEXT_FILES. ".doc", ".docx", ".odt", ".xls",
* ".xlsx", ".ppt", ".pptx" ".txt", ".rtf", ".log", ".text", ".xml" ".html", * ".xlsx", ".ppt", ".pptx" ".txt", ".rtf", ".log", ".text", ".xml" ".html",
* ".htm", ".css", ".js", ".php", ".aspx" ".pdf" * ".htm", ".css", ".js", ".php", ".aspx" ".pdf"
* //ignore text/plain due to large number of results with that type
*/ */
static final Set<String> TEXT_FILES_MIME_TYPES = Stream.of( static final Set<String> TEXT_FILES_MIME_TYPES = Stream.of(
"text/plain", //NON-NLS
"application/rtf", //NON-NLS "application/rtf", //NON-NLS
"application/pdf", //NON-NLS "application/pdf", //NON-NLS
"text/css", //NON-NLS "text/css", //NON-NLS

View File

@ -44,5 +44,5 @@ CommonAttributePanel.resultsDisplayLabel.text_2=Display results organized by:
CommonAttributePanel.organizeByCaseRadio.text=Case CommonAttributePanel.organizeByCaseRadio.text=Case
CommonAttributePanel.organizeByCountRadio.text=Number of occurrences CommonAttributePanel.organizeByCountRadio.text=Number of occurrences
CommonAttributePanel.caseResultsRadioButton.text=Case 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: CommonAttributePanel.displayResultsLabel.text_2=Display results organized by:

View File

@ -60,7 +60,7 @@ CommonFilesSearchResultsViewerTable.noDescText=\
CommonFilesSearchResultsViewerTable.pathColLbl=Parent Path CommonFilesSearchResultsViewerTable.pathColLbl=Parent Path
CommonFilesSearchResultsViewerTable.valueColLbl=Value CommonFilesSearchResultsViewerTable.valueColLbl=Value
InstanceCountNode.createSheet.noDescription=\ InstanceCountNode.createSheet.noDescription=\
InstanceCountNode.displayName=Files with %s instances (%s) InstanceCountNode.displayName=Exists in %s data sources (%s)
IntraCasePanel.selectDataSourceComboBox.actionCommand= IntraCasePanel.selectDataSourceComboBox.actionCommand=
CommonAttributePanel.jCheckBox1.text=Hide files found in over CommonAttributePanel.jCheckBox1.text=Hide files found in over
CommonAttributePanel.jLabel1.text=% of data sources in central repository. 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.organizeByCaseRadio.text=Case
CommonAttributePanel.organizeByCountRadio.text=Number of occurrences CommonAttributePanel.organizeByCountRadio.text=Number of occurrences
CommonAttributePanel.caseResultsRadioButton.text=Case 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: CommonAttributePanel.displayResultsLabel.text_2=Display results organized by:
# {0} - case name # {0} - case name
# {1} - attr type # {1} - attr type

View File

@ -25,6 +25,7 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.TreeMap;
import java.util.logging.Level; import java.util.logging.Level;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeNormalizationException; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeNormalizationException;
@ -58,7 +59,7 @@ final public class CommonAttributeCountSearchResults {
*/ */
CommonAttributeCountSearchResults(Map<Integer, CommonAttributeValueList> metadata, int percentageThreshold, CorrelationAttributeInstance.Type resultType) { CommonAttributeCountSearchResults(Map<Integer, CommonAttributeValueList> metadata, int percentageThreshold, CorrelationAttributeInstance.Type resultType) {
//wrap in a new object in case any client code has used an unmodifiable collection //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.percentageThreshold = percentageThreshold;
this.resultTypeId = resultType.getId(); this.resultTypeId = resultType.getId();
} }
@ -73,7 +74,7 @@ final public class CommonAttributeCountSearchResults {
*/ */
CommonAttributeCountSearchResults(Map<Integer, CommonAttributeValueList> metadata, int percentageThreshold) { CommonAttributeCountSearchResults(Map<Integer, CommonAttributeValueList> metadata, int percentageThreshold) {
//wrap in a new object in case any client code has used an unmodifiable collection //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.percentageThreshold = percentageThreshold;
this.resultTypeId = CorrelationAttributeInstance.FILES_TYPE_ID; this.resultTypeId = CorrelationAttributeInstance.FILES_TYPE_ID;
} }

View File

@ -294,7 +294,11 @@ final class CommonAttributePanel extends javax.swing.JDialog implements Observer
DataResultTopComponent.createInstance(tabTitle, Bundle.CommonAttributePanel_search_results_pathText(), commonFilesNode, 1); DataResultTopComponent.createInstance(tabTitle, Bundle.CommonAttributePanel_search_results_pathText(), commonFilesNode, 1);
} else { } else {
// -3969 // -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)); DataResultFilterNode dataResultFilterNode = new DataResultFilterNode(commonFilesNode, ExplorerManager.find(CommonAttributePanel.this));
TableFilterNode tableFilterWithDescendantsNode = new TableFilterNode(dataResultFilterNode, 3); TableFilterNode tableFilterWithDescendantsNode = new TableFilterNode(dataResultFilterNode, 3);
DataResultViewerTable table = new CommonAttributesSearchResultsViewerTable(); DataResultViewerTable table = new CommonAttributesSearchResultsViewerTable();

View File

@ -25,6 +25,7 @@ import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children; import org.openide.nodes.Children;
import org.openide.nodes.Node; import org.openide.nodes.Node;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
import org.sleuthkit.autopsy.datamodel.DisplayableItemNode; import org.sleuthkit.autopsy.datamodel.DisplayableItemNode;
import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor; import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor;
@ -35,8 +36,8 @@ import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor;
*/ */
final public class CommonAttributeSearchResultRootNode extends DisplayableItemNode { final public class CommonAttributeSearchResultRootNode extends DisplayableItemNode {
CommonAttributeSearchResultRootNode(CommonAttributeCountSearchResults metadataList) { CommonAttributeSearchResultRootNode(CommonAttributeCountSearchResults metadataList, CorrelationAttributeInstance.Type type) {
super(Children.create(new InstanceCountNodeFactory(metadataList), true)); super(Children.create(new InstanceCountNodeFactory(metadataList, type), true));
} }
CommonAttributeSearchResultRootNode(CommonAttributeCaseSearchResults metadataList) { CommonAttributeSearchResultRootNode(CommonAttributeCaseSearchResults metadataList) {
@ -73,6 +74,7 @@ final public class CommonAttributeSearchResultRootNode extends DisplayableItemNo
private static final Logger LOGGER = Logger.getLogger(InstanceCountNodeFactory.class.getName()); private static final Logger LOGGER = Logger.getLogger(InstanceCountNodeFactory.class.getName());
private final CommonAttributeCountSearchResults searchResults; private final CommonAttributeCountSearchResults searchResults;
private final CorrelationAttributeInstance.Type type;
/** /**
* Build a factory which converts a * Build a factory which converts a
@ -81,8 +83,9 @@ final public class CommonAttributeSearchResultRootNode extends DisplayableItemNo
* *
* @param searchResults * @param searchResults
*/ */
InstanceCountNodeFactory(CommonAttributeCountSearchResults searchResults) { InstanceCountNodeFactory(CommonAttributeCountSearchResults searchResults, CorrelationAttributeInstance.Type type) {
this.searchResults = searchResults; this.searchResults = searchResults;
this.type = type;
} }
@Override @Override
@ -94,7 +97,7 @@ final public class CommonAttributeSearchResultRootNode extends DisplayableItemNo
@Override @Override
protected Node createNodeForKey(Integer instanceCount) { protected Node createNodeForKey(Integer instanceCount) {
CommonAttributeValueList attributeValues = this.searchResults.getAttributeValuesForInstanceCount(instanceCount); CommonAttributeValueList attributeValues = this.searchResults.getAttributeValuesForInstanceCount(instanceCount);
return new InstanceCountNode(instanceCount, attributeValues); return new InstanceCountNode(instanceCount, attributeValues, type);
} }
} }

View File

@ -2,7 +2,7 @@
* *
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2018 Basis Technology Corp. * Copyright 2018-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * 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.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.sleuthkit.datamodel.AbstractFile;
/** /**
* Defines a value that was in the common file search results as well as * 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 String value;
private final List<AbstractCommonAttributeInstance> fileInstances; private final List<AbstractCommonAttributeInstance> fileInstances;
private final Map<String, Integer> fileNames = new HashMap<>();
CommonAttributeValue(String value) { CommonAttributeValue(String value) {
this.value = value; this.value = value;
@ -45,6 +49,23 @@ final public class CommonAttributeValue {
return this.value; 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 * 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(", ")); 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<String> getDataSources() {
Set<String> sources = new HashSet<>(); Set<String> sources = new HashSet<>();
for (AbstractCommonAttributeInstance data : this.fileInstances) { for (AbstractCommonAttributeInstance data : this.fileInstances) {
sources.add(data.getDataSource()); 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<Long> 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) { 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); this.fileInstances.add(metadata);
} }

View File

@ -2,7 +2,7 @@
* *
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2018 Basis Technology Corp. * Copyright 2018-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * 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.Node;
import org.openide.nodes.Sheet; import org.openide.nodes.Sheet;
import org.openide.util.NbBundle; 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.AbstractAbstractFileNode;
import org.sleuthkit.autopsy.datamodel.DisplayableItemNode; import org.sleuthkit.autopsy.datamodel.DisplayableItemNode;
import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor; import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor;
@ -49,15 +51,19 @@ public class CommonAttributeValueNode extends DisplayableItemNode {
* *
* @param data the common feature, and the children * @param data the common feature, and the children
*/ */
public CommonAttributeValueNode(CommonAttributeValue data) { public CommonAttributeValueNode(CommonAttributeValue data, CorrelationAttributeInstance.Type type) {
super(Children.create( super(Children.create(
new FileInstanceNodeFactory(data), true)); new FileInstanceNodeFactory(data), true));
this.commonFileCount = data.getInstanceCount(); this.commonFileCount = data.getInstanceCount();
this.cases = data.getCases(); 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.dataSources = String.join(", ", data.getDataSources());
this.value = data.getValue(); this.value = data.getValue();
//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.setDisplayName(String.format(Bundle.CommonAttributeValueNode_CommonAttributeValueNode_format(), this.value));
}
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/fileset-icon-16.png"); //NON-NLS this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/fileset-icon-16.png"); //NON-NLS
} }

View File

@ -2,7 +2,7 @@
* *
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2018 Basis Technology Corp. * Copyright 2018-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * 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.Node;
import org.openide.nodes.Sheet; import org.openide.nodes.Sheet;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode; import org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode;
@ -45,6 +46,7 @@ public final class InstanceCountNode extends DisplayableItemNode {
final private int instanceCount; final private int instanceCount;
final private CommonAttributeValueList attributeValues; final private CommonAttributeValueList attributeValues;
final private CorrelationAttributeInstance.Type type;
/** /**
* Create a node with the given number of instances, and the given selection * 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 * @param attributeValues
*/ */
@NbBundle.Messages({ @NbBundle.Messages({
"InstanceCountNode.displayName=Files with %s instances (%s)" "InstanceCountNode.displayName=Exists in %s data sources (%s)"
}) })
public InstanceCountNode(int instanceCount, CommonAttributeValueList attributeValues) { public InstanceCountNode(int instanceCount, CommonAttributeValueList attributeValues, CorrelationAttributeInstance.Type type) {
super(Children.create(new CommonAttributeValueNodeFactory(attributeValues.getMetadataList()), false)); super(Children.create(new CommonAttributeValueNodeFactory(attributeValues.getMetadataList(), type), false));
this.type = type;
this.instanceCount = instanceCount; this.instanceCount = instanceCount;
this.attributeValues = attributeValues; this.attributeValues = attributeValues;
@ -81,7 +83,7 @@ public final class InstanceCountNode extends DisplayableItemNode {
*/ */
void createChildren() { void createChildren() {
attributeValues.displayDelayedMetadata(); 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 (??) // maps sting version of value to value Object (??)
private final Map<String, CommonAttributeValue> metadata; private final Map<String, CommonAttributeValue> metadata;
private final CorrelationAttributeInstance.Type type;
CommonAttributeValueNodeFactory(List<CommonAttributeValue> attributeValues) { CommonAttributeValueNodeFactory(List<CommonAttributeValue> attributeValues, CorrelationAttributeInstance.Type type) {
this.metadata = new HashMap<>(); this.metadata = new HashMap<>();
this.type = type;
Iterator<CommonAttributeValue> iterator = attributeValues.iterator(); Iterator<CommonAttributeValue> iterator = attributeValues.iterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
CommonAttributeValue attributeValue = iterator.next(); CommonAttributeValue attributeValue = iterator.next();
@ -167,7 +170,7 @@ public final class InstanceCountNode extends DisplayableItemNode {
@Override @Override
protected Node createNodeForKey(String attributeValue) { protected Node createNodeForKey(String attributeValue) {
CommonAttributeValue md5Metadata = this.metadata.get(attributeValue); CommonAttributeValue md5Metadata = this.metadata.get(attributeValue);
return new CommonAttributeValueNode(md5Metadata); return new CommonAttributeValueNode(md5Metadata, type);
} }
} }
} }

View File

@ -134,7 +134,6 @@
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor"> <Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="buttonGroup"/> <ComponentRef name="buttonGroup"/>
</Property> </Property>
<Property name="selected" type="boolean" value="true"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonpropertiessearch/Bundle.properties" key="InterCasePanel.allFileCategoriesRadioButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/commonpropertiessearch/Bundle.properties" key="InterCasePanel.allFileCategoriesRadioButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
@ -152,6 +151,7 @@
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor"> <Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="buttonGroup"/> <ComponentRef name="buttonGroup"/>
</Property> </Property>
<Property name="selected" type="boolean" value="true"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonpropertiessearch/Bundle.properties" key="InterCasePanel.selectedFileCategoriesButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/commonpropertiessearch/Bundle.properties" key="InterCasePanel.selectedFileCategoriesButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>

View File

@ -2,7 +2,7 @@
* *
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2018 Basis Technology Corp. * Copyright 2018-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * 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 categoriesLabel.setName(""); // NOI18N
buttonGroup.add(allFileCategoriesRadioButton); buttonGroup.add(allFileCategoriesRadioButton);
allFileCategoriesRadioButton.setSelected(true);
org.openide.awt.Mnemonics.setLocalizedText(allFileCategoriesRadioButton, org.openide.util.NbBundle.getMessage(InterCasePanel.class, "InterCasePanel.allFileCategoriesRadioButton.text")); // NOI18N 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.setToolTipText(org.openide.util.NbBundle.getMessage(InterCasePanel.class, "InterCasePanel.allFileCategoriesRadioButton.toolTipText")); // NOI18N
allFileCategoriesRadioButton.setEnabled(false); allFileCategoriesRadioButton.setEnabled(false);
@ -175,6 +174,7 @@ public final class InterCasePanel extends javax.swing.JPanel {
}); });
buttonGroup.add(selectedFileCategoriesButton); buttonGroup.add(selectedFileCategoriesButton);
selectedFileCategoriesButton.setSelected(true);
org.openide.awt.Mnemonics.setLocalizedText(selectedFileCategoriesButton, org.openide.util.NbBundle.getMessage(InterCasePanel.class, "InterCasePanel.selectedFileCategoriesButton.text")); // NOI18N 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.setToolTipText(org.openide.util.NbBundle.getMessage(InterCasePanel.class, "InterCasePanel.selectedFileCategoriesButton.toolTipText")); // NOI18N
selectedFileCategoriesButton.setEnabled(false); selectedFileCategoriesButton.setEnabled(false);

View File

@ -29,7 +29,9 @@ import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.TreeMap;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.stream.Collectors;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance.Type; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance.Type;
@ -173,7 +175,7 @@ final class InterCaseSearchResultsProcessor {
} catch (EamDbException | TskCoreException ex) { } catch (EamDbException | TskCoreException ex) {
LOGGER.log(Level.SEVERE, "Error accessing EamDb processing CaseInstancesTable.", 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) { } catch (EamDbException | TskCoreException ex) {
LOGGER.log(Level.SEVERE, "Error accessing EamDb processing CaseInstancesTable.", 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 class InterCaseByCountCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback, InstanceTableCallback {
private final Map<Integer, CommonAttributeValueList> instanceCollatedCommonFiles = new HashMap<>(); private final TreeMap<Integer, CommonAttributeValueList> instanceCollatedCommonFiles = new TreeMap<>();
private final int caseID; private final int caseID;
private final int targetCase; private final int targetCase;
@ -284,7 +286,7 @@ final class InterCaseSearchResultsProcessor {
} else { } else {
instances = EamDb.getInstance().getArtifactInstancesByTypeValuesAndCases(correlationType, Arrays.asList(corValue), targetCases); 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) { if (size > 1) {
CommonAttributeValue commonAttributeValue = new CommonAttributeValue(corValue); CommonAttributeValue commonAttributeValue = new CommonAttributeValue(corValue);
boolean anotherCase = false; boolean anotherCase = false;
@ -311,7 +313,7 @@ final class InterCaseSearchResultsProcessor {
} }
Map<Integer, CommonAttributeValueList> getInstanceCollatedCommonFiles() { Map<Integer, CommonAttributeValueList> getInstanceCollatedCommonFiles() {
return Collections.unmodifiableMap(instanceCollatedCommonFiles); return Collections.unmodifiableSortedMap(instanceCollatedCommonFiles);
} }
} }

View File

@ -2,7 +2,7 @@
* *
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2018 Basis Technology Corp. * Copyright 2018-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -37,8 +37,8 @@ import org.sleuthkit.datamodel.TskCoreException;
/** /**
* *
* Generates a <code>List<CommonFilesMetadata></code> when * Generates a <code>List<CommonFilesMetadata></code> when
* <code>findMatchesByCount()</code> is called, which organizes files by md5 to prepare * <code>findMatchesByCount()</code> is called, which organizes files by md5 to
* to display in viewer. * prepare to display in viewer.
* *
* This entire thing runs on a background thread where exceptions are handled. * This entire thing runs on a background thread where exceptions are handled.
*/ */

View File

@ -99,6 +99,7 @@
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor"> <Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="buttonGroup"/> <ComponentRef name="buttonGroup"/>
</Property> </Property>
<Property name="selected" type="boolean" value="true"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonpropertiessearch/Bundle.properties" key="IntraCasePanel.selectedFileCategoriesButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/commonpropertiessearch/Bundle.properties" key="IntraCasePanel.selectedFileCategoriesButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
@ -116,7 +117,6 @@
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonpropertiessearch/Bundle.properties" key="IntraCasePanel.pictureVideoCheckbox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/commonpropertiessearch/Bundle.properties" key="IntraCasePanel.pictureVideoCheckbox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties> </Properties>
<Events> <Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="pictureVideoCheckboxActionPerformed"/> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="pictureVideoCheckboxActionPerformed"/>
@ -128,7 +128,6 @@
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonpropertiessearch/Bundle.properties" key="IntraCasePanel.documentsCheckbox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/commonpropertiessearch/Bundle.properties" key="IntraCasePanel.documentsCheckbox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties> </Properties>
<Events> <Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="documentsCheckboxActionPerformed"/> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="documentsCheckboxActionPerformed"/>
@ -139,7 +138,6 @@
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor"> <Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="buttonGroup"/> <ComponentRef name="buttonGroup"/>
</Property> </Property>
<Property name="selected" type="boolean" value="true"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonpropertiessearch/Bundle.properties" key="IntraCasePanel.allFileCategoriesRadioButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/commonpropertiessearch/Bundle.properties" key="IntraCasePanel.allFileCategoriesRadioButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>

View File

@ -2,7 +2,7 @@
* *
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2018 Basis Technology Corp. * Copyright 2018-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * 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 categoriesLabel.setName(""); // NOI18N
buttonGroup.add(selectedFileCategoriesButton); buttonGroup.add(selectedFileCategoriesButton);
selectedFileCategoriesButton.setSelected(true);
org.openide.awt.Mnemonics.setLocalizedText(selectedFileCategoriesButton, org.openide.util.NbBundle.getMessage(IntraCasePanel.class, "IntraCasePanel.selectedFileCategoriesButton.text")); // NOI18N 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.setToolTipText(org.openide.util.NbBundle.getMessage(IntraCasePanel.class, "IntraCasePanel.selectedFileCategoriesButton.toolTipText")); // NOI18N
selectedFileCategoriesButton.addActionListener(new java.awt.event.ActionListener() { selectedFileCategoriesButton.addActionListener(new java.awt.event.ActionListener() {
@ -164,7 +165,6 @@ public final class IntraCasePanel extends javax.swing.JPanel {
pictureVideoCheckbox.setSelected(true); pictureVideoCheckbox.setSelected(true);
org.openide.awt.Mnemonics.setLocalizedText(pictureVideoCheckbox, org.openide.util.NbBundle.getMessage(IntraCasePanel.class, "IntraCasePanel.pictureVideoCheckbox.text")); // NOI18N 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() { pictureVideoCheckbox.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) { public void actionPerformed(java.awt.event.ActionEvent evt) {
pictureVideoCheckboxActionPerformed(evt); pictureVideoCheckboxActionPerformed(evt);
@ -173,7 +173,6 @@ public final class IntraCasePanel extends javax.swing.JPanel {
documentsCheckbox.setSelected(true); documentsCheckbox.setSelected(true);
org.openide.awt.Mnemonics.setLocalizedText(documentsCheckbox, org.openide.util.NbBundle.getMessage(IntraCasePanel.class, "IntraCasePanel.documentsCheckbox.text")); // NOI18N 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() { documentsCheckbox.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) { public void actionPerformed(java.awt.event.ActionEvent evt) {
documentsCheckboxActionPerformed(evt); documentsCheckboxActionPerformed(evt);
@ -181,7 +180,6 @@ public final class IntraCasePanel extends javax.swing.JPanel {
}); });
buttonGroup.add(allFileCategoriesRadioButton); buttonGroup.add(allFileCategoriesRadioButton);
allFileCategoriesRadioButton.setSelected(true);
org.openide.awt.Mnemonics.setLocalizedText(allFileCategoriesRadioButton, org.openide.util.NbBundle.getMessage(IntraCasePanel.class, "IntraCasePanel.allFileCategoriesRadioButton.text")); // NOI18N 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.setToolTipText(org.openide.util.NbBundle.getMessage(IntraCasePanel.class, "IntraCasePanel.allFileCategoriesRadioButton.toolTipText")); // NOI18N
allFileCategoriesRadioButton.addActionListener(new java.awt.event.ActionListener() { allFileCategoriesRadioButton.addActionListener(new java.awt.event.ActionListener() {

View File

@ -32,7 +32,16 @@ VisualizationPanel.zoomInButton.toolTipText=Zoom in
VisualizationPanel.zoomInButton.text= VisualizationPanel.zoomInButton.text=
VisualizationPanel.zoomOutButton.toolTipText=Zoom out VisualizationPanel.zoomOutButton.toolTipText=Zoom out
VisualizationPanel.zoomOutButton.text= VisualizationPanel.zoomOutButton.text=
<<<<<<< HEAD
VisualizationPanel.fastOrganicLayoutButton.text=Redraw VisualizationPanel.fastOrganicLayoutButton.text=Redraw
VisualizationPanel.clearVizButton.text_1=Clear VisualizationPanel.clearVizButton.text_1=Clear
VisualizationPanel.backButton.text_1=Undo VisualizationPanel.backButton.text_1=Undo
VisualizationPanel.forwardButton.text=Redo 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

View File

@ -32,6 +32,23 @@ ResetAndPinAccountsAction.singularText=Visualize Only Selected Account
UnpinAccountsAction.pluralText=Remove Selected Accounts UnpinAccountsAction.pluralText=Remove Selected Accounts
UnpinAccountsAction.singularText=Remove Selected Account UnpinAccountsAction.singularText=Remove Selected Account
VisalizationPanel.paintingError=Problem painting visualization. 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.cancelButton.text=Cancel
VisualizationPanel.computingLayout=Computing Layout VisualizationPanel.computingLayout=Computing Layout
VisualizationPanel.jButton1.text=Fast Organic VisualizationPanel.jButton1.text=Fast Organic
@ -59,7 +76,20 @@ VisualizationPanel.zoomInButton.toolTipText=Zoom in
VisualizationPanel.zoomInButton.text= VisualizationPanel.zoomInButton.text=
VisualizationPanel.zoomOutButton.toolTipText=Zoom out VisualizationPanel.zoomOutButton.toolTipText=Zoom out
VisualizationPanel.zoomOutButton.text= VisualizationPanel.zoomOutButton.text=
<<<<<<< HEAD
VisualizationPanel.fastOrganicLayoutButton.text=Redraw VisualizationPanel.fastOrganicLayoutButton.text=Redraw
VisualizationPanel.clearVizButton.text_1=Clear VisualizationPanel.clearVizButton.text_1=Clear
VisualizationPanel.backButton.text_1=Undo VisualizationPanel.backButton.text_1=Undo
VisualizationPanel.forwardButton.text=Redo 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.

View File

@ -11,7 +11,7 @@
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/> <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/> <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/> <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,0,-127,0,0,5,3"/> <AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,0,121,0,0,5,3"/>
</AuxValues> </AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/> <Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
@ -49,9 +49,9 @@
<DimensionLayout dim="0"> <DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0">
<EmptySpace pref="280" max="32767" attributes="0"/> <EmptySpace pref="143" max="32767" attributes="0"/>
<Component id="jTextArea1" min="-2" pref="424" max="-2" attributes="0"/> <Component id="jTextArea1" min="-2" pref="424" max="-2" attributes="0"/>
<EmptySpace pref="455" max="32767" attributes="0"/> <EmptySpace pref="317" max="32767" attributes="0"/>
</Group> </Group>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
@ -114,7 +114,11 @@
<Component id="zoomActualButton" min="-2" pref="33" max="-2" attributes="0"/> <Component id="zoomActualButton" min="-2" pref="33" max="-2" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/> <EmptySpace min="-2" max="-2" attributes="0"/>
<Component id="fitZoomButton" min="-2" pref="32" max="-2" attributes="0"/> <Component id="fitZoomButton" min="-2" pref="32" max="-2" attributes="0"/>
<EmptySpace pref="157" max="32767" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="jSeparator3" min="-2" pref="10" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="snapshotButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
</Group> </Group>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
@ -134,6 +138,8 @@
<Component id="jSeparator2" alignment="2" max="32767" attributes="0"/> <Component id="jSeparator2" alignment="2" max="32767" attributes="0"/>
<Component id="backButton" alignment="2" min="-2" max="-2" attributes="0"/> <Component id="backButton" alignment="2" min="-2" max="-2" attributes="0"/>
<Component id="forwardButton" alignment="2" min="-2" max="-2" attributes="0"/> <Component id="forwardButton" alignment="2" min="-2" max="-2" attributes="0"/>
<Component id="snapshotButton" alignment="2" min="-2" max="-2" attributes="0"/>
<Component id="jSeparator3" alignment="2" max="32767" attributes="0"/>
</Group> </Group>
<EmptySpace min="-2" pref="3" max="-2" attributes="0"/> <EmptySpace min="-2" pref="3" max="-2" attributes="0"/>
</Group> </Group>
@ -279,6 +285,24 @@
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="forwardButtonActionPerformed"/> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="forwardButtonActionPerformed"/>
</Events> </Events>
</Component> </Component>
<Component class="javax.swing.JButton" name="snapshotButton">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/report/images/image.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="VisualizationPanel.snapshotButton.text_1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="snapshotButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JToolBar$Separator" name="jSeparator3">
<Properties>
<Property name="orientation" type="int" value="1"/>
</Properties>
</Component>
</SubComponents> </SubComponents>
</Container> </Container>
<Container class="javafx.embed.swing.JFXPanel" name="notificationsJFXPanel"> <Container class="javafx.embed.swing.JFXPanel" name="notificationsJFXPanel">

View File

@ -29,6 +29,7 @@ import com.mxgraph.model.mxCell;
import com.mxgraph.model.mxICell; import com.mxgraph.model.mxICell;
import com.mxgraph.swing.handler.mxRubberband; import com.mxgraph.swing.handler.mxRubberband;
import com.mxgraph.swing.mxGraphComponent; import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.util.mxCellRenderer;
import com.mxgraph.util.mxEvent; import com.mxgraph.util.mxEvent;
import com.mxgraph.util.mxEventObject; import com.mxgraph.util.mxEventObject;
import com.mxgraph.util.mxEventSource; import com.mxgraph.util.mxEventSource;
@ -41,20 +42,30 @@ import com.mxgraph.view.mxGraph;
import com.mxgraph.view.mxGraphView; import com.mxgraph.view.mxGraphView;
import java.awt.BorderLayout; import java.awt.BorderLayout;
import java.awt.Color; import java.awt.Color;
import java.awt.Cursor;
import java.awt.Desktop;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Font; import java.awt.Font;
import java.awt.Frame; import java.awt.Frame;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter; import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent; import java.awt.event.MouseWheelEvent;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.beans.PropertyVetoException; 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.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Arrays; import java.util.Arrays;
import java.util.Date;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
@ -76,14 +87,17 @@ import javax.swing.ImageIcon;
import javax.swing.JButton; import javax.swing.JButton;
import javax.swing.JLabel; import javax.swing.JLabel;
import javax.swing.JMenuItem; import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel; import javax.swing.JPanel;
import javax.swing.JPopupMenu; import javax.swing.JPopupMenu;
import javax.swing.JSplitPane; import javax.swing.JSplitPane;
import javax.swing.JTextArea; import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.JToolBar; import javax.swing.JToolBar;
import javax.swing.SwingConstants; import javax.swing.SwingConstants;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import javax.swing.SwingWorker; import javax.swing.SwingWorker;
import org.apache.commons.lang3.StringUtils;
import org.controlsfx.control.Notifications; import org.controlsfx.control.Notifications;
import org.jdesktop.layout.GroupLayout; import org.jdesktop.layout.GroupLayout;
import org.jdesktop.layout.LayoutStyle; import org.jdesktop.layout.LayoutStyle;
@ -93,8 +107,11 @@ import org.openide.nodes.Node;
import org.openide.util.Lookup; import org.openide.util.Lookup;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.lookup.ProxyLookup; import org.openide.util.lookup.ProxyLookup;
import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; 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.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.progress.ModalDialogProgressIndicator; 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.CALL_LOG;
import static org.sleuthkit.datamodel.Relationship.Type.MESSAGE; import static org.sleuthkit.datamodel.Relationship.Type.MESSAGE;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
/** /**
* A panel that goes in the Visualize tab of the Communications Visualization * A panel that goes in the Visualize tab of the Communications Visualization
* Tool. Hosts an JGraphX mxGraphComponent that implements the communications * 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(); jSeparator2 = new JToolBar.Separator();
backButton = new JButton(); backButton = new JButton();
forwardButton = new JButton(); forwardButton = new JButton();
snapshotButton = new JButton();
jSeparator3 = new JToolBar.Separator();
notificationsJFXPanel = new JFXPanel(); notificationsJFXPanel = new JFXPanel();
setLayout(new BorderLayout()); setLayout(new BorderLayout());
@ -411,9 +429,9 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
placeHolderPanel.setLayout(placeHolderPanelLayout); placeHolderPanel.setLayout(placeHolderPanelLayout);
placeHolderPanelLayout.setHorizontalGroup(placeHolderPanelLayout.createParallelGroup(GroupLayout.LEADING) placeHolderPanelLayout.setHorizontalGroup(placeHolderPanelLayout.createParallelGroup(GroupLayout.LEADING)
.add(placeHolderPanelLayout.createSequentialGroup() .add(placeHolderPanelLayout.createSequentialGroup()
.addContainerGap(280, Short.MAX_VALUE) .addContainerGap(143, Short.MAX_VALUE)
.add(jTextArea1, GroupLayout.PREFERRED_SIZE, 424, GroupLayout.PREFERRED_SIZE) .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) placeHolderPanelLayout.setVerticalGroup(placeHolderPanelLayout.createParallelGroup(GroupLayout.LEADING)
.add(placeHolderPanelLayout.createSequentialGroup() .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); GroupLayout toolbarLayout = new GroupLayout(toolbar);
toolbar.setLayout(toolbarLayout); toolbar.setLayout(toolbarLayout);
toolbarLayout.setHorizontalGroup(toolbarLayout.createParallelGroup(GroupLayout.LEADING) 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) .add(zoomActualButton, GroupLayout.PREFERRED_SIZE, 33, GroupLayout.PREFERRED_SIZE)
.addPreferredGap(LayoutStyle.RELATED) .addPreferredGap(LayoutStyle.RELATED)
.add(fitZoomButton, GroupLayout.PREFERRED_SIZE, 32, GroupLayout.PREFERRED_SIZE) .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) toolbarLayout.setVerticalGroup(toolbarLayout.createParallelGroup(GroupLayout.LEADING)
.add(toolbarLayout.createSequentialGroup() .add(toolbarLayout.createSequentialGroup()
@ -547,7 +579,9 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
.add(clearVizButton) .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(backButton) .add(backButton)
.add(forwardButton)) .add(forwardButton)
.add(snapshotButton)
.add(jSeparator3, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.add(3, 3, 3)) .add(3, 3, 3))
); );
@ -692,6 +726,24 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
forwardButton.setEnabled(stateManager.canAdvance()); 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() { private void fitGraph() {
graphComponent.zoomTo(1, true); graphComponent.zoomTo(1, true);
mxPoint translate = graph.getView().getTranslate(); mxPoint translate = graph.getView().getTranslate();
@ -719,6 +771,128 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
graphComponent.zoom((heightFactor + widthFactor) / 2.0); 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 // Variables declaration - do not modify//GEN-BEGIN:variables
private JButton backButton; private JButton backButton;
@ -729,9 +903,11 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
private JButton forwardButton; private JButton forwardButton;
private JLabel jLabel2; private JLabel jLabel2;
private JToolBar.Separator jSeparator2; private JToolBar.Separator jSeparator2;
private JToolBar.Separator jSeparator3;
private JTextArea jTextArea1; private JTextArea jTextArea1;
private JFXPanel notificationsJFXPanel; private JFXPanel notificationsJFXPanel;
private JPanel placeHolderPanel; private JPanel placeHolderPanel;
private JButton snapshotButton;
private JSplitPane splitPane; private JSplitPane splitPane;
private JPanel toolbar; private JPanel toolbar;
private JButton zoomActualButton; private JButton zoomActualButton;

View File

@ -0,0 +1,175 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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<String, Object> snapShotContext = new HashMap<>();
snapShotContext.put("reportTitle", getReportName()); //NON-NLS
List<SubFilter> 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<Account.Type> selectedAccounts = ((AccountTypeFilter) filter).getAccountTypes();
ArrayList<ReportWriterHelper> 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<String> ids = ((DeviceFilter) filter).getDevices();
ArrayList<ReportWriterHelper> 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;
}
}
}

View File

@ -0,0 +1,21 @@
<html>
<head>
<title>Communications Snapshot: {{reportTitle}}</title>
<link rel="stylesheet" type="text/css" href="index.css" />
<link rel="icon" type="image/ico" href="favicon.ico" />
</head>
<body><div id="header">Communications Snapshot</div>
<div id="content">
<img id="snapshot" src="snapshot.png" alt="Snapshot" >
<table>
<tr><td><b>Date Range</b></td><td></td></tr>
<tr><td>Start:</td><td>{{startTime}}</td></tr>
<tr><td>End:</td><td>{{endTime}}</td></tr>
<tr><td><b>Devices:</b></td><td></td></tr>
{{#devices}}<tr><td>{{label}}</td><td><input type="checkbox" {{#selected}}checked{{/selected}}{{^selected}} {{/selected}} disabled></td></tr>{{/devices}}
<tr><td><b>Account Types</b></td><td></td></tr>
{{#accounts}}<tr><td>{{label}}</td><td><input type="checkbox" {{#selected}}checked{{/selected}}{{^selected}} {{/selected}} disabled></td></tr>{{/accounts}}
</table>
</div>
</body>
</html>

View File

@ -342,9 +342,9 @@
<attr name="instanceCreate" methodvalue="org.sleuthkit.autopsy.modules.stix.STIXReportModule.getDefault"/> <attr name="instanceCreate" methodvalue="org.sleuthkit.autopsy.modules.stix.STIXReportModule.getDefault"/>
<attr name="position" intvalue="910"/> <attr name="position" intvalue="910"/>
</file> </file>
<file name="org-sleuthkit-autopsy-modules-case_uco-ReportCaseUco.instance"> <file name="org-sleuthkit-autopsy-report-caseuco-ReportCaseUco.instance">
<attr name="instanceOf" stringvalue="org.sleuthkit.autopsy.report.GeneralReportModule"/> <attr name="instanceOf" stringvalue="org.sleuthkit.autopsy.report.GeneralReportModule"/>
<attr name="instanceCreate" methodvalue="org.sleuthkit.autopsy.modules.case_uco.ReportCaseUco.getDefault"/> <attr name="instanceCreate" methodvalue="org.sleuthkit.autopsy.report.caseuco.ReportCaseUco.getDefault"/>
<attr name="position" intvalue="911"/> <attr name="position" intvalue="911"/>
</file> </file>
<!--<folder name="JavaHelp"> <!--<folder name="JavaHelp">

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2018 Basis Technology Corp. * Copyright 2011-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -18,7 +18,8 @@
*/ */
package org.sleuthkit.autopsy.corecomponents; 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.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.io.File; import java.io.File;
@ -26,7 +27,6 @@ import java.io.IOException;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.logging.Level; import java.util.logging.Level;
import org.sleuthkit.autopsy.coreutils.PlatformUtil; import org.sleuthkit.autopsy.coreutils.PlatformUtil;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import javax.swing.JMenuItem; 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.ContentUtils;
import org.sleuthkit.autopsy.datamodel.DataConversion; import org.sleuthkit.autopsy.datamodel.DataConversion;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.TskException; import org.sleuthkit.datamodel.TskCoreException;
/** /**
* Hex view of file contents. * Hex view of file contents.
@ -55,8 +55,8 @@ import org.sleuthkit.datamodel.TskException;
@ServiceProvider(service = DataContentViewer.class, position = 1) @ServiceProvider(service = DataContentViewer.class, position = 1)
public class DataContentViewerHex extends javax.swing.JPanel implements DataContentViewer { public class DataContentViewerHex extends javax.swing.JPanel implements DataContentViewer {
private static final long pageLength = 16384; private static final long PAGE_LENGTH = 16384;
private final byte[] data = new byte[(int) pageLength]; private final byte[] data = new byte[(int) PAGE_LENGTH];
private static int currentPage = 1; private static int currentPage = 1;
private int totalPages; private int totalPages;
private Content dataSource; 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 private void goToPageTextFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_goToPageTextFieldActionPerformed
String pageNumberStr = goToPageTextField.getText(); String pageNumberStr = goToPageTextField.getText();
int pageNumber = 0; int pageNumber;
try { try {
pageNumber = Integer.parseInt(pageNumberStr); pageNumber = Integer.parseInt(pageNumberStr);
} catch (NumberFormatException ex) { } catch (NumberFormatException ex) {
@ -330,7 +329,7 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont
Utilities.getRowEnd(outputTextArea, outputTextArea.getCaretPosition())) Utilities.getRowEnd(outputTextArea, outputTextArea.getCaretPosition()))
.toString(); .toString();
// NOTE: This needs to change if the outputFormat of outputTextArea changes. // 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; return Long.decode(hexForUserSelectedLine) + userInput;
} catch (BadLocationException | StringIndexOutOfBoundsException | NumberFormatException ex) { } catch (BadLocationException | StringIndexOutOfBoundsException | NumberFormatException ex) {
@ -368,10 +367,11 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont
}//GEN-LAST:event_launchHxDButtonActionPerformed }//GEN-LAST:event_launchHxDButtonActionPerformed
/** /**
* Performs the file copying and process launching in a SwingWorker so that the * Performs the file copying and process launching in a SwingWorker so that
* UI is not blocked when opening large files. * the UI is not blocked when opening large files.
*/ */
private class BackgroundFileCopyTask extends SwingWorker<Void, Void> { private class BackgroundFileCopyTask extends SwingWorker<Void, Void> {
private boolean wasCancelled = false; private boolean wasCancelled = false;
@Override @Override
@ -385,7 +385,7 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont
try { try {
File HxDExecutable = new File(UserPreferences.getExternalHexEditorPath()); File HxDExecutable = new File(UserPreferences.getExternalHexEditorPath());
if(!HxDExecutable.exists() || !HxDExecutable.canExecute()) { if (!HxDExecutable.exists() || !HxDExecutable.canExecute()) {
JOptionPane.showMessageDialog(null, DataContentViewerHex_launchError()); JOptionPane.showMessageDialog(null, DataContentViewerHex_launchError());
return null; return null;
} }
@ -397,7 +397,7 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont
progress.start(100); progress.start(100);
ContentUtils.writeToFile(dataSource, tempFile, progress, this, true); ContentUtils.writeToFile(dataSource, tempFile, progress, this, true);
if(wasCancelled) { if (wasCancelled) {
tempFile.delete(); tempFile.delete();
progress.finish(); progress.finish();
return null; return null;
@ -425,7 +425,6 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont
} }
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JMenuItem copyMenuItem; private javax.swing.JMenuItem copyMenuItem;
private javax.swing.JLabel currentPageLabel; private javax.swing.JLabel currentPageLabel;
@ -461,7 +460,7 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont
return; return;
} }
currentPage = page; currentPage = page;
long offset = (currentPage - 1) * pageLength; long offset = (currentPage - 1) * PAGE_LENGTH;
setDataView(offset); setDataView(offset);
goToOffsetTextField.setText(Long.toString(offset)); goToOffsetTextField.setText(Long.toString(offset));
} }
@ -475,7 +474,7 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont
if (this.dataSource == null) { if (this.dataSource == null) {
return; return;
} }
currentPage = (int) (offset / pageLength) + 1; currentPage = (int) (offset / PAGE_LENGTH) + 1;
setDataView(offset); setDataView(offset);
goToPageTextField.setText(Integer.toString(currentPage)); goToPageTextField.setText(Integer.toString(currentPage));
} }
@ -489,10 +488,10 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont
int bytesRead = 0; int bytesRead = 0;
if (dataSource.getSize() > 0) { if (dataSource.getSize() > 0) {
try { try {
bytesRead = dataSource.read(data, offset, pageLength); // read the data bytesRead = dataSource.read(data, offset, PAGE_LENGTH); // read the data
} catch (TskException ex) { } catch (TskCoreException ex) {
errorText = NbBundle.getMessage(this.getClass(), "DataContentViewerHex.setDataView.errorText", offset, 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 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 // set the data on the bottom and show it
if (bytesRead <= 0) { if (bytesRead <= 0) {
errorText = NbBundle.getMessage(this.getClass(), "DataContentViewerHex.setDataView.errorText", offset, errorText = NbBundle.getMessage(this.getClass(), "DataContentViewerHex.setDataView.errorText", offset,
offset + pageLength); offset + PAGE_LENGTH);
} }
// disable or enable the next button // disable or enable the next button
@ -521,7 +520,7 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont
// set the output view // set the output view
if (errorText == null) { 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)); outputTextArea.setText(DataConversion.byteArrayToHex(data, showLength, offset));
} else { } else {
outputTextArea.setText(errorText); outputTextArea.setText(errorText);
@ -547,7 +546,7 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont
dataSource = content; dataSource = content;
totalPages = 0; totalPages = 0;
if (dataSource.getSize() > 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)); totalPageLabel.setText(Integer.toString(totalPages));
@ -605,12 +604,8 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont
if (node == null) { if (node == null) {
return false; return false;
} }
Content content = node.getLookup().lookup(Content.class); Content content = DataContentViewerUtility.getDefaultContent(node);
if (content != null && content.getSize() > 0) { return content != null && content.getSize() > 0;
return true;
}
return false;
} }
@Override @Override

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2018 Basis Technology Corp. * Copyright 2011-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -18,12 +18,12 @@
*/ */
package org.sleuthkit.autopsy.corecomponents; 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.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.util.List; import java.util.List;
import java.util.logging.Level; import java.util.logging.Level;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import javax.swing.JMenuItem; 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.coreutils.StringExtract.StringExtractUnicodeTable.SCRIPT;
import org.sleuthkit.autopsy.datamodel.StringContent; import org.sleuthkit.autopsy.datamodel.StringContent;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.TskException; import org.sleuthkit.datamodel.TskCoreException;
/** /**
* Viewer displays strings extracted from contents. * 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 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 //@@@ this is part of the code dealing with the data viewer. could be copied/removed to implement the scrollbar
currentOffset -= PAGE_LENGTH; currentOffset -= PAGE_LENGTH;
currentPage = currentPage - 1; currentPage -= 1;
currentPageLabel.setText(Integer.toString(currentPage)); currentPageLabel.setText(Integer.toString(currentPage));
setDataView(dataSource, currentOffset); setDataView(dataSource, currentOffset);
}//GEN-LAST:event_prevPageButtonActionPerformed }//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 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 //@@@ this is part of the code dealing with the data viewer. could be copied/removed to implement the scrollbar
currentOffset += PAGE_LENGTH; currentOffset += PAGE_LENGTH;
currentPage = currentPage + 1; currentPage += 1;
currentPageLabel.setText(Integer.toString(currentPage)); currentPageLabel.setText(Integer.toString(currentPage));
setDataView(dataSource, currentOffset); setDataView(dataSource, currentOffset);
}//GEN-LAST:event_nextPageButtonActionPerformed }//GEN-LAST:event_nextPageButtonActionPerformed
@ -348,18 +348,15 @@ public class DataContentViewerString extends javax.swing.JPanel implements DataC
int bytesRead = 0; int bytesRead = 0;
// set the data on the bottom and show it // set the data on the bottom and show it
String text = "";
if (dataSource.getSize() > 0) { if (dataSource.getSize() > 0) {
try { try {
bytesRead = dataSource.read(data, offset, PAGE_LENGTH); // read the data bytesRead = dataSource.read(data, offset, PAGE_LENGTH); // read the data
} catch (TskException ex) { } catch (TskCoreException ex) {
text = NbBundle.getMessage(this.getClass(),
"DataContentViewerString.setDataView.errorText", currentOffset,
currentOffset + PAGE_LENGTH);
logger.log(Level.WARNING, "Error while trying to show the String content.", ex); //NON-NLS logger.log(Level.WARNING, "Error while trying to show the String content.", ex); //NON-NLS
} }
} }
String text;
if (bytesRead > 0) { if (bytesRead > 0) {
//text = DataConversion.getString(data, bytesRead, 4); //text = DataConversion.getString(data, bytesRead, 4);
final SCRIPT selScript = (SCRIPT) languageCombo.getSelectedItem(); final SCRIPT selScript = (SCRIPT) languageCombo.getSelectedItem();
@ -498,7 +495,7 @@ public class DataContentViewerString extends javax.swing.JPanel implements DataC
if (node == null) { if (node == null) {
return false; return false;
} }
Content content = node.getLookup().lookup(Content.class); Content content = DataContentViewerUtility.getDefaultContent(node);
return (content != null && content.getSize() > 0); return (content != null && content.getSize() > 0);
} }
@ -511,18 +508,4 @@ public class DataContentViewerString extends javax.swing.JPanel implements DataC
public Component getComponent() { public Component getComponent() {
return this; 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);
}
}
} }

View File

@ -2,7 +2,7 @@
* *
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2018 Basis Technology Corp. * Copyright 2018-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -46,8 +46,7 @@ public class DataSourceLoader {
//try block releases resources - exceptions are handled in done() //try block releases resources - exceptions are handled in done()
try ( try (
SleuthkitCase.CaseDbQuery query = tskDb.executeQuery(SELECT_DATA_SOURCES_LOGICAL); SleuthkitCase.CaseDbQuery query = tskDb.executeQuery(SELECT_DATA_SOURCES_LOGICAL);
ResultSet resultSet = query.getResultSet() ResultSet resultSet = query.getResultSet()) {
) {
while (resultSet.next()) { while (resultSet.next()) {
Long objectId = resultSet.getLong(1); Long objectId = resultSet.getLong(1);
String dataSourceName = resultSet.getString(2); String dataSourceName = resultSet.getString(2);
@ -56,7 +55,7 @@ public class DataSourceLoader {
} }
} }
private void loadImageSources(SleuthkitCase tskDb, Map<Long, String> dataSouceMap) throws SQLException, TskCoreException { private void loadImageSources(SleuthkitCase tskDb, Map<Long, String> dataSourceMap) throws SQLException, TskCoreException {
//try block releases resources - exceptions are handled in done() //try block releases resources - exceptions are handled in done()
try ( try (
SleuthkitCase.CaseDbQuery query = tskDb.executeQuery(SELECT_DATA_SOURCES_IMAGE); SleuthkitCase.CaseDbQuery query = tskDb.executeQuery(SELECT_DATA_SOURCES_IMAGE);
@ -64,10 +63,12 @@ public class DataSourceLoader {
while (resultSet.next()) { while (resultSet.next()) {
Long objectId = resultSet.getLong(1); Long objectId = resultSet.getLong(1);
if (!dataSourceMap.containsKey(objectId)) {
String dataSourceName = resultSet.getString(2); String dataSourceName = resultSet.getString(2);
File image = new File(dataSourceName); File image = new File(dataSourceName);
String dataSourceNameTrimmed = image.getName(); String dataSourceNameTrimmed = image.getName();
dataSouceMap.put(objectId, dataSourceNameTrimmed); dataSourceMap.put(objectId, dataSourceNameTrimmed);
}
} }
} }
} }
@ -76,6 +77,7 @@ public class DataSourceLoader {
* Get a map of data source Ids to their string names for the current case. * Get a map of data source Ids to their string names for the current case.
* *
* @return Map of Long (id) to String (name) * @return Map of Long (id) to String (name)
*
* @throws NoCurrentCaseException * @throws NoCurrentCaseException
* @throws TskCoreException * @throws TskCoreException
* @throws SQLException * @throws SQLException

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2017 Basis Technology Corp. * Copyright 2011-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -16,7 +16,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.sleuthkit.autopsy.experimental.autoingest; package org.sleuthkit.autopsy.datasourceprocessors;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
@ -32,7 +32,7 @@ import org.sleuthkit.datamodel.Content;
* processor finishes running in its own thread. * processor finishes running in its own thread.
*/ */
@Immutable @Immutable
class AddDataSourceCallback extends DataSourceProcessorCallback { public class AddDataSourceCallback extends DataSourceProcessorCallback {
private final Case caseForJob; private final Case caseForJob;
private final AutoIngestDataSource dataSourceInfo; private final AutoIngestDataSource dataSourceInfo;
@ -48,7 +48,7 @@ class AddDataSourceCallback extends DataSourceProcessorCallback {
* @param dataSourceInfo The data source * @param dataSourceInfo The data source
* @param taskId The task id to associate with ingest job events. * @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.caseForJob = caseForJob;
this.dataSourceInfo = dataSourceInfo; this.dataSourceInfo = dataSourceInfo;
this.taskId = taskId; this.taskId = taskId;
@ -87,7 +87,7 @@ class AddDataSourceCallback extends DataSourceProcessorCallback {
* @param result The result code for the processing of the data source. * @param result The result code for the processing of the data source.
* @param errorMessages Any error messages generated during the processing * @param errorMessages Any error messages generated during the processing
* of the data source. * of the data source.
* @param dataSourceContent The content produced by processing the data * @param dataSources The content produced by processing the data
* source. * source.
*/ */
@Override @Override

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2017 Basis Technology Corp. * Copyright 2011-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -16,7 +16,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.sleuthkit.autopsy.experimental.autoingest; package org.sleuthkit.autopsy.datasourceprocessors;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.ArrayList; import java.util.ArrayList;
@ -26,7 +26,7 @@ import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
@ThreadSafe @ThreadSafe
class AutoIngestDataSource { public class AutoIngestDataSource {
private final String deviceId; private final String deviceId;
private final Path path; private final Path path;
@ -34,34 +34,34 @@ class AutoIngestDataSource {
private List<String> errorMessages; private List<String> errorMessages;
private List<Content> content; private List<Content> content;
AutoIngestDataSource(String deviceId, Path path) { public AutoIngestDataSource(String deviceId, Path path) {
this.deviceId = deviceId; this.deviceId = deviceId;
this.path = path; this.path = path;
} }
String getDeviceId() { public String getDeviceId() {
return deviceId; return deviceId;
} }
Path getPath() { public Path getPath() {
return this.path; return this.path;
} }
synchronized void setDataSourceProcessorOutput(DataSourceProcessorResult result, List<String> errorMessages, List<Content> content) { public synchronized void setDataSourceProcessorOutput(DataSourceProcessorResult result, List<String> errorMessages, List<Content> content) {
this.resultCode = result; this.resultCode = result;
this.errorMessages = new ArrayList<>(errorMessages); this.errorMessages = new ArrayList<>(errorMessages);
this.content = new ArrayList<>(content); this.content = new ArrayList<>(content);
} }
synchronized DataSourceProcessorResult getResultDataSourceProcessorResultCode() { public synchronized DataSourceProcessorResult getResultDataSourceProcessorResultCode() {
return resultCode; return resultCode;
} }
synchronized List<String> getDataSourceProcessorErrorMessages() { public synchronized List<String> getDataSourceProcessorErrorMessages() {
return new ArrayList<>(errorMessages); return new ArrayList<>(errorMessages);
} }
synchronized List<Content> getContent() { public synchronized List<Content> getContent() {
return new ArrayList<>(content); return new ArrayList<>(content);
} }

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2019-2019 Basis Technology Corp. * Copyright 2011-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -16,7 +16,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.sleuthkit.autopsy.commandlineingest; package org.sleuthkit.autopsy.datasourceprocessors;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Collection; import java.util.Collection;
@ -25,13 +25,12 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.openide.util.Lookup; import org.openide.util.Lookup;
import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor;
import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException; import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException;
/** /**
* A utility class to find Data Source Processors * A utility class to find Data Source Processors
*/ */
final class DataSourceProcessorUtility { public class DataSourceProcessorUtility {
private DataSourceProcessorUtility() { private DataSourceProcessorUtility() {
} }
@ -47,7 +46,7 @@ final class DataSourceProcessorUtility {
* @throws * @throws
* org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException * org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException
*/ */
static Map<AutoIngestDataSourceProcessor, Integer> getDataSourceProcessorForFile(Path dataSourcePath, Collection<? extends AutoIngestDataSourceProcessor> processorCandidates) throws AutoIngestDataSourceProcessorException { public static Map<AutoIngestDataSourceProcessor, Integer> getDataSourceProcessorForFile(Path dataSourcePath, Collection<? extends AutoIngestDataSourceProcessor> processorCandidates) throws AutoIngestDataSourceProcessorException {
Map<AutoIngestDataSourceProcessor, Integer> validDataSourceProcessorsMap = new HashMap<>(); Map<AutoIngestDataSourceProcessor, Integer> validDataSourceProcessorsMap = new HashMap<>();
for (AutoIngestDataSourceProcessor processor : processorCandidates) { for (AutoIngestDataSourceProcessor processor : processorCandidates) {
int confidence = processor.canProcess(dataSourcePath); int confidence = processor.canProcess(dataSourcePath);
@ -74,7 +73,7 @@ final class DataSourceProcessorUtility {
* @throws * @throws
* org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException * org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException
*/ */
static List<AutoIngestDataSourceProcessor> getOrderedListOfDataSourceProcessors(Path dataSourcePath) throws AutoIngestDataSourceProcessorException { public static List<AutoIngestDataSourceProcessor> getOrderedListOfDataSourceProcessors(Path dataSourcePath) throws AutoIngestDataSourceProcessorException {
// lookup all AutomatedIngestDataSourceProcessors // lookup all AutomatedIngestDataSourceProcessors
Collection<? extends AutoIngestDataSourceProcessor> processorCandidates = Lookup.getDefault().lookupAll(AutoIngestDataSourceProcessor.class); Collection<? extends AutoIngestDataSourceProcessor> processorCandidates = Lookup.getDefault().lookupAll(AutoIngestDataSourceProcessor.class);
return getOrderedListOfDataSourceProcessors(dataSourcePath, processorCandidates); return getOrderedListOfDataSourceProcessors(dataSourcePath, processorCandidates);
@ -96,7 +95,7 @@ final class DataSourceProcessorUtility {
* @throws * @throws
* org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException * org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException
*/ */
static List<AutoIngestDataSourceProcessor> getOrderedListOfDataSourceProcessors(Path dataSourcePath, Collection<? extends AutoIngestDataSourceProcessor> processorCandidates) throws AutoIngestDataSourceProcessorException { public static List<AutoIngestDataSourceProcessor> getOrderedListOfDataSourceProcessors(Path dataSourcePath, Collection<? extends AutoIngestDataSourceProcessor> processorCandidates) throws AutoIngestDataSourceProcessorException {
Map<AutoIngestDataSourceProcessor, Integer> validDataSourceProcessorsMap = getDataSourceProcessorForFile(dataSourcePath, processorCandidates); Map<AutoIngestDataSourceProcessor, Integer> validDataSourceProcessorsMap = getDataSourceProcessorForFile(dataSourcePath, processorCandidates);
return orderDataSourceProcessorsByConfidence(validDataSourceProcessorsMap); return orderDataSourceProcessorsByConfidence(validDataSourceProcessorsMap);
} }
@ -110,7 +109,7 @@ final class DataSourceProcessorUtility {
* the data source along with their confidence score * the data source along with their confidence score
* @return Ordered list of data source processors * @return Ordered list of data source processors
*/ */
static List<AutoIngestDataSourceProcessor> orderDataSourceProcessorsByConfidence(Map<AutoIngestDataSourceProcessor, Integer> validDataSourceProcessorsMap) { public static List<AutoIngestDataSourceProcessor> orderDataSourceProcessorsByConfidence(Map<AutoIngestDataSourceProcessor, Integer> validDataSourceProcessorsMap) {
List<AutoIngestDataSourceProcessor> validDataSourceProcessors = validDataSourceProcessorsMap.entrySet().stream() List<AutoIngestDataSourceProcessor> validDataSourceProcessors = validDataSourceProcessorsMap.entrySet().stream()
.sorted(Map.Entry.<AutoIngestDataSourceProcessor, Integer>comparingByValue().reversed()) .sorted(Map.Entry.<AutoIngestDataSourceProcessor, Integer>comparingByValue().reversed())
.map(Map.Entry::getKey) .map(Map.Entry::getKey)

View File

@ -5,7 +5,6 @@ DataSourceIntegrityIngestModule.process.errProcImg=Error processing {0}
DataSourceIntegrityIngestModule.process.skipNonEwf=Skipping non-disk image data source {0} DataSourceIntegrityIngestModule.process.skipNonEwf=Skipping non-disk image data source {0}
DataSourceIntegrityIngestModule.process.noStoredHash=Image {0} does not have stored hash. DataSourceIntegrityIngestModule.process.noStoredHash=Image {0} does not have stored hash.
DataSourceIntegrityIngestModule.process.startingImg=Starting {0} DataSourceIntegrityIngestModule.process.startingImg=Starting {0}
DataSourceIntegrityIngestModule.process.errGetSizeOfImg=Error getting size of {0}. Image will not be processed.
DataSourceIntegrityIngestModule.process.errReadImgAtChunk=Error reading {0} at chunk {1} DataSourceIntegrityIngestModule.process.errReadImgAtChunk=Error reading {0} at chunk {1}
DataSourceIntegrityIngestModule.shutDown.verified=\ verified DataSourceIntegrityIngestModule.shutDown.verified=\ verified
DataSourceIntegrityIngestModule.shutDown.notVerified=\ not verified DataSourceIntegrityIngestModule.shutDown.notVerified=\ not verified

View File

@ -37,7 +37,6 @@ DataSourceIntegrityIngestModule.process.errProcImg=Error processing {0}
DataSourceIntegrityIngestModule.process.skipNonEwf=Skipping non-disk image data source {0} DataSourceIntegrityIngestModule.process.skipNonEwf=Skipping non-disk image data source {0}
DataSourceIntegrityIngestModule.process.noStoredHash=Image {0} does not have stored hash. DataSourceIntegrityIngestModule.process.noStoredHash=Image {0} does not have stored hash.
DataSourceIntegrityIngestModule.process.startingImg=Starting {0} DataSourceIntegrityIngestModule.process.startingImg=Starting {0}
DataSourceIntegrityIngestModule.process.errGetSizeOfImg=Error getting size of {0}. Image will not be processed.
DataSourceIntegrityIngestModule.process.errReadImgAtChunk=Error reading {0} at chunk {1} DataSourceIntegrityIngestModule.process.errReadImgAtChunk=Error reading {0} at chunk {1}
DataSourceIntegrityIngestModule.shutDown.verified=\ verified DataSourceIntegrityIngestModule.shutDown.verified=\ verified
DataSourceIntegrityIngestModule.shutDown.notVerified=\ not verified DataSourceIntegrityIngestModule.shutDown.notVerified=\ not verified

View File

@ -5,7 +5,6 @@ DataSourceIntegrityModuleFactory.moduleDesc.text=E01\u30d5\u30a1\u30a4\u30eb\u30
DataSourceIntegrityIngestModule.process.skipNonEwf=E01\u30a4\u30e1\u30fc\u30b8\u3067\u306f\u306a\u3044{0}\u3092\u30b9\u30ad\u30c3\u30d7\u3057\u3066\u3044\u307e\u3059 DataSourceIntegrityIngestModule.process.skipNonEwf=E01\u30a4\u30e1\u30fc\u30b8\u3067\u306f\u306a\u3044{0}\u3092\u30b9\u30ad\u30c3\u30d7\u3057\u3066\u3044\u307e\u3059
DataSourceIntegrityIngestModule.process.noStoredHash=\u30a4\u30e1\u30fc\u30b8{0}\u306f\u4fdd\u5b58\u3055\u308c\u3066\u3044\u308b\u30cf\u30c3\u30b7\u30e5\u304c\u3042\u308a\u307e\u305b\u3093\u3002 DataSourceIntegrityIngestModule.process.noStoredHash=\u30a4\u30e1\u30fc\u30b8{0}\u306f\u4fdd\u5b58\u3055\u308c\u3066\u3044\u308b\u30cf\u30c3\u30b7\u30e5\u304c\u3042\u308a\u307e\u305b\u3093\u3002
DataSourceIntegrityIngestModule.process.startingImg={0}\u3092\u958b\u59cb\u4e2d DataSourceIntegrityIngestModule.process.startingImg={0}\u3092\u958b\u59cb\u4e2d
DataSourceIntegrityIngestModule.process.errGetSizeOfImg={0}\u306e\u30b5\u30a4\u30ba\u306e\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002\u30a4\u30e1\u30fc\u30b8\u306f\u51e6\u7406\u3055\u308c\u307e\u305b\u3093\u3002
DataSourceIntegrityIngestModule.process.errReadImgAtChunk={0}\u306e\u30c1\u30e3\u30f3\u30af{1}\u306e\u8aad\u307f\u53d6\u308a\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f DataSourceIntegrityIngestModule.process.errReadImgAtChunk={0}\u306e\u30c1\u30e3\u30f3\u30af{1}\u306e\u8aad\u307f\u53d6\u308a\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f
DataSourceIntegrityIngestModule.shutDown.calcHashLi=<li>\u8a08\u7b97\u3055\u308c\u305f\u30cf\u30c3\u30b7\u30e5\u5024\uff1a{0}</li> DataSourceIntegrityIngestModule.shutDown.calcHashLi=<li>\u8a08\u7b97\u3055\u308c\u305f\u30cf\u30c3\u30b7\u30e5\u5024\uff1a{0}</li>
DataSourceIntegrityIngestModule.shutDown.notVerified=\u8a8d\u8a3c\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f DataSourceIntegrityIngestModule.shutDown.notVerified=\u8a8d\u8a3c\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f

View File

@ -127,14 +127,10 @@ public class DataSourceIntegrityIngestModule implements DataSourceIngestModule {
} }
Image img = (Image) dataSource; Image img = (Image) dataSource;
// Make sure the image size we have is not zero // Get the image size. Log a warning if it is zero.
long size = img.getSize(); long size = img.getSize();
if (size == 0) { if (size == 0) {
logger.log(Level.WARNING, "Size of image {0} was 0 when queried.", imgName); //NON-NLS logger.log(Level.WARNING, "Size of image {0} was 0 when queried.", imgName); //NON-NLS
services.postMessage(IngestMessage.createMessage(MessageType.ERROR, DataSourceIntegrityModuleFactory.getModuleName(),
NbBundle.getMessage(this.getClass(),
"DataSourceIntegrityIngestModule.process.errGetSizeOfImg",
imgName)));
} }
// Determine which mode we're in. // Determine which mode we're in.

View File

@ -5,6 +5,7 @@ CreatePortableCaseModule.createCase.caseDirExists=Case folder {0} already exists
CreatePortableCaseModule.createCase.errorCreatingCase=Error creating case CreatePortableCaseModule.createCase.errorCreatingCase=Error creating case
# {0} - folder # {0} - folder
CreatePortableCaseModule.createCase.errorCreatingFolder=Error creating folder {0} CreatePortableCaseModule.createCase.errorCreatingFolder=Error creating folder {0}
CreatePortableCaseModule.createCase.errorStoringMaxIds=Error storing maximum database IDs
CreatePortableCaseModule.generateReport.caseClosed=Current case has been closed CreatePortableCaseModule.generateReport.caseClosed=Current case has been closed
# {0} - tag name # {0} - tag name
CreatePortableCaseModule.generateReport.copyingArtifacts=Copying artifacts tagged as {0}... CreatePortableCaseModule.generateReport.copyingArtifacts=Copying artifacts tagged as {0}...

View File

@ -24,6 +24,8 @@ import java.util.logging.Level;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
@ -41,6 +43,7 @@ import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardArtifactTag; import org.sleuthkit.datamodel.BlackboardArtifactTag;
import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.CaseDbAccessManager;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.ContentTag; import org.sleuthkit.datamodel.ContentTag;
import org.sleuthkit.datamodel.FileSystem; import org.sleuthkit.datamodel.FileSystem;
@ -63,6 +66,7 @@ public class CreatePortableCaseModule implements GeneralReportModule {
private static final Logger logger = Logger.getLogger(CreatePortableCaseModule.class.getName()); private static final Logger logger = Logger.getLogger(CreatePortableCaseModule.class.getName());
private static final String FILE_FOLDER_NAME = "PortableCaseFiles"; private static final String FILE_FOLDER_NAME = "PortableCaseFiles";
private static final String UNKNOWN_FILE_TYPE_FOLDER = "Other"; private static final String UNKNOWN_FILE_TYPE_FOLDER = "Other";
private static final String MAX_ID_TABLE_NAME = "portable_case_max_ids";
private CreatePortableCasePanel configPanel; private CreatePortableCasePanel configPanel;
// These are the types for the exported file subfolders // These are the types for the exported file subfolders
@ -290,6 +294,7 @@ public class CreatePortableCaseModule implements GeneralReportModule {
"CreatePortableCaseModule.createCase.errorCreatingCase=Error creating case", "CreatePortableCaseModule.createCase.errorCreatingCase=Error creating case",
"# {0} - folder", "# {0} - folder",
"CreatePortableCaseModule.createCase.errorCreatingFolder=Error creating folder {0}", "CreatePortableCaseModule.createCase.errorCreatingFolder=Error creating folder {0}",
"CreatePortableCaseModule.createCase.errorStoringMaxIds=Error storing maximum database IDs",
}) })
private void createCase(File outputDir, ReportProgressPanel progressPanel) { private void createCase(File outputDir, ReportProgressPanel progressPanel) {
@ -312,6 +317,15 @@ public class CreatePortableCaseModule implements GeneralReportModule {
return; return;
} }
// Store the highest IDs
try {
saveHighestIds();
} catch (TskCoreException ex) {
handleError("Error storing maximum database IDs",
Bundle.CreatePortableCaseModule_createCase_errorStoringMaxIds(), ex, progressPanel);
return;
}
// Create the base folder for the copied files // Create the base folder for the copied files
copiedFilesFolder = Paths.get(caseFolder.toString(), FILE_FOLDER_NAME).toFile(); copiedFilesFolder = Paths.get(caseFolder.toString(), FILE_FOLDER_NAME).toFile();
if (! copiedFilesFolder.mkdir()) { if (! copiedFilesFolder.mkdir()) {
@ -338,6 +352,26 @@ 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 = "( table_name TEXT PRIMARY KEY, "
+ " max_id TEXT)";
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"));
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. * Add all files with a given tag to the portable case.
* *
@ -674,19 +708,53 @@ public class CreatePortableCaseModule implements GeneralReportModule {
oldIdToNewContent.clear(); oldIdToNewContent.clear();
newIdToContent.clear(); newIdToContent.clear();
oldTagNameToNewTagName.clear(); oldTagNameToNewTagName.clear();
oldArtTypeIdToNewArtTypeId.clear();
oldAttrTypeIdToNewAttrType.clear();
oldArtifactIdToNewArtifact.clear();
currentCase = null; currentCase = null;
if (portableSkCase != null) { if (portableSkCase != null) {
// We want to close the database connections here but it is currently not possible. JIRA-4736 portableSkCase.close();
portableSkCase = null; portableSkCase = null;
} }
caseFolder = null; caseFolder = null;
copiedFilesFolder = null; copiedFilesFolder = null;
} }
@Override @Override
public JPanel getConfigurationPanel() { public JPanel getConfigurationPanel() {
configPanel = new CreatePortableCasePanel(); configPanel = new CreatePortableCasePanel();
return configPanel; return configPanel;
} }
private 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 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);
} 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);
}
}
}
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 516 B

View File

@ -0,0 +1,241 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2016 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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 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();
}
/**
* Get the name for the report.
*
* @return Returns the reportName
*/
protected String getReportName() {
return reportName;
}
/**
* 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
* 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<String, Object> 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<String, Object> 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 favicon
if (StringUtils.isBlank(agencyLogoPath)) {
copyInternalResource("/org/sleuthkit/autopsy/report/images/favicon.ico", "favicon.ico");
} else {
Files.copy(Files.newInputStream(Paths.get(agencyLogoPath)), reportFolderPath.resolve("favicon.ico")); //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
}
}
}

View File

@ -9,7 +9,7 @@
<h1>Report Navigation</h1> <h1>Report Navigation</h1>
<ul class="nav"> <ul class="nav">
<li style="background: url(summary.png) left center no-repeat;"><a href="summary.html" target="content">Case Summary</a></li> <li style="background: url(summary.png) left center no-repeat;"><a href="summary.html" target="content">Case Summary</a></li>
<li style="background: url(snapshot_icon.png) left center no-repeat;"><a href="snapshot.html" target="content">Timeline Snapshot</a></li> <li style="background: url(snapshot_icon.png) left center no-repeat;"><a href="snapshot.html" target="content">Snapshot</a></li>
</ul> </ul>
</div> </div>
</body> </body>

View File

@ -9,7 +9,7 @@
<div id="wrapper"> <div id="wrapper">
<h1>{{reportBranding.getReportTitle}}: {{reportName}}{{#ingestRunning}}<span>Warning, this report was run before ingest services completed!</span>{{/ingestRunning}}</h1> <h1>{{reportBranding.getReportTitle}}: {{reportName}}{{#ingestRunning}}<span>Warning, this report was run before ingest services completed!</span>{{/ingestRunning}}</h1>
<p class="subheadding">Timeline Report generated on {{generationDateTime}}</p> <p class="subheadding">Report generated on {{generationDateTime}}</p>
<div class="title"> <div class="title">
{{#reportBranding.getAgencyLogoPath}} {{#reportBranding.getAgencyLogoPath}}
<div class="left"> <div class="left">

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2016 Basis Technology Corp. * Copyright 2016 - 2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -18,46 +18,23 @@
*/ */
package org.sleuthkit.autopsy.timeline.snapshot; 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.awt.image.BufferedImage;
import java.io.IOException; 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.Path;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormat;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.report.uisnapshot.UiSnapShotReportWriter;
import org.sleuthkit.autopsy.report.ReportBranding;
import org.sleuthkit.autopsy.timeline.zooming.ZoomParams; import org.sleuthkit.autopsy.timeline.zooming.ZoomParams;
/** /**
* Generate and write the Timeline snapshot report to disk. * Generate and write the Timeline snapshot report to disk.
*/ */
public class SnapShotReportWriter { public class SnapShotReportWriter extends 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 final ZoomParams zoomParams; private final ZoomParams zoomParams;
private final Date generationDate;
private final BufferedImage image; private final BufferedImage image;
/** /**
@ -74,37 +51,9 @@ public class SnapShotReportWriter {
* report. * report.
*/ */
public SnapShotReportWriter(Case currentCase, Path reportFolderPath, String reportName, ZoomParams zoomParams, Date generationDate, BufferedImage snapshot) { public SnapShotReportWriter(Case currentCase, Path reportFolderPath, String reportName, ZoomParams zoomParams, Date generationDate, BufferedImage snapshot) {
this.currentCase = currentCase; super(currentCase, reportFolderPath, reportName, generationDate);
this.reportFolderPath = reportFolderPath;
this.reportName = reportName;
this.zoomParams = zoomParams; this.zoomParams = zoomParams;
this.generationDate = generationDate;
this.image = snapshot; 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. * @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 //make a map of context objects to resolve template paramaters against
HashMap<String, Object> snapShotContext = new HashMap<>(); HashMap<String, Object> 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("startTime", zoomParams.getTimeRange().getStart().toString(DateTimeFormat.fullDateTime())); //NON-NLS
snapShotContext.put("endTime", zoomParams.getTimeRange().getEnd().toString(DateTimeFormat.fullDateTime())); //NON-NLS snapShotContext.put("endTime", zoomParams.getTimeRange().getEnd().toString(DateTimeFormat.fullDateTime())); //NON-NLS
snapShotContext.put("zoomParams", zoomParams); //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 fillTemplateAndWrite("/org/sleuthkit/autopsy/timeline/snapshot/snapshot_template.html", "Snapshot", snapShotContext, getReportFolderPath().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<String, Object> 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<String, Object> 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
}
} }
} }

View File

@ -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.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.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.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.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.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 file.reference.guava-19.0.jar=release/modules/ext/guava-19.0.jar

View File

@ -1,374 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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;
/*
* 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";
/**
* 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<String> errorMessages = new ArrayList<>();
List<Content> 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<String> 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 {0}. Deleteing the directory", destinationFolder.toString());
FileUtils.deleteDirectory(destinationFolder.toFile());
throw ex;
}
// lookup all AutomatedIngestDataSourceProcessors so that we only do it once.
// LocalDisk, LocalFiles, and ArchiveDSP are removed from the list.
List<AutoIngestDataSourceProcessor> processorCandidates = getListOfValidDataSourceProcessors();
// do processing
for (String file : extractedFiles) {
// we only care about files, skip directories
File fileObject = new File(file);
if (fileObject.isDirectory()) {
numExtractedFilesRemaining--;
continue;
}
// identify all "valid" DSPs that can process this file
List<AutoIngestDataSourceProcessor> 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, newFilePath);
DataSourceProcessorCallback internalArchiveDspCallBack = new AddDataSourceCallback(currentCase, internalDataSource, taskId, archiveDspLock);
selectedProcessor.process(deviceId, newFilePath, 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());
// 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);
}
}
// 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.
numExtractedFilesRemaining--;
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 (numExtractedFilesRemaining > 0) {
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<String> 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, pathsList, 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);
}
}
/**
* Get a list of data source processors. LocalFiles, RawDSProcessor, and
* ArchiveDSP are removed from the list.
*
* @return List of data source processors
*/
private List<AutoIngestDataSourceProcessor> getListOfValidDataSourceProcessors() {
Collection<? extends AutoIngestDataSourceProcessor> processorCandidates = Lookup.getDefault().lookupAll(AutoIngestDataSourceProcessor.class);
List<AutoIngestDataSourceProcessor> validDataSourceProcessors = processorCandidates.stream().collect(Collectors.toList());
for (Iterator<AutoIngestDataSourceProcessor> 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<String> for error messages
* @param errorMessages List of AutoIngestDataSourceProcessor to try
*
* @return Ordered list of applicable DSPs
*/
private List<AutoIngestDataSourceProcessor> getDataSourceProcessorsForFile(Path dataSourcePath, List<String> errorMessages,
List<AutoIngestDataSourceProcessor> processorCandidates) {
// Get an ordered list of data source processors to try
List<AutoIngestDataSourceProcessor> 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;
}
}

View File

@ -1,176 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2017 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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;
}
}

View File

@ -1,94 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<Properties>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 65]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[403, 65]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Component id="pathTextField" max="32767" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="browseButton" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="2" max="-2" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="pathLabel" min="-2" max="-2" attributes="0"/>
<Component id="errorLabel" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="0" pref="277" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Component id="pathLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="browseButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="pathTextField" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="3" max="-2" attributes="0"/>
<Component id="errorLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JLabel" name="pathLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="ArchiveFilePanel.pathLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="browseButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="ArchiveFilePanel.browseButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="browseButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JTextField" name="pathTextField">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="ArchiveFilePanel.pathTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="errorLabel">
<Properties>
<Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="0" green="0" red="ff" type="rgb"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="ArchiveFilePanel.errorLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Form>

View File

@ -1,289 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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<FileFilter> 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<FileFilter> 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.
*/
// <editor-fold defaultstate="collapsed" desc="Generated Code">//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))
);
}// </editor-fold>//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();
}
}

View File

@ -1,313 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2015 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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<String> 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<FileFilter> ARCHIVE_FILTERS = new ArrayList<>();
static {
ARCHIVE_FILTERS.add(SEVEN_ZIP_FILTER);
}
private ArchiveUtil() {
}
static List<FileFilter> 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<FileFilter> 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<String> 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<String> 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<String> 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<String> 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);
}
}
}
}
}

View File

@ -32,7 +32,6 @@ import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.BasicFileAttributes;
import java.sql.SQLException;
import java.time.Duration; import java.time.Duration;
import java.time.Instant; import java.time.Instant;
import java.util.ArrayList; 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.RuntimeProperties;
import org.sleuthkit.autopsy.core.ServicesMonitor; import org.sleuthkit.autopsy.core.ServicesMonitor;
import org.sleuthkit.autopsy.core.ServicesMonitor.ServicesMonitorException; 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;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback.DataSourceProcessorResult; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback.DataSourceProcessorResult;
import static org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS; 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.experimental.configuration.SharedConfiguration.SharedConfigurationException;
import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor; import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor;
import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException; 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.AutoIngestJob.AutoIngestJobException;
import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestNodeControlEvent.ControlEventType; import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestNodeControlEvent.ControlEventType;
import org.sleuthkit.autopsy.ingest.IngestJob; import org.sleuthkit.autopsy.ingest.IngestJob;

View File

@ -219,10 +219,6 @@ AutoIngestMetricsDialog.reportTextArea.text=
AutoIngestMetricsDialog.metricsButton.text=Generate Metrics Report AutoIngestMetricsDialog.metricsButton.text=Generate Metrics Report
AutoIngestMetricsDialog.closeButton.text=Close AutoIngestMetricsDialog.closeButton.text=Close
AutoIngestMetricsDialog.datePicker.toolTipText=Choose a date 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: AutoIngestMetricsDialog.startingDataLabel.text=Starting Date:
AutoIngestControlPanel.bnDeprioritizeCase.text=Deprioritize Case AutoIngestControlPanel.bnDeprioritizeCase.text=Deprioritize Case
AutoIngestControlPanel.bnDeprioritizeJob.text=Deprioritize Job AutoIngestControlPanel.bnDeprioritizeJob.text=Deprioritize Job

View File

@ -8,9 +8,6 @@ AinStatusNode.status.shuttingdown=Shutting Down
AinStatusNode.status.startingup=Starting Up AinStatusNode.status.startingup=Starting Up
AinStatusNode.status.title=Status AinStatusNode.status.title=Status
AinStatusNode.status.unknown=Unknown 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.cancelJobAction.title=Cancel Job
AutoIngestAdminActions.cancelModuleAction.title=Cancel Module AutoIngestAdminActions.cancelModuleAction.title=Cancel Module
AutoIngestAdminActions.deleteCaseAction.error=Failed to delete case. AutoIngestAdminActions.deleteCaseAction.error=Failed to delete case.
@ -170,12 +167,9 @@ CTL_AutoIngestDashboardOpenAction=Auto Ingest Dashboard
CTL_AutoIngestDashboardTopComponent=Auto Ingest Jobs CTL_AutoIngestDashboardTopComponent=Auto Ingest Jobs
CTL_CasesDashboardAction=Multi-User Cases Dashboard CTL_CasesDashboardAction=Multi-User Cases Dashboard
CTL_CasesDashboardTopComponent=Cases 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 DeleteCaseInputDirectoriesAction.menuItemText=Delete Input Directories
DeleteCasesAction.menuItemText=Delete Case and Jobs DeleteCasesAction.menuItemText=Delete Case and Jobs
DeleteCasesForReprocessingAction.menuItemText=Delete for Reprocessing 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 HINT_CasesDashboardTopComponent=This is an adminstrative dashboard for multi-user cases
OpenAutoIngestLogAction.deletedLogErrorMsg=The case auto ingest log has been deleted. OpenAutoIngestLogAction.deletedLogErrorMsg=The case auto ingest log has been deleted.
OpenAutoIngestLogAction.logOpenFailedErrorMsg=Failed to open case auto ingest log. See application log for details. 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.metricsButton.text=Generate Metrics Report
AutoIngestMetricsDialog.closeButton.text=Close AutoIngestMetricsDialog.closeButton.text=Close
AutoIngestMetricsDialog.datePicker.toolTipText=Choose a date 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: AutoIngestMetricsDialog.startingDataLabel.text=Starting Date:
AutoIngestControlPanel.bnDeprioritizeCase.text=Deprioritize Case AutoIngestControlPanel.bnDeprioritizeCase.text=Deprioritize Case
AutoIngestControlPanel.bnDeprioritizeJob.text=Deprioritize Job AutoIngestControlPanel.bnDeprioritizeJob.text=Deprioritize Job

View File

@ -1,121 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2017 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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.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
*/
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<AutoIngestDataSourceProcessor, Integer> getDataSourceProcessorForFile(Path dataSourcePath, Collection<? extends AutoIngestDataSourceProcessor> processorCandidates) throws AutoIngestDataSourceProcessorException {
Map<AutoIngestDataSourceProcessor, Integer> 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<AutoIngestDataSourceProcessor> getOrderedListOfDataSourceProcessors(Path dataSourcePath) throws AutoIngestDataSourceProcessorException {
// lookup all AutomatedIngestDataSourceProcessors
Collection<? extends AutoIngestDataSourceProcessor> 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<AutoIngestDataSourceProcessor> getOrderedListOfDataSourceProcessors(Path dataSourcePath, Collection<? extends AutoIngestDataSourceProcessor> processorCandidates) throws AutoIngestDataSourceProcessorException {
Map<AutoIngestDataSourceProcessor, Integer> 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<AutoIngestDataSourceProcessor> orderDataSourceProcessorsByConfidence(Map<AutoIngestDataSourceProcessor, Integer> validDataSourceProcessorsMap) {
List<AutoIngestDataSourceProcessor> validDataSourceProcessors = validDataSourceProcessorsMap.entrySet().stream()
.sorted(Map.Entry.<AutoIngestDataSourceProcessor, Integer>comparingByValue().reversed())
.map(Map.Entry::getKey)
.collect(Collectors.toList());
return validDataSourceProcessors;
}
}

View File

@ -88,7 +88,7 @@ public class NextUnseenGroup extends Action {
if (group.isPresent()) { if (group.isPresent()) {
// NOTE: We need to wait for current group to be marked as seen because the 'advance' // NOTE: We need to wait for current group to be marked as seen because the 'advance'
// method grabs the top of the unseen list // method grabs the top of the unseen list
groupManager.markGroupSeen(group.get(), true) groupManager.markGroupSeen(group.get())
.addListener(this::advanceToNextUnseenGroup, MoreExecutors.newDirectExecutorService()); .addListener(this::advanceToNextUnseenGroup, MoreExecutors.newDirectExecutorService());
return; return;
} }

View File

@ -1141,42 +1141,65 @@ public final class DrawableDB {
} }
/** /**
* Record in the DB that the group with the given key has the given seen * Record in the DB that the group with the given key is seen
* state for the given examiner id. * by given examiner id.
* *
* @param groupKey * @param groupKey key identifying the group.
* @param seen * @param examinerID examiner id.
* @param examinerID
* *
* @throws TskCoreException * @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. * 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); Boolean cachedValue = groupSeenCache.getIfPresent(groupKey);
if (cachedValue != null && cachedValue == seen) { if (cachedValue != null && cachedValue == true) {
return; return;
} }
// query to find the group id from attribute/value // query to find the group id from attribute/value
String innerQuery = String.format("( SELECT group_id FROM " + GROUPS_TABLENAME String innerQuery = String.format("( SELECT group_id FROM " + GROUPS_TABLENAME //NON-NLS
+ " WHERE attribute = \'%s\' AND value = \'%s\' and data_source_obj_id = %d )", + " WHERE attribute = \'%s\' AND value = \'%s\' and data_source_obj_id = %d )", //NON-NLS
SleuthkitCase.escapeSingleQuotes(groupKey.getAttribute().attrName.toString()), SleuthkitCase.escapeSingleQuotes(groupKey.getAttribute().attrName.toString()),
SleuthkitCase.escapeSingleQuotes(groupKey.getValueDisplayName()), SleuthkitCase.escapeSingleQuotes(groupKey.getValueDisplayName()),
groupKey.getAttribute() == DrawableAttribute.PATH ? groupKey.getDataSourceObjId() : 0); 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); //NON-NLS
if (DbType.POSTGRESQL == tskCase.getDatabaseType()) { 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); //NON-NLS
} }
tskCase.getCaseDbAccessManager().insertOrUpdate(GROUPS_SEEN_TABLENAME, insertSQL); 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) + ")" ); //NON-NLS
tskCase.getCaseDbAccessManager().update(GROUPS_SEEN_TABLENAME, updateSQL);
groupSeenCache.put(groupKey, false);
} }
/** /**

View File

@ -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 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 * @return A ListenableFuture that encapsulates saving the seen state to the
* DB. * DB.
* *
* *
*/ */
public ListenableFuture<?> markGroupSeen(DrawableGroup group, boolean seen) { public ListenableFuture<?> markGroupSeen(DrawableGroup group) {
return exec.submit(() -> { return exec.submit(() -> {
try { try {
Examiner examiner = controller.getSleuthKitCase().getCurrentExaminer(); 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 // only update and reshuffle if its new results
if (group.isSeen() != seen) { if (group.isSeen() != true) {
group.setSeen(seen); group.setSeen(true);
updateUnSeenGroups(group); updateUnSeenGroups(group);
} }
} catch (TskCoreException ex) { } 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 * Update unseenGroups list accordingly based on the current status of
* 'group'. Removes it if it is seen or adds it if it is unseen. * '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 we're grouping by category, we don't want to remove empty groups.
if (group.getFileIDs().isEmpty()) { if (group.getFileIDs().isEmpty()) {
markGroupSeen(group, true); markGroupSeen(group);
if (groupKey.getAttribute() != DrawableAttribute.CATEGORY) { if (groupKey.getAttribute() != DrawableAttribute.CATEGORY) {
if (analyzedGroups.contains(group)) { if (analyzedGroups.contains(group)) {
analyzedGroups.remove(group); analyzedGroups.remove(group);
@ -570,7 +593,7 @@ public class GroupManager {
// reset the seen status for the group (if it is currently considered analyzed) // reset the seen status for the group (if it is currently considered analyzed)
if (group != null) { if (group != null) {
markGroupSeen(group, false); markGroupUnseen(group);
} }
} }

View File

@ -35,7 +35,7 @@ KeywordSearchResultFactory.createNodeForKey.noResultsFound.text=No results found
KeywordSearchResultFactory.query.exception.msg=Could not perform the query KeywordSearchResultFactory.query.exception.msg=Could not perform the query
OpenIDE-Module-Display-Category=Ingest Module 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 OpenIDE-Module-Name=KeywordSearch
OptionsCategory_Name_KeywordSearchOptions=Keyword Search OptionsCategory_Name_KeywordSearchOptions=Keyword Search
OptionsCategory_Keywords_KeywordSearchOptions=Keyword Search OptionsCategory_Keywords_KeywordSearchOptions=Keyword Search

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2018 Basis Technology Corp. * Copyright 2011-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -25,7 +25,6 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level; import java.util.logging.Level;
import org.openide.util.Exceptions;
import org.openide.util.Lookup; import org.openide.util.Lookup;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
@ -72,8 +71,10 @@ import org.sleuthkit.datamodel.TskData.FileKnown;
}) })
public final class KeywordSearchIngestModule implements FileIngestModule { public final class KeywordSearchIngestModule implements FileIngestModule {
/** generally text extractors should ignore archives and let unpacking /**
* modules take care of them */ * generally text extractors should ignore archives and let unpacking
* modules take care of them
*/
private static final List<String> ARCHIVE_MIME_TYPES private static final List<String> ARCHIVE_MIME_TYPES
= ImmutableList.of( = ImmutableList.of(
//ignore unstructured binary and compressed data, for which string extraction or unzipper works better //ignore unstructured binary and compressed data, for which string extraction or unzipper works better
@ -117,7 +118,6 @@ public final class KeywordSearchIngestModule implements FileIngestModule {
EXTRACT_UTF8, ///< extract UTF8 text, true/false EXTRACT_UTF8, ///< extract UTF8 text, true/false
}; };
enum UpdateFrequency { enum UpdateFrequency {
FAST(20), FAST(20),
@ -484,10 +484,10 @@ public final class KeywordSearchIngestModule implements FileIngestModule {
Lookup extractionContext = Lookups.fixed(imageConfig, terminator); Lookup extractionContext = Lookups.fixed(imageConfig, terminator);
try { try {
TextExtractor extractor = TextExtractorFactory.getExtractor(aFile,extractionContext); TextExtractor extractor = TextExtractorFactory.getExtractor(aFile, extractionContext);
Reader extractedTextReader = extractor.getReader(); Reader extractedTextReader = extractor.getReader();
//divide into chunks and index //divide into chunks and index
return Ingester.getDefault().indexText(extractedTextReader,aFile.getId(),aFile.getName(), aFile, context); return Ingester.getDefault().indexText(extractedTextReader, aFile.getId(), aFile.getName(), aFile, context);
} catch (TextExtractorFactory.NoTextExtractorFound | TextExtractor.InitReaderException ex) { } catch (TextExtractorFactory.NoTextExtractorFound | TextExtractor.InitReaderException ex) {
//No text extractor found... run the default instead //No text extractor found... run the default instead
return false; return false;
@ -509,7 +509,7 @@ public final class KeywordSearchIngestModule implements FileIngestModule {
} }
TextExtractor stringsExtractor = TextExtractorFactory.getStringsExtractor(aFile, stringsExtractionContext); TextExtractor stringsExtractor = TextExtractorFactory.getStringsExtractor(aFile, stringsExtractionContext);
Reader extractedTextReader = stringsExtractor.getReader(); Reader extractedTextReader = stringsExtractor.getReader();
if (Ingester.getDefault().indexText(extractedTextReader,aFile.getId(),aFile.getName(), aFile, KeywordSearchIngestModule.this.context)) { if (Ingester.getDefault().indexText(extractedTextReader, aFile.getId(), aFile.getName(), aFile, KeywordSearchIngestModule.this.context)) {
putIngestStatus(jobId, aFile.getId(), IngestStatus.STRINGS_INGESTED); putIngestStatus(jobId, aFile.getId(), IngestStatus.STRINGS_INGESTED);
return true; return true;
} else { } else {
@ -619,12 +619,16 @@ public final class KeywordSearchIngestModule implements FileIngestModule {
try { try {
TextFileExtractor textFileExtractor = new TextFileExtractor(); TextFileExtractor textFileExtractor = new TextFileExtractor();
Reader textReader = textFileExtractor.getReader(aFile); Reader textReader = textFileExtractor.getReader(aFile);
if (Ingester.getDefault().indexText(textReader, aFile.getId(), aFile.getName(), aFile, context)) { if (textReader == null) {
logger.log(Level.INFO, "Unable to extract with TextFileExtractor, Reader was null for file: {0}", aFile.getName());
} else if (Ingester.getDefault().indexText(textReader, aFile.getId(), aFile.getName(), aFile, context)) {
putIngestStatus(jobId, aFile.getId(), IngestStatus.TEXT_INGESTED); putIngestStatus(jobId, aFile.getId(), IngestStatus.TEXT_INGESTED);
wasTextAdded = true; wasTextAdded = true;
} }
} catch (IngesterException | TextFileExtractorException ex) { } catch (IngesterException ex) {
logger.log(Level.WARNING, "Unable to index as unicode", ex); logger.log(Level.WARNING, "Unable to index as unicode", ex);
} catch (TextFileExtractorException ex) {
logger.log(Level.INFO, "Could not extract text with TextFileExtractor", ex);
} }
} }

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2018 Basis Technology Corp. * Copyright 2018-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -48,7 +48,9 @@ final class TextFileExtractor {
throw new TextFileExtractorException("Unable to get string from detected text in TextFileExtractor", ex); throw new TextFileExtractorException("Unable to get string from detected text in TextFileExtractor", ex);
} }
CharsetMatch match = detector.detect(); CharsetMatch match = detector.detect();
if (match.getConfidence() < MIN_MATCH_CONFIDENCE) { if (match == null) {
throw new TextFileExtractorException("Unable to detect any matches using TextFileExtractor");
} else if (match.getConfidence() < MIN_MATCH_CONFIDENCE) {
throw new TextFileExtractorException("Text does not match any character set with a high enough confidence for TextFileExtractor"); throw new TextFileExtractorException("Text does not match any character set with a high enough confidence for TextFileExtractor");
} }

View File

@ -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.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.casesLabel.text_1=Cases
MultiCaseKeywordSearchPanel.resultsLabel.text=Results MultiCaseKeywordSearchPanel.resultsLabel.text=Results
MultiCaseKeywordSearchPanel.uncheckButton.text=Uncheck All
MultiCaseKeywordSearchPanel.checkButton.text=Check All
MultiCaseKeywordSearchPanel.searchButton.text=Search MultiCaseKeywordSearchPanel.searchButton.text=Search
MultiCaseKeywordSearchPanel.viewErrorsButton.text=View Errors MultiCaseKeywordSearchPanel.viewErrorsButton.text=View Errors
MultiCaseKeywordSearchPanel.warningLabel.text= MultiCaseKeywordSearchPanel.warningLabel.text=
MultiCaseKeywordSearchPanel.exportButton.text=Export Results MultiCaseKeywordSearchPanel.exportButton.text=Export Results
MultiCaseKeywordSearchPanel.cancelButton.text=Cancel MultiCaseKeywordSearchPanel.cancelButton.text=Cancel
MultiCaseKeywordSearchPanel.resultsCountLabel.text= MultiCaseKeywordSearchPanel.resultsCountLabel.text=
MultiCaseKeywordSearchPanel.pickCasesButton.text_1=Pick Cases MultiCaseKeywordSearchPanel.pickCasesButton.text_1=Add Cases
SelectMultiUserCasesPanel.refreshButton.text=Refresh SelectMultiUserCasesPanel.refreshButton.text=Refresh

View File

@ -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.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.casesLabel.text_1=Cases
MultiCaseKeywordSearchPanel.resultsLabel.text=Results MultiCaseKeywordSearchPanel.resultsLabel.text=Results
MultiCaseKeywordSearchPanel.uncheckButton.text=Uncheck All
MultiCaseKeywordSearchPanel.checkButton.text=Check All
MultiCaseKeywordSearchPanel.searchButton.text=Search MultiCaseKeywordSearchPanel.searchButton.text=Search
MultiCaseKeywordSearchPanel.viewErrorsButton.text=View Errors MultiCaseKeywordSearchPanel.viewErrorsButton.text=View Errors
MultiCaseKeywordSearchPanel.warningLabel.text= MultiCaseKeywordSearchPanel.warningLabel.text=
MultiCaseKeywordSearchPanel.exportButton.text=Export Results MultiCaseKeywordSearchPanel.exportButton.text=Export Results
MultiCaseKeywordSearchPanel.cancelButton.text=Cancel MultiCaseKeywordSearchPanel.cancelButton.text=Cancel
MultiCaseKeywordSearchPanel.resultsCountLabel.text= MultiCaseKeywordSearchPanel.resultsCountLabel.text=
MultiCaseKeywordSearchPanel.pickCasesButton.text_1=Pick Cases MultiCaseKeywordSearchPanel.pickCasesButton.text_1=Add Cases
SelectMultiUserCasesPanel.refreshButton.text=Refresh SelectMultiUserCasesPanel.refreshButton.text=Refresh

View File

@ -39,29 +39,22 @@
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="regexRadioButton" min="-2" max="-2" attributes="0"/> <Component id="regexRadioButton" min="-2" max="-2" attributes="0"/>
</Group> </Group>
<Component id="keywordTextField" pref="679" max="32767" attributes="0"/> <Component id="keywordTextField" pref="591" max="32767" attributes="0"/>
</Group> </Group>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="toolDescriptionScrollPane" min="-2" pref="295" max="-2" attributes="0"/> <Component id="toolDescriptionScrollPane" min="-2" pref="295" max="-2" attributes="0"/>
</Group> </Group>
<Group type="102" attributes="0"> <Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" max="-2" attributes="0">
<Component id="casesLabel" min="-2" max="-2" attributes="0"/> <Component id="casesLabel" min="-2" max="-2" attributes="0"/>
<Component id="casesScrollPane" min="-2" max="-2" attributes="0"/> <Component id="jScrollPane1" min="-2" pref="174" max="-2" attributes="0"/>
<Group type="103" alignment="0" groupAlignment="1" max="-2" attributes="0">
<Group type="102" alignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0">
<Component id="pickCasesButton" min="-2" pref="84" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<Component id="searchButton" min="-2" max="-2" attributes="0"/> <Component id="searchButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="pickCasesButton" max="32767" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<Component id="uncheckButton" linkSize="5" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="checkButton" linkSize="5" max="32767" attributes="0"/>
</Group> </Group>
</Group> </Group>
</Group> <EmptySpace type="unrelated" max="-2" attributes="0"/>
<EmptySpace type="unrelated" min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0"> <Group type="102" attributes="0">
<Component id="resultsLabel" min="-2" pref="154" max="-2" attributes="0"/> <Component id="resultsLabel" min="-2" pref="154" max="-2" attributes="0"/>
@ -72,7 +65,7 @@
<Group type="102" alignment="1" attributes="0"> <Group type="102" alignment="1" attributes="0">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Component id="viewErrorsButton" alignment="0" min="-2" max="-2" attributes="0"/> <Component id="viewErrorsButton" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="warningLabel" alignment="0" pref="695" max="32767" attributes="0"/> <Component id="warningLabel" alignment="0" pref="607" max="32767" attributes="0"/>
</Group> </Group>
<EmptySpace min="-2" pref="14" max="-2" attributes="0"/> <EmptySpace min="-2" pref="14" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" max="-2" attributes="0"> <Group type="103" groupAlignment="0" max="-2" attributes="0">
@ -88,7 +81,7 @@
<Group type="103" rootIndex="1" groupAlignment="0" attributes="0"> <Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="196" max="-2" attributes="0"/> <EmptySpace min="-2" pref="196" max="-2" attributes="0"/>
<Component id="searchProgressBar" pref="769" max="32767" attributes="0"/> <Component id="searchProgressBar" pref="608" max="32767" attributes="0"/>
<EmptySpace min="-2" pref="108" max="-2" attributes="0"/> <EmptySpace min="-2" pref="108" max="-2" attributes="0"/>
</Group> </Group>
</Group> </Group>
@ -122,25 +115,21 @@
</Group> </Group>
<EmptySpace min="-2" max="-2" attributes="0"/> <EmptySpace min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Component id="resultsScrollPane" max="32767" attributes="0"/> <Component id="resultsScrollPane" pref="281" max="32767" attributes="0"/>
<Component id="casesScrollPane" max="32767" attributes="0"/> <Component id="jScrollPane1" max="32767" attributes="0"/>
</Group> </Group>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="103" groupAlignment="3" attributes="0">
<Component id="uncheckButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="checkButton" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="warningLabel" min="-2" pref="15" max="-2" attributes="0"/> <Component id="warningLabel" min="-2" pref="15" max="-2" attributes="0"/>
<Component id="exportButton" min="-2" max="-2" attributes="0"/> <Component id="exportButton" min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="pickCasesButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="searchButton" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
</Group> </Group>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="103" groupAlignment="3" attributes="0"> <Component id="viewErrorsButton" min="-2" max="-2" attributes="0"/>
<Component id="searchButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="viewErrorsButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="pickCasesButton" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="cancelButton" alignment="0" min="-2" max="-2" attributes="0"/> <Component id="cancelButton" alignment="0" min="-2" max="-2" attributes="0"/>
</Group> </Group>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
@ -232,31 +221,6 @@
</Property> </Property>
</Properties> </Properties>
</Component> </Component>
<Container class="javax.swing.JScrollPane" name="casesScrollPane">
<Properties>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[174, 281]"/>
</Property>
</Properties>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Container class="javax.swing.JPanel" name="casesPanel">
<Properties>
<Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="ff" green="ff" red="ff" type="rgb"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[152, 197]"/>
</Property>
</Properties>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBoxLayout">
<Property name="axis" type="int" value="1"/>
</Layout>
</Container>
</SubComponents>
</Container>
<Component class="javax.swing.JLabel" name="casesLabel"> <Component class="javax.swing.JLabel" name="casesLabel">
<Properties> <Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
@ -271,47 +235,6 @@
</Property> </Property>
</Properties> </Properties>
</Component> </Component>
<Component class="javax.swing.JButton" name="uncheckButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/multicase/Bundle.properties" key="MultiCaseKeywordSearchPanel.uncheckButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor">
<Insets value="[2, 6, 2, 6]"/>
</Property>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[84, 23]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[84, 23]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[84, 23]"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="uncheckButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="checkButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/multicase/Bundle.properties" key="MultiCaseKeywordSearchPanel.checkButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[84, 23]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[84, 23]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[84, 23]"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="checkButtonActionPerformed"/>
</Events>
</Component>
<Container class="javax.swing.JScrollPane" name="toolDescriptionScrollPane"> <Container class="javax.swing.JScrollPane" name="toolDescriptionScrollPane">
<AuxValues> <AuxValues>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/> <AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
@ -428,5 +351,24 @@
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="pickCasesButtonActionPerformed"/> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="pickCasesButtonActionPerformed"/>
</Events> </Events>
</Component> </Component>
<Container class="javax.swing.JScrollPane" name="jScrollPane1">
<AuxValues>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JList" name="caseSelectionList">
<Properties>
<Property name="model" type="javax.swing.ListModel" editor="org.netbeans.modules.form.editors2.ListModelEditor">
<StringArray count="0"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
</AuxValues>
</Component>
</SubComponents>
</Container>
</SubComponents> </SubComponents>
</Form> </Form>

View File

@ -30,6 +30,7 @@ import java.io.IOException;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
@ -38,11 +39,14 @@ import java.util.Map;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.swing.AbstractButton; import javax.swing.AbstractButton;
import javax.swing.DefaultListModel;
import javax.swing.DefaultListSelectionModel;
import javax.swing.JCheckBox; import javax.swing.JCheckBox;
import javax.swing.JFileChooser; import javax.swing.JFileChooser;
import javax.swing.table.TableColumn; import javax.swing.table.TableColumn;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
import javax.swing.JTable; import javax.swing.JTable;
import javax.swing.ListModel;
import javax.swing.ListSelectionModel; import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import javax.swing.filechooser.FileNameExtensionFilter; import javax.swing.filechooser.FileNameExtensionFilter;
@ -117,6 +121,14 @@ final class MultiCaseKeywordSearchPanel extends javax.swing.JPanel implements Ex
outline.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); outline.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
caseNameToCaseDataMap = new HashMap<>(); caseNameToCaseDataMap = new HashMap<>();
setColumnWidths(); 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 * Get the list of cases from the Multi user case browser
*/ */
private void populateCasesList(List<CaseNodeData> selectedNodes) { private void populateCasesList(List<CaseNodeData> selectedNodes) {
Collection<String> disabledCases = getCases(false); caseSelectionList.removeAll();
casesPanel.removeAll(); caseSelectionList.revalidate();
casesPanel.revalidate(); caseSelectionList.repaint();
casesPanel.repaint();
caseNameToCaseDataMap.clear(); caseNameToCaseDataMap.clear();
int casePanelWidth = casesPanel.getPreferredSize().width; DefaultListModel<String> listModel = new DefaultListModel<>();
int heightOfAllRows = 0; Collections.sort(selectedNodes, (CaseNodeData o1, CaseNodeData o2) -> {
for (CaseNodeData data : selectedNodes) { return o1.getName().toLowerCase()
//select all new cases and cases which were previously selected .compareTo(o2.getName().toLowerCase());
});
for (int i = 0; i < selectedNodes.size(); i++) {
CaseNodeData data = selectedNodes.get(i);
String multiUserCaseName = data.getName(); 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); caseNameToCaseDataMap.put(multiUserCaseName, data);
boolean isSelected = true;
if (disabledCases.contains(multiUserCaseName)) {
isSelected = false;
} }
JCheckBox caseCheckBox = new JCheckBox(multiUserCaseName, isSelected); caseSelectionList.setModel(listModel);
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));
} }
@Override @Override
@ -283,12 +292,8 @@ final class MultiCaseKeywordSearchPanel extends javax.swing.JPanel implements Ex
keywordTextField = new javax.swing.JTextField(); keywordTextField = new javax.swing.JTextField();
exactRadioButton = new javax.swing.JRadioButton(); exactRadioButton = new javax.swing.JRadioButton();
regexRadioButton = new javax.swing.JRadioButton(); regexRadioButton = new javax.swing.JRadioButton();
casesScrollPane = new javax.swing.JScrollPane();
casesPanel = new javax.swing.JPanel();
casesLabel = new javax.swing.JLabel(); casesLabel = new javax.swing.JLabel();
resultsLabel = new javax.swing.JLabel(); resultsLabel = new javax.swing.JLabel();
uncheckButton = new javax.swing.JButton();
checkButton = new javax.swing.JButton();
toolDescriptionScrollPane = new javax.swing.JScrollPane(); toolDescriptionScrollPane = new javax.swing.JScrollPane();
toolDescriptionTextArea = new javax.swing.JTextArea(); toolDescriptionTextArea = new javax.swing.JTextArea();
resultsScrollPane = new javax.swing.JScrollPane(); resultsScrollPane = new javax.swing.JScrollPane();
@ -299,6 +304,8 @@ final class MultiCaseKeywordSearchPanel extends javax.swing.JPanel implements Ex
resultsCountLabel = new javax.swing.JLabel(); resultsCountLabel = new javax.swing.JLabel();
viewErrorsButton = new javax.swing.JButton(); viewErrorsButton = new javax.swing.JButton();
pickCasesButton = new javax.swing.JButton(); pickCasesButton = new javax.swing.JButton();
jScrollPane1 = new javax.swing.JScrollPane();
caseSelectionList = new javax.swing.JList<>();
setName(""); // NOI18N setName(""); // NOI18N
setOpaque(false); setOpaque(false);
@ -335,38 +342,10 @@ final class MultiCaseKeywordSearchPanel extends javax.swing.JPanel implements Ex
searchTypeGroup.add(regexRadioButton); searchTypeGroup.add(regexRadioButton);
org.openide.awt.Mnemonics.setLocalizedText(regexRadioButton, org.openide.util.NbBundle.getMessage(MultiCaseKeywordSearchPanel.class, "MultiCaseKeywordSearchPanel.regexRadioButton.text_1")); // NOI18N 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(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(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.setEditable(false);
toolDescriptionTextArea.setBackground(new java.awt.Color(240, 240, 240)); toolDescriptionTextArea.setBackground(new java.awt.Color(240, 240, 240));
toolDescriptionTextArea.setColumns(20); 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); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout); this.setLayout(layout);
layout.setHorizontalGroup( layout.setHorizontalGroup(
@ -438,22 +419,17 @@ final class MultiCaseKeywordSearchPanel extends javax.swing.JPanel implements Ex
.addComponent(substringRadioButton) .addComponent(substringRadioButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(regexRadioButton)) .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) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(toolDescriptionScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 295, javax.swing.GroupLayout.PREFERRED_SIZE)) .addComponent(toolDescriptionScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 295, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
.addComponent(casesLabel) .addComponent(casesLabel)
.addComponent(casesScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 174, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) .addGroup(layout.createSequentialGroup()
.addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup() .addComponent(pickCasesButton, javax.swing.GroupLayout.PREFERRED_SIZE, 84, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(searchButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(searchButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
.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))))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup() .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(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(viewErrorsButton) .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) .addGap(14, 14, 14)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
.addComponent(exportButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .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.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addGap(196, 196, 196) .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))) .addGap(108, 108, 108)))
); );
layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {checkButton, uncheckButton});
layout.setVerticalGroup( layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup() .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))) .addComponent(resultsCountLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 14, javax.swing.GroupLayout.PREFERRED_SIZE)))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(resultsScrollPane, 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(casesScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addComponent(jScrollPane1))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .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(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) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .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(viewErrorsButton)
.addComponent(pickCasesButton))
.addComponent(cancelButton)) .addComponent(cancelButton))
.addContainerGap()) .addContainerGap())
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .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 private void searchButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_searchButtonActionPerformed
if (null == searchThread) { if (null == searchThread) {
Collection<String> cases = getCases(true); Collection<String> cases = getCases();
String searchString = keywordTextField.getText(); String searchString = keywordTextField.getText();
if (cases.isEmpty()) { if (cases.isEmpty()) {
warningLabel.setText(Bundle.MultiCaseKeywordSearchPanel_warningText_noCases()); warningLabel.setText(Bundle.MultiCaseKeywordSearchPanel_warningText_noCases());
@ -566,21 +536,16 @@ final class MultiCaseKeywordSearchPanel extends javax.swing.JPanel implements Ex
}//GEN-LAST:event_searchButtonActionPerformed }//GEN-LAST:event_searchButtonActionPerformed
/** /**
* Get the cases which match the selected status specified by isSelected. * Get the case names from the Case List
*
* @param isSelected true to get selected cases false to get unselected
* cases
* *
* @return cases the cases that match the selected status of isSelected * @return cases the cases that match the selected status of isSelected
*/ */
private Collection<String> getCases(boolean isSelected) { private Collection<String> getCases() {
Collection<String> cases = new HashSet<>(); Collection<String> cases = new HashSet<>();
for (Component comp : casesPanel.getComponents()) { ListModel<String> listModel = caseSelectionList.getModel();
if (comp instanceof JCheckBox) { for(int i = 0; i < listModel.getSize(); i++) {
if (((AbstractButton) comp).isSelected() == isSelected) { String caseName = listModel.getElementAt(i);
cases.add(((AbstractButton) comp).getText()); cases.add(caseName);
}
}
} }
return cases; 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)); 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. * Cancel the current multi-case search which is being performed.
* *
@ -718,10 +665,10 @@ final class MultiCaseKeywordSearchPanel extends javax.swing.JPanel implements Ex
}//GEN-LAST:event_viewErrorsButtonActionPerformed }//GEN-LAST:event_viewErrorsButtonActionPerformed
private void pickCasesButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pickCasesButtonActionPerformed private void pickCasesButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pickCasesButtonActionPerformed
caseSelectionDialog.setVisible(true);
if (currentConfirmedSelections != null) { if (currentConfirmedSelections != null) {
caseSelectionDialog.setNodeSelections(currentConfirmedSelections); caseSelectionDialog.setNodeSelections(currentConfirmedSelections);
} }
caseSelectionDialog.setVisible(true);
}//GEN-LAST:event_pickCasesButtonActionPerformed }//GEN-LAST:event_pickCasesButtonActionPerformed
@ -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 * Ask the user if they want to continue their search while this window is
* closed. Cancels the current search if they select no. * 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 // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton cancelButton; private javax.swing.JButton cancelButton;
private javax.swing.JList<String> caseSelectionList;
private javax.swing.JLabel casesLabel; 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.JRadioButton exactRadioButton;
private javax.swing.JButton exportButton; private javax.swing.JButton exportButton;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JTextField keywordTextField; private javax.swing.JTextField keywordTextField;
private javax.swing.JButton pickCasesButton; private javax.swing.JButton pickCasesButton;
private javax.swing.JRadioButton regexRadioButton; 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.JRadioButton substringRadioButton;
private javax.swing.JScrollPane toolDescriptionScrollPane; private javax.swing.JScrollPane toolDescriptionScrollPane;
private javax.swing.JTextArea toolDescriptionTextArea; private javax.swing.JTextArea toolDescriptionTextArea;
private javax.swing.JButton uncheckButton;
private javax.swing.JButton viewErrorsButton; private javax.swing.JButton viewErrorsButton;
private javax.swing.JLabel warningLabel; private javax.swing.JLabel warningLabel;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables

View File

@ -64,7 +64,7 @@ class SelectMultiUserCasesDialog extends javax.swing.JDialog {
try { try {
multiUserCasesPanel.setSelections(selections); multiUserCasesPanel.setSelections(selections);
} catch (PropertyVetoException ex) { } catch (PropertyVetoException ex) {
//Do-nothing
} }
} }

View File

@ -2,6 +2,11 @@ cannotBuildXmlParser=Unable to build XML parser:
cannotLoadSEUQA=Unable to load Search Engine URL Query Analyzer settings file, SEUQAMappings.xml: cannotLoadSEUQA=Unable to load Search Engine URL Query Analyzer settings file, SEUQAMappings.xml:
cannotParseXml=Unable to parse XML file: cannotParseXml=Unable to parse XML file:
ChromeCacheExtractor.moduleName=ChromeCacheExtractor 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_AndroidMedia=Android Media Card
DataSourceUsage_FlashDrive=Flash Drive DataSourceUsage_FlashDrive=Flash Drive
# {0} - OS name # {0} - OS name
@ -134,6 +139,7 @@ Progress_Message_Analyze_Registry=Analyzing Registry Files
Progress_Message_Analyze_Usage=Data Sources Usage Analysis Progress_Message_Analyze_Usage=Data Sources Usage Analysis
Progress_Message_Chrome_AutoFill=Chrome Auto Fill Progress_Message_Chrome_AutoFill=Chrome Auto Fill
Progress_Message_Chrome_Bookmarks=Chrome Bookmarks Progress_Message_Chrome_Bookmarks=Chrome Bookmarks
Progress_Message_Chrome_Cache=Chrome Cache
Progress_Message_Chrome_Cookies=Chrome Cookies Progress_Message_Chrome_Cookies=Chrome Cookies
Progress_Message_Chrome_Downloads=Chrome Downloads Progress_Message_Chrome_Downloads=Chrome Downloads
Progress_Message_Chrome_FormHistory=Chrome Form History Progress_Message_Chrome_FormHistory=Chrome Form History

View File

@ -93,6 +93,7 @@ class Chrome extends Extract {
"Progress_Message_Chrome_FormHistory=Chrome Form History", "Progress_Message_Chrome_FormHistory=Chrome Form History",
"Progress_Message_Chrome_AutoFill=Chrome Auto Fill", "Progress_Message_Chrome_AutoFill=Chrome Auto Fill",
"Progress_Message_Chrome_Logins=Chrome Logins", "Progress_Message_Chrome_Logins=Chrome Logins",
"Progress_Message_Chrome_Cache=Chrome Cache",
}) })
Chrome() { Chrome() {
@ -123,7 +124,8 @@ class Chrome extends Extract {
progressBar.progress(Bundle.Progress_Message_Chrome_Downloads()); progressBar.progress(Bundle.Progress_Message_Chrome_Downloads());
this.getDownload(); this.getDownload();
ChromeCacheExtractor chromeCacheExtractor = new ChromeCacheExtractor(dataSource, context); progressBar.progress(Bundle.Progress_Message_Chrome_Cache());
ChromeCacheExtractor chromeCacheExtractor = new ChromeCacheExtractor(dataSource, context, progressBar);
chromeCacheExtractor.getCaches(); chromeCacheExtractor.getCaches();
} }

View File

@ -44,6 +44,7 @@ import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.casemodule.services.FileManager; import org.sleuthkit.autopsy.casemodule.services.FileManager;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.ContentUtils; import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress;
import org.sleuthkit.autopsy.ingest.IngestJobContext; import org.sleuthkit.autopsy.ingest.IngestJobContext;
import org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException; import org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException;
import org.sleuthkit.autopsy.ingest.IngestServices; 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.BlackboardAttribute;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.DerivedFile; import org.sleuthkit.datamodel.DerivedFile;
import org.sleuthkit.datamodel.ReadContentInputStream;
import org.sleuthkit.datamodel.TimeUtilities; import org.sleuthkit.datamodel.TimeUtilities;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.TskData;
@ -75,7 +75,7 @@ import org.sleuthkit.datamodel.TskException;
*/ */
final class ChromeCacheExtractor { 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 String BROTLI_MIMETYPE ="application/x-brotli"; //NON-NLS
private final static long UINT32_MASK = 0xFFFFFFFFl; private final static long UINT32_MASK = 0xFFFFFFFFl;
@ -93,11 +93,16 @@ final class ChromeCacheExtractor {
private final Content dataSource; private final Content dataSource;
private final IngestJobContext context; private final IngestJobContext context;
private final DataSourceIngestModuleProgress progressBar;
private final IngestServices services = IngestServices.getInstance(); private final IngestServices services = IngestServices.getInstance();
private Case currentCase; private Case currentCase;
private FileManager fileManager; private FileManager fileManager;
private final Map<String, CacheFileCopy> filesTable = new HashMap<>(); // A file table to cache copies of index and data_n files.
private final Map<String, CacheFileCopy> fileCopyCache = new HashMap<>();
// A file table to cache the f_* files.
private final Map<String, AbstractFile> externalFilesTable = new HashMap<>();
/** /**
* Encapsulates abstract file for a cache file as well as a temp file copy * Encapsulates abstract file for a cache file as well as a temp file copy
@ -127,12 +132,18 @@ final class ChromeCacheExtractor {
} }
@NbBundle.Messages({ @NbBundle.Messages({
"ChromeCacheExtractor.moduleName=ChromeCacheExtractor" "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 ) { ChromeCacheExtractor(Content dataSource, IngestJobContext context, DataSourceIngestModuleProgress progressBar ) {
moduleName = Bundle.ChromeCacheExtractor_moduleName(); moduleName = Bundle.ChromeCacheExtractor_moduleName();
this.dataSource = dataSource; this.dataSource = dataSource;
this.context = context; this.context = context;
this.progressBar = progressBar;
} }
@ -141,7 +152,7 @@ final class ChromeCacheExtractor {
* *
* @throws IngestModuleException * @throws IngestModuleException
*/ */
void moduleInit() throws IngestModuleException { private void moduleInit() throws IngestModuleException {
try { try {
currentCase = Case.getCurrentCaseThrows(); currentCase = Case.getCurrentCaseThrows();
@ -168,9 +179,10 @@ final class ChromeCacheExtractor {
* *
* @throws org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException * @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; String cacheAbsOutputFolderName = this.getAbsOutputFolderName() + cachePath;
File outDir = new File(cacheAbsOutputFolderName); File outDir = new File(cacheAbsOutputFolderName);
@ -191,9 +203,9 @@ final class ChromeCacheExtractor {
* Removes any temp copies of cache files created during extraction. * Removes any temp copies of cache files created during extraction.
* *
*/ */
void cleanup () { private void cleanup () {
for (Entry<String, CacheFileCopy> entry : this.filesTable.entrySet()) { for (Entry<String, CacheFileCopy> entry : this.fileCopyCache.entrySet()) {
Path tempFilePath = Paths.get(RAImageIngestModule.getRATempPath(currentCase, moduleName), entry.getKey() ); Path tempFilePath = Paths.get(RAImageIngestModule.getRATempPath(currentCase, moduleName), entry.getKey() );
try { try {
entry.getValue().getFileCopy().getChannel().close(); entry.getValue().getFileCopy().getChannel().close();
@ -228,7 +240,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. * A data source may have multiple Chrome user profiles and caches.
* *
@ -243,14 +255,14 @@ final class ChromeCacheExtractor {
return; return;
} }
// Find all possible caches // Find and process the cache folders. There could be one per user
List<AbstractFile> indexFiles; List<AbstractFile> indexFiles;
try { try {
indexFiles = findCacheFiles("index"); //NON-NLS indexFiles = findCacheIndexFiles();
// Get each of the caches // Process each of the caches
for (AbstractFile indexFile: indexFiles) { for (AbstractFile indexFile: indexFiles) {
getCache(indexFile); processCacheIndexFile(indexFile);
} }
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
@ -260,24 +272,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(); String cachePath = indexAbstractFile.getParentPath();
Optional<CacheFileCopy> indexFile; Optional<CacheFileCopy> indexFileCopy;
try { try {
subInit(cachePath); resetForNewFolder(cachePath);
indexFile = this.getCacheFileCopy(indexAbstractFile.getName(), cachePath); // @@@ This is little ineffecient because we later in this call search for the AbstractFile that we currently have
if (!indexFile.isPresent()) { indexFileCopy = this.getCacheFileCopy(indexAbstractFile.getName(), cachePath);
if (!indexFileCopy.isPresent()) {
String msg = String.format("Failed to find copy cache index file %s", indexAbstractFile.getUniquePath()); String msg = String.format("Failed to find copy cache index file %s", indexAbstractFile.getUniquePath());
logger.log(Level.SEVERE, msg); logger.log(Level.SEVERE, msg);
return; return;
} }
// load the data files. We do this now to load them into the cache
for (int i = 0; i < 4; i ++) { for (int i = 0; i < 4; i ++) {
Optional<CacheFileCopy> dataFile = findAndCopyCacheFile(String.format("data_%1d",i), cachePath ); Optional<CacheFileCopy> dataFile = findAndCopyCacheFile(String.format("data_%1d",i), cachePath );
if (!dataFile.isPresent()) { if (!dataFile.isPresent()) {
@ -285,19 +300,24 @@ final class ChromeCacheExtractor {
} }
} }
// find all f_* files in a single query and load them into the cache
findExternalFiles(cachePath);
} catch (TskCoreException | IngestModuleException ex) { } catch (TskCoreException | IngestModuleException ex) {
String msg = "Failed to find cache files in path " + cachePath; //NON-NLS String msg = "Failed to find cache files in path " + cachePath; //NON-NLS
logger.log(Level.SEVERE, msg, ex); logger.log(Level.SEVERE, msg, ex);
return; return;
} }
// parse the index file
logger.log(Level.INFO, "{0}- Now reading Cache index file from path {1}", new Object[]{moduleName, cachePath }); //NON-NLS logger.log(Level.INFO, "{0}- Now reading Cache index file from path {1}", new Object[]{moduleName, cachePath }); //NON-NLS
List<AbstractFile> derivedFiles = new ArrayList<>(); List<AbstractFile> derivedFiles = new ArrayList<>();
Collection<BlackboardArtifact> sourceArtifacts = new ArrayList<>(); Collection<BlackboardArtifact> sourceArtifacts = new ArrayList<>();
Collection<BlackboardArtifact> webCacheArtifacts = new ArrayList<>(); Collection<BlackboardArtifact> webCacheArtifacts = new ArrayList<>();
ByteBuffer indexFileROBuffer = indexFile.get().getByteBuffer(); ByteBuffer indexFileROBuffer = indexFileCopy.get().getByteBuffer();
IndexFileHeader indexHdr = new IndexFileHeader(indexFileROBuffer); IndexFileHeader indexHdr = new IndexFileHeader(indexFileROBuffer);
// seek past the header // seek past the header
@ -306,10 +326,12 @@ final class ChromeCacheExtractor {
// Process each address in the table // Process each address in the table
for (int i = 0; i < indexHdr.getTableLen(); i++) { for (int i = 0; i < indexHdr.getTableLen(); i++) {
CacheAddress addr = new CacheAddress(indexFileROBuffer.getInt() & UINT32_MASK, cachePath); CacheAddress addr = new CacheAddress(indexFileROBuffer.getInt() & UINT32_MASK, cachePath);
if (addr.isInitialized()) { if (addr.isInitialized()) {
progressBar.progress( NbBundle.getMessage(this.getClass(),
"ChromeCacheExtractor.progressMsg",
moduleName, i, indexHdr.getTableLen(), cachePath) );
try { try {
List<DerivedFile> addedFiles = this.getCacheEntry(addr, sourceArtifacts, webCacheArtifacts); List<DerivedFile> addedFiles = this.processCacheEntry(addr, sourceArtifacts, webCacheArtifacts);
derivedFiles.addAll(addedFiles); derivedFiles.addAll(addedFiles);
} }
catch (TskCoreException | IngestModuleException ex) { catch (TskCoreException | IngestModuleException ex) {
@ -341,15 +363,16 @@ final class ChromeCacheExtractor {
* *
* @return Optional derived file, is a derived file is added for the given entry * @return Optional derived file, is a derived file is added for the given entry
*/ */
List<DerivedFile> getCacheEntry(CacheAddress cacheEntryAddress, Collection<BlackboardArtifact> sourceArtifacts, Collection<BlackboardArtifact> webCacheArtifacts ) throws TskCoreException, IngestModuleException { private List<DerivedFile> processCacheEntry(CacheAddress cacheEntryAddress, Collection<BlackboardArtifact> sourceArtifacts, Collection<BlackboardArtifact> webCacheArtifacts ) throws TskCoreException, IngestModuleException {
List<DerivedFile> derivedFiles = new ArrayList<>(); List<DerivedFile> derivedFiles = new ArrayList<>();
String cacheEntryFileName = cacheEntryAddress.getFilename(); // get the path to the corresponding data_X file
String dataFileName = cacheEntryAddress.getFilename();
String cachePath = cacheEntryAddress.getCachePath(); String cachePath = cacheEntryAddress.getCachePath();
Optional<CacheFileCopy> cacheEntryFile = this.getCacheFileCopy(cacheEntryFileName, cachePath); Optional<CacheFileCopy> cacheEntryFile = this.getCacheFileCopy(dataFileName, cachePath);
if (!cacheEntryFile.isPresent()) { if (!cacheEntryFile.isPresent()) {
String msg = String.format("Failed to get cache entry at address %s", cacheEntryAddress); //NON-NLS String msg = String.format("Failed to get cache entry at address %s", cacheEntryAddress); //NON-NLS
throw new IngestModuleException(msg); throw new IngestModuleException(msg);
@ -358,33 +381,40 @@ final class ChromeCacheExtractor {
// Get the cache entry and its data segments // Get the cache entry and its data segments
CacheEntry cacheEntry = new CacheEntry(cacheEntryAddress, cacheEntryFile.get() ); CacheEntry cacheEntry = new CacheEntry(cacheEntryAddress, cacheEntryFile.get() );
List<CacheData> dataEntries = cacheEntry.getData();
List<CacheData> dataEntries = cacheEntry.getData();
// 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<AbstractFile> 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, BlackboardAttribute urlAttr = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_URL,
moduleName, moduleName,
((cacheEntry.getKey() != null) ? cacheEntry.getKey() : "")); ((cacheEntry.getKey() != null) ? cacheEntry.getKey() : ""));
BlackboardAttribute createTimeAttr = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED, BlackboardAttribute createTimeAttr = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
moduleName, moduleName,
cacheEntry.getCreationTime()); cacheEntry.getCreationTime());
BlackboardAttribute httpHeaderAttr = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_HEADERS,
BlackboardAttribute hhtpHeaderAttr = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_HEADERS,
moduleName, moduleName,
cacheEntry.getHTTPHeaders()); cacheEntry.getHTTPHeaders());
// 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<AbstractFile> dataFile = this.findCacheFile(dataFilename, cachePath);
boolean isBrotliCompressed = false;
if (data.getType() != CacheDataTypeEnum.HTTP_HEADER && cacheEntry.isBrotliCompressed() ) {
isBrotliCompressed = true;
}
Collection<BlackboardAttribute> sourceArtifactAttributes = new ArrayList<>(); Collection<BlackboardAttribute> sourceArtifactAttributes = new ArrayList<>();
sourceArtifactAttributes.add(urlAttr); sourceArtifactAttributes.add(urlAttr);
sourceArtifactAttributes.add(createTimeAttr); sourceArtifactAttributes.add(createTimeAttr);
@ -392,12 +422,13 @@ final class ChromeCacheExtractor {
Collection<BlackboardAttribute> webCacheAttributes = new ArrayList<>(); Collection<BlackboardAttribute> webCacheAttributes = new ArrayList<>();
webCacheAttributes.add(urlAttr); webCacheAttributes.add(urlAttr);
webCacheAttributes.add(createTimeAttr); webCacheAttributes.add(createTimeAttr);
webCacheAttributes.add(hhtpHeaderAttr); webCacheAttributes.add(httpHeaderAttr);
if (dataFile.isPresent()) {
if (data.isInExternalFile() ) { // add artifacts to the f_XXX file
if (dataSegment.isInExternalFile() ) {
try { try {
BlackboardArtifact sourceArtifact = dataFile.get().newArtifact(ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE); BlackboardArtifact sourceArtifact = cachedFileAbstractFile.get().newArtifact(ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE);
if (sourceArtifact != null) { if (sourceArtifact != null) {
sourceArtifact.addAttributes(sourceArtifactAttributes); sourceArtifact.addAttributes(sourceArtifactAttributes);
sourceArtifacts.add(sourceArtifact); sourceArtifacts.add(sourceArtifact);
@ -410,35 +441,34 @@ final class ChromeCacheExtractor {
// Add path of f_* file as attribute // Add path of f_* file as attribute
webCacheArtifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH, webCacheArtifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH,
moduleName, moduleName,
dataFile.get().getUniquePath())); cachedFileAbstractFile.get().getUniquePath()));
long pathID = Util.findID(dataSource, dataFile.get().getUniquePath());
if (pathID != -1) {
webCacheArtifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID, webCacheArtifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID,
moduleName, pathID)); moduleName, cachedFileAbstractFile.get().getId()));
}
webCacheArtifacts.add(webCacheArtifact); webCacheArtifacts.add(webCacheArtifact);
} }
if (isBrotliCompressed) { if (isBrotliCompressed) {
dataFile.get().setMIMEType(BROTLI_MIMETYPE); cachedFileAbstractFile.get().setMIMEType(BROTLI_MIMETYPE);
dataFile.get().save(); cachedFileAbstractFile.get().save();
} }
} catch (TskException ex) { } catch (TskException ex) {
logger.log(Level.SEVERE, "Error while trying to add an artifact", ex); //NON-NLS logger.log(Level.SEVERE, "Error while trying to add an artifact", ex); //NON-NLS
} }
} else { }
// 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 // Data segments in "data_x" files are saved in individual files and added as derived files
String filename = data.save(); String filename = dataSegment.save();
String relPathname = getRelOutputFolderName() + data.getAddress().getCachePath() + filename; String relPathname = getRelOutputFolderName() + dataSegment.getAddress().getCachePath() + filename;
try { try {
DerivedFile derivedFile = fileManager.addDerivedFile(filename, relPathname, DerivedFile derivedFile = fileManager.addDerivedFile(filename, relPathname,
data.getDataLength(), dataSegment.getDataLength(),
cacheEntry.getCreationTime(), cacheEntry.getCreationTime(), cacheEntry.getCreationTime(), cacheEntry.getCreationTime(), // TBD cacheEntry.getCreationTime(), cacheEntry.getCreationTime(), cacheEntry.getCreationTime(), cacheEntry.getCreationTime(), // TBD
true, true,
dataFile.get(), cachedFileAbstractFile.get(),
"", "",
moduleName, moduleName,
VERSION_NUMBER, VERSION_NUMBER,
@ -459,11 +489,9 @@ final class ChromeCacheExtractor {
webCacheArtifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH, webCacheArtifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH,
moduleName, moduleName,
derivedFile.getUniquePath())); derivedFile.getUniquePath()));
long pathID = Util.findID(dataSource, derivedFile.getUniquePath());
if (pathID != -1) {
webCacheArtifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID, webCacheArtifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID,
moduleName, pathID)); moduleName, derivedFile.getId()));
}
webCacheArtifacts.add(webCacheArtifact); webCacheArtifacts.add(webCacheArtifact);
} }
@ -478,25 +506,49 @@ final class ChromeCacheExtractor {
logger.log(Level.SEVERE, "Error while trying to add an artifact", ex); //NON-NLS logger.log(Level.SEVERE, "Error while trying to add an artifact", ex); //NON-NLS
} }
} }
}
}
return derivedFiles; return derivedFiles;
} }
/** /**
* 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.
* *
* @param cacheFileName
* @return Opt
* @throws TskCoreException * @throws TskCoreException
*/ */
Optional<AbstractFile> findCacheFile(String cacheFileName, String cachePath) throws TskCoreException { private void findExternalFiles(String cachePath) throws TskCoreException {
List<AbstractFile> 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 Optional abstract file
* @throws TskCoreException
*/
private Optional<AbstractFile> 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 (fileCopyCache.containsKey(fileTableKey)) {
return Optional.of(fileCopyCache.get(fileTableKey).getAbstractFile());
}
List<AbstractFile> cacheFiles = fileManager.findFiles(dataSource, cacheFileName, cachePath); //NON-NLS List<AbstractFile> cacheFiles = fileManager.findFiles(dataSource, cacheFileName, cachePath); //NON-NLS
if (!cacheFiles.isEmpty()) { if (!cacheFiles.isEmpty()) {
for (AbstractFile abstractFile: cacheFiles ) { 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); return Optional.of(abstractFile);
} }
} }
@ -509,12 +561,11 @@ final class ChromeCacheExtractor {
/** /**
* Finds abstract file(s) for a cache file with the specified name. * Finds abstract file(s) for a cache file with the specified name.
* *
* @param cacheFileName
* @return list of abstract files matching the specified file name * @return list of abstract files matching the specified file name
* @throws TskCoreException * @throws TskCoreException
*/ */
List<AbstractFile> findCacheFiles(String cacheFileName) throws TskCoreException { private List<AbstractFile> findCacheIndexFiles() throws TskCoreException {
return fileManager.findFiles(dataSource, cacheFileName, DEFAULT_CACHE_STR); //NON-NLS return fileManager.findFiles(dataSource, "index", DEFAULT_CACHE_PATH_STR); //NON-NLS
} }
@ -522,16 +573,17 @@ final class ChromeCacheExtractor {
* Returns CacheFileCopy for the specified file from the file table. * 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. * 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 * @return CacheFileCopy
* @throws TskCoreException * @throws TskCoreException
*/ */
Optional<CacheFileCopy> getCacheFileCopy(String cacheFileName, String cachePath) throws TskCoreException, IngestModuleException { private Optional<CacheFileCopy> 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; String fileTableKey = cachePath + cacheFileName;
if (filesTable.containsKey(fileTableKey)) { if (fileCopyCache.containsKey(fileTableKey)) {
return Optional.of(filesTable.get(fileTableKey)); return Optional.of(fileCopyCache.get(fileTableKey));
} }
return findAndCopyCacheFile(cacheFileName, cachePath); return findAndCopyCacheFile(cacheFileName, cachePath);
@ -544,13 +596,17 @@ final class ChromeCacheExtractor {
* @return Cache file copy * @return Cache file copy
* @throws TskCoreException * @throws TskCoreException
*/ */
Optional<CacheFileCopy> findAndCopyCacheFile(String cacheFileName, String cachePath) throws TskCoreException, IngestModuleException { private Optional<CacheFileCopy> findAndCopyCacheFile(String cacheFileName, String cachePath) throws TskCoreException, IngestModuleException {
Optional<AbstractFile> cacheFileOptional = findCacheFile(cacheFileName, cachePath); Optional<AbstractFile> cacheFileOptional = findCacheFile(cacheFileName, cachePath);
if (!cacheFileOptional.isPresent()) { if (!cacheFileOptional.isPresent()) {
return Optional.empty(); 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(); AbstractFile cacheFile = cacheFileOptional.get();
RandomAccessFile randomAccessFile = null; RandomAccessFile randomAccessFile = null;
String tempFilePathname = RAImageIngestModule.getRATempPath(currentCase, moduleName) + cachePath + cacheFile.getName(); //NON-NLS String tempFilePathname = RAImageIngestModule.getRATempPath(currentCase, moduleName) + cachePath + cacheFile.getName(); //NON-NLS
@ -567,7 +623,7 @@ final class ChromeCacheExtractor {
CacheFileCopy cacheFileCopy = new CacheFileCopy(cacheFile, randomAccessFile, cacheFileROBuf ); CacheFileCopy cacheFileCopy = new CacheFileCopy(cacheFile, randomAccessFile, cacheFileROBuf );
if (!cacheFileName.startsWith("f_")) { if (!cacheFileName.startsWith("f_")) {
filesTable.put(cachePath + cacheFileName, cacheFileCopy); fileCopyCache.put(cachePath + cacheFileName, cacheFileCopy);
} }
return Optional.of(cacheFileCopy); return Optional.of(cacheFileCopy);
@ -751,6 +807,10 @@ final class ChromeCacheExtractor {
return fileType; return fileType;
} }
/**
* Name where cached file is stored. Either a data or f_ file.
* @return
*/
String getFilename() { String getFilename() {
return fileName; return fileName;
} }
@ -914,9 +974,11 @@ final class ChromeCacheExtractor {
return; return;
} }
cacheFileCopy = getCacheFileCopy(address.getFilename(), address.getCachePath()).get(); // Don't extract data from external files.
if (!address.isInExternalFile() ) { if (!address.isInExternalFile() ) {
cacheFileCopy = getCacheFileCopy(address.getFilename(), address.getCachePath()).get();
this.data = new byte [length]; this.data = new byte [length];
ByteBuffer buf = cacheFileCopy.getByteBuffer(); ByteBuffer buf = cacheFileCopy.getByteBuffer();
int dataOffset = DATAFILE_HDR_SIZE + address.getStartBlock() * address.getBlockSize(); int dataOffset = DATAFILE_HDR_SIZE + address.getStartBlock() * address.getBlockSize();
@ -952,8 +1014,8 @@ final class ChromeCacheExtractor {
i++; i++;
} }
// hhtp headers are terminated by 0x00 0x00 // http headers are terminated by 0x00 0x00
if (data[i+1] == 0) { if (i == data.length || data[i+1] == 0) {
done = true; done = true;
} }
@ -965,11 +1027,12 @@ final class ChromeCacheExtractor {
httpResponse = headerLine; httpResponse = headerLine;
} else { } else {
int nPos = headerLine.indexOf(':'); int nPos = headerLine.indexOf(':');
if (nPos > 0 ) {
String key = headerLine.substring(0, nPos); String key = headerLine.substring(0, nPos);
String val= headerLine.substring(nPos+1); String val= headerLine.substring(nPos+1);
httpHeaders.put(key.toLowerCase(), val); httpHeaders.put(key.toLowerCase(), val);
} }
}
i++; i++;
hdrNum++; hdrNum++;

View File

@ -1,3 +1,3 @@
<project name="TSK_VERSION"> <project name="TSK_VERSION">
<property name="TSK_VERSION" value="4.6.5"/> <property name="TSK_VERSION" value="4.6.6"/>
</project> </project>

View File

@ -38,7 +38,7 @@ PROJECT_NAME = "Autopsy User Documentation"
# could be handy for archiving the generated documentation or if some version # could be handy for archiving the generated documentation or if some version
# control system is used. # 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 # 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 # 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. # The default directory is: html.
# This tag requires that the tag GENERATE_HTML is set to YES. # 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 # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
# generated HTML page (for example: .htm, .php, .asp). # generated HTML page (for example: .htm, .php, .asp).

View File

@ -38,7 +38,7 @@ PROJECT_NAME = "Autopsy"
# could be handy for archiving the generated documentation or if some version # could be handy for archiving the generated documentation or if some version
# control system is used. # 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 # 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 # 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. # The default directory is: html.
# This tag requires that the tag GENERATE_HTML is set to YES. # 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 # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
# generated HTML page (for example: .htm, .php, .asp). # generated HTML page (for example: .htm, .php, .asp).

View File

@ -4,7 +4,7 @@ app.title=Autopsy
### lowercase version of above ### lowercase version of above
app.name=${branding.token} app.name=${branding.token}
### if left unset, version will default to today's date ### 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 must be one of: DEVELOPMENT, RELEASE
#build.type=RELEASE #build.type=RELEASE
build.type=DEVELOPMENT build.type=DEVELOPMENT

View File

@ -94,7 +94,7 @@
<rule ref="rulesets/java/controversial.xml/UnnecessaryConstructor"/> <rule ref="rulesets/java/controversial.xml/UnnecessaryConstructor"/>
--> -->
<rule ref="rulesets/java/coupling.xml/CouplingBetweenObjects"/> <rule ref="rulesets/java/coupling.xml/CouplingBetweenObjects"/>
<rule ref="rulesets/java/coupling.xml/ExcessiveImports"/> <!--<rule ref="rulesets/java/coupling.xml/ExcessiveImports"/>-->
<rule ref="rulesets/java/coupling.xml/LooseCoupling"/> <rule ref="rulesets/java/coupling.xml/LooseCoupling"/>
<rule ref="rulesets/java/coupling.xml/LoosePackageCoupling"/> <rule ref="rulesets/java/coupling.xml/LoosePackageCoupling"/>
<!-- Commented out because we have not enforced this in the past <!-- Commented out because we have not enforced this in the past
@ -225,7 +225,7 @@
<!--<rule ref="rulesets/java/naming.xml/GenericsNaming"/>--> <!--<rule ref="rulesets/java/naming.xml/GenericsNaming"/>-->
<rule ref="rulesets/java/naming.xml/NoPackage"/> <rule ref="rulesets/java/naming.xml/NoPackage"/>
<rule ref="rulesets/java/naming.xml/MethodWithSameNameAsEnclosingClass"/> <rule ref="rulesets/java/naming.xml/MethodWithSameNameAsEnclosingClass"/>
<rule ref="rulesets/java/naming.xml/ShortVariable"/> <!--<rule ref="rulesets/java/naming.xml/ShortVariable"/>-->
<!-- Commented out because clarity trumps brevity, developers can use their own judgement <!-- Commented out because clarity trumps brevity, developers can use their own judgement
<rule ref="rulesets/java/naming.xml/LongVariable"/> --> <rule ref="rulesets/java/naming.xml/LongVariable"/> -->
<rule ref="rulesets/java/naming.xml/ShortMethodName"/> <rule ref="rulesets/java/naming.xml/ShortMethodName"/>
@ -258,8 +258,8 @@
<rule ref="rulesets/java/strictexception.xml/AvoidCatchingGenericException"/> <rule ref="rulesets/java/strictexception.xml/AvoidCatchingGenericException"/>
<rule ref="rulesets/java/strictexception.xml/AvoidThrowingRawExceptionTypes"/> <rule ref="rulesets/java/strictexception.xml/AvoidThrowingRawExceptionTypes"/>
<rule ref="rulesets/java/strictexception.xml/AvoidThrowingNullPointerException"/> <rule ref="rulesets/java/strictexception.xml/AvoidThrowingNullPointerException"/>
<rule ref="rulesets/java/strings.xml/AvoidDuplicateLiterals"/> <!--<rule ref="rulesets/java/strings.xml/AvoidDuplicateLiterals"/>-->
<rule ref="rulesets/java/strings.xml/InefficientStringBuffering"/> <!--<rule ref="rulesets/java/strings.xml/InefficientStringBuffering"/>-->
<rule ref="rulesets/java/strings.xml/AppendCharacterWithChar"/> <rule ref="rulesets/java/strings.xml/AppendCharacterWithChar"/>
<rule ref="rulesets/java/strings.xml/ConsecutiveAppendsShouldReuse"/> <rule ref="rulesets/java/strings.xml/ConsecutiveAppendsShouldReuse"/>
<rule ref="rulesets/java/strings.xml/ConsecutiveLiteralAppends"/> <rule ref="rulesets/java/strings.xml/ConsecutiveLiteralAppends"/>
@ -289,7 +289,7 @@
<rule ref="rulesets/java/unnecessary.xml/UselessQualifiedThis"/> <rule ref="rulesets/java/unnecessary.xml/UselessQualifiedThis"/>
<rule ref="rulesets/java/unnecessary.xml/UselessOperationOnImmutable"/> <rule ref="rulesets/java/unnecessary.xml/UselessOperationOnImmutable"/>
<rule ref="rulesets/java/unnecessary.xml/UnnecessaryReturn"/> <rule ref="rulesets/java/unnecessary.xml/UnnecessaryReturn"/>
<rule ref="rulesets/java/unusedcode.xml/UnusedModifier"/> <!--<rule ref="rulesets/java/unusedcode.xml/UnusedModifier"/>-->
<rule ref="rulesets/java/unusedcode.xml/UnusedLocalVariable"/> <rule ref="rulesets/java/unusedcode.xml/UnusedLocalVariable"/>
<!-- Commented out because it flagged many event listeners and other interfaces. <!-- Commented out because it flagged many event listeners and other interfaces.
<rule ref="rulesets/java/unusedcode.xml/UnusedFormalParameter"/> --> <rule ref="rulesets/java/unusedcode.xml/UnusedFormalParameter"/> -->

View File

@ -166,7 +166,34 @@ final class VcardParser {
extractPhotos(vcard, abstractFile); 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()) { for (Telephone telephone : vcard.getTelephoneNumbers()) {
addPhoneAttributes(telephone, abstractFile, attributes); addPhoneAttributes(telephone, abstractFile, attributes);

2
unix_setup.sh Executable file → Normal file
View File

@ -5,7 +5,7 @@
# NOTE: update_sleuthkit_version.pl updates this value and relies # NOTE: update_sleuthkit_version.pl updates this value and relies
# on it keeping the same name and whitespace. Don't change it. # 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... # In the beginning...