mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-12 16:06:15 +00:00
merged in develop
This commit is contained in:
commit
f73b255ce8
@ -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
|
||||||
|
@ -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>
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -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();
|
||||||
|
@ -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)) {
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -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
|
||||||
|
@ -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:
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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, "{key}")"/>
|
<ResourceString bundle="org/sleuthkit/autopsy/commonpropertiessearch/Bundle.properties" key="InterCasePanel.allFileCategoriesRadioButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
</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, "{key}")"/>
|
<ResourceString bundle="org/sleuthkit/autopsy/commonpropertiessearch/Bundle.properties" key="InterCasePanel.selectedFileCategoriesButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
</Property>
|
</Property>
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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.
|
||||||
*/
|
*/
|
||||||
|
@ -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, "{key}")"/>
|
<ResourceString bundle="org/sleuthkit/autopsy/commonpropertiessearch/Bundle.properties" key="IntraCasePanel.selectedFileCategoriesButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
</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, "{key}")"/>
|
<ResourceString bundle="org/sleuthkit/autopsy/commonpropertiessearch/Bundle.properties" key="IntraCasePanel.pictureVideoCheckbox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
</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, "{key}")"/>
|
<ResourceString bundle="org/sleuthkit/autopsy/commonpropertiessearch/Bundle.properties" key="IntraCasePanel.documentsCheckbox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
</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, "{key}")"/>
|
<ResourceString bundle="org/sleuthkit/autopsy/commonpropertiessearch/Bundle.properties" key="IntraCasePanel.allFileCategoriesRadioButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
</Property>
|
</Property>
|
||||||
|
@ -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() {
|
||||||
|
@ -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
|
||||||
|
@ -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.
|
||||||
|
@ -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, "{key}")"/>
|
||||||
|
</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">
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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>
|
@ -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">
|
||||||
|
@ -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
|
||||||
@ -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
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
@ -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)
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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.
|
||||||
|
@ -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}...
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
BIN
Core/src/org/sleuthkit/autopsy/report/images/image.png
Executable file
BIN
Core/src/org/sleuthkit/autopsy/report/images/image.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 516 B |
241
Core/src/org/sleuthkit/autopsy/report/uisnapshot/UiSnapShotReportWriter.java
Executable file
241
Core/src/org/sleuthkit/autopsy/report/uisnapshot/UiSnapShotReportWriter.java
Executable 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
0
Core/src/org/sleuthkit/autopsy/timeline/snapshot/index.css → Core/src/org/sleuthkit/autopsy/report/uisnapshot/index.css
Normal file → Executable file
0
Core/src/org/sleuthkit/autopsy/timeline/snapshot/index.css → Core/src/org/sleuthkit/autopsy/report/uisnapshot/index.css
Normal file → Executable 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>
|
0
Core/src/org/sleuthkit/autopsy/timeline/snapshot/summary.css → Core/src/org/sleuthkit/autopsy/report/uisnapshot/summary.css
Normal file → Executable file
0
Core/src/org/sleuthkit/autopsy/timeline/snapshot/summary.css → Core/src/org/sleuthkit/autopsy/report/uisnapshot/summary.css
Normal file → Executable 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">
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -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, "{key}")"/>
|
|
||||||
</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, "{key}")"/>
|
|
||||||
</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, "{key}")"/>
|
|
||||||
</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, "{key}")"/>
|
|
||||||
</Property>
|
|
||||||
</Properties>
|
|
||||||
</Component>
|
|
||||||
</SubComponents>
|
|
||||||
</Form>
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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),
|
||||||
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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, "{key}")"/>
|
|
||||||
</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, "{key}")"/>
|
|
||||||
</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="<String>"/>
|
||||||
|
</AuxValues>
|
||||||
|
</Component>
|
||||||
|
</SubComponents>
|
||||||
|
</Container>
|
||||||
</SubComponents>
|
</SubComponents>
|
||||||
</Form>
|
</Form>
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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++;
|
||||||
|
@ -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>
|
||||||
|
@ -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).
|
||||||
|
@ -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).
|
||||||
|
@ -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
|
||||||
|
10
ruleset.xml
10
ruleset.xml
@ -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"/> -->
|
||||||
|
@ -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
2
unix_setup.sh
Executable file → Normal 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...
|
||||||
|
Loading…
x
Reference in New Issue
Block a user