mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-17 18:17:43 +00:00
Merge branch 'file-search' of https://github.com/sleuthkit/autopsy into 5368-GroupByPathModifications
This commit is contained in:
commit
aa01285223
3
.gitignore
vendored
3
.gitignore
vendored
@ -82,7 +82,8 @@ hs_err_pid*.log
|
||||
/RecentActivity/release/
|
||||
/CentralRepository/release/
|
||||
|
||||
/.idea/
|
||||
.idea/
|
||||
*.iml
|
||||
|
||||
*.img
|
||||
*.vhd
|
||||
|
49
.travis.yml
49
.travis.yml
@ -1,24 +1,65 @@
|
||||
language: java
|
||||
sudo: required
|
||||
dist: trusty
|
||||
dist: bionic
|
||||
os:
|
||||
- linux
|
||||
|
||||
env:
|
||||
global:
|
||||
- TSK_HOME=$TRAVIS_BUILD_DIR/sleuthkit/sleuthkit
|
||||
|
||||
addons:
|
||||
apt:
|
||||
update: true
|
||||
packages:
|
||||
- libafflib-dev
|
||||
- libewf-dev
|
||||
- libpq-dev
|
||||
- autopoint
|
||||
- libsqlite3-dev
|
||||
- ant
|
||||
- ant-optional
|
||||
- libcppunit-dev
|
||||
- wget
|
||||
- openjdk-8-jdk
|
||||
- openjfx=8u161-b12-1ubuntu2
|
||||
- libopenjfx-java=8u161-b12-1ubuntu2
|
||||
- libopenjfx-jni=8u161-b12-1ubuntu2
|
||||
homebrew:
|
||||
update: true
|
||||
packages:
|
||||
- ant
|
||||
- ant-optional
|
||||
- libewf
|
||||
- gettext
|
||||
- cppunit
|
||||
- afflib
|
||||
|
||||
python:
|
||||
- "2.7"
|
||||
jdk:
|
||||
- oraclejdk8
|
||||
|
||||
before_install:
|
||||
- git clone https://github.com/sleuthkit/sleuthkit.git sleuthkit/sleuthkit
|
||||
- python setupSleuthkitBranch.py
|
||||
|
||||
install:
|
||||
- sudo apt-get install testdisk
|
||||
- cd sleuthkit/sleuthkit
|
||||
- sh travis_build.sh
|
||||
- ./travis_install_libs.sh
|
||||
|
||||
before_script:
|
||||
- if [ $TRAVIS_OS_NAME = linux ]; then
|
||||
sudo update-alternatives --set java /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java;
|
||||
sudo update-alternatives --set javac /usr/lib/jvm/java-8-openjdk-amd64/bin/javac;
|
||||
export PATH=/usr/bin:$PATH;
|
||||
unset JAVA_HOME;
|
||||
fi
|
||||
|
||||
script:
|
||||
- set -e
|
||||
- echo "Building TSK..."
|
||||
- ./bootstrap && ./configure --prefix=/usr && make
|
||||
- pushd bindings/java/ && ant -q dist-PostgreSQL && popd
|
||||
- echo "Building Autopsy..." && echo -en 'travis_fold:start:script.build\\r'
|
||||
- cd $TRAVIS_BUILD_DIR/
|
||||
- ant build
|
||||
|
@ -22,12 +22,12 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.casemodule.services.FileManager;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.autopsy.casemodule.services.FileManager;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.datamodel.LocalFilesDataSource;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TskDataException;
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011-2017 Basis Technology Corp.
|
||||
* Copyright 2011-2019 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -24,6 +24,7 @@ import java.beans.PropertyChangeListener;
|
||||
import java.io.Serializable;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
@ -59,6 +60,7 @@ final class CollaborationMonitor {
|
||||
private static final String COLLABORATION_MONITOR_EVENT = "COLLABORATION_MONITOR_EVENT"; //NON-NLS
|
||||
private static final Set<Case.Events> CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.ADDING_DATA_SOURCE,
|
||||
Case.Events.DATA_SOURCE_ADDED, Case.Events.ADDING_DATA_SOURCE_FAILED);
|
||||
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.DATA_SOURCE_ANALYSIS_STARTED, IngestManager.IngestJobEvent.DATA_SOURCE_ANALYSIS_COMPLETED);
|
||||
private static final int NUMBER_OF_PERIODIC_TASK_THREADS = 2;
|
||||
private static final String PERIODIC_TASK_THREAD_NAME = "collab-monitor-periodic-tasks-%d"; //NON-NLS
|
||||
private static final long HEARTBEAT_INTERVAL_MINUTES = 1;
|
||||
@ -113,7 +115,7 @@ final class CollaborationMonitor {
|
||||
* Create a local tasks manager to track and broadcast local tasks.
|
||||
*/
|
||||
localTasksManager = new LocalTasksManager();
|
||||
IngestManager.getInstance().addIngestJobEventListener(localTasksManager);
|
||||
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, localTasksManager);
|
||||
Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, localTasksManager);
|
||||
|
||||
/**
|
||||
@ -538,7 +540,7 @@ final class CollaborationMonitor {
|
||||
* @return A mapping of task IDs to current tasks
|
||||
*/
|
||||
Map<Long, Task> getCurrentTasks() {
|
||||
return currentTasks;
|
||||
return Collections.unmodifiableMap(currentTasks);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -23,7 +23,9 @@ import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
@ -44,6 +46,7 @@ import org.sleuthkit.datamodel.DataSource;
|
||||
public final class IngestJobInfoPanel extends javax.swing.JPanel {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(IngestJobInfoPanel.class.getName());
|
||||
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.STARTED, IngestManager.IngestJobEvent.CANCELLED, IngestManager.IngestJobEvent.COMPLETED);
|
||||
private List<IngestJobInfo> ingestJobs;
|
||||
private final List<IngestJobInfo> ingestJobsForSelectedDataSource = new ArrayList<>();
|
||||
private IngestJobTableModel ingestJobTableModel = new IngestJobTableModel();
|
||||
@ -69,7 +72,7 @@ public final class IngestJobInfoPanel extends javax.swing.JPanel {
|
||||
this.ingestModuleTable.setModel(this.ingestModuleTableModel);
|
||||
});
|
||||
|
||||
IngestManager.getInstance().addIngestJobEventListener((PropertyChangeEvent evt) -> {
|
||||
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST , (PropertyChangeEvent evt) -> {
|
||||
if (evt.getPropertyName().equals(IngestManager.IngestJobEvent.STARTED.toString())
|
||||
|| evt.getPropertyName().equals(IngestManager.IngestJobEvent.CANCELLED.toString())
|
||||
|| evt.getPropertyName().equals(IngestManager.IngestJobEvent.COMPLETED.toString())) {
|
||||
|
@ -30,7 +30,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Observer;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
import org.openide.nodes.Node;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
|
@ -22,7 +22,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import java.util.Collections;
|
||||
|
@ -24,7 +24,7 @@ import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.datamodel.CaseDbAccessManager;
|
||||
|
||||
/**
|
||||
|
@ -24,7 +24,7 @@ import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.datamodel.CaseDbAccessManager;
|
||||
|
||||
/**
|
||||
|
@ -21,7 +21,7 @@ package org.sleuthkit.autopsy.casemodule.datasourcesummary;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.datamodel.CaseDbAccessManager;
|
||||
|
@ -81,7 +81,7 @@
|
||||
<Component class="javax.swing.JLabel" name="byMimeTypeLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties" key="DataSourceSummaryCountsPanel.byMimeTypeLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties" key="DataSourceSummaryCountsPanel.byMimeTypeLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
@ -104,14 +104,14 @@
|
||||
<Component class="javax.swing.JLabel" name="byCategoryLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties" key="DataSourceSummaryCountsPanel.byCategoryLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties" key="DataSourceSummaryCountsPanel.byCategoryLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="jLabel1">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties" key="DataSourceSummaryCountsPanel.jLabel1.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties" key="DataSourceSummaryCountsPanel.jLabel1.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
|
@ -21,7 +21,7 @@ package org.sleuthkit.autopsy.casemodule.datasourcesummary;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
import javax.swing.table.DefaultTableCellRenderer;
|
||||
|
@ -52,7 +52,7 @@
|
||||
<Component class="javax.swing.JLabel" name="displayNameLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.displayNameLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.displayNameLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
@ -64,7 +64,7 @@
|
||||
<Component class="javax.swing.JLabel" name="originalNameLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.originalNameLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.originalNameLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
@ -76,7 +76,7 @@
|
||||
<Component class="javax.swing.JLabel" name="sha1HashValue">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.sha1HashValue.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.sha1HashValue.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
@ -88,10 +88,10 @@
|
||||
<Component class="javax.swing.JLabel" name="operatingSystemValue">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.operatingSystemValue.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.operatingSystemValue.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.operatingSystemValue.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.operatingSystemValue.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
@ -103,7 +103,7 @@
|
||||
<Component class="javax.swing.JLabel" name="displayNameValue">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.displayNameValue.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.displayNameValue.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
@ -115,7 +115,7 @@
|
||||
<Component class="javax.swing.JLabel" name="sha256HashValue">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.sha256HashValue.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.sha256HashValue.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
@ -127,7 +127,7 @@
|
||||
<Component class="javax.swing.JLabel" name="originalNameValue">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.originalNameValue.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.originalNameValue.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
@ -139,10 +139,10 @@
|
||||
<Component class="javax.swing.JLabel" name="deviceIdValue">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.deviceIdValue.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.deviceIdValue.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.deviceIdValue.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.deviceIdValue.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
@ -179,7 +179,7 @@
|
||||
<TableColumnModel selectionModel="0">
|
||||
<Column maxWidth="-1" minWidth="-1" prefWidth="-1" resizable="true">
|
||||
<Title editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.filePathsTable.columnModel.title0" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.filePathsTable.columnModel.title0" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Title>
|
||||
<Editor/>
|
||||
<Renderer/>
|
||||
@ -196,7 +196,7 @@
|
||||
<Component class="javax.swing.JLabel" name="dataSourceUsageValue">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.dataSourceUsageValue.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.dataSourceUsageValue.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
@ -208,7 +208,7 @@
|
||||
<Component class="javax.swing.JLabel" name="timeZoneValue">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.timeZoneValue.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.timeZoneValue.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
@ -220,10 +220,10 @@
|
||||
<Component class="javax.swing.JLabel" name="imageTypeValue">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.imageTypeValue.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.imageTypeValue.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.imageTypeValue.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.imageTypeValue.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
@ -235,10 +235,10 @@
|
||||
<Component class="javax.swing.JLabel" name="md5HashValue">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.md5HashValue.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.md5HashValue.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.md5HashValue.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.md5HashValue.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
@ -250,7 +250,7 @@
|
||||
<Component class="javax.swing.JLabel" name="sectorSizeValue">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.sectorSizeValue.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.sectorSizeValue.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
@ -262,7 +262,7 @@
|
||||
<Component class="javax.swing.JLabel" name="sizeValue">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.sizeValue.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.sizeValue.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
@ -274,7 +274,7 @@
|
||||
<Component class="javax.swing.JLabel" name="filePathsLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.filePathsLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.filePathsLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
@ -286,7 +286,7 @@
|
||||
<Component class="javax.swing.JLabel" name="sha256HashLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.sha256HashLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.sha256HashLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
@ -298,7 +298,7 @@
|
||||
<Component class="javax.swing.JLabel" name="sha1HashLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.sha1HashLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.sha1HashLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
@ -310,7 +310,7 @@
|
||||
<Component class="javax.swing.JLabel" name="md5HashLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.md5HashLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.md5HashLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
@ -322,7 +322,7 @@
|
||||
<Component class="javax.swing.JLabel" name="sectorSizeLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.sectorSizeLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.sectorSizeLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
@ -334,7 +334,7 @@
|
||||
<Component class="javax.swing.JLabel" name="sizeLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.sizeLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.sizeLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
@ -346,7 +346,7 @@
|
||||
<Component class="javax.swing.JLabel" name="imageTypeLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.imageTypeLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.imageTypeLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
@ -358,7 +358,7 @@
|
||||
<Component class="javax.swing.JLabel" name="acquisitionDetailsLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.acquisitionDetailsLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.acquisitionDetailsLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
@ -370,7 +370,7 @@
|
||||
<Component class="javax.swing.JLabel" name="timeZoneLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.timeZoneLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.timeZoneLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
@ -382,7 +382,7 @@
|
||||
<Component class="javax.swing.JLabel" name="dataSourceUsageLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.dataSourceUsageLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.dataSourceUsageLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
@ -394,7 +394,7 @@
|
||||
<Component class="javax.swing.JLabel" name="deviceIdLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.deviceIdLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.deviceIdLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
@ -427,7 +427,7 @@
|
||||
</Property>
|
||||
<Property name="rows" type="int" value="4"/>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.acquisitionDetailsTextArea.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.acquisitionDetailsTextArea.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
|
||||
<Border info="null"/>
|
||||
@ -469,7 +469,7 @@
|
||||
<Component class="javax.swing.JLabel" name="unallocatedSizeLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.unallocatedSizeLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.unallocatedSizeLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
@ -481,7 +481,7 @@
|
||||
<Component class="javax.swing.JLabel" name="unallocatedSizeValue">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.unallocatedSizeValue.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.unallocatedSizeValue.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
|
@ -22,7 +22,7 @@ import java.text.DecimalFormat;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import javax.swing.table.DefaultTableModel;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
|
@ -20,9 +20,11 @@ package org.sleuthkit.autopsy.casemodule.datasourcesummary;
|
||||
|
||||
import java.awt.Frame;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Map;
|
||||
import java.util.Observable;
|
||||
import java.util.Observer;
|
||||
import java.util.Set;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.casemodule.IngestJobInfoPanel;
|
||||
@ -38,6 +40,7 @@ import org.sleuthkit.datamodel.IngestJobInfo;
|
||||
final class DataSourceSummaryDialog extends javax.swing.JDialog implements Observer {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.DATA_SOURCE_ANALYSIS_COMPLETED);
|
||||
private final DataSourceSummaryCountsPanel countsPanel;
|
||||
private final DataSourceSummaryDetailsPanel detailsPanel;
|
||||
private final DataSourceBrowser dataSourcesPanel;
|
||||
@ -77,7 +80,7 @@ final class DataSourceSummaryDialog extends javax.swing.JDialog implements Obser
|
||||
}
|
||||
});
|
||||
//add listener to refresh jobs with Started status when they complete
|
||||
IngestManager.getInstance().addIngestJobEventListener((PropertyChangeEvent evt) -> {
|
||||
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, (PropertyChangeEvent evt) -> {
|
||||
if (evt instanceof DataSourceAnalysisCompletedEvent) {
|
||||
DataSourceAnalysisCompletedEvent dsEvent = (DataSourceAnalysisCompletedEvent) evt;
|
||||
if (dsEvent.getResult() == Reason.ANALYSIS_COMPLETED) {
|
||||
|
@ -25,8 +25,10 @@ import static java.lang.Boolean.FALSE;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.EnumSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.logging.Level;
|
||||
@ -38,10 +40,8 @@ import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeNormalizationException;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamArtifactUtil;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.ThreadUtils;
|
||||
import org.sleuthkit.autopsy.ingest.IngestManager;
|
||||
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase;
|
||||
@ -51,10 +51,13 @@ import org.sleuthkit.datamodel.Blackboard;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
|
||||
import org.sleuthkit.autopsy.coreutils.ThreadUtils;
|
||||
import static org.sleuthkit.autopsy.ingest.IngestManager.IngestModuleEvent.DATA_ADDED;
|
||||
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT;
|
||||
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT;
|
||||
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME;
|
||||
import org.sleuthkit.autopsy.ingest.events.DataSourceAnalysisCompletedEvent;
|
||||
import org.sleuthkit.autopsy.ingest.events.DataSourceAnalysisEvent;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.Image;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
@ -68,17 +71,18 @@ import org.sleuthkit.datamodel.TskCoreException;
|
||||
public class IngestEventsListener {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(CorrelationAttributeInstance.class.getName());
|
||||
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.DATA_SOURCE_ANALYSIS_COMPLETED);
|
||||
private static final Set<IngestManager.IngestModuleEvent> INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(DATA_ADDED);
|
||||
private static final String MODULE_NAME = Bundle.IngestEventsListener_ingestmodule_name();
|
||||
|
||||
final Collection<String> recentlyAddedCeArtifacts = new LinkedHashSet<>();
|
||||
private static int correlationModuleInstanceCount;
|
||||
private static boolean flagNotableItems;
|
||||
private static boolean flagSeenDevices;
|
||||
private static boolean createCrProperties;
|
||||
private final ExecutorService jobProcessingExecutor;
|
||||
private static final String INGEST_EVENT_THREAD_NAME = "Ingest-Event-Listener-%d";
|
||||
private final ExecutorService jobProcessingExecutor;
|
||||
private final PropertyChangeListener pcl1 = new IngestModuleEventListener();
|
||||
private final PropertyChangeListener pcl2 = new IngestJobEventListener();
|
||||
final Collection<String> recentlyAddedCeArtifacts = new LinkedHashSet<>();
|
||||
|
||||
IngestEventsListener() {
|
||||
jobProcessingExecutor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat(INGEST_EVENT_THREAD_NAME).build());
|
||||
@ -92,8 +96,8 @@ public class IngestEventsListener {
|
||||
* Add all of our Ingest Event Listeners to the IngestManager Instance.
|
||||
*/
|
||||
public void installListeners() {
|
||||
IngestManager.getInstance().addIngestModuleEventListener(pcl1);
|
||||
IngestManager.getInstance().addIngestJobEventListener(pcl2);
|
||||
IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, pcl1);
|
||||
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl2);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -348,8 +352,7 @@ public class IngestEventsListener {
|
||||
String dataSourceName = "";
|
||||
long dataSourceObjectId = -1;
|
||||
try {
|
||||
dataSource = ((DataSourceAnalysisCompletedEvent) event).getDataSource();
|
||||
|
||||
dataSource = ((DataSourceAnalysisEvent) event).getDataSource();
|
||||
/*
|
||||
* We only care about Images for the purpose of
|
||||
* updating hash values.
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Central Repository
|
||||
*
|
||||
* Copyright 2015-2018 Basis Technology Corp.
|
||||
* Copyright 2015-2019 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -24,6 +24,7 @@ import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.SwingUtilities;
|
||||
import org.netbeans.spi.options.OptionsPanelController;
|
||||
@ -49,7 +50,7 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Logger logger = Logger.getLogger(GlobalSettingsPanel.class.getName());
|
||||
|
||||
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.STARTED, IngestManager.IngestJobEvent.CANCELLED, IngestManager.IngestJobEvent.COMPLETED);
|
||||
private final IngestJobEventPropertyChangeListener ingestJobEventListener;
|
||||
|
||||
/**
|
||||
@ -72,7 +73,7 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
|
||||
}
|
||||
|
||||
private void addIngestJobEventsListener() {
|
||||
IngestManager.getInstance().addIngestJobEventListener(ingestJobEventListener);
|
||||
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, ingestJobEventListener);
|
||||
ingestStateUpdated(Case.isCaseOpen());
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,9 @@ import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.Collection;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import org.netbeans.spi.sendopts.OptionProcessor;
|
||||
import org.openide.LifecycleManager;
|
||||
@ -65,6 +67,7 @@ import org.sleuthkit.datamodel.Content;
|
||||
public class CommandLineIngestManager {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(CommandLineIngestManager.class.getName());
|
||||
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.CANCELLED, IngestManager.IngestJobEvent.COMPLETED);
|
||||
private Path rootOutputDirectory;
|
||||
|
||||
public CommandLineIngestManager() {
|
||||
@ -100,6 +103,7 @@ public class CommandLineIngestManager {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
LOGGER.log(Level.INFO, "Job processing task started");
|
||||
|
||||
@ -198,10 +202,11 @@ public class CommandLineIngestManager {
|
||||
System.out.println("Unable to ingest data source " + dataSourcePath + ". Exiting...");
|
||||
} catch (Throwable ex) {
|
||||
/*
|
||||
* Unexpected runtime exceptions firewall. This task is designed to
|
||||
* be able to be run in an executor service thread pool without
|
||||
* calling get() on the task's Future<Void>, so this ensures that
|
||||
* such errors get logged.
|
||||
* Unexpected runtime exceptions firewall. This task is
|
||||
* designed to be able to be run in an executor service
|
||||
* thread pool without calling get() on the task's
|
||||
* Future<Void>, so this ensures that such errors get
|
||||
* logged.
|
||||
*/
|
||||
LOGGER.log(Level.SEVERE, "Unexpected error while ingesting data source " + dataSourcePath, ex);
|
||||
System.out.println("Unexpected error while ingesting data source " + dataSourcePath + ". Exiting...");
|
||||
@ -229,6 +234,7 @@ public class CommandLineIngestManager {
|
||||
* object.
|
||||
*
|
||||
* @param dataSource DataSource object
|
||||
*
|
||||
* @return object ID
|
||||
*/
|
||||
private Long getDataSourceId(AutoIngestDataSource dataSource) {
|
||||
@ -268,12 +274,11 @@ public class CommandLineIngestManager {
|
||||
* @param dataSource The data source.
|
||||
*
|
||||
* @throws
|
||||
* AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException
|
||||
* if there was a DSP processing error
|
||||
* AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException if
|
||||
* there was a DSP processing error
|
||||
*
|
||||
* @throws InterruptedException if the thread running the job processing
|
||||
* task is interrupted while blocked, i.e., if auto ingest is shutting
|
||||
* down.
|
||||
* @throws InterruptedException if the thread running the job processing
|
||||
* task is interrupted while blocked, i.e., if auto ingest is shutting down.
|
||||
*/
|
||||
private void runDataSourceProcessor(Case caseForJob, AutoIngestDataSource dataSource) throws InterruptedException, AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException {
|
||||
|
||||
@ -384,7 +389,7 @@ public class CommandLineIngestManager {
|
||||
|
||||
LOGGER.log(Level.INFO, "Starting ingest modules analysis for {0} ", dataSource.getPath());
|
||||
IngestJobEventListener ingestJobEventListener = new IngestJobEventListener();
|
||||
IngestManager.getInstance().addIngestJobEventListener(ingestJobEventListener);
|
||||
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, ingestJobEventListener);
|
||||
try {
|
||||
synchronized (ingestLock) {
|
||||
IngestJobSettings ingestJobSettings = new IngestJobSettings(UserPreferences.getCommandLineModeIngestModuleContextString());
|
||||
@ -447,7 +452,7 @@ public class CommandLineIngestManager {
|
||||
* the path.
|
||||
*
|
||||
* @param caseFoldersPath The root case folders path.
|
||||
* @param caseName The name of the case.
|
||||
* @param caseName The name of the case.
|
||||
*
|
||||
* @return A case folder path with a time stamp suffix.
|
||||
*/
|
||||
@ -461,8 +466,8 @@ public class CommandLineIngestManager {
|
||||
* for a case.
|
||||
*
|
||||
* @param folderToSearch The folder to be searched.
|
||||
* @param caseName The name of the case for which a case folder is to be
|
||||
* found.
|
||||
* @param caseName The name of the case for which a case folder is
|
||||
* to be found.
|
||||
*
|
||||
* @return The path of the case folder, or null if it is not found.
|
||||
*/
|
||||
|
@ -23,7 +23,7 @@ import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.netbeans.api.sendopts.CommandException;
|
||||
import org.netbeans.spi.sendopts.Env;
|
||||
import org.netbeans.spi.sendopts.Option;
|
||||
|
@ -20,7 +20,7 @@ package org.sleuthkit.autopsy.commonpropertiessearch;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.openide.nodes.ChildFactory;
|
||||
import org.openide.nodes.Children;
|
||||
import org.openide.nodes.Node;
|
||||
|
@ -24,7 +24,7 @@ import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import javax.swing.table.TableColumn;
|
||||
import javax.swing.table.TableColumnModel;
|
||||
import org.openide.util.NbBundle;
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2017-2018 Basis Technology Corp.
|
||||
* Copyright 2017-2019 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -34,6 +34,7 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
@ -81,7 +82,8 @@ final public class FiltersPanel extends JPanel {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Logger logger = Logger.getLogger(FiltersPanel.class.getName());
|
||||
|
||||
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.COMPLETED);
|
||||
private static final Set<IngestManager.IngestModuleEvent> INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(DATA_ADDED);
|
||||
/**
|
||||
* Map from Account.Type to the checkbox for that account type's filter.
|
||||
*/
|
||||
@ -151,8 +153,8 @@ final public class FiltersPanel extends JPanel {
|
||||
|
||||
updateFilters(true);
|
||||
UserPreferences.addChangeListener(preferenceChangeEvent -> {
|
||||
if (preferenceChangeEvent.getKey().equals(UserPreferences.DISPLAY_TIMES_IN_LOCAL_TIME) ||
|
||||
preferenceChangeEvent.getKey().equals(UserPreferences.TIME_ZONE_FOR_DISPLAYS)) {
|
||||
if (preferenceChangeEvent.getKey().equals(UserPreferences.DISPLAY_TIMES_IN_LOCAL_TIME)
|
||||
|| preferenceChangeEvent.getKey().equals(UserPreferences.TIME_ZONE_FOR_DISPLAYS)) {
|
||||
updateTimeZone();
|
||||
}
|
||||
});
|
||||
@ -166,22 +168,21 @@ final public class FiltersPanel extends JPanel {
|
||||
&& (eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE.getTypeID()
|
||||
|| eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT.getTypeID()
|
||||
|| eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG.getTypeID()
|
||||
|| eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID()))
|
||||
{
|
||||
updateFilters(true);
|
||||
needsRefresh = true;
|
||||
validateFilters();
|
||||
|| eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID())) {
|
||||
updateFilters(true);
|
||||
needsRefresh = true;
|
||||
validateFilters();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.ingestJobListener = pce -> {
|
||||
String eventType = pce.getPropertyName();
|
||||
if (eventType.equals(COMPLETED.toString()) &&
|
||||
updateFilters(true)) {
|
||||
if (eventType.equals(COMPLETED.toString())
|
||||
&& updateFilters(true)) {
|
||||
|
||||
needsRefresh = true;
|
||||
validateFilters();
|
||||
needsRefresh = true;
|
||||
validateFilters();
|
||||
}
|
||||
};
|
||||
|
||||
@ -210,14 +211,14 @@ final public class FiltersPanel extends JPanel {
|
||||
}
|
||||
|
||||
private boolean validateLimitValue() {
|
||||
String selectedValue = (String)limitComboBox.getSelectedItem();
|
||||
if(selectedValue.trim().equalsIgnoreCase("all")) {
|
||||
String selectedValue = (String) limitComboBox.getSelectedItem();
|
||||
if (selectedValue.trim().equalsIgnoreCase("all")) {
|
||||
return true;
|
||||
} else {
|
||||
try{
|
||||
try {
|
||||
int value = Integer.parseInt(selectedValue);
|
||||
return value > 0;
|
||||
} catch( NumberFormatException ex) {
|
||||
} catch (NumberFormatException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -250,8 +251,8 @@ final public class FiltersPanel extends JPanel {
|
||||
@Override
|
||||
public void addNotify() {
|
||||
super.addNotify();
|
||||
IngestManager.getInstance().addIngestModuleEventListener(ingestListener);
|
||||
IngestManager.getInstance().addIngestJobEventListener(ingestJobListener);
|
||||
IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, ingestListener);
|
||||
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, ingestJobListener);
|
||||
Case.addEventTypeSubscriber(EnumSet.of(CURRENT_CASE), evt -> {
|
||||
//clear the device filter widget when the case changes.
|
||||
devicesMap.clear();
|
||||
@ -310,7 +311,7 @@ final public class FiltersPanel extends JPanel {
|
||||
* Helper function to create a new instance of the CheckBoxIconPanel base on
|
||||
* the Account.Type and initalState (check box state).
|
||||
*
|
||||
* @param type Account.Type to display on the panel
|
||||
* @param type Account.Type to display on the panel
|
||||
* @param initalState initial check box state
|
||||
*
|
||||
* @return instance of the CheckBoxIconPanel
|
||||
@ -339,7 +340,7 @@ final public class FiltersPanel extends JPanel {
|
||||
|
||||
for (DataSource dataSource : sleuthkitCase.getDataSources()) {
|
||||
String dsName = sleuthkitCase.getContentById(dataSource.getId()).getName();
|
||||
if(devicesMap.containsKey(dataSource.getDeviceId())) {
|
||||
if (devicesMap.containsKey(dataSource.getDeviceId())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -357,7 +358,7 @@ final public class FiltersPanel extends JPanel {
|
||||
logger.log(Level.SEVERE, "There was a error loading the datasources for the case.", tskCoreException);
|
||||
}
|
||||
|
||||
if(newOneFound) {
|
||||
if (newOneFound) {
|
||||
devicesListPane.revalidate();
|
||||
}
|
||||
|
||||
@ -373,12 +374,12 @@ final public class FiltersPanel extends JPanel {
|
||||
public void setFilters(CommunicationsFilter commFilter) {
|
||||
List<CommunicationsFilter.SubFilter> subFilters = commFilter.getAndFilters();
|
||||
subFilters.forEach(subFilter -> {
|
||||
if( subFilter instanceof DeviceFilter ) {
|
||||
setDeviceFilter((DeviceFilter)subFilter);
|
||||
} else if( subFilter instanceof AccountTypeFilter) {
|
||||
if (subFilter instanceof DeviceFilter) {
|
||||
setDeviceFilter((DeviceFilter) subFilter);
|
||||
} else if (subFilter instanceof AccountTypeFilter) {
|
||||
setAccountTypeFilter((AccountTypeFilter) subFilter);
|
||||
} else if (subFilter instanceof MostRecentFilter ) {
|
||||
setMostRecentFilter((MostRecentFilter)subFilter);
|
||||
} else if (subFilter instanceof MostRecentFilter) {
|
||||
setMostRecentFilter((MostRecentFilter) subFilter);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -395,12 +396,13 @@ final public class FiltersPanel extends JPanel {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the state of the account type checkboxes to match the passed in filter
|
||||
/**
|
||||
* Set the state of the account type checkboxes to match the passed in
|
||||
* filter
|
||||
*
|
||||
* @param typeFilter Account Types to be selected
|
||||
*/
|
||||
private void setAccountTypeFilter(AccountTypeFilter typeFilter){
|
||||
private void setAccountTypeFilter(AccountTypeFilter typeFilter) {
|
||||
|
||||
accountTypeMap.forEach((type, cb) -> {
|
||||
cb.setSelected(typeFilter.getAccountTypes().contains(type));
|
||||
@ -439,7 +441,7 @@ final public class FiltersPanel extends JPanel {
|
||||
*/
|
||||
private void setMostRecentFilter(MostRecentFilter filter) {
|
||||
int limit = filter.getLimit();
|
||||
if(limit > 0) {
|
||||
if (limit > 0) {
|
||||
limitComboBox.setSelectedItem(filter.getLimit());
|
||||
} else {
|
||||
limitComboBox.setSelectedItem("All");
|
||||
@ -448,7 +450,7 @@ final public class FiltersPanel extends JPanel {
|
||||
|
||||
@Subscribe
|
||||
void filtersBack(CVTEvents.StateChangeEvent event) {
|
||||
if(event.getCommunicationsState().getCommunicationsFilter() != null){
|
||||
if (event.getCommunicationsState().getCommunicationsFilter() != null) {
|
||||
setFilters(event.getCommunicationsState().getCommunicationsFilter());
|
||||
setStartDateControlState(event.getCommunicationsState().getStartControlState());
|
||||
setEndDateControlState(event.getCommunicationsState().getEndControlState());
|
||||
@ -831,7 +833,7 @@ final public class FiltersPanel extends JPanel {
|
||||
*
|
||||
* @return an instance of CommunicationsFilter
|
||||
*/
|
||||
protected CommunicationsFilter getFilter() {
|
||||
private CommunicationsFilter getFilter() {
|
||||
CommunicationsFilter commsFilter = new CommunicationsFilter();
|
||||
commsFilter.addAndFilter(getDeviceFilter());
|
||||
commsFilter.addAndFilter(getAccountTypeFilter());
|
||||
@ -878,35 +880,36 @@ final public class FiltersPanel extends JPanel {
|
||||
private DateRangeFilter getDateRangeFilter() {
|
||||
ZoneId zone = Utils.getUserPreferredZoneId();
|
||||
|
||||
return new DateRangeFilter( startCheckBox.isSelected() ? startDatePicker.getDate().atStartOfDay(zone).toEpochSecond() : 0,
|
||||
endCheckBox.isSelected() ? endDatePicker.getDate().atStartOfDay(zone).toEpochSecond() : 0);
|
||||
return new DateRangeFilter(startCheckBox.isSelected() ? startDatePicker.getDate().atStartOfDay(zone).toEpochSecond() : 0,
|
||||
endCheckBox.isSelected() ? endDatePicker.getDate().atStartOfDay(zone).toEpochSecond() : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a MostRecentFilter that based on the current state of the ui controls.
|
||||
* Get a MostRecentFilter that based on the current state of the ui
|
||||
* controls.
|
||||
*
|
||||
* @return A new instance of MostRecentFilter
|
||||
*/
|
||||
private MostRecentFilter getMostRecentFilter() {
|
||||
String value = (String)limitComboBox.getSelectedItem();
|
||||
if(value.trim().equalsIgnoreCase("all")){
|
||||
String value = (String) limitComboBox.getSelectedItem();
|
||||
if (value.trim().equalsIgnoreCase("all")) {
|
||||
return new MostRecentFilter(-1);
|
||||
} else{
|
||||
} else {
|
||||
try {
|
||||
int count = Integer.parseInt(value);
|
||||
return new MostRecentFilter(count);
|
||||
} catch(NumberFormatException ex) {
|
||||
} catch (NumberFormatException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private DateControlState getStartControlState() {
|
||||
return new DateControlState (startDatePicker.getDate(), startCheckBox.isSelected());
|
||||
return new DateControlState(startDatePicker.getDate(), startCheckBox.isSelected());
|
||||
}
|
||||
|
||||
private DateControlState getEndControlState() {
|
||||
return new DateControlState (endDatePicker.getDate(), endCheckBox.isSelected());
|
||||
return new DateControlState(endDatePicker.getDate(), endCheckBox.isSelected());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -947,19 +950,19 @@ final public class FiltersPanel extends JPanel {
|
||||
*/
|
||||
private void initalizeDateTimeFilters() {
|
||||
Case currentCase = null;
|
||||
try{
|
||||
try {
|
||||
currentCase = Case.getCurrentCaseThrows();
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
logger.log(Level.INFO, "Tried to intialize communication filters date range filters without an open case, using default values");
|
||||
logger.log(Level.INFO, "Tried to intialize communication filters date range filters without an open case, using default values");
|
||||
}
|
||||
|
||||
if(currentCase == null) {
|
||||
if (currentCase == null) {
|
||||
setDateTimeFiltersToDefault();
|
||||
openCase = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if(!currentCase.equals(openCase)) {
|
||||
if (!currentCase.equals(openCase)) {
|
||||
setDateTimeFiltersToDefault();
|
||||
openCase = currentCase;
|
||||
(new DatePickerWorker()).execute();
|
||||
@ -1002,11 +1005,12 @@ final public class FiltersPanel extends JPanel {
|
||||
}//GEN-LAST:event_limitComboBoxActionPerformed
|
||||
|
||||
/**
|
||||
* A class to wrap the state of the date controls that consist of a date picker
|
||||
* and a checkbox.
|
||||
* A class to wrap the state of the date controls that consist of a date
|
||||
* picker and a checkbox.
|
||||
*
|
||||
*/
|
||||
final class DateControlState {
|
||||
|
||||
private final LocalDate date;
|
||||
private final boolean enabled;
|
||||
|
||||
@ -1014,7 +1018,7 @@ final public class FiltersPanel extends JPanel {
|
||||
* Wraps the state of the date controls that consist of a date picker
|
||||
* and checkbox
|
||||
*
|
||||
* @param date LocalDate value of the datepicker
|
||||
* @param date LocalDate value of the datepicker
|
||||
* @param enabled State of the checkbox
|
||||
*/
|
||||
protected DateControlState(LocalDate date, boolean enabled) {
|
||||
@ -1027,7 +1031,7 @@ final public class FiltersPanel extends JPanel {
|
||||
*
|
||||
* @return Current state LocalDate
|
||||
*/
|
||||
public LocalDate getDate(){
|
||||
public LocalDate getDate() {
|
||||
return date;
|
||||
}
|
||||
|
||||
@ -1078,13 +1082,15 @@ final public class FiltersPanel extends JPanel {
|
||||
private final javax.swing.JButton unCheckAllDevicesButton = new javax.swing.JButton();
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
|
||||
/**
|
||||
* This class is a small panel that appears to just be a checkbox but
|
||||
* adds the functionality of being able to show an icon between the checkbox
|
||||
* and label.
|
||||
* This class is a small panel that appears to just be a checkbox but adds
|
||||
* the functionality of being able to show an icon between the checkbox and
|
||||
* label.
|
||||
*/
|
||||
final class CheckBoxIconPanel extends JPanel{
|
||||
final class CheckBoxIconPanel extends JPanel {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final JCheckBox checkbox;
|
||||
private final JLabel label;
|
||||
|
||||
@ -1092,7 +1098,7 @@ final public class FiltersPanel extends JPanel {
|
||||
* Creates a JPanel instance with the specified label and image.
|
||||
*
|
||||
* @param labelText The text to be displayed by the checkbox label.
|
||||
* @param image The image to be dispayed by the label.
|
||||
* @param image The image to be dispayed by the label.
|
||||
*/
|
||||
private CheckBoxIconPanel(String labelText, Icon image) {
|
||||
checkbox = new JCheckBox();
|
||||
@ -1139,8 +1145,8 @@ final public class FiltersPanel extends JPanel {
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple class that implements CaseDbAccessQueryCallback. Can be used
|
||||
* as an anonymous innerclass with the CaseDbAccessManager select function.
|
||||
* A simple class that implements CaseDbAccessQueryCallback. Can be used as
|
||||
* an anonymous innerclass with the CaseDbAccessManager select function.
|
||||
*/
|
||||
class FilterPanelQueryCallback implements CaseDbAccessQueryCallback {
|
||||
|
||||
|
@ -1,12 +1,10 @@
|
||||
ContactDetailsPane.nameLabel.text=Placeholder
|
||||
SummaryViewer.countsPanel.border.title=Counts
|
||||
SummaryViewer.contactsLabel.text=Contacts:
|
||||
SummaryViewer.attachmentsLabel.text=Media Attachments:
|
||||
OutlineViewPanel.messageLabel.text=<Control Disabled>
|
||||
SummaryViewer.messagesDataLabel.text=messages
|
||||
SummaryViewer.callLogsDataLabel.text=callLogs
|
||||
SummaryViewer.contactsDataLabel.text=contacts
|
||||
SummaryViewer.attachmentsDataLabel.text=attachments
|
||||
SummaryViewer.messagesLabel.text=Messages:
|
||||
SummaryViewer.callLogsLabel.text=Call Logs:
|
||||
ThreadRootMessagePanel.showAllCheckBox.text=Show All Messages
|
||||
@ -19,3 +17,7 @@ MessageViewer.showingMessagesLabel.text=Showing Messages for Thread:
|
||||
MessageViewer.backButton.AccessibleContext.accessibleDescription=
|
||||
MessageViewer.backButton.text=Threads
|
||||
MessageViewer.showAllButton.text=All Messages
|
||||
SummaryViewer.thumbnailCntLabel.text=Media Attachments:
|
||||
SummaryViewer.attachmentsLable.text=Total Attachments:
|
||||
SummaryViewer.thumbnailsDataLabel.text=attachments
|
||||
SummaryViewer.attachmentDataLabel.text=count
|
||||
|
@ -37,12 +37,10 @@ MessageViewer_viewMessage_selected=Selected
|
||||
MessageViewer_viewMessage_unthreaded=Unthreaded
|
||||
SummaryViewer.countsPanel.border.title=Counts
|
||||
SummaryViewer.contactsLabel.text=Contacts:
|
||||
SummaryViewer.attachmentsLabel.text=Media Attachments:
|
||||
OutlineViewPanel.messageLabel.text=<Control Disabled>
|
||||
SummaryViewer.messagesDataLabel.text=messages
|
||||
SummaryViewer.callLogsDataLabel.text=callLogs
|
||||
SummaryViewer.contactsDataLabel.text=contacts
|
||||
SummaryViewer.attachmentsDataLabel.text=attachments
|
||||
SummaryViewer.messagesLabel.text=Messages:
|
||||
SummaryViewer.callLogsLabel.text=Call Logs:
|
||||
SummaryViewer_CaseRefNameColumn_Title=Case Name
|
||||
@ -61,3 +59,7 @@ MessageViewer.showingMessagesLabel.text=Showing Messages for Thread:
|
||||
MessageViewer.backButton.AccessibleContext.accessibleDescription=
|
||||
MessageViewer.backButton.text=Threads
|
||||
MessageViewer.showAllButton.text=All Messages
|
||||
SummaryViewer.thumbnailCntLabel.text=Media Attachments:
|
||||
SummaryViewer.attachmentsLable.text=Total Attachments:
|
||||
SummaryViewer.thumbnailsDataLabel.text=attachments
|
||||
SummaryViewer.attachmentDataLabel.text=count
|
||||
|
@ -21,9 +21,10 @@ package org.sleuthkit.autopsy.communications.relationships;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.coreutils.ImageUtils;
|
||||
import org.sleuthkit.datamodel.Account;
|
||||
import org.sleuthkit.datamodel.AccountDeviceInstance;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
@ -51,9 +52,9 @@ public final class SelectionInfo {
|
||||
/**
|
||||
* Wraps the details of the currently selected accounts.
|
||||
*
|
||||
* @param selectedNodes Selected AccountDeviceInstances
|
||||
* @param selectedEdges Selected pairs of AccountDeviceInstances
|
||||
* @param communicationFilter Currently selected communications filters
|
||||
* @param selectedNodes Selected AccountDeviceInstances
|
||||
* @param selectedEdges Selected pairs of AccountDeviceInstances
|
||||
* @param communicationFilter Currently selected communications filters
|
||||
*/
|
||||
public SelectionInfo(Set<AccountDeviceInstance> selectedNodes, Set<GraphEdge> selectedEdges,
|
||||
CommunicationsFilter communicationFilter) {
|
||||
@ -102,6 +103,7 @@ public final class SelectionInfo {
|
||||
* Get the set of relationship sources from the case database
|
||||
*
|
||||
* @return the relationship sources (may be empty)
|
||||
*
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
Set<Content> getRelationshipSources() throws TskCoreException {
|
||||
@ -131,7 +133,7 @@ public final class SelectionInfo {
|
||||
}
|
||||
|
||||
public Set<BlackboardArtifact> getArtifacts() {
|
||||
if(accountArtifacts == null) {
|
||||
if (accountArtifacts == null) {
|
||||
accountArtifacts = new HashSet<>();
|
||||
|
||||
try {
|
||||
@ -149,45 +151,54 @@ public final class SelectionInfo {
|
||||
}
|
||||
|
||||
public SelectionSummary getSummary() {
|
||||
if(summary == null) {
|
||||
if (summary == null) {
|
||||
summary = new SelectionSummary();
|
||||
}
|
||||
|
||||
return summary;
|
||||
}
|
||||
|
||||
final class SelectionSummary{
|
||||
final class SelectionSummary {
|
||||
|
||||
int attachmentCnt;
|
||||
int messagesCnt;
|
||||
int emailCnt;
|
||||
int callLogCnt;
|
||||
int contactsCnt;
|
||||
int mediaCnt;
|
||||
|
||||
SelectionSummary() {
|
||||
getCounts();
|
||||
}
|
||||
|
||||
private void getCounts(){
|
||||
for(BlackboardArtifact artifact: getArtifacts()) {
|
||||
private void getCounts() {
|
||||
for (BlackboardArtifact artifact : getArtifacts()) {
|
||||
BlackboardArtifact.ARTIFACT_TYPE fromID = BlackboardArtifact.ARTIFACT_TYPE.fromID(artifact.getArtifactTypeID());
|
||||
if(null != fromID) switch (fromID) {
|
||||
case TSK_EMAIL_MSG:
|
||||
emailCnt++;
|
||||
break;
|
||||
case TSK_CALLLOG:
|
||||
callLogCnt++;
|
||||
break;
|
||||
case TSK_MESSAGE:
|
||||
messagesCnt++;
|
||||
break;
|
||||
case TSK_CONTACT:
|
||||
contactsCnt++;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
if (null != fromID) {
|
||||
switch (fromID) {
|
||||
case TSK_EMAIL_MSG:
|
||||
emailCnt++;
|
||||
break;
|
||||
case TSK_CALLLOG:
|
||||
callLogCnt++;
|
||||
break;
|
||||
case TSK_MESSAGE:
|
||||
messagesCnt++;
|
||||
break;
|
||||
case TSK_CONTACT:
|
||||
contactsCnt++;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
try{
|
||||
attachmentCnt+= artifact.getChildrenCount();
|
||||
try {
|
||||
attachmentCnt += artifact.getChildrenCount();
|
||||
for (Content childContent : artifact.getChildren()) {
|
||||
if (ImageUtils.thumbnailSupported(childContent)) {
|
||||
mediaCnt++;
|
||||
}
|
||||
}
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.WARNING, String.format("Exception thrown "
|
||||
+ "from getChildrenCount artifactID: %d",
|
||||
@ -215,12 +226,17 @@ public final class SelectionInfo {
|
||||
public int getContactsCnt() {
|
||||
return contactsCnt;
|
||||
}
|
||||
|
||||
public int getThumbnailCnt() {
|
||||
return mediaCnt;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility class to represent an edge from the graph visualization.
|
||||
*/
|
||||
public static class GraphEdge {
|
||||
|
||||
AccountDeviceInstance startNode;
|
||||
AccountDeviceInstance endNode;
|
||||
|
||||
|
@ -41,16 +41,18 @@
|
||||
<Component id="messagesLabel" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="callLogsLabel" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="contactsLabel" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="attachmentsLabel" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="thumbnailCntLabel" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="attachmentsLable" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace type="separate" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="attachmentsDataLabel" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="attachmentDataLabel" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="thumbnailsDataLabel" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="contactsDataLabel" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="callLogsDataLabel" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="messagesDataLabel" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace pref="959" max="32767" attributes="0"/>
|
||||
<EmptySpace pref="845" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
@ -74,10 +76,14 @@
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="attachmentsLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="attachmentsDataLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="thumbnailCntLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="thumbnailsDataLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="32767" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="attachmentsLable" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="attachmentDataLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
@ -104,17 +110,17 @@
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="attachmentsLabel">
|
||||
<Component class="javax.swing.JLabel" name="thumbnailCntLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/communications/relationships/Bundle.properties" key="SummaryViewer.attachmentsLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/communications/relationships/Bundle.properties" key="SummaryViewer.thumbnailCntLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="attachmentsDataLabel">
|
||||
<Component class="javax.swing.JLabel" name="thumbnailsDataLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/communications/relationships/Bundle.properties" key="SummaryViewer.attachmentsDataLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/communications/relationships/Bundle.properties" key="SummaryViewer.thumbnailsDataLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
@ -139,6 +145,20 @@
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="attachmentsLable">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/communications/relationships/Bundle.properties" key="SummaryViewer.attachmentsLable.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="attachmentDataLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/communications/relationships/Bundle.properties" key="SummaryViewer.attachmentDataLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Component class="org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel" name="fileReferencesPanel">
|
||||
|
@ -104,10 +104,11 @@ public class SummaryViewer extends javax.swing.JPanel implements RelationshipsVi
|
||||
} else {
|
||||
SelectionSummary summaryDetails = info.getSummary();
|
||||
|
||||
attachmentsDataLabel.setText(Integer.toString(summaryDetails.getAttachmentCnt()));
|
||||
thumbnailsDataLabel.setText(Integer.toString(summaryDetails.getThumbnailCnt()));
|
||||
callLogsDataLabel.setText(Integer.toString(summaryDetails.getCallLogCnt()));
|
||||
contactsDataLabel.setText(Integer.toString(summaryDetails.getContactsCnt()));
|
||||
messagesDataLabel.setText(Integer.toString(summaryDetails.getMessagesCnt() + summaryDetails.getEmailCnt()));
|
||||
attachmentDataLabel.setText(Integer.toString(summaryDetails.getAttachmentCnt()));
|
||||
|
||||
fileReferencesPanel.showOutlineView();
|
||||
|
||||
@ -131,7 +132,7 @@ public class SummaryViewer extends javax.swing.JPanel implements RelationshipsVi
|
||||
@Override
|
||||
public void setEnabled(boolean enabled) {
|
||||
super.setEnabled(enabled);
|
||||
attachmentsLabel.setEnabled(enabled);
|
||||
thumbnailCntLabel.setEnabled(enabled);
|
||||
callLogsLabel.setEnabled(enabled);
|
||||
contactsLabel.setEnabled(enabled);
|
||||
messagesLabel.setEnabled(enabled);
|
||||
@ -144,10 +145,11 @@ public class SummaryViewer extends javax.swing.JPanel implements RelationshipsVi
|
||||
* Clears the text fields and OutlookViews.
|
||||
*/
|
||||
private void clearControls() {
|
||||
attachmentsDataLabel.setText("");
|
||||
thumbnailsDataLabel.setText("");
|
||||
callLogsDataLabel.setText("");
|
||||
contactsDataLabel.setText("");
|
||||
messagesDataLabel.setText("");
|
||||
attachmentDataLabel.setText("");
|
||||
|
||||
fileReferencesPanel.setNode(new AbstractNode(Children.LEAF));
|
||||
caseReferencesPanel.setNode(new AbstractNode(Children.LEAF));
|
||||
@ -187,11 +189,13 @@ public class SummaryViewer extends javax.swing.JPanel implements RelationshipsVi
|
||||
contactsLabel = new javax.swing.JLabel();
|
||||
messagesLabel = new javax.swing.JLabel();
|
||||
callLogsLabel = new javax.swing.JLabel();
|
||||
attachmentsLabel = new javax.swing.JLabel();
|
||||
attachmentsDataLabel = new javax.swing.JLabel();
|
||||
thumbnailCntLabel = new javax.swing.JLabel();
|
||||
thumbnailsDataLabel = new javax.swing.JLabel();
|
||||
messagesDataLabel = new javax.swing.JLabel();
|
||||
callLogsDataLabel = new javax.swing.JLabel();
|
||||
contactsDataLabel = new javax.swing.JLabel();
|
||||
attachmentsLable = new javax.swing.JLabel();
|
||||
attachmentDataLabel = new javax.swing.JLabel();
|
||||
fileReferencesPanel = new org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel();
|
||||
caseReferencesPanel = new org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel();
|
||||
|
||||
@ -205,9 +209,9 @@ public class SummaryViewer extends javax.swing.JPanel implements RelationshipsVi
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(callLogsLabel, org.openide.util.NbBundle.getMessage(SummaryViewer.class, "SummaryViewer.callLogsLabel.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(attachmentsLabel, org.openide.util.NbBundle.getMessage(SummaryViewer.class, "SummaryViewer.attachmentsLabel.text")); // NOI18N
|
||||
org.openide.awt.Mnemonics.setLocalizedText(thumbnailCntLabel, org.openide.util.NbBundle.getMessage(SummaryViewer.class, "SummaryViewer.thumbnailCntLabel.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(attachmentsDataLabel, org.openide.util.NbBundle.getMessage(SummaryViewer.class, "SummaryViewer.attachmentsDataLabel.text")); // NOI18N
|
||||
org.openide.awt.Mnemonics.setLocalizedText(thumbnailsDataLabel, org.openide.util.NbBundle.getMessage(SummaryViewer.class, "SummaryViewer.thumbnailsDataLabel.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(messagesDataLabel, org.openide.util.NbBundle.getMessage(SummaryViewer.class, "SummaryViewer.messagesDataLabel.text")); // NOI18N
|
||||
|
||||
@ -215,6 +219,10 @@ public class SummaryViewer extends javax.swing.JPanel implements RelationshipsVi
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(contactsDataLabel, org.openide.util.NbBundle.getMessage(SummaryViewer.class, "SummaryViewer.contactsDataLabel.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(attachmentsLable, org.openide.util.NbBundle.getMessage(SummaryViewer.class, "SummaryViewer.attachmentsLable.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(attachmentDataLabel, org.openide.util.NbBundle.getMessage(SummaryViewer.class, "SummaryViewer.attachmentDataLabel.text")); // NOI18N
|
||||
|
||||
javax.swing.GroupLayout countsPanelLayout = new javax.swing.GroupLayout(countsPanel);
|
||||
countsPanel.setLayout(countsPanelLayout);
|
||||
countsPanelLayout.setHorizontalGroup(
|
||||
@ -225,14 +233,16 @@ public class SummaryViewer extends javax.swing.JPanel implements RelationshipsVi
|
||||
.addComponent(messagesLabel)
|
||||
.addComponent(callLogsLabel)
|
||||
.addComponent(contactsLabel)
|
||||
.addComponent(attachmentsLabel))
|
||||
.addComponent(thumbnailCntLabel)
|
||||
.addComponent(attachmentsLable))
|
||||
.addGap(18, 18, 18)
|
||||
.addGroup(countsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(attachmentsDataLabel)
|
||||
.addComponent(attachmentDataLabel)
|
||||
.addComponent(thumbnailsDataLabel)
|
||||
.addComponent(contactsDataLabel)
|
||||
.addComponent(callLogsDataLabel)
|
||||
.addComponent(messagesDataLabel))
|
||||
.addContainerGap(959, Short.MAX_VALUE))
|
||||
.addContainerGap(845, Short.MAX_VALUE))
|
||||
);
|
||||
countsPanelLayout.setVerticalGroup(
|
||||
countsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
@ -251,9 +261,12 @@ public class SummaryViewer extends javax.swing.JPanel implements RelationshipsVi
|
||||
.addComponent(contactsDataLabel))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(countsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(attachmentsLabel)
|
||||
.addComponent(attachmentsDataLabel))
|
||||
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
.addComponent(thumbnailCntLabel)
|
||||
.addComponent(thumbnailsDataLabel))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addGroup(countsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(attachmentsLable)
|
||||
.addComponent(attachmentDataLabel)))
|
||||
);
|
||||
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
@ -287,8 +300,8 @@ public class SummaryViewer extends javax.swing.JPanel implements RelationshipsVi
|
||||
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JLabel attachmentsDataLabel;
|
||||
private javax.swing.JLabel attachmentsLabel;
|
||||
private javax.swing.JLabel attachmentDataLabel;
|
||||
private javax.swing.JLabel attachmentsLable;
|
||||
private javax.swing.JLabel callLogsDataLabel;
|
||||
private javax.swing.JLabel callLogsLabel;
|
||||
private org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel caseReferencesPanel;
|
||||
@ -298,6 +311,8 @@ public class SummaryViewer extends javax.swing.JPanel implements RelationshipsVi
|
||||
private org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel fileReferencesPanel;
|
||||
private javax.swing.JLabel messagesDataLabel;
|
||||
private javax.swing.JLabel messagesLabel;
|
||||
private javax.swing.JLabel thumbnailCntLabel;
|
||||
private javax.swing.JLabel thumbnailsDataLabel;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
|
@ -22,7 +22,7 @@ import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.netbeans.api.sendopts.CommandException;
|
||||
import org.netbeans.spi.sendopts.Env;
|
||||
import org.netbeans.spi.sendopts.Option;
|
||||
|
@ -436,7 +436,7 @@
|
||||
<folder name="Windows2">
|
||||
<folder name="Components">
|
||||
<file name="DirectoryTreeTopComponent.settings" url="DirectoryTreeTopComponentSettings.xml"/>
|
||||
<!-- <file name="DataContentTopComponent.settings" url="DataContentTopComponentSettings.xml" /> -->
|
||||
<file name="DataContentTopComponent.settings" url="DataContentTopComponentSettings.xml" />
|
||||
<file name="IngestMessageTopComponent.settings" url="IngestMessageTopComponentSettings.xml"/>
|
||||
</folder>
|
||||
<folder name="Modes">
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011-2018 Basis Technology Corp.
|
||||
* Copyright 2011-2019 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -40,9 +40,6 @@ import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
* startup).
|
||||
*/
|
||||
// Registered as a service provider in layer.xml
|
||||
//@TopComponent.Description(preferredID = "DataContentTopComponent")
|
||||
//@TopComponent.Registration(mode = "output", openAtStartup = true)
|
||||
//@TopComponent.OpenActionRegistration(displayName = "#CTL_DataContentAction", preferredID = "DataContentTopComponent")
|
||||
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
||||
public final class DataContentTopComponent extends TopComponent implements DataContent, ExplorerManager.Provider {
|
||||
|
||||
@ -125,15 +122,18 @@ public final class DataContentTopComponent extends TopComponent implements DataC
|
||||
public static synchronized DataContentTopComponent findInstance() {
|
||||
TopComponent win = WindowManager.getDefault().findTopComponent(PREFERRED_ID);
|
||||
if (win == null) {
|
||||
logger.warning("Cannot find " + PREFERRED_ID + " component. It will not be located properly in the window system."); //NON-NLS
|
||||
logger.log(Level.INFO, "Cannot find " + PREFERRED_ID + " component. It will "
|
||||
+ "not be located properly in the window system."); //NON-NLS
|
||||
return getDefault();
|
||||
}
|
||||
|
||||
if (win instanceof DataContentTopComponent) {
|
||||
return (DataContentTopComponent) win;
|
||||
}
|
||||
logger.warning(
|
||||
"There seem to be multiple components with the '" + PREFERRED_ID //NON-NLS
|
||||
|
||||
logger.log(Level.INFO, "There seem to be multiple components with the '" + PREFERRED_ID //NON-NLS
|
||||
+ "' ID. That is a potential source of errors and unexpected behavior."); //NON-NLS
|
||||
|
||||
return getDefault();
|
||||
}
|
||||
|
||||
|
1321
Core/src/org/sleuthkit/autopsy/coreutils/AppDBParserHelper.java
Normal file
1321
Core/src/org/sleuthkit/autopsy/coreutils/AppDBParserHelper.java
Normal file
File diff suppressed because it is too large
Load Diff
326
Core/src/org/sleuthkit/autopsy/coreutils/AppSQLiteDB.java
Normal file
326
Core/src/org/sleuthkit/autopsy/coreutils/AppSQLiteDB.java
Normal file
@ -0,0 +1,326 @@
|
||||
/*
|
||||
* 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.coreutils;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.casemodule.services.FileManager;
|
||||
import org.sleuthkit.autopsy.casemodule.services.Services;
|
||||
import org.sleuthkit.autopsy.datamodel.ContentUtils;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
import org.sleuthkit.datamodel.ReadContentInputStream;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* An abstraction around an SQLite app DB found in a data source.
|
||||
* This class makes a copy of it, along with any meta files (WAL, SHM),
|
||||
* opens a SQLite connection to it, and runs queries on it.
|
||||
*/
|
||||
public final class AppSQLiteDB implements Closeable {
|
||||
private final Logger logger = Logger.getLogger(AppSQLiteDB.class.getName());
|
||||
|
||||
private final AbstractFile dbAbstractFile; // AbstractFile for the DB file
|
||||
|
||||
private final Connection connection;
|
||||
private final Statement statement;
|
||||
|
||||
|
||||
/**
|
||||
* Class to abstract the abstract file for a DB file and its on disk copy
|
||||
*
|
||||
*/
|
||||
private static final class AppSQLiteDBFileBundle {
|
||||
private final AbstractFile dbAbstractFile;
|
||||
private final File dbFileCopy;
|
||||
|
||||
AppSQLiteDBFileBundle(AbstractFile dbAbstractFile, File dbFileCopy) {
|
||||
this.dbAbstractFile = dbAbstractFile;
|
||||
this.dbFileCopy = dbFileCopy;
|
||||
}
|
||||
|
||||
AbstractFile getAbstractFile() {
|
||||
return dbAbstractFile;
|
||||
}
|
||||
|
||||
File getFileCopy() {
|
||||
return dbFileCopy;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private AppSQLiteDB(AppSQLiteDBFileBundle appSQLiteDBFileBundle) throws ClassNotFoundException, SQLException {
|
||||
this.dbAbstractFile = appSQLiteDBFileBundle.getAbstractFile();
|
||||
|
||||
Class.forName("org.sqlite.JDBC"); //NON-NLS //load JDBC driver
|
||||
connection = DriverManager.getConnection("jdbc:sqlite:" + appSQLiteDBFileBundle.getFileCopy().getPath()); //NON-NLS
|
||||
statement = connection.createStatement();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Looks for the given SQLIte database filename, with matching path substring.
|
||||
* It looks for exact name or a pattern match based on a input parameter.
|
||||
* It makes a copy of each matching file, and creates an instance of
|
||||
* AppSQLiteDB to help query the DB.
|
||||
*
|
||||
* A list of AppSQLiteDB instances is returned, one for each
|
||||
* match found.,
|
||||
* .
|
||||
* @param dataSource data source to search in
|
||||
* @param dbName db file name to search
|
||||
* @param matchExactName whether to look for exact file name or a pattern match
|
||||
* @param parentPathSubstr path substring to match
|
||||
*
|
||||
* @return A list of abstract files matching the specified name and path.
|
||||
* Returns an empty list if no matching database is found.
|
||||
*/
|
||||
public static Collection<AppSQLiteDB> findAppDatabases(DataSource dataSource,
|
||||
String dbName, boolean matchExactName, String parentPathSubstr) {
|
||||
|
||||
List<AppSQLiteDB> appDbs = new ArrayList<> ();
|
||||
try {
|
||||
Collection<AppSQLiteDBFileBundle> dbFileBundles = findAndCopySQLiteDB( dataSource, dbName, matchExactName, parentPathSubstr, false);
|
||||
dbFileBundles.forEach((dbFileBundle) -> {
|
||||
try {
|
||||
AppSQLiteDB appSQLiteDB = new AppSQLiteDB(dbFileBundle);
|
||||
appDbs.add(appSQLiteDB);
|
||||
} catch (ClassNotFoundException | SQLException ex) {
|
||||
Logger.getLogger(AppSQLiteDB.class.getName()).log(Level.SEVERE, String.format("Failed to open a DB connection for file = '%s' and path = '%s'.", dbFileBundle.dbAbstractFile.getName(), dbFileBundle.getFileCopy().getPath()), ex); //NON-NLS
|
||||
}
|
||||
});
|
||||
} catch (TskCoreException ex) {
|
||||
Logger.getLogger(AppSQLiteDB.class.getName()).log(Level.SEVERE, String.format("Error finding App database files with name = '%s' and path = '%s'.", dbName, parentPathSubstr), ex); //NON-NLS
|
||||
}
|
||||
|
||||
return appDbs;
|
||||
}
|
||||
|
||||
public AbstractFile getDBFile() {
|
||||
return this.dbAbstractFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches a database to the current connection.
|
||||
*
|
||||
* Finds the specified database file in the specified folder.
|
||||
* If found, makes copy of the database in the case folder and
|
||||
* run ATTACH DATABASE sql.
|
||||
*
|
||||
* @param dataSource data source in which to look file the db file
|
||||
* @param dbName name of db file to look for
|
||||
* @param dbPath path in which to look for the db file
|
||||
* @param dbAlias alias name to attach the database as
|
||||
*
|
||||
* @return abstract file for the matching db file.
|
||||
* null if no match is found.
|
||||
*
|
||||
* @throws SQLException in case of an SQL error
|
||||
*/
|
||||
public AbstractFile attachDatabase(DataSource dataSource, String dbName,
|
||||
String dbPath, String dbAlias) throws SQLException {
|
||||
try {
|
||||
// find and copy DB files with exact name and path.
|
||||
Collection<AppSQLiteDBFileBundle> dbFileBundles = findAndCopySQLiteDB(dataSource, dbName, true, dbPath, true);
|
||||
if (!dbFileBundles.isEmpty()) {
|
||||
AppSQLiteDBFileBundle dbFileBundle = dbFileBundles.iterator().next();
|
||||
String attachDbSql = String.format("ATTACH DATABASE '%s' AS '%s'", dbFileBundle.getFileCopy().getPath(), dbAlias); //NON-NLS
|
||||
statement.executeUpdate(attachDbSql);
|
||||
|
||||
return dbFileBundle.getAbstractFile();
|
||||
}
|
||||
} catch (TskCoreException ex) {
|
||||
Logger.getLogger(AppSQLiteDB.class.getName()).log(Level.SEVERE, String.format("Error attaching to App database files with name = '%s' and path = '%s'.", dbName, dbPath), ex); //NON-NLS
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds database file with the specified name, makes a copy of the file in the case directory,
|
||||
* and returns the AbstractFile as well as the file copy.
|
||||
*
|
||||
* @param dataSource data source to search in
|
||||
* @param dbName db file name to search
|
||||
* @param matchExactName whether to look for exact file name or a pattern match
|
||||
* @param dbPath path to match
|
||||
* @param matchExactName whether to look for exact path name or a substring match
|
||||
*
|
||||
* @return a collection of AppSQLiteDBFileBundle
|
||||
*
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
private static Collection<AppSQLiteDBFileBundle> findAndCopySQLiteDB(DataSource dataSource, String dbName,
|
||||
boolean matchExactName, String dbPath, boolean matchExactPath) throws TskCoreException {
|
||||
|
||||
Case openCase;
|
||||
try {
|
||||
openCase = Case.getCurrentCaseThrows();
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
throw new TskCoreException("Failed to get current case.", ex);
|
||||
}
|
||||
|
||||
List<AppSQLiteDBFileBundle> dbFileBundles = new ArrayList<> ();
|
||||
long fileId = 0;
|
||||
String localDiskPath = "";
|
||||
|
||||
SleuthkitCase skCase = openCase.getSleuthkitCase();
|
||||
String parentPath = dbPath.replace("\\", "/");
|
||||
parentPath = SleuthkitCase.escapeSingleQuotes(parentPath);
|
||||
|
||||
String whereClause;
|
||||
if (matchExactName) {
|
||||
whereClause = String.format("LOWER(name) = LOWER('%s')", dbName);
|
||||
} else {
|
||||
whereClause = String.format("LOWER(name) LIKE LOWER('%%%s%%') AND LOWER(name) NOT LIKE LOWER('%%journal%%')", dbName );
|
||||
}
|
||||
if (matchExactPath) {
|
||||
whereClause += String.format(" AND LOWER(parent_path) = LOWER('%s')", parentPath );
|
||||
} else {
|
||||
whereClause += String.format(" AND LOWER(parent_path) LIKE LOWER('%%%s%%')", parentPath );
|
||||
}
|
||||
whereClause += String.format(" AND data_source_obj_id = %s", dataSource.getId());
|
||||
|
||||
List<AbstractFile> absFiles = skCase.findAllFilesWhere(whereClause);
|
||||
for (AbstractFile absFile : absFiles) {
|
||||
try {
|
||||
localDiskPath = openCase.getTempDirectory()
|
||||
+ File.separator + absFile.getId() + absFile.getName();
|
||||
File jFile = new java.io.File(localDiskPath);
|
||||
fileId = absFile.getId();
|
||||
ContentUtils.writeToFile(absFile, jFile);
|
||||
|
||||
//Find and copy both WAL and SHM meta files
|
||||
findAndCopySQLiteMetaFile(absFile, absFile.getName() + "-wal");
|
||||
findAndCopySQLiteMetaFile(absFile, absFile.getName() + "-shm");
|
||||
|
||||
AppSQLiteDBFileBundle dbFileBundle = new AppSQLiteDBFileBundle(absFile, jFile);
|
||||
dbFileBundles.add(dbFileBundle);
|
||||
|
||||
} catch (ReadContentInputStream.ReadContentInputStreamException ex) {
|
||||
Logger.getLogger(AppSQLiteDB.class.getName()).log(Level.WARNING, String.format("Error reading content from file '%s' (id=%d).", absFile.getName(), fileId), ex); //NON-NLS
|
||||
} catch (IOException | NoCurrentCaseException | TskCoreException ex) {
|
||||
Logger.getLogger(AppSQLiteDB.class.getName()).log(Level.SEVERE, String.format("Error creating AppSQLiteDB for file '%s' (id=%d) to copied to '%s'.", absFile.getName(), fileId, localDiskPath), ex); //NON-NLS
|
||||
}
|
||||
}
|
||||
|
||||
return dbFileBundles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detaches the specified database from the connection
|
||||
*
|
||||
* @param dbAlias alias for database to detach
|
||||
*
|
||||
* @throws SQLException
|
||||
*/
|
||||
public void detachDatabase(String dbAlias) throws SQLException {
|
||||
String detachDbSql = String.format("DETACH DATABASE '%s'", dbAlias);
|
||||
statement.executeUpdate(detachDbSql); //NON-NLS
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Runs the given query on the database and returns result set.
|
||||
|
||||
* @param queryStr SQL string for the query to run
|
||||
*
|
||||
* @return ResultSet from running the query.
|
||||
*
|
||||
* @throws SQLException in case of an error.
|
||||
*
|
||||
*/
|
||||
public ResultSet runQuery(String queryStr) throws SQLException {
|
||||
ResultSet resultSet = null;
|
||||
|
||||
if (null != queryStr) {
|
||||
resultSet = statement.executeQuery(queryStr); //NON-NLS
|
||||
}
|
||||
return resultSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the DB connection
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
|
||||
// Close the DB connection
|
||||
try {
|
||||
statement.close();
|
||||
connection.close();
|
||||
} catch (SQLException e) {
|
||||
logger.log(Level.SEVERE, "Error closing the database", e); //NON-NLS
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Searches for a meta file associated with the give SQLite database. If
|
||||
* found, it copies this file into the temp directory of the current case.
|
||||
*
|
||||
* @param sqliteFile file being processed
|
||||
* @param metaFileName name of meta file to look for
|
||||
*
|
||||
* @throws NoCurrentCaseException Case has been closed.
|
||||
* @throws TskCoreException fileManager cannot find AbstractFile
|
||||
* files.
|
||||
* @throws IOException Issue during writing to file.
|
||||
*/
|
||||
private static void findAndCopySQLiteMetaFile(AbstractFile sqliteFile,
|
||||
String metaFileName) throws NoCurrentCaseException, TskCoreException, IOException {
|
||||
|
||||
Case openCase = Case.getCurrentCaseThrows();
|
||||
SleuthkitCase sleuthkitCase = openCase.getSleuthkitCase();
|
||||
Services services = new Services(sleuthkitCase);
|
||||
FileManager fileManager = services.getFileManager();
|
||||
|
||||
List<AbstractFile> metaFiles = fileManager.findFiles(
|
||||
sqliteFile.getDataSource(), metaFileName,
|
||||
sqliteFile.getParent().getName());
|
||||
|
||||
if (metaFiles != null) {
|
||||
for (AbstractFile metaFile : metaFiles) {
|
||||
String localDiskPath = openCase.getTempDirectory()
|
||||
+ File.separator + sqliteFile.getId() + metaFile.getName();
|
||||
File localMetaFile = new File(localDiskPath);
|
||||
if (!localMetaFile.exists()) {
|
||||
ContentUtils.writeToFile(metaFile, localMetaFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -20,6 +20,8 @@ package org.sleuthkit.autopsy.coreutils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.logging.Level;
|
||||
import org.openide.filesystems.FileObject;
|
||||
import java.nio.file.Files;
|
||||
@ -172,6 +174,18 @@ public class FileUtil {
|
||||
return fileName.replaceAll("[\\p{Cntrl}/:\"*?<>|]+", "_");
|
||||
}
|
||||
|
||||
/**
|
||||
* UTF-8 sanitize and escape special characters in a file name or a file name component
|
||||
*
|
||||
* @param fileName to escape
|
||||
*
|
||||
* @return Sanitized string
|
||||
*/
|
||||
public static String utf8SanitizeFileName(String fileName) {
|
||||
Charset charset = StandardCharsets.UTF_8;
|
||||
return charset.decode(charset.encode(escapeFileName(fileName))).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the current user has read and write access to the dirPath.
|
||||
*
|
||||
|
@ -54,6 +54,7 @@ import static org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode.AbstractF
|
||||
import org.sleuthkit.autopsy.datamodel.BaseChildFactory.NoSuchEventBusException;
|
||||
import org.sleuthkit.autopsy.datamodel.BaseChildFactory.RefreshKeysEvent;
|
||||
import org.sleuthkit.autopsy.ingest.IngestManager;
|
||||
import static org.sleuthkit.autopsy.ingest.IngestManager.IngestModuleEvent.CONTENT_CHANGED;
|
||||
import org.sleuthkit.autopsy.ingest.ModuleContentEvent;
|
||||
import org.sleuthkit.autopsy.texttranslation.NoServiceProviderException;
|
||||
import org.sleuthkit.autopsy.texttranslation.TextTranslationService;
|
||||
@ -77,6 +78,7 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
|
||||
|
||||
private static final Set<Case.Events> CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.CURRENT_CASE,
|
||||
Case.Events.CONTENT_TAG_ADDED, Case.Events.CONTENT_TAG_DELETED, Case.Events.CR_COMMENT_CHANGED);
|
||||
private static final Set<IngestManager.IngestModuleEvent> INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(CONTENT_CHANGED);
|
||||
|
||||
/**
|
||||
* @param abstractFile file to wrap
|
||||
@ -89,7 +91,7 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
|
||||
// If this is an archive file we will listen for ingest events
|
||||
// that will notify us when new content has been identified.
|
||||
if (FileTypeExtensions.getArchiveExtensions().contains(ext)) {
|
||||
IngestManager.getInstance().addIngestModuleEventListener(weakPcl);
|
||||
IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, weakPcl);
|
||||
}
|
||||
}
|
||||
|
||||
@ -330,13 +332,11 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
|
||||
if (EamDb.isEnabled()) {
|
||||
properties.add(new NodeProperty<>(OCCURRENCES.toString(), OCCURRENCES.toString(), VALUE_LOADING, ""));
|
||||
}
|
||||
// Get the SCO columns data in a background task
|
||||
backgroundTasksPool.submit(new GetSCOTask(
|
||||
new WeakReference<>(this), weakPcl));
|
||||
}
|
||||
|
||||
// Get the SCO columns data in a background task
|
||||
backgroundTasksPool.submit(new GetSCOTask(
|
||||
new WeakReference<>(this), weakPcl));
|
||||
|
||||
properties.add(new NodeProperty<>(LOCATION.toString(), LOCATION.toString(), NO_DESCR, getContentPath(content)));
|
||||
properties.add(new NodeProperty<>(MOD_TIME.toString(), MOD_TIME.toString(), NO_DESCR, ContentUtils.getStringTime(content.getMtime(), content)));
|
||||
properties.add(new NodeProperty<>(CHANGED_TIME.toString(), CHANGED_TIME.toString(), NO_DESCR, ContentUtils.getStringTime(content.getCtime(), content)));
|
||||
properties.add(new NodeProperty<>(ACCESS_TIME.toString(), ACCESS_TIME.toString(), NO_DESCR, ContentUtils.getStringTime(content.getAtime(), content)));
|
||||
@ -345,6 +345,7 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
|
||||
properties.add(new NodeProperty<>(FLAGS_DIR.toString(), FLAGS_DIR.toString(), NO_DESCR, content.getDirFlagAsString()));
|
||||
properties.add(new NodeProperty<>(FLAGS_META.toString(), FLAGS_META.toString(), NO_DESCR, content.getMetaFlagsAsString()));
|
||||
properties.add(new NodeProperty<>(KNOWN.toString(), KNOWN.toString(), NO_DESCR, content.getKnown().getName()));
|
||||
properties.add(new NodeProperty<>(LOCATION.toString(), LOCATION.toString(), NO_DESCR, getContentPath(content)));
|
||||
properties.add(new NodeProperty<>(MD5HASH.toString(), MD5HASH.toString(), NO_DESCR, StringUtils.defaultString(content.getMd5Hash())));
|
||||
properties.add(new NodeProperty<>(MIMETYPE.toString(), MIMETYPE.toString(), NO_DESCR, StringUtils.defaultString(content.getMIMEType())));
|
||||
properties.add(new NodeProperty<>(EXTENSION.toString(), EXTENSION.toString(), NO_DESCR, content.getNameExtension()));
|
||||
|
@ -28,7 +28,7 @@ import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import java.util.prefs.PreferenceChangeEvent;
|
||||
import java.util.stream.Collectors;
|
||||
import org.openide.nodes.ChildFactory;
|
||||
|
@ -369,12 +369,11 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
if (EamDb.isEnabled()) {
|
||||
sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_count_name(), Bundle.BlackboardArtifactNode_createSheet_count_displayName(), VALUE_LOADING, ""));
|
||||
}
|
||||
// Get the SCO columns data in a background task
|
||||
backgroundTasksPool.submit(new GetSCOTask(
|
||||
new WeakReference<>(this), weakPcl));
|
||||
}
|
||||
|
||||
// Get the SCO columns data in a background task
|
||||
backgroundTasksPool.submit(new GetSCOTask(
|
||||
new WeakReference<>(this), weakPcl));
|
||||
|
||||
if (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID()) {
|
||||
try {
|
||||
BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT));
|
||||
|
@ -39,6 +39,7 @@ import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.ingest.IngestManager;
|
||||
import static org.sleuthkit.autopsy.ingest.IngestManager.IngestModuleEvent.CONTENT_CHANGED;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.ContentVisitor;
|
||||
@ -190,10 +191,12 @@ public class DeletedContent implements AutopsyVisitableItem {
|
||||
Case.Events.DATA_SOURCE_ADDED,
|
||||
Case.Events.CURRENT_CASE
|
||||
);
|
||||
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED);
|
||||
private static final Set<IngestManager.IngestModuleEvent> INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(CONTENT_CHANGED);
|
||||
|
||||
DeletedContentsChildrenObservable() {
|
||||
IngestManager.getInstance().addIngestJobEventListener(pcl);
|
||||
IngestManager.getInstance().addIngestModuleEventListener(pcl);
|
||||
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl);
|
||||
IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, pcl);
|
||||
Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, pcl);
|
||||
}
|
||||
|
||||
|
@ -63,6 +63,9 @@ public class EmailExtracted implements AutopsyVisitableItem {
|
||||
private static final String MAIL_ACCOUNT = NbBundle.getMessage(EmailExtracted.class, "EmailExtracted.mailAccount.text");
|
||||
private static final String MAIL_FOLDER = NbBundle.getMessage(EmailExtracted.class, "EmailExtracted.mailFolder.text");
|
||||
private static final String MAIL_PATH_SEPARATOR = "/";
|
||||
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED);
|
||||
private static final Set<IngestManager.IngestModuleEvent> INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestModuleEvent.DATA_ADDED);
|
||||
|
||||
/**
|
||||
* Parse the path of the email msg to get the account name and folder in
|
||||
* which the email is contained.
|
||||
@ -88,8 +91,6 @@ public class EmailExtracted implements AutopsyVisitableItem {
|
||||
private final EmailResults emailResults;
|
||||
private final long filteringDSObjId; // 0 if not filtering/grouping by data source
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
@ -102,7 +103,7 @@ public class EmailExtracted implements AutopsyVisitableItem {
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param skCase Case DB
|
||||
* @param skCase Case DB
|
||||
* @param objId Object id of the data source
|
||||
*
|
||||
*/
|
||||
@ -112,11 +113,11 @@ public class EmailExtracted implements AutopsyVisitableItem {
|
||||
emailResults = new EmailResults();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public <T> T accept(AutopsyItemVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
private final class EmailResults extends Observable {
|
||||
|
||||
// NOTE: the map can be accessed by multiple worker threads and needs to be synchronized
|
||||
@ -161,7 +162,7 @@ public class EmailExtracted implements AutopsyVisitableItem {
|
||||
+ " AND blackboard_attributes.artifact_id=blackboard_artifacts.artifact_id" //NON-NLS
|
||||
+ " AND blackboard_artifacts.artifact_type_id=" + artId; //NON-NLS
|
||||
if (filteringDSObjId > 0) {
|
||||
query += " AND blackboard_artifacts.data_source_obj_id = " + filteringDSObjId;
|
||||
query += " AND blackboard_artifacts.data_source_obj_id = " + filteringDSObjId;
|
||||
}
|
||||
|
||||
try (CaseDbQuery dbQuery = skCase.executeQuery(query)) {
|
||||
@ -307,8 +308,8 @@ public class EmailExtracted implements AutopsyVisitableItem {
|
||||
|
||||
@Override
|
||||
protected void addNotify() {
|
||||
IngestManager.getInstance().addIngestJobEventListener(pcl);
|
||||
IngestManager.getInstance().addIngestModuleEventListener(pcl);
|
||||
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl);
|
||||
IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, pcl);
|
||||
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl);
|
||||
emailResults.update();
|
||||
emailResults.addObserver(this);
|
||||
|
@ -26,6 +26,7 @@ import java.util.Comparator;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import org.openide.nodes.ChildFactory;
|
||||
import org.openide.nodes.Children;
|
||||
@ -51,7 +52,6 @@ import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWO
|
||||
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TskException;
|
||||
|
||||
/**
|
||||
* Parent of the "extracted content" artifacts to be displayed in the tree.
|
||||
@ -59,10 +59,12 @@ import org.sleuthkit.datamodel.TskException;
|
||||
*/
|
||||
public class ExtractedContent implements AutopsyVisitableItem {
|
||||
|
||||
private SleuthkitCase skCase; // set to null after case has been closed
|
||||
private Blackboard blackboard;
|
||||
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED);
|
||||
private static final Set<IngestManager.IngestModuleEvent> INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestModuleEvent.DATA_ADDED);
|
||||
public static final String NAME = NbBundle.getMessage(RootNode.class, "ExtractedContentNode.name.text");
|
||||
private final long filteringDSObjId; // 0 if not filtering/grouping by data source
|
||||
private SleuthkitCase skCase; // set to null after case has been closed
|
||||
private Blackboard blackboard;
|
||||
|
||||
/**
|
||||
* Constructs extracted content object
|
||||
@ -77,7 +79,7 @@ public class ExtractedContent implements AutopsyVisitableItem {
|
||||
* Constructs extracted content object
|
||||
*
|
||||
* @param skCase Case DB
|
||||
* @param objId Object id of the parent datasource
|
||||
* @param objId Object id of the parent datasource
|
||||
*/
|
||||
public ExtractedContent(SleuthkitCase skCase, long objId) {
|
||||
this.skCase = skCase;
|
||||
@ -144,8 +146,8 @@ public class ExtractedContent implements AutopsyVisitableItem {
|
||||
return filePath + "gps-search.png"; //NON-NLS
|
||||
} else if (typeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_PROG_RUN.getTypeID()) {
|
||||
return filePath + "installed.png"; //NON-NLS
|
||||
} else if (typeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED.getTypeID() ||
|
||||
typeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_SUSPECTED.getTypeID()) {
|
||||
} else if (typeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED.getTypeID()
|
||||
|| typeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_SUSPECTED.getTypeID()) {
|
||||
return filePath + "encrypted-file.png"; //NON-NLS
|
||||
} else if (typeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_EXT_MISMATCH_DETECTED.getTypeID()) {
|
||||
return filePath + "mismatch-16.png"; //NON-NLS
|
||||
@ -223,7 +225,7 @@ public class ExtractedContent implements AutopsyVisitableItem {
|
||||
// maps the artifact type to its child node
|
||||
private final HashMap<BlackboardArtifact.Type, TypeNode> typeNodeList = new HashMap<>();
|
||||
|
||||
public TypeFactory() {
|
||||
TypeFactory() {
|
||||
super();
|
||||
|
||||
// these are shown in other parts of the UI tree
|
||||
@ -235,7 +237,7 @@ public class ExtractedContent implements AutopsyVisitableItem {
|
||||
doNotShow.add(new BlackboardArtifact.Type(TSK_INTERESTING_ARTIFACT_HIT));
|
||||
doNotShow.add(new BlackboardArtifact.Type(TSK_ACCOUNT));
|
||||
doNotShow.add(new BlackboardArtifact.Type(TSK_DATA_SOURCE_USAGE));
|
||||
doNotShow.add(new BlackboardArtifact.Type(TSK_DOWNLOAD_SOURCE) );
|
||||
doNotShow.add(new BlackboardArtifact.Type(TSK_DOWNLOAD_SOURCE));
|
||||
}
|
||||
|
||||
private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> {
|
||||
@ -288,8 +290,8 @@ public class ExtractedContent implements AutopsyVisitableItem {
|
||||
|
||||
@Override
|
||||
protected void addNotify() {
|
||||
IngestManager.getInstance().addIngestJobEventListener(pcl);
|
||||
IngestManager.getInstance().addIngestModuleEventListener(pcl);
|
||||
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl);
|
||||
IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, pcl);
|
||||
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl);
|
||||
}
|
||||
|
||||
@ -305,9 +307,9 @@ public class ExtractedContent implements AutopsyVisitableItem {
|
||||
protected boolean createKeys(List<BlackboardArtifact.Type> list) {
|
||||
if (skCase != null) {
|
||||
try {
|
||||
List<BlackboardArtifact.Type> types = (filteringDSObjId > 0) ?
|
||||
blackboard.getArtifactTypesInUse(filteringDSObjId) :
|
||||
skCase.getArtifactTypesInUse() ;
|
||||
List<BlackboardArtifact.Type> types = (filteringDSObjId > 0)
|
||||
? blackboard.getArtifactTypesInUse(filteringDSObjId)
|
||||
: skCase.getArtifactTypesInUse();
|
||||
|
||||
types.removeAll(doNotShow);
|
||||
Collections.sort(types,
|
||||
@ -370,10 +372,10 @@ public class ExtractedContent implements AutopsyVisitableItem {
|
||||
// a performance increase might be had by adding a
|
||||
// "getBlackboardArtifactCount()" method to skCase
|
||||
try {
|
||||
this.childCount = (filteringDSObjId > 0) ?
|
||||
blackboard.getArtifactsCount(type.getTypeID(), filteringDSObjId) :
|
||||
skCase.getBlackboardArtifactsTypeCount(type.getTypeID());
|
||||
} catch (TskException ex) {
|
||||
this.childCount = (filteringDSObjId > 0)
|
||||
? blackboard.getArtifactsCount(type.getTypeID(), filteringDSObjId)
|
||||
: skCase.getBlackboardArtifactsTypeCount(type.getTypeID());
|
||||
} catch (TskCoreException ex) {
|
||||
Logger.getLogger(TypeNode.class.getName())
|
||||
.log(Level.WARNING, "Error getting child count", ex); //NON-NLS
|
||||
}
|
||||
@ -425,7 +427,7 @@ public class ExtractedContent implements AutopsyVisitableItem {
|
||||
|
||||
private BlackboardArtifact.Type type;
|
||||
|
||||
public ArtifactFactory(BlackboardArtifact.Type type) {
|
||||
ArtifactFactory(BlackboardArtifact.Type type) {
|
||||
super(type.getTypeName());
|
||||
this.type = type;
|
||||
}
|
||||
@ -480,8 +482,8 @@ public class ExtractedContent implements AutopsyVisitableItem {
|
||||
|
||||
@Override
|
||||
protected void onAdd() {
|
||||
IngestManager.getInstance().addIngestJobEventListener(pcl);
|
||||
IngestManager.getInstance().addIngestModuleEventListener(pcl);
|
||||
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl);
|
||||
IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, pcl);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -502,7 +504,7 @@ public class ExtractedContent implements AutopsyVisitableItem {
|
||||
return (filteringDSObjId > 0)
|
||||
? blackboard.getArtifacts(type.getTypeID(), filteringDSObjId)
|
||||
: skCase.getBlackboardArtifacts(type.getTypeID());
|
||||
} catch (TskException ex) {
|
||||
} catch (TskCoreException ex) {
|
||||
Logger.getLogger(ArtifactFactory.class.getName()).log(Level.SEVERE, "Couldn't get blackboard artifacts from database", ex); //NON-NLS
|
||||
}
|
||||
}
|
||||
|
@ -128,7 +128,7 @@ public class FileNode extends AbstractFsContentNode<AbstractFile> {
|
||||
private void setIcon(AbstractFile file) {
|
||||
if (file.isDirNameFlagSet(TSK_FS_NAME_FLAG_ENUM.UNALLOC)) {
|
||||
if (file.getType().equals(TSK_DB_FILES_TYPE_ENUM.CARVED)) {
|
||||
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/carved-file-icon-16.png"); //NON-NLS
|
||||
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/carved-file-x-icon-16.png"); //NON-NLS
|
||||
} else {
|
||||
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file-icon-deleted.png"); //NON-NLS
|
||||
}
|
||||
|
@ -117,6 +117,7 @@ public class FileSize implements AutopsyVisitableItem {
|
||||
long filteringDataSourceObjId() {
|
||||
return this.filteringDSObjId;
|
||||
}
|
||||
|
||||
/*
|
||||
* Root node. Children are nodes for specific sizes.
|
||||
*/
|
||||
@ -169,7 +170,7 @@ public class FileSize implements AutopsyVisitableItem {
|
||||
public static class FileSizeRootChildren extends ChildFactory<org.sleuthkit.autopsy.datamodel.FileSize.FileSizeFilter> {
|
||||
|
||||
private SleuthkitCase skCase;
|
||||
private final long datasourceObjId;
|
||||
private final long datasourceObjId;
|
||||
private Observable notifier;
|
||||
|
||||
public FileSizeRootChildren(SleuthkitCase skCase, long datasourceObjId) {
|
||||
@ -185,10 +186,12 @@ public class FileSize implements AutopsyVisitableItem {
|
||||
private static final class FileSizeRootChildrenObservable extends Observable {
|
||||
|
||||
private static final Set<Case.Events> CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.DATA_SOURCE_ADDED, Case.Events.CURRENT_CASE);
|
||||
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED);
|
||||
private static final Set<IngestManager.IngestModuleEvent> INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestModuleEvent.CONTENT_CHANGED);
|
||||
|
||||
FileSizeRootChildrenObservable() {
|
||||
IngestManager.getInstance().addIngestJobEventListener(pcl);
|
||||
IngestManager.getInstance().addIngestModuleEventListener(pcl);
|
||||
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl);
|
||||
IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, pcl);
|
||||
Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, pcl);
|
||||
}
|
||||
|
||||
@ -266,7 +269,7 @@ public class FileSize implements AutopsyVisitableItem {
|
||||
*/
|
||||
public class FileSizeNode extends DisplayableItemNode {
|
||||
|
||||
private FileSizeFilter filter;
|
||||
private final FileSizeFilter filter;
|
||||
private final long datasourceObjId;
|
||||
|
||||
// use version with observer instead so that it updates
|
||||
@ -282,9 +285,10 @@ public class FileSize implements AutopsyVisitableItem {
|
||||
*
|
||||
* @param skCase
|
||||
* @param filter
|
||||
* @param o Observable that provides updates when events are
|
||||
* fired
|
||||
* @param datasourceObjId filter by data source, if configured in user preferences
|
||||
* @param o Observable that provides updates when
|
||||
* events are fired
|
||||
* @param datasourceObjId filter by data source, if configured in
|
||||
* user preferences
|
||||
*/
|
||||
FileSizeNode(SleuthkitCase skCase, FileSizeFilter filter, Observable o, long datasourceObjId) {
|
||||
super(Children.create(new FileSizeChildren(filter, skCase, o, datasourceObjId), true), Lookups.singleton(filter.getDisplayName()));
|
||||
@ -360,11 +364,11 @@ public class FileSize implements AutopsyVisitableItem {
|
||||
*/
|
||||
static class FileSizeChildren extends BaseChildFactory<AbstractFile> {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(FileSizeChildren.class.getName());
|
||||
private final SleuthkitCase skCase;
|
||||
private final FileSizeFilter filter;
|
||||
private final Observable notifier;
|
||||
private final long datasourceObjId;
|
||||
private static final Logger logger = Logger.getLogger(FileSizeChildren.class.getName());
|
||||
|
||||
/**
|
||||
*
|
||||
@ -435,7 +439,7 @@ public class FileSize implements AutopsyVisitableItem {
|
||||
|
||||
// filter by datasource if indicated in case preferences
|
||||
if (filteringDSObjId > 0) {
|
||||
query += " AND data_source_obj_id = " + filteringDSObjId;
|
||||
query += " AND data_source_obj_id = " + filteringDSObjId;
|
||||
}
|
||||
|
||||
return query;
|
||||
|
@ -53,6 +53,8 @@ import org.sleuthkit.datamodel.TskData;
|
||||
public final class FileTypesByExtension implements AutopsyVisitableItem {
|
||||
|
||||
private final static Logger logger = Logger.getLogger(FileTypesByExtension.class.getName());
|
||||
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED);
|
||||
private static final Set<IngestManager.IngestModuleEvent> INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestModuleEvent.CONTENT_CHANGED);
|
||||
private final SleuthkitCase skCase;
|
||||
private final FileTypes typesRoot;
|
||||
|
||||
@ -115,8 +117,8 @@ public final class FileTypesByExtension implements AutopsyVisitableItem {
|
||||
}
|
||||
};
|
||||
|
||||
IngestManager.getInstance().addIngestJobEventListener(pcl);
|
||||
IngestManager.getInstance().addIngestModuleEventListener(pcl);
|
||||
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl);
|
||||
IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, pcl);
|
||||
Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, pcl);
|
||||
}
|
||||
|
||||
@ -279,7 +281,7 @@ public final class FileTypesByExtension implements AutopsyVisitableItem {
|
||||
* Node for a specific file type / extension. Children of it will be the
|
||||
* files of that type.
|
||||
*/
|
||||
class FileExtensionNode extends FileTypes.BGCountUpdatingNode {
|
||||
final class FileExtensionNode extends FileTypes.BGCountUpdatingNode {
|
||||
|
||||
private final FileTypesByExtension.SearchFilterInterface filter;
|
||||
|
||||
@ -365,11 +367,11 @@ public final class FileTypesByExtension implements AutopsyVisitableItem {
|
||||
? " AND (known IS NULL OR known != " + TskData.FileKnown.KNOWN.getFileKnownValue() + ")"
|
||||
: " ")
|
||||
+ (filteringDataSourceObjId() > 0
|
||||
? " AND data_source_obj_id = " + filteringDataSourceObjId()
|
||||
: " ")
|
||||
? " AND data_source_obj_id = " + filteringDataSourceObjId()
|
||||
: " ")
|
||||
+ " AND (extension IN (" + filter.getFilter().stream()
|
||||
.map(String::toLowerCase)
|
||||
.map(s -> "'"+StringUtils.substringAfter(s, ".")+"'")
|
||||
.map(s -> "'" + StringUtils.substringAfter(s, ".") + "'")
|
||||
.collect(Collectors.joining(", ")) + "))";
|
||||
}
|
||||
|
||||
@ -384,10 +386,10 @@ public final class FileTypesByExtension implements AutopsyVisitableItem {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param filter Extensions to display
|
||||
* @param filter Extensions to display
|
||||
* @param skCase
|
||||
* @param o Observable that will notify when there could be new
|
||||
* data to display
|
||||
* @param o Observable that will notify when there could be new
|
||||
* data to display
|
||||
* @param nodeName
|
||||
*/
|
||||
private FileExtensionNodeChildren(FileTypesByExtension.SearchFilterInterface filter, SleuthkitCase skCase, Observable o, String nodeName) {
|
||||
@ -493,7 +495,7 @@ public final class FileTypesByExtension implements AutopsyVisitableItem {
|
||||
|
||||
@Override
|
||||
public List<String> getFilter() {
|
||||
return this.filter;
|
||||
return Collections.unmodifiableList(this.filter);
|
||||
}
|
||||
}
|
||||
|
||||
@ -550,7 +552,7 @@ public final class FileTypesByExtension implements AutopsyVisitableItem {
|
||||
|
||||
@Override
|
||||
public List<String> getFilter() {
|
||||
return this.filter;
|
||||
return Collections.unmodifiableList(this.filter);
|
||||
}
|
||||
}
|
||||
|
||||
@ -597,7 +599,7 @@ public final class FileTypesByExtension implements AutopsyVisitableItem {
|
||||
|
||||
@Override
|
||||
public List<String> getFilter() {
|
||||
return this.filter;
|
||||
return Collections.unmodifiableList(this.filter);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -61,7 +61,7 @@ import org.sleuthkit.datamodel.TskData;
|
||||
public final class FileTypesByMimeType extends Observable implements AutopsyVisitableItem {
|
||||
|
||||
private final static Logger logger = Logger.getLogger(FileTypesByMimeType.class.getName());
|
||||
|
||||
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED);
|
||||
private final SleuthkitCase skCase;
|
||||
/**
|
||||
* The nodes of this tree will be determined dynamically by the mimetypes
|
||||
@ -101,7 +101,7 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
|
||||
+ TskData.TSK_DB_FILES_TYPE_ENUM.LOCAL.ordinal()
|
||||
+ (hideSlackFilesInViewsTree() ? "" : ("," + TskData.TSK_DB_FILES_TYPE_ENUM.SLACK.ordinal()))
|
||||
+ "))"
|
||||
+ ( (filteringDataSourceObjId() > 0) ? " AND data_source_obj_id = " + this.filteringDataSourceObjId() : " ")
|
||||
+ ((filteringDataSourceObjId() > 0) ? " AND data_source_obj_id = " + this.filteringDataSourceObjId() : " ")
|
||||
+ (hideKnownFilesInViewsTree() ? (" AND (known IS NULL OR known != " + TskData.FileKnown.KNOWN.getFileKnownValue() + ")") : "");
|
||||
}
|
||||
|
||||
@ -180,7 +180,7 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
|
||||
}
|
||||
}
|
||||
};
|
||||
IngestManager.getInstance().addIngestJobEventListener(pcl);
|
||||
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl);
|
||||
Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, pcl);
|
||||
populateHashMap();
|
||||
}
|
||||
@ -370,7 +370,7 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
|
||||
* Node which represents the media sub type in the By MIME type tree, the
|
||||
* media subtype is the portion of the MIME type following the /.
|
||||
*/
|
||||
class MediaSubTypeNode extends FileTypes.BGCountUpdatingNode {
|
||||
final class MediaSubTypeNode extends FileTypes.BGCountUpdatingNode {
|
||||
|
||||
@NbBundle.Messages({"FileTypesByMimeTypeNode.createSheet.mediaSubtype.name=Subtype",
|
||||
"FileTypesByMimeTypeNode.createSheet.mediaSubtype.displayName=Subtype",
|
||||
|
@ -51,7 +51,6 @@ import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TskException;
|
||||
|
||||
/**
|
||||
* Hash set hits node support. Inner classes have all of the nodes in the tree.
|
||||
@ -61,15 +60,16 @@ public class HashsetHits implements AutopsyVisitableItem {
|
||||
private static final String HASHSET_HITS = BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getLabel();
|
||||
private static final String DISPLAY_NAME = BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getDisplayName();
|
||||
private static final Logger logger = Logger.getLogger(HashsetHits.class.getName());
|
||||
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED);
|
||||
private static final Set<IngestManager.IngestModuleEvent> INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestModuleEvent.DATA_ADDED);
|
||||
private SleuthkitCase skCase;
|
||||
private final HashsetResults hashsetResults;
|
||||
private final long filteringDSObjId; // 0 if not filtering/grouping by data source
|
||||
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param skCase Case DB
|
||||
* @param skCase Case DB
|
||||
*
|
||||
*/
|
||||
public HashsetHits(SleuthkitCase skCase) {
|
||||
@ -79,7 +79,7 @@ public class HashsetHits implements AutopsyVisitableItem {
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param skCase Case DB
|
||||
* @param skCase Case DB
|
||||
* @param objId Object id of the data source
|
||||
*
|
||||
*/
|
||||
@ -141,7 +141,7 @@ public class HashsetHits implements AutopsyVisitableItem {
|
||||
+ " AND blackboard_attributes.artifact_id=blackboard_artifacts.artifact_id" //NON-NLS
|
||||
+ " AND blackboard_artifacts.artifact_type_id=" + artId; //NON-NLS
|
||||
if (filteringDSObjId > 0) {
|
||||
query += " AND blackboard_artifacts.data_source_obj_id = " + filteringDSObjId;
|
||||
query += " AND blackboard_artifacts.data_source_obj_id = " + filteringDSObjId;
|
||||
}
|
||||
|
||||
try (CaseDbQuery dbQuery = skCase.executeQuery(query)) {
|
||||
@ -151,7 +151,7 @@ public class HashsetHits implements AutopsyVisitableItem {
|
||||
String setName = resultSet.getString("value_text"); //NON-NLS
|
||||
long artifactId = resultSet.getLong("artifact_id"); //NON-NLS
|
||||
if (!hashSetHitsMap.containsKey(setName)) {
|
||||
hashSetHitsMap.put(setName, new HashSet<Long>());
|
||||
hashSetHitsMap.put(setName, new HashSet<>());
|
||||
}
|
||||
hashSetHitsMap.get(setName).add(artifactId);
|
||||
}
|
||||
@ -275,8 +275,8 @@ public class HashsetHits implements AutopsyVisitableItem {
|
||||
|
||||
@Override
|
||||
protected void addNotify() {
|
||||
IngestManager.getInstance().addIngestJobEventListener(pcl);
|
||||
IngestManager.getInstance().addIngestModuleEventListener(pcl);
|
||||
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl);
|
||||
IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, pcl);
|
||||
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl);
|
||||
hashsetResults.update();
|
||||
hashsetResults.addObserver(this);
|
||||
@ -377,8 +377,8 @@ public class HashsetHits implements AutopsyVisitableItem {
|
||||
*/
|
||||
private class HitFactory extends BaseChildFactory<BlackboardArtifact> implements Observer {
|
||||
|
||||
private String hashsetName;
|
||||
private Map<Long, BlackboardArtifact> artifactHits = new HashMap<>();
|
||||
private final String hashsetName;
|
||||
private final Map<Long, BlackboardArtifact> artifactHits = new HashMap<>();
|
||||
|
||||
private HitFactory(String hashsetName) {
|
||||
super(hashsetName);
|
||||
@ -415,7 +415,7 @@ public class HashsetHits implements AutopsyVisitableItem {
|
||||
BlackboardArtifact art = skCase.getBlackboardArtifact(id);
|
||||
artifactHits.put(id, art);
|
||||
}
|
||||
} catch (TskException ex) {
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "TSK Exception occurred", ex); //NON-NLS
|
||||
}
|
||||
});
|
||||
|
@ -26,6 +26,7 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.Action;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
@ -59,6 +60,7 @@ import org.sleuthkit.datamodel.Tag;
|
||||
public class ImageNode extends AbstractContentNode<Image> {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(ImageNode.class.getName());
|
||||
private static final Set<IngestManager.IngestModuleEvent> INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestModuleEvent.CONTENT_CHANGED);
|
||||
|
||||
/**
|
||||
* Helper so that the display name and the name used in building the path
|
||||
@ -84,7 +86,7 @@ public class ImageNode extends AbstractContentNode<Image> {
|
||||
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/hard-drive-icon.jpg"); //NON-NLS
|
||||
|
||||
// Listen for ingest events so that we can detect new added files (e.g. carved)
|
||||
IngestManager.getInstance().addIngestModuleEventListener(pcl);
|
||||
IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, pcl);
|
||||
// Listen for case events so that we can detect when case is closed
|
||||
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl);
|
||||
}
|
||||
@ -117,7 +119,7 @@ public class ImageNode extends AbstractContentNode<Image> {
|
||||
actionsList.add(new RunIngestModulesAction(Collections.<Content>singletonList(content)));
|
||||
actionsList.add(new NewWindowViewAction(
|
||||
NbBundle.getMessage(this.getClass(), "ImageNode.getActions.viewInNewWin.text"), this));
|
||||
return actionsList.toArray(new Action[0]);
|
||||
return actionsList.toArray(new Action[actionsList.size()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -57,6 +57,8 @@ public class InterestingHits implements AutopsyVisitableItem {
|
||||
.getMessage(InterestingHits.class, "InterestingHits.interestingItems.text");
|
||||
private static final String DISPLAY_NAME = NbBundle.getMessage(InterestingHits.class, "InterestingHits.displayName.text");
|
||||
private static final Logger logger = Logger.getLogger(InterestingHits.class.getName());
|
||||
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED);
|
||||
private static final Set<IngestManager.IngestModuleEvent> INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestModuleEvent.DATA_ADDED);
|
||||
private SleuthkitCase skCase;
|
||||
private final InterestingResults interestingResults = new InterestingResults();
|
||||
private final long filteringDSObjId; // 0 if not filtering/grouping by data source
|
||||
@ -64,7 +66,7 @@ public class InterestingHits implements AutopsyVisitableItem {
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param skCase Case DB
|
||||
* @param skCase Case DB
|
||||
*
|
||||
*/
|
||||
public InterestingHits(SleuthkitCase skCase) {
|
||||
@ -74,7 +76,7 @@ public class InterestingHits implements AutopsyVisitableItem {
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param skCase Case DB
|
||||
* @param skCase Case DB
|
||||
* @param objId Object id of the data source
|
||||
*
|
||||
*/
|
||||
@ -132,7 +134,7 @@ public class InterestingHits implements AutopsyVisitableItem {
|
||||
+ " AND blackboard_attributes.artifact_id=blackboard_artifacts.artifact_id" //NON-NLS
|
||||
+ " AND blackboard_artifacts.artifact_type_id=" + artId; //NON-NLS
|
||||
if (filteringDSObjId > 0) {
|
||||
query += " AND blackboard_artifacts.data_source_obj_id = " + filteringDSObjId;
|
||||
query += " AND blackboard_artifacts.data_source_obj_id = " + filteringDSObjId;
|
||||
}
|
||||
|
||||
try (CaseDbQuery dbQuery = skCase.executeQuery(query)) {
|
||||
@ -217,17 +219,17 @@ public class InterestingHits implements AutopsyVisitableItem {
|
||||
if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
|
||||
/**
|
||||
* Checking for a current case is a stop gap measure until a
|
||||
* different way of handling the closing of cases is worked
|
||||
* out. Currently, remote events may be received for a case
|
||||
* that is already closed.
|
||||
* different way of handling the closing of cases is worked out.
|
||||
* Currently, remote events may be received for a case that is
|
||||
* already closed.
|
||||
*/
|
||||
try {
|
||||
Case.getCurrentCaseThrows();
|
||||
/**
|
||||
* Even with the check above, it is still possible that
|
||||
* the case will be closed in a different thread before
|
||||
* this code executes. If that happens, it is possible
|
||||
* for the event to have a null oldValue.
|
||||
* Even with the check above, it is still possible that the
|
||||
* case will be closed in a different thread before this
|
||||
* code executes. If that happens, it is possible for the
|
||||
* event to have a null oldValue.
|
||||
*/
|
||||
ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
|
||||
if (null != eventData && (eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID()
|
||||
@ -243,9 +245,9 @@ public class InterestingHits implements AutopsyVisitableItem {
|
||||
|| eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
|
||||
/**
|
||||
* Checking for a current case is a stop gap measure until a
|
||||
* different way of handling the closing of cases is worked
|
||||
* out. Currently, remote events may be received for a case
|
||||
* that is already closed.
|
||||
* different way of handling the closing of cases is worked out.
|
||||
* Currently, remote events may be received for a case that is
|
||||
* already closed.
|
||||
*/
|
||||
try {
|
||||
Case.getCurrentCaseThrows();
|
||||
@ -266,8 +268,8 @@ public class InterestingHits implements AutopsyVisitableItem {
|
||||
|
||||
@Override
|
||||
protected void addNotify() {
|
||||
IngestManager.getInstance().addIngestJobEventListener(pcl);
|
||||
IngestManager.getInstance().addIngestModuleEventListener(pcl);
|
||||
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl);
|
||||
IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, pcl);
|
||||
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl);
|
||||
interestingResults.update();
|
||||
interestingResults.addObserver(this);
|
||||
|
@ -62,7 +62,8 @@ import org.sleuthkit.datamodel.TskCoreException;
|
||||
public class KeywordHits implements AutopsyVisitableItem {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(KeywordHits.class.getName());
|
||||
|
||||
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED);
|
||||
private static final Set<IngestManager.IngestModuleEvent> INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestModuleEvent.DATA_ADDED);
|
||||
@NbBundle.Messages("KeywordHits.kwHits.text=Keyword Hits")
|
||||
private static final String KEYWORD_HITS = KeywordHits_kwHits_text();
|
||||
@NbBundle.Messages("KeywordHits.simpleLiteralSearch.text=Single Literal Keyword Search")
|
||||
@ -160,14 +161,11 @@ public class KeywordHits implements AutopsyVisitableItem {
|
||||
// doesn't know about Keyword Search NBM
|
||||
if (o1.startsWith("Single Literal Keyword Search")) {
|
||||
return -1;
|
||||
}
|
||||
else if (o2.startsWith("Single Literal Keyword Search")) {
|
||||
} else if (o2.startsWith("Single Literal Keyword Search")) {
|
||||
return 1;
|
||||
}
|
||||
else if (o1.startsWith("Single Regular Expression Search")) {
|
||||
} else if (o1.startsWith("Single Regular Expression Search")) {
|
||||
return -1;
|
||||
}
|
||||
else if (o2.startsWith("Single Regular Expression Search")) {
|
||||
} else if (o2.startsWith("Single Regular Expression Search")) {
|
||||
return 1;
|
||||
}
|
||||
return o1.compareTo(o2);
|
||||
@ -501,8 +499,8 @@ public class KeywordHits implements AutopsyVisitableItem {
|
||||
|
||||
@Override
|
||||
protected void addNotify() {
|
||||
IngestManager.getInstance().addIngestJobEventListener(pcl);
|
||||
IngestManager.getInstance().addIngestModuleEventListener(pcl);
|
||||
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl);
|
||||
IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, pcl);
|
||||
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl);
|
||||
keywordResults.update();
|
||||
super.addNotify();
|
||||
@ -529,6 +527,7 @@ public class KeywordHits implements AutopsyVisitableItem {
|
||||
}
|
||||
|
||||
private abstract class KWHitsNodeBase extends DisplayableItemNode implements Observer {
|
||||
|
||||
private String displayName;
|
||||
|
||||
private KWHitsNodeBase(Children children, Lookup lookup, String displayName) {
|
||||
|
@ -73,7 +73,7 @@ public class LayoutFileNode extends AbstractAbstractFileNode<LayoutFile> {
|
||||
this.setDisplayName(nameForLayoutFile(lf));
|
||||
|
||||
if (lf.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.CARVED)) {
|
||||
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/carved-file-icon-16.png"); //NON-NLS
|
||||
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/carved-file-x-icon-16.png"); //NON-NLS
|
||||
} else if (lf.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.LAYOUT_FILE)) {
|
||||
if (lf.isDirNameFlagSet(TskData.TSK_FS_NAME_FLAG_ENUM.UNALLOC)) {
|
||||
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file-icon-deleted.png"); //NON-NLS
|
||||
|
@ -63,7 +63,7 @@ public class SlackFileNode extends AbstractFsContentNode<AbstractFile> {
|
||||
// set name, display name, and icon
|
||||
if (file.isDirNameFlagSet(TSK_FS_NAME_FLAG_ENUM.UNALLOC)) {
|
||||
if (file.getType().equals(TSK_DB_FILES_TYPE_ENUM.CARVED)) {
|
||||
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/carved-file-icon-16.png"); //NON-NLS
|
||||
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/carved-file-x-icon-16.png"); //NON-NLS
|
||||
} else {
|
||||
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file-icon-deleted.png"); //NON-NLS
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011-2018 Basis Technology Corp.
|
||||
* Copyright 2011-2019 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -54,9 +54,10 @@ public class Tags implements AutopsyVisitableItem {
|
||||
// by a CreateAutopsyNodeVisitor dispatched from the AbstractContentChildren
|
||||
// override of Children.Keys<T>.createNodes().
|
||||
|
||||
private final TagResults tagResults = new TagResults();
|
||||
private final static String DISPLAY_NAME = NbBundle.getMessage(RootNode.class, "TagsNode.displayName.text");
|
||||
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED);
|
||||
private static final String USER_NAME_PROPERTY = "user.name"; //NON-NLS
|
||||
private final TagResults tagResults = new TagResults();
|
||||
private final String ICON_PATH = "org/sleuthkit/autopsy/images/tag-folder-blue-icon-16.png"; //NON-NLS
|
||||
|
||||
private final long filteringDSObjId; // 0 if not filtering/grouping by data source
|
||||
@ -223,8 +224,7 @@ public class Tags implements AutopsyVisitableItem {
|
||||
|
||||
@Override
|
||||
protected void addNotify() {
|
||||
IngestManager.getInstance().addIngestJobEventListener(pcl);
|
||||
IngestManager.getInstance().addIngestModuleEventListener(pcl);
|
||||
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl);
|
||||
Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, pcl);
|
||||
tagResults.update();
|
||||
tagResults.addObserver(this);
|
||||
@ -233,7 +233,6 @@ public class Tags implements AutopsyVisitableItem {
|
||||
@Override
|
||||
protected void removeNotify() {
|
||||
IngestManager.getInstance().removeIngestJobEventListener(pcl);
|
||||
IngestManager.getInstance().removeIngestModuleEventListener(pcl);
|
||||
Case.removeEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, pcl);
|
||||
tagResults.deleteObserver(this);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011-2018 Basis Technology Corp.
|
||||
* Copyright 2011-2019 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -20,8 +20,6 @@ package org.sleuthkit.autopsy.datamodel;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import org.openide.nodes.Sheet;
|
||||
import org.openide.util.NbBundle;
|
||||
@ -119,7 +117,19 @@ public class VirtualDirectoryNode extends SpecialDirectoryNode {
|
||||
}
|
||||
|
||||
//Otherwise default to the AAFN createSheet method.
|
||||
return super.createSheet();
|
||||
Sheet defaultSheet = super.createSheet();
|
||||
Sheet.Set defaultSheetSet = defaultSheet.get(Sheet.PROPERTIES);
|
||||
|
||||
//Pick out the location column
|
||||
//This path should not show because VDs are not part of the data source
|
||||
String locationCol = NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.locationColLbl");
|
||||
for (Property<?> p : defaultSheetSet.getProperties()) {
|
||||
if(locationCol.equals(p.getName())) {
|
||||
defaultSheetSet.remove(p.getName());
|
||||
}
|
||||
}
|
||||
|
||||
return defaultSheet;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -23,6 +23,7 @@ import java.beans.PropertyChangeListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.Action;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
@ -52,6 +53,7 @@ import org.sleuthkit.datamodel.Tag;
|
||||
public class VolumeNode extends AbstractContentNode<Volume> {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(VolumeNode.class.getName());
|
||||
private static final Set<IngestManager.IngestModuleEvent> INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestModuleEvent.CONTENT_CHANGED);
|
||||
|
||||
/**
|
||||
* Helper so that the display name and the name used in building the path
|
||||
@ -81,7 +83,7 @@ public class VolumeNode extends AbstractContentNode<Volume> {
|
||||
|
||||
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/vol-icon.png"); //NON-NLS
|
||||
// Listen for ingest events so that we can detect new added files (e.g. carved)
|
||||
IngestManager.getInstance().addIngestModuleEventListener(pcl);
|
||||
IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, pcl);
|
||||
// Listen for case events so that we can detect when case is closed
|
||||
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl);
|
||||
}
|
||||
@ -159,7 +161,7 @@ public class VolumeNode extends AbstractContentNode<Volume> {
|
||||
NbBundle.getMessage(this.getClass(), "VolumeNode.getActions.viewInNewWin.text"), this));
|
||||
actionsList.addAll(ExplorerNodeActionVisitor.getActions(content));
|
||||
|
||||
return actionsList.toArray(new Action[0]);
|
||||
return actionsList.toArray(new Action[actionsList.size()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011-2018 Basis Technology Corp.
|
||||
* Copyright 2011-2019 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -89,6 +89,8 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(Accounts.class.getName());
|
||||
private static final String ICON_BASE_PATH = "/org/sleuthkit/autopsy/images/"; //NON-NLS
|
||||
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED);
|
||||
private static final Set<IngestManager.IngestModuleEvent> INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestModuleEvent.DATA_ADDED);
|
||||
|
||||
@NbBundle.Messages("AccountsRootNode.name=Accounts")
|
||||
final public static String NAME = Bundle.AccountsRootNode_name();
|
||||
@ -98,7 +100,9 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
|
||||
private final EventBus reviewStatusBus = new EventBus("ReviewStatusBus");
|
||||
|
||||
/* Should rejected accounts be shown in the accounts section of the tree. */
|
||||
/*
|
||||
* Should rejected accounts be shown in the accounts section of the tree.
|
||||
*/
|
||||
private boolean showRejected = false; //NOPMD redundant initializer
|
||||
|
||||
private final RejectAccounts rejectActionInstance;
|
||||
@ -127,7 +131,6 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
this.approveActionInstance = new ApproveAccounts();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public <T> T accept(AutopsyItemVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
@ -190,6 +193,7 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
* Create of keys used by this Children object to represent the child
|
||||
* nodes.
|
||||
*/
|
||||
@Override
|
||||
abstract protected boolean createKeys(List<X> list);
|
||||
|
||||
/**
|
||||
@ -320,14 +324,14 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
|
||||
@Override
|
||||
protected boolean createKeys(List<String> list) {
|
||||
String accountTypesInUseQuery =
|
||||
"SELECT DISTINCT blackboard_attributes.value_text as account_type "
|
||||
String accountTypesInUseQuery
|
||||
= "SELECT DISTINCT blackboard_attributes.value_text as account_type "
|
||||
+ " FROM blackboard_artifacts " //NON-NLS
|
||||
+ " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS
|
||||
+ " WHERE blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID()
|
||||
+ getFilterByDataSourceClause();
|
||||
|
||||
try (SleuthkitCase.CaseDbQuery executeQuery = skCase.executeQuery(accountTypesInUseQuery );
|
||||
try (SleuthkitCase.CaseDbQuery executeQuery = skCase.executeQuery(accountTypesInUseQuery);
|
||||
ResultSet resultSet = executeQuery.getResultSet()) {
|
||||
while (resultSet.next()) {
|
||||
String accountType = resultSet.getString("account_type");
|
||||
@ -368,8 +372,8 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
|
||||
@Override
|
||||
protected void addNotify() {
|
||||
IngestManager.getInstance().addIngestJobEventListener(pcl);
|
||||
IngestManager.getInstance().addIngestModuleEventListener(pcl);
|
||||
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl);
|
||||
IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, pcl);
|
||||
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl);
|
||||
super.addNotify();
|
||||
refresh(true);
|
||||
@ -439,8 +443,8 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
|
||||
@Override
|
||||
protected void addNotify() {
|
||||
IngestManager.getInstance().addIngestJobEventListener(pcl);
|
||||
IngestManager.getInstance().addIngestModuleEventListener(pcl);
|
||||
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl);
|
||||
IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, pcl);
|
||||
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl);
|
||||
super.addNotify();
|
||||
}
|
||||
@ -455,8 +459,8 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
|
||||
@Override
|
||||
protected boolean createKeys(List<Long> list) {
|
||||
String query =
|
||||
"SELECT blackboard_artifacts.artifact_id " //NON-NLS
|
||||
String query
|
||||
= "SELECT blackboard_artifacts.artifact_id " //NON-NLS
|
||||
+ " FROM blackboard_artifacts " //NON-NLS
|
||||
+ " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS
|
||||
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
|
||||
@ -603,8 +607,8 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
|
||||
@Override
|
||||
protected void addNotify() {
|
||||
IngestManager.getInstance().addIngestJobEventListener(pcl);
|
||||
IngestManager.getInstance().addIngestModuleEventListener(pcl);
|
||||
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl);
|
||||
IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, pcl);
|
||||
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl);
|
||||
super.addNotify();
|
||||
}
|
||||
@ -727,8 +731,8 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
|
||||
@Override
|
||||
protected void addNotify() {
|
||||
IngestManager.getInstance().addIngestJobEventListener(pcl);
|
||||
IngestManager.getInstance().addIngestModuleEventListener(pcl);
|
||||
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl);
|
||||
IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, pcl);
|
||||
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl);
|
||||
super.addNotify();
|
||||
}
|
||||
@ -755,8 +759,8 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
|
||||
@Override
|
||||
protected boolean createKeys(List<FileWithCCN> list) {
|
||||
String query =
|
||||
"SELECT blackboard_artifacts.obj_id," //NON-NLS
|
||||
String query
|
||||
= "SELECT blackboard_artifacts.obj_id," //NON-NLS
|
||||
+ " solr_attribute.value_text AS solr_document_id, "; //NON-NLS
|
||||
if (skCase.getDatabaseType().equals(DbType.POSTGRESQL)) {
|
||||
query += " string_agg(blackboard_artifacts.artifact_id::character varying, ',') AS artifact_IDs, " //NON-NLS
|
||||
@ -833,8 +837,8 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
"# {0} - number of children",
|
||||
"Accounts.ByFileNode.displayName=By File ({0})"})
|
||||
private void updateDisplayName() {
|
||||
String query =
|
||||
"SELECT count(*) FROM ( SELECT count(*) AS documents "
|
||||
String query
|
||||
= "SELECT count(*) FROM ( SELECT count(*) AS documents "
|
||||
+ " FROM blackboard_artifacts " //NON-NLS
|
||||
+ " LEFT JOIN blackboard_attributes as solr_attribute ON blackboard_artifacts.artifact_id = solr_attribute.artifact_id " //NON-NLS
|
||||
+ " AND solr_attribute.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_DOCUMENT_ID.getTypeID() //NON-NLS
|
||||
@ -941,8 +945,8 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
|
||||
@Override
|
||||
protected void addNotify() {
|
||||
IngestManager.getInstance().addIngestJobEventListener(pcl);
|
||||
IngestManager.getInstance().addIngestModuleEventListener(pcl);
|
||||
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl);
|
||||
IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, pcl);
|
||||
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl);
|
||||
super.addNotify();
|
||||
}
|
||||
@ -972,8 +976,8 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
|
||||
RangeMap<Integer, BinResult> binRanges = TreeRangeMap.create();
|
||||
|
||||
String query =
|
||||
"SELECT SUBSTR(blackboard_attributes.value_text,1,8) AS BIN, " //NON-NLS
|
||||
String query
|
||||
= "SELECT SUBSTR(blackboard_attributes.value_text,1,8) AS BIN, " //NON-NLS
|
||||
+ " COUNT(blackboard_artifacts.artifact_id) AS count " //NON-NLS
|
||||
+ " FROM blackboard_artifacts " //NON-NLS
|
||||
+ " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id" //NON-NLS
|
||||
@ -1040,8 +1044,8 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
"# {0} - number of children",
|
||||
"Accounts.ByBINNode.displayName=By BIN ({0})"})
|
||||
private void updateDisplayName() {
|
||||
String query =
|
||||
"SELECT count(distinct SUBSTR(blackboard_attributes.value_text,1,8)) AS BINs " //NON-NLS
|
||||
String query
|
||||
= "SELECT count(distinct SUBSTR(blackboard_attributes.value_text,1,8)) AS BINs " //NON-NLS
|
||||
+ " FROM blackboard_artifacts " //NON-NLS
|
||||
+ " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id" //NON-NLS
|
||||
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
|
||||
@ -1171,7 +1175,7 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
* @return the artifact ids of the account artifacts from this file.
|
||||
*/
|
||||
public List<Long> getArtifactIDs() {
|
||||
return artifactIDs;
|
||||
return Collections.unmodifiableList(artifactIDs);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1189,7 +1193,7 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
* @return the status(s) of the account artifacts from this file.
|
||||
*/
|
||||
public Set<BlackboardArtifact.ReviewStatus> getStatuses() {
|
||||
return statuses;
|
||||
return Collections.unmodifiableSet(statuses);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1335,8 +1339,8 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
@Override
|
||||
protected boolean createKeys(List<Long> list) {
|
||||
|
||||
String query =
|
||||
"SELECT blackboard_artifacts.artifact_id " //NON-NLS
|
||||
String query
|
||||
= "SELECT blackboard_artifacts.artifact_id " //NON-NLS
|
||||
+ " FROM blackboard_artifacts " //NON-NLS
|
||||
+ " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS
|
||||
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
|
||||
@ -1383,7 +1387,9 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
|
||||
final public class BINNode extends DisplayableItemNode {
|
||||
|
||||
/** Creates the nodes for the credit card numbers */
|
||||
/**
|
||||
* Creates the nodes for the credit card numbers
|
||||
*/
|
||||
private final BinResult bin;
|
||||
|
||||
private BINNode(BinResult bin) {
|
||||
@ -1407,8 +1413,8 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
}
|
||||
|
||||
private void updateDisplayName() {
|
||||
String query =
|
||||
"SELECT count(blackboard_artifacts.artifact_id ) AS count" //NON-NLS
|
||||
String query
|
||||
= "SELECT count(blackboard_artifacts.artifact_id ) AS count" //NON-NLS
|
||||
+ " FROM blackboard_artifacts " //NON-NLS
|
||||
+ " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS
|
||||
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
|
||||
@ -1549,7 +1555,9 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
return true;
|
||||
}
|
||||
|
||||
/** The number of accounts with this BIN */
|
||||
/**
|
||||
* The number of accounts with this BIN
|
||||
*/
|
||||
private final long count;
|
||||
|
||||
private final BINRange binRange;
|
||||
@ -1726,8 +1734,10 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
|
||||
/* get paths for selected nodes to reselect after applying review
|
||||
* status change */
|
||||
/*
|
||||
* get paths for selected nodes to reselect after applying review
|
||||
* status change
|
||||
*/
|
||||
List<String[]> selectedPaths = Utilities.actionsGlobalContext().lookupAll(Node.class).stream()
|
||||
.map(node -> {
|
||||
String[] createPath;
|
||||
@ -1746,9 +1756,11 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
: siblings.get(Integer.max(indexOf + 1, siblings.size() - 1));
|
||||
createPath = NodeOp.createPath(sibling, null);
|
||||
} else {
|
||||
/* if there are no other siblings to select,
|
||||
/*
|
||||
* if there are no other siblings to select,
|
||||
* just return null, but note we need to filter
|
||||
* this out of stream below */
|
||||
* this out of stream below
|
||||
*/
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
|
@ -0,0 +1,209 @@
|
||||
/*
|
||||
*
|
||||
* 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.datamodel.utils;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.SpecialDirectory;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TskData;
|
||||
|
||||
/**
|
||||
* Utility class for adding local files with specified paths in the data source.
|
||||
* It is currently assumed that the data source is empty to start or that at
|
||||
* least the paths to the files being added do not exist; no checks will be done
|
||||
* to see if folders exist prior to creating them through addLocalFile().
|
||||
*/
|
||||
public class LocalFileImporter {
|
||||
private static final Logger logger = Logger.getLogger(LocalFileImporter.class.getName());
|
||||
|
||||
SleuthkitCase.CaseDbTransaction globalTrans = null;
|
||||
boolean useSingleTransaction = true;
|
||||
SleuthkitCase sleuthkitCase;
|
||||
private final Map<String, SpecialDirectory> localFileDirMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Create a LocalFileImporter.
|
||||
*
|
||||
* @param sleuthkitCase The current SleuthkitCase
|
||||
*/
|
||||
public LocalFileImporter(SleuthkitCase sleuthkitCase) {
|
||||
this.sleuthkitCase = sleuthkitCase;
|
||||
this.useSingleTransaction = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a LocalFileImporter. The caller is responsible for committing
|
||||
* or rolling back the transaction.
|
||||
*
|
||||
* @param sleuthkitCase The current SleuthkitCase
|
||||
* @param trans The open CaseDbTransaction
|
||||
*/
|
||||
public LocalFileImporter(SleuthkitCase sleuthkitCase, SleuthkitCase.CaseDbTransaction trans) {
|
||||
this.sleuthkitCase = sleuthkitCase;
|
||||
this.globalTrans = trans;
|
||||
this.useSingleTransaction = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a local file to the database with the specified parameters. Will create
|
||||
* any necessary parent folders.
|
||||
*
|
||||
* Will not fail if the fileOnDisk does not exist.
|
||||
*
|
||||
* @param fileOnDisk The local file on disk
|
||||
* @param name The name to use in the data source
|
||||
* @param parentPath The path to use in the data source
|
||||
* @param ctime Change time
|
||||
* @param crtime Created time
|
||||
* @param atime Access time
|
||||
* @param mtime Modified time
|
||||
* @param dataSource The data source to add the file to
|
||||
*
|
||||
* @return The AbstractFile that was just created
|
||||
*
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
public AbstractFile addLocalFile(File fileOnDisk, String name, String parentPath,
|
||||
Long ctime, Long crtime, Long atime, Long mtime,
|
||||
DataSource dataSource) throws TskCoreException {
|
||||
|
||||
// Get the parent folder, creating it and any of its parent folders if necessary
|
||||
SpecialDirectory parentDir = getOrMakeDirInDataSource(new File(parentPath), dataSource);
|
||||
|
||||
SleuthkitCase.CaseDbTransaction trans = null;
|
||||
try {
|
||||
if (useSingleTransaction) {
|
||||
trans = globalTrans;
|
||||
} else {
|
||||
trans = sleuthkitCase.beginTransaction();
|
||||
}
|
||||
|
||||
// Try to get the file size
|
||||
long size = 0;
|
||||
if (fileOnDisk.exists()) {
|
||||
size = fileOnDisk.length();
|
||||
}
|
||||
|
||||
// Create the new file
|
||||
AbstractFile file = sleuthkitCase.addLocalFile(name, fileOnDisk.getAbsolutePath(), size,
|
||||
ctime, crtime, atime, mtime,
|
||||
true, TskData.EncodingType.NONE, parentDir, trans);
|
||||
|
||||
if (! useSingleTransaction) {
|
||||
trans.commit();
|
||||
}
|
||||
return file;
|
||||
} catch (TskCoreException ex) {
|
||||
if ((!useSingleTransaction) && (null != trans)) {
|
||||
try {
|
||||
trans.rollback();
|
||||
} catch (TskCoreException ex2) {
|
||||
logger.log(Level.SEVERE, String.format("Failed to rollback transaction after exception: %s", ex.getMessage()), ex2);
|
||||
}
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the SpecialDirectory object corresponding to the given directory, creating
|
||||
* it and its parents as needed.
|
||||
*
|
||||
* @param directory The file to get the SpecialDirectory for
|
||||
* @param dataSource The data source
|
||||
*
|
||||
* @return The SpecialDirectory object corresponding to the given file
|
||||
*
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
private SpecialDirectory getOrMakeDirInDataSource(File directory, Content dataSource) throws TskCoreException {
|
||||
if ((directory == null) || directory.getPath().isEmpty()) {
|
||||
throw new TskCoreException("Can not create directory from null path");
|
||||
}
|
||||
|
||||
// Check if we've already created it
|
||||
if (localFileDirMap.containsKey(directory.toString())) {
|
||||
return localFileDirMap.get(directory.toString());
|
||||
}
|
||||
|
||||
File parent = directory.getParentFile();
|
||||
if (parent == null) {
|
||||
// This is the root of the path and it isn't in the map, so create it
|
||||
SpecialDirectory dir = createLocalFilesDir(dataSource.getId(), directory.getName());
|
||||
localFileDirMap.put(directory.getName(), dir);
|
||||
return dir;
|
||||
|
||||
} else {
|
||||
// Create everything above this in the tree, and then add the parent folder
|
||||
SpecialDirectory parentDir = getOrMakeDirInDataSource(parent, dataSource);
|
||||
SpecialDirectory dir = createLocalFilesDir(parentDir.getId(), directory.getName());
|
||||
localFileDirMap.put(directory.getPath(), dir);
|
||||
return dir;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new LocalDirectory
|
||||
*
|
||||
* @param parentId The object ID for parent
|
||||
* @param name The name of the new local directory
|
||||
*
|
||||
* @return The new LocalDirectory
|
||||
*
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
private SpecialDirectory createLocalFilesDir(long parentId, String name) throws TskCoreException {
|
||||
SleuthkitCase.CaseDbTransaction trans = null;
|
||||
|
||||
try {
|
||||
if (useSingleTransaction) {
|
||||
trans = globalTrans;
|
||||
} else {
|
||||
trans = sleuthkitCase.beginTransaction();
|
||||
}
|
||||
SpecialDirectory dir;
|
||||
|
||||
dir = sleuthkitCase.addLocalDirectory(parentId, name, trans);
|
||||
|
||||
if (! useSingleTransaction) {
|
||||
trans.commit();
|
||||
}
|
||||
return dir;
|
||||
} catch (TskCoreException ex) {
|
||||
if (( !useSingleTransaction) && (null != trans)) {
|
||||
try {
|
||||
trans.rollback();
|
||||
} catch (TskCoreException ex2) {
|
||||
logger.log(Level.SEVERE, String.format("Failed to rollback transaction after exception: %s", ex.getMessage()), ex2);
|
||||
}
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -179,9 +179,7 @@ public class DataResultFilterNode extends FilterNode {
|
||||
newPs.setShortDescription(ps.getShortDescription());
|
||||
|
||||
newPs.put(ps.getProperties());
|
||||
if (newPs.remove(AbstractFsContentNode.HIDE_PARENT) != null) {
|
||||
newPs.remove(AbstractFilePropertyType.LOCATION.toString());
|
||||
}
|
||||
newPs.remove(AbstractFsContentNode.HIDE_PARENT);
|
||||
propertySets[i] = newPs;
|
||||
}
|
||||
}
|
||||
|
@ -85,7 +85,6 @@ import org.sleuthkit.autopsy.datamodel.Tags;
|
||||
import org.sleuthkit.autopsy.datamodel.ViewsNode;
|
||||
import org.sleuthkit.autopsy.datamodel.accounts.Accounts;
|
||||
import org.sleuthkit.autopsy.datamodel.accounts.BINRange;
|
||||
import org.sleuthkit.autopsy.ingest.IngestManager;
|
||||
import org.sleuthkit.datamodel.Account;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
@ -128,7 +127,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
||||
getTree().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
|
||||
//Hook into the JTree and pre-expand the Views Node and Results node when a user
|
||||
//expands an item in the tree that makes these nodes visible.
|
||||
((ExpansionBeanTreeView )getTree()).addTreeExpansionListener(new TreeExpansionListener() {
|
||||
((ExpansionBeanTreeView) getTree()).addTreeExpansionListener(new TreeExpansionListener() {
|
||||
@Override
|
||||
public void treeExpanded(TreeExpansionEvent event) {
|
||||
//Bail immediately if we are not in the Group By view.
|
||||
@ -238,8 +237,6 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
||||
|
||||
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE, Case.Events.DATA_SOURCE_ADDED), this);
|
||||
this.em.addPropertyChangeListener(this);
|
||||
IngestManager.getInstance().addIngestJobEventListener(this);
|
||||
IngestManager.getInstance().addIngestModuleEventListener(this);
|
||||
}
|
||||
|
||||
public void setDirectoryListingActive() {
|
||||
@ -799,9 +796,6 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
||||
} // change in node selection
|
||||
else if (changed.equals(ExplorerManager.PROP_SELECTED_NODES)) {
|
||||
respondSelection((Node[]) event.getOldValue(), (Node[]) event.getNewValue());
|
||||
} else if (changed.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
|
||||
// nothing to do here.
|
||||
// all nodes should be listening for these events and update accordingly.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -296,6 +296,7 @@
|
||||
<Property name="model" type="javax.swing.ListModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
||||
<Connection code="instancesListModel" type="code"/>
|
||||
</Property>
|
||||
<Property name="selectionMode" type="int" value="0"/>
|
||||
<Property name="cellRenderer" type="javax.swing.ListCellRenderer" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
||||
<Connection code="new InstancesCellRenderer()" type="code"/>
|
||||
</Property>
|
||||
|
@ -427,6 +427,7 @@ public class ResultsPanel extends javax.swing.JPanel {
|
||||
|
||||
instancesList.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(ResultsPanel.class, "ResultsPanel.instancesList.border.title"))); // NOI18N
|
||||
instancesList.setModel(instancesListModel);
|
||||
instancesList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
|
||||
instancesList.setCellRenderer(new InstancesCellRenderer());
|
||||
instancesScrollPane.setViewportView(instancesList);
|
||||
|
||||
|
BIN
Core/src/org/sleuthkit/autopsy/images/carved-file-x-icon-16.png
Normal file
BIN
Core/src/org/sleuthkit/autopsy/images/carved-file-x-icon-16.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.5 KiB |
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011-2018 Basis Technology Corp.
|
||||
* Copyright 2011-2019 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -21,6 +21,8 @@ package org.sleuthkit.autopsy.imagewriter;
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
@ -44,15 +46,16 @@ import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* The ImageWriter class is used to complete VHD copies created from local disks
|
||||
* after the ingest process completes. The AddImageTask for this data source must have included
|
||||
* a non-empty imageWriterPath parameter to enable Image Writer.
|
||||
* after the ingest process completes. The AddImageTask for this data source
|
||||
* must have included a non-empty imageWriterPath parameter to enable Image
|
||||
* Writer.
|
||||
*
|
||||
* Most of the cancellation/cleanup is handled through ImageWriterService
|
||||
*/
|
||||
class ImageWriter implements PropertyChangeListener{
|
||||
|
||||
private final Logger logger = Logger.getLogger(ImageWriter.class.getName());
|
||||
class ImageWriter implements PropertyChangeListener {
|
||||
|
||||
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.DATA_SOURCE_ANALYSIS_COMPLETED);
|
||||
private static final Logger logger = Logger.getLogger(ImageWriter.class.getName());
|
||||
private final Long dataSourceId;
|
||||
private final ImageWriterSettings settings;
|
||||
|
||||
@ -63,18 +66,19 @@ class ImageWriter implements PropertyChangeListener{
|
||||
private boolean isCancelled = false;
|
||||
private boolean isStarted = false;
|
||||
private final Object currentTasksLock = new Object(); // Get this lock before accessing imageHandle, finishTask, progressHandle, progressUpdateTask,
|
||||
// isCancelled, isStarted, or isFinished
|
||||
// isCancelled, isStarted, or isFinished
|
||||
|
||||
private ScheduledThreadPoolExecutor periodicTasksExecutor = null;
|
||||
private final boolean doUI;
|
||||
private SleuthkitCase caseDb = null;
|
||||
|
||||
/**
|
||||
* Create the Image Writer object.
|
||||
* After creation, startListeners() should be called.
|
||||
* Create the Image Writer object. After creation, startListeners() should
|
||||
* be called.
|
||||
*
|
||||
* @param dataSourceId
|
||||
*/
|
||||
ImageWriter(Long dataSourceId, ImageWriterSettings settings){
|
||||
ImageWriter(Long dataSourceId, ImageWriterSettings settings) {
|
||||
this.dataSourceId = dataSourceId;
|
||||
this.settings = settings;
|
||||
doUI = RuntimeProperties.runningWithGUI();
|
||||
@ -82,9 +86,9 @@ class ImageWriter implements PropertyChangeListener{
|
||||
// We save the reference to the sleuthkit case here in case getOpenCase() is set to
|
||||
// null before Image Writer finishes. The user can still elect to wait for image writer
|
||||
// (in ImageWriterService.closeCaseResources) even though the case is closing.
|
||||
try{
|
||||
try {
|
||||
caseDb = Case.getCurrentCaseThrows().getSleuthkitCase();
|
||||
} catch (NoCurrentCaseException ex){
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
logger.log(Level.SEVERE, "Unable to load case. Image writer will be cancelled.");
|
||||
this.isCancelled = true;
|
||||
}
|
||||
@ -93,33 +97,34 @@ class ImageWriter implements PropertyChangeListener{
|
||||
/**
|
||||
* Add this ImageWriter object as a listener to the necessary events
|
||||
*/
|
||||
void subscribeToEvents(){
|
||||
IngestManager.getInstance().addIngestJobEventListener(this);
|
||||
void subscribeToEvents() {
|
||||
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deregister this object from the events. This is ok to call multiple times.
|
||||
* Deregister this object from the events. This is ok to call multiple
|
||||
* times.
|
||||
*/
|
||||
void unsubscribeFromEvents(){
|
||||
void unsubscribeFromEvents() {
|
||||
IngestManager.getInstance().removeIngestJobEventListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the events:
|
||||
* DATA_SOURCE_ANALYSIS_COMPLETED - start the finish image process and clean up after it is complete
|
||||
* Handle the events: DATA_SOURCE_ANALYSIS_COMPLETED - start the finish
|
||||
* image process and clean up after it is complete
|
||||
*/
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
if(evt instanceof DataSourceAnalysisCompletedEvent){
|
||||
if (evt instanceof DataSourceAnalysisCompletedEvent) {
|
||||
|
||||
DataSourceAnalysisCompletedEvent event = (DataSourceAnalysisCompletedEvent)evt;
|
||||
DataSourceAnalysisCompletedEvent event = (DataSourceAnalysisCompletedEvent) evt;
|
||||
|
||||
if(event.getDataSource() != null){
|
||||
if (event.getDataSource() != null) {
|
||||
long imageId = event.getDataSource().getId();
|
||||
String name = event.getDataSource().getName();
|
||||
|
||||
// Check that the event corresponds to this datasource
|
||||
if(imageId != dataSourceId){
|
||||
if (imageId != dataSourceId) {
|
||||
return;
|
||||
}
|
||||
new Thread(() -> {
|
||||
@ -136,25 +141,25 @@ class ImageWriter implements PropertyChangeListener{
|
||||
"# {0} - data source name",
|
||||
"ImageWriter.progressBar.message=Finishing acquisition of {0} (unplug device to cancel)"
|
||||
})
|
||||
private void startFinishImage(String dataSourceName){
|
||||
private void startFinishImage(String dataSourceName) {
|
||||
|
||||
synchronized(currentTasksLock){
|
||||
if(isCancelled){
|
||||
synchronized (currentTasksLock) {
|
||||
if (isCancelled) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If we've already started the finish process for this datasource, return.
|
||||
// Multiple DataSourceAnalysisCompletedEvent events can come from
|
||||
// the same image if more ingest modules are run later
|
||||
if(isStarted){
|
||||
if (isStarted) {
|
||||
return;
|
||||
}
|
||||
|
||||
Image image;
|
||||
try{
|
||||
try {
|
||||
image = Case.getCurrentCaseThrows().getSleuthkitCase().getImageById(dataSourceId);
|
||||
imageHandle = image.getImageHandle();
|
||||
} catch (NoCurrentCaseException ex){
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
// This exception means that getOpenCase() failed because no case was open.
|
||||
// This can happen when the user closes the case while ingest is ongoing - canceling
|
||||
// ingest fires off the DataSourceAnalysisCompletedEvent while the case is in the
|
||||
@ -162,7 +167,7 @@ class ImageWriter implements PropertyChangeListener{
|
||||
logger.log(Level.WARNING, String.format("Case closed before ImageWriter could start the finishing process for %s",
|
||||
dataSourceName));
|
||||
return;
|
||||
} catch (TskCoreException ex){
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Error loading image", ex);
|
||||
return;
|
||||
}
|
||||
@ -170,7 +175,7 @@ class ImageWriter implements PropertyChangeListener{
|
||||
logger.log(Level.INFO, String.format("Finishing VHD image for %s",
|
||||
dataSourceName)); //NON-NLS
|
||||
|
||||
if(doUI){
|
||||
if (doUI) {
|
||||
periodicTasksExecutor = new ScheduledThreadPoolExecutor(1, new ThreadFactoryBuilder().setNameFormat("image-writer-progress-update-%d").build()); //NON-NLS
|
||||
progressHandle = ProgressHandle.createHandle(Bundle.ImageWriter_progressBar_message(dataSourceName));
|
||||
progressHandle.start(100);
|
||||
@ -181,20 +186,20 @@ class ImageWriter implements PropertyChangeListener{
|
||||
// The added complexity here with the Future is because we absolutely need to make sure
|
||||
// the call to finishImageWriter returns before allowing the TSK data structures to be freed
|
||||
// during case close.
|
||||
finishTask = Executors.newSingleThreadExecutor().submit(new Callable<Integer>(){
|
||||
finishTask = Executors.newSingleThreadExecutor().submit(new Callable<Integer>() {
|
||||
@Override
|
||||
public Integer call() throws TskCoreException{
|
||||
try{
|
||||
public Integer call() throws TskCoreException {
|
||||
try {
|
||||
int result = SleuthkitJNI.finishImageWriter(imageHandle);
|
||||
|
||||
// We've decided to always update the path to the VHD, even if it wasn't finished.
|
||||
// This supports the case where an analyst has partially ingested a device
|
||||
// but has to stop before completion. They will at least have part of the image.
|
||||
if(settings.getUpdateDatabasePath()){
|
||||
if (settings.getUpdateDatabasePath()) {
|
||||
caseDb.updateImagePath(settings.getPath(), dataSourceId);
|
||||
}
|
||||
return result;
|
||||
} catch (TskCoreException ex){
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Error finishing VHD image", ex); //NON-NLS
|
||||
return -1;
|
||||
}
|
||||
@ -207,15 +212,15 @@ class ImageWriter implements PropertyChangeListener{
|
||||
|
||||
// Wait for finishImageWriter to complete
|
||||
int result = 0;
|
||||
try{
|
||||
try {
|
||||
// The call to get() can happen multiple times if the user closes the case, which is ok
|
||||
result = finishTask.get();
|
||||
} catch (InterruptedException | ExecutionException ex){
|
||||
} catch (InterruptedException | ExecutionException ex) {
|
||||
logger.log(Level.SEVERE, "Error finishing VHD image", ex); //NON-NLS
|
||||
}
|
||||
|
||||
synchronized(currentTasksLock){
|
||||
if(doUI){
|
||||
synchronized (currentTasksLock) {
|
||||
if (doUI) {
|
||||
// Some of these may be called twice if the user closes the case
|
||||
progressUpdateTask.cancel(true);
|
||||
progressHandle.finish();
|
||||
@ -223,7 +228,7 @@ class ImageWriter implements PropertyChangeListener{
|
||||
}
|
||||
}
|
||||
|
||||
if(result == 0){
|
||||
if (result == 0) {
|
||||
logger.log(Level.INFO, String.format("Successfully finished writing VHD image for %s", dataSourceName)); //NON-NLS
|
||||
} else {
|
||||
logger.log(Level.INFO, String.format("Finished VHD image for %s with errors", dataSourceName)); //NON-NLS
|
||||
@ -231,14 +236,13 @@ class ImageWriter implements PropertyChangeListener{
|
||||
}
|
||||
|
||||
/**
|
||||
* If a task hasn't been started yet, set the cancel flag so it can no longer
|
||||
* start.
|
||||
* This is intended to be used in case close so a job doesn't suddenly start
|
||||
* up during cleanup.
|
||||
* If a task hasn't been started yet, set the cancel flag so it can no
|
||||
* longer start. This is intended to be used in case close so a job doesn't
|
||||
* suddenly start up during cleanup.
|
||||
*/
|
||||
void cancelIfNotStarted(){
|
||||
synchronized(currentTasksLock){
|
||||
if(! isStarted){
|
||||
void cancelIfNotStarted() {
|
||||
synchronized (currentTasksLock) {
|
||||
if (!isStarted) {
|
||||
isCancelled = true;
|
||||
}
|
||||
}
|
||||
@ -246,25 +250,26 @@ class ImageWriter implements PropertyChangeListener{
|
||||
|
||||
/**
|
||||
* Check if the finishTask process is running.
|
||||
* @return true if the finish task is still going on, false if it is finished or
|
||||
* never started
|
||||
*
|
||||
* @return true if the finish task is still going on, false if it is
|
||||
* finished or never started
|
||||
*/
|
||||
boolean jobIsInProgress(){
|
||||
synchronized(currentTasksLock){
|
||||
return((isStarted) && (! finishTask.isDone()));
|
||||
boolean jobIsInProgress() {
|
||||
synchronized (currentTasksLock) {
|
||||
return ((isStarted) && (!finishTask.isDone()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels a single job.
|
||||
* Does not wait for the job to complete. Safe to call with Image Writer in any state.
|
||||
* Cancels a single job. Does not wait for the job to complete. Safe to call
|
||||
* with Image Writer in any state.
|
||||
*/
|
||||
void cancelJob(){
|
||||
synchronized(currentTasksLock){
|
||||
void cancelJob() {
|
||||
synchronized (currentTasksLock) {
|
||||
// All of the following is redundant but safe to call on a complete job
|
||||
isCancelled = true;
|
||||
|
||||
if(isStarted){
|
||||
if (isStarted) {
|
||||
SleuthkitJNI.cancelFinishImage(imageHandle);
|
||||
|
||||
// Stop the progress bar update task.
|
||||
@ -273,7 +278,7 @@ class ImageWriter implements PropertyChangeListener{
|
||||
// when that happens.
|
||||
// Since we've stopped the update task, we'll stop the associated progress
|
||||
// bar now, too.
|
||||
if(doUI){
|
||||
if (doUI) {
|
||||
progressUpdateTask.cancel(true);
|
||||
progressHandle.finish();
|
||||
}
|
||||
@ -282,19 +287,19 @@ class ImageWriter implements PropertyChangeListener{
|
||||
}
|
||||
|
||||
/**
|
||||
* Blocks while all finishImage tasks complete.
|
||||
* Also makes sure the progressUpdateTask is canceled.
|
||||
* Blocks while all finishImage tasks complete. Also makes sure the
|
||||
* progressUpdateTask is canceled.
|
||||
*/
|
||||
void waitForJobToFinish(){
|
||||
synchronized(currentTasksLock){
|
||||
void waitForJobToFinish() {
|
||||
synchronized (currentTasksLock) {
|
||||
// Wait for the finish task to end
|
||||
if(isStarted){
|
||||
try{
|
||||
if (isStarted) {
|
||||
try {
|
||||
finishTask.get();
|
||||
} catch (InterruptedException | ExecutionException ex){
|
||||
} catch (InterruptedException | ExecutionException ex) {
|
||||
Logger.getLogger(ImageWriter.class.getName()).log(Level.SEVERE, "Error finishing VHD image", ex); //NON-NLS
|
||||
}
|
||||
if(doUI){
|
||||
if (doUI) {
|
||||
progressUpdateTask.cancel(true);
|
||||
}
|
||||
}
|
||||
@ -305,10 +310,11 @@ class ImageWriter implements PropertyChangeListener{
|
||||
* Task to query the Sleuthkit processing to get the percentage done.
|
||||
*/
|
||||
private final class ProgressUpdateTask implements Runnable {
|
||||
|
||||
final long imageHandle;
|
||||
final ProgressHandle progressHandle;
|
||||
|
||||
ProgressUpdateTask(ProgressHandle progressHandle, long imageHandle){
|
||||
ProgressUpdateTask(ProgressHandle progressHandle, long imageHandle) {
|
||||
this.imageHandle = imageHandle;
|
||||
this.progressHandle = progressHandle;
|
||||
}
|
||||
|
@ -105,7 +105,7 @@ EditNonFullPathsRulePanel.minSizeCheckbox.text=Minimum size:
|
||||
NewRulePanel.chooseLabel.text=Choose the type of rule
|
||||
ConfigVisualPanel1.configureDriveRadioButton.text_1=Configure selected external drive:
|
||||
ConfigVisualPanel1.configureFolderRadioButton.text_1=Configure in a folder:
|
||||
ConfigVisualPanel1.descriptionTextArea.text=Select a location for the Logical Imager. This location will contain the imaging program and a configuration file. If that location already contains a configuration file, it will be loaded to edit. Imaging results will be saved to this location, so ensure it has enough free space.
|
||||
ConfigVisualPanel1.descriptionTextArea.text=Select a location for the Logical Imager. This location will contain the imaging program and a configuration file. If that location already contains a configuration file, it will be loaded to edit. Imaging results will be saved to this location, so ensure it has enough free space. Drives with FAT format are not supported.
|
||||
ConfigVisualPanel1.refreshButton.text=Refresh
|
||||
ConfigVisualPanel3.saveButton.text=Save
|
||||
ConfigVisualPanel3.configLabel.text=Logical Imager config file save status:
|
||||
@ -122,3 +122,4 @@ EditNonFullPathsRulePanel.fileNamesInfoLabel.text=File names are case insensitiv
|
||||
EditNonFullPathsRulePanel.extensionsInfoLabel.text=Extensions are case insensitive.
|
||||
ConfigVisualPanel2.promptBeforeExit.text=Prompt before exiting imager
|
||||
ConfigVisualPanel2.promptBeforeExit.actionCommand=
|
||||
ConfigVisualPanel2.createVHDCheckBox.text=Create VHD
|
||||
|
@ -26,10 +26,16 @@ ConfigVisualPanel1.chooseFileTitle=Select a Logical Imager configuration
|
||||
# {0} - filename
|
||||
ConfigVisualPanel1.configFileIsEmpty=Configuration file {0} is empty
|
||||
ConfigVisualPanel1.configurationError=Configuration error
|
||||
# {0} - root
|
||||
# {1} - description
|
||||
# {2} - size with unit
|
||||
# {3} - file system
|
||||
ConfigVisualPanel1.driveListItem={0} ({1}) ({2}) - File system: {3}
|
||||
ConfigVisualPanel1.fileNameExtensionFilter=Configuration JSON File
|
||||
ConfigVisualPanel1.invalidConfigJson=Invalid config JSON:
|
||||
ConfigVisualPanel1.messageLabel.noExternalDriveFound=No drive found
|
||||
ConfigVisualPanel1.selectConfigurationFile=Select location
|
||||
ConfigVisualPanel1.unknown=Unknown
|
||||
ConfigVisualPanel2.cancel=Cancel
|
||||
ConfigVisualPanel2.deleteRuleSet=Delete rule
|
||||
ConfigVisualPanel2.deleteRuleSetConfirmation=Delete rule confirmation
|
||||
@ -174,7 +180,7 @@ LogicalImagerConfigDeserializer.unsupportedKeyException=Unsupported key: {0}
|
||||
NewRulePanel.chooseLabel.text=Choose the type of rule
|
||||
ConfigVisualPanel1.configureDriveRadioButton.text_1=Configure selected external drive:
|
||||
ConfigVisualPanel1.configureFolderRadioButton.text_1=Configure in a folder:
|
||||
ConfigVisualPanel1.descriptionTextArea.text=Select a location for the Logical Imager. This location will contain the imaging program and a configuration file. If that location already contains a configuration file, it will be loaded to edit. Imaging results will be saved to this location, so ensure it has enough free space.
|
||||
ConfigVisualPanel1.descriptionTextArea.text=Select a location for the Logical Imager. This location will contain the imaging program and a configuration file. If that location already contains a configuration file, it will be loaded to edit. Imaging results will be saved to this location, so ensure it has enough free space. Drives with FAT format are not supported.
|
||||
ConfigVisualPanel1.refreshButton.text=Refresh
|
||||
ConfigVisualPanel3.saveButton.text=Save
|
||||
ConfigVisualPanel3.configLabel.text=Logical Imager config file save status:
|
||||
@ -191,6 +197,7 @@ EditNonFullPathsRulePanel.fileNamesInfoLabel.text=File names are case insensitiv
|
||||
EditNonFullPathsRulePanel.extensionsInfoLabel.text=Extensions are case insensitive.
|
||||
ConfigVisualPanel2.promptBeforeExit.text=Prompt before exiting imager
|
||||
ConfigVisualPanel2.promptBeforeExit.actionCommand=
|
||||
ConfigVisualPanel2.createVHDCheckBox.text=Create VHD
|
||||
NewRuleSetPanel.attributeRule.description=Search for files based on one or more attributes or metadata fields.
|
||||
NewRuleSetPanel.attributeRule.name=Attribute
|
||||
NewRuleSetPanel.fullPathRule.description=Search for files based on full exact match path.
|
||||
|
@ -29,7 +29,11 @@ import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.FileStore;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.spi.FileSystemProvider;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
@ -241,10 +245,31 @@ final class ConfigVisualPanel1 extends JPanel {
|
||||
firePropertyChange(UPDATE_UI_EVENT_NAME, false, true); // NON-NLS
|
||||
}//GEN-LAST:event_driveListMouseReleasedSelection
|
||||
|
||||
/*
|
||||
* Return the Windows file system name of the drive
|
||||
* @param drive File system drive, should be of the form "C:\"
|
||||
*
|
||||
*/
|
||||
@Messages({"ConfigVisualPanel1.unknown=Unknown"})
|
||||
private String getFileSystemName(String drive) {
|
||||
FileSystem fileSystem = FileSystems.getDefault();
|
||||
FileSystemProvider provider = fileSystem.provider();
|
||||
try {
|
||||
FileStore fileStore = provider.getFileStore(Paths.get(drive));
|
||||
return fileStore.type();
|
||||
} catch (IOException ex) {
|
||||
return Bundle.ConfigVisualPanel1_unknown();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the list of local drives on the current machine
|
||||
*/
|
||||
@Messages({"ConfigVisualPanel1.messageLabel.noExternalDriveFound=No drive found"})
|
||||
@NbBundle.Messages({
|
||||
"ConfigVisualPanel1.messageLabel.noExternalDriveFound=No drive found",
|
||||
"# {0} - root", "# {1} - description", "# {2} - size with unit", "# {3} - file system",
|
||||
"ConfigVisualPanel1.driveListItem={0} ({1}) ({2}) - File system: {3}"
|
||||
})
|
||||
private void refreshDriveList() {
|
||||
List<String> listData = new ArrayList<>();
|
||||
File[] roots = File.listRoots();
|
||||
@ -257,7 +282,8 @@ final class ConfigVisualPanel1 extends JPanel {
|
||||
String description = FileSystemView.getFileSystemView().getSystemTypeDescription(root);
|
||||
long spaceInBytes = root.getTotalSpace();
|
||||
String sizeWithUnit = DriveListUtils.humanReadableByteCount(spaceInBytes, false);
|
||||
listData.add(root + " (" + description + ") (" + sizeWithUnit + ")");
|
||||
String fileSystem = getFileSystemName(root.toString());
|
||||
listData.add(Bundle.ConfigVisualPanel1_driveListItem(root, description, sizeWithUnit, fileSystem));
|
||||
if (firstRemovableDrive == -1) {
|
||||
try {
|
||||
FileStore fileStore = Files.getFileStore(root.toPath());
|
||||
@ -266,7 +292,7 @@ final class ConfigVisualPanel1 extends JPanel {
|
||||
}
|
||||
} catch (IOException ignored) {
|
||||
//unable to get this removable drive for default selection will try and select next removable drive by default
|
||||
logger.log(Level.INFO, "Unable to select first removable drive found", ignored);
|
||||
logger.log(Level.INFO, String.format("Unable to select first removable drive found %s", root.toString())); // NON-NLS
|
||||
}
|
||||
}
|
||||
i++;
|
||||
@ -431,8 +457,7 @@ final class ConfigVisualPanel1 extends JPanel {
|
||||
return UPDATE_UI_EVENT_NAME;
|
||||
}
|
||||
|
||||
void setConfigFilename(String filename
|
||||
) {
|
||||
void setConfigFilename(String filename) {
|
||||
configFileTextField.setText(filename);
|
||||
}
|
||||
|
||||
@ -442,9 +467,11 @@ final class ConfigVisualPanel1 extends JPanel {
|
||||
* @return true if panel has valid settings selected, false otherwise
|
||||
*/
|
||||
boolean isPanelValid() {
|
||||
return !StringUtils.isBlank(getConfigPath()) && ((configureDriveRadioButton.isSelected() && !StringUtils.isBlank(driveList.getSelectedValue()))
|
||||
|| (configureFolderRadioButton.isSelected() && (!configFileTextField.getText().isEmpty())));
|
||||
|
||||
return !StringUtils.isBlank(getConfigPath())
|
||||
&& !(getFileSystemName(getConfigPath().substring(0, 3)).equals("FAT") // NON-NLS
|
||||
|| getFileSystemName(getConfigPath().substring(0, 3)).equals("FAT32")) // NON-NLS
|
||||
&& ((configureDriveRadioButton.isSelected() && !StringUtils.isBlank(driveList.getSelectedValue()))
|
||||
|| (configureFolderRadioButton.isSelected() && (!configFileTextField.getText().isEmpty())));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -103,6 +103,7 @@
|
||||
<Component id="flagEncryptionProgramsCheckBox" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="finalizeImageWriter" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="promptBeforeExit" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="createVHDCheckBox" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
@ -193,7 +194,8 @@
|
||||
<Component id="finalizeImageWriter" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace type="unrelated" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="promptBeforeExit" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="21" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="createVHDCheckBox" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
@ -582,5 +584,15 @@
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="promptBeforeExitActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JCheckBox" name="createVHDCheckBox">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/logicalimager/configuration/Bundle.properties" key="ConfigVisualPanel2.createVHDCheckBox.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="createVHDCheckBoxActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Form>
|
||||
|
@ -111,6 +111,7 @@ final class ConfigVisualPanel2 extends JPanel {
|
||||
maxSizeLabel = new javax.swing.JLabel();
|
||||
maxSizeTextField = new javax.swing.JFormattedTextField();
|
||||
promptBeforeExit = new javax.swing.JCheckBox();
|
||||
createVHDCheckBox = new javax.swing.JCheckBox();
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(modifiedDateLabel, org.openide.util.NbBundle.getMessage(ConfigVisualPanel2.class, "ConfigVisualPanel2.modifiedDateLabel.text")); // NOI18N
|
||||
|
||||
@ -264,6 +265,13 @@ final class ConfigVisualPanel2 extends JPanel {
|
||||
}
|
||||
});
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(createVHDCheckBox, org.openide.util.NbBundle.getMessage(ConfigVisualPanel2.class, "ConfigVisualPanel2.createVHDCheckBox.text")); // NOI18N
|
||||
createVHDCheckBox.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
createVHDCheckBoxActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
@ -338,7 +346,8 @@ final class ConfigVisualPanel2 extends JPanel {
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(flagEncryptionProgramsCheckBox)
|
||||
.addComponent(finalizeImageWriter)
|
||||
.addComponent(promptBeforeExit))
|
||||
.addComponent(promptBeforeExit)
|
||||
.addComponent(createVHDCheckBox))
|
||||
.addGap(0, 0, Short.MAX_VALUE))
|
||||
.addComponent(jSeparator1)))))
|
||||
);
|
||||
@ -412,7 +421,8 @@ final class ConfigVisualPanel2 extends JPanel {
|
||||
.addComponent(finalizeImageWriter)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addComponent(promptBeforeExit)
|
||||
.addGap(21, 21, 21))))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(createVHDCheckBox))))
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
@ -546,6 +556,10 @@ final class ConfigVisualPanel2 extends JPanel {
|
||||
config.setPromptBeforeExit(promptBeforeExit.isSelected());
|
||||
}//GEN-LAST:event_promptBeforeExitActionPerformed
|
||||
|
||||
private void createVHDCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_createVHDCheckBoxActionPerformed
|
||||
config.setCreateVHD(createVHDCheckBox.isSelected());
|
||||
}//GEN-LAST:event_createVHDCheckBoxActionPerformed
|
||||
|
||||
/**
|
||||
* Set the whether the a rule for detecting encryption programs will be
|
||||
* added to the rules in this config
|
||||
@ -588,6 +602,7 @@ final class ConfigVisualPanel2 extends JPanel {
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JTextField configFileTextField;
|
||||
private javax.swing.JCheckBox createVHDCheckBox;
|
||||
private javax.swing.JLabel daysIncludedLabel;
|
||||
private javax.swing.JButton deleteRuleButton;
|
||||
private javax.swing.JTextField descriptionEditTextField;
|
||||
@ -638,13 +653,14 @@ final class ConfigVisualPanel2 extends JPanel {
|
||||
* Update the panel to reflect the rules in the current config
|
||||
*
|
||||
* @param configFilePath path of the config file being modified
|
||||
* @param config contents of the config file being modifed
|
||||
* @param config contents of the config file being modified
|
||||
* @param rowSelectionkey the name of the rule to select by default
|
||||
*/
|
||||
private void updatePanel(String configFilePath, LogicalImagerConfig config, String rowSelectionkey) {
|
||||
configFileTextField.setText(configFilePath);
|
||||
finalizeImageWriter.setSelected(config.isFinalizeImageWriter());
|
||||
promptBeforeExit.setSelected(config.isPromptBeforeExit());
|
||||
createVHDCheckBox.setSelected(config.isCreateVHD());
|
||||
LogicalImagerRuleSet ruleSet = getRuleSetFromCurrentConfig();
|
||||
flagEncryptionProgramsCheckBox.setSelected(ruleSet.find(EncryptionProgramsRule.getName()) != null);
|
||||
RulesTableModel rulesTableModel = new RulesTableModel();
|
||||
|
@ -42,6 +42,10 @@ class LogicalImagerConfig {
|
||||
@Expose(serialize = true)
|
||||
private boolean promptBeforeExit;
|
||||
|
||||
@SerializedName("create-VHD")
|
||||
@Expose(serialize = true)
|
||||
private boolean createVHD;
|
||||
|
||||
@SerializedName("rule-sets")
|
||||
@Expose(serialize = true)
|
||||
private List<LogicalImagerRuleSet> ruleSets;
|
||||
@ -50,6 +54,7 @@ class LogicalImagerConfig {
|
||||
this.version = CURRENT_VERSION;
|
||||
this.finalizeImageWriter = false;
|
||||
this.promptBeforeExit = true;
|
||||
this.createVHD = false;
|
||||
this.ruleSets = new ArrayList<>();
|
||||
}
|
||||
|
||||
@ -60,6 +65,7 @@ class LogicalImagerConfig {
|
||||
this.version = CURRENT_VERSION;
|
||||
this.finalizeImageWriter = finalizeImageWriter;
|
||||
this.promptBeforeExit = true;
|
||||
this.createVHD = false;
|
||||
this.ruleSets = ruleSets;
|
||||
}
|
||||
|
||||
@ -71,6 +77,7 @@ class LogicalImagerConfig {
|
||||
this.version = version;
|
||||
this.finalizeImageWriter = finalizeImageWriter;
|
||||
this.promptBeforeExit = true;
|
||||
this.createVHD = false;
|
||||
this.ruleSets = ruleSets;
|
||||
}
|
||||
|
||||
@ -78,11 +85,13 @@ class LogicalImagerConfig {
|
||||
String version,
|
||||
boolean finalizeImageWriter,
|
||||
boolean promptBeforeExit,
|
||||
boolean createVHD,
|
||||
List<LogicalImagerRuleSet> ruleSets
|
||||
) {
|
||||
this.version = version;
|
||||
this.finalizeImageWriter = finalizeImageWriter;
|
||||
this.promptBeforeExit = promptBeforeExit;
|
||||
this.createVHD = createVHD;
|
||||
this.ruleSets = ruleSets;
|
||||
}
|
||||
|
||||
@ -114,6 +123,14 @@ class LogicalImagerConfig {
|
||||
this.promptBeforeExit = promptBeforeExit;
|
||||
}
|
||||
|
||||
boolean isCreateVHD() {
|
||||
return createVHD;
|
||||
}
|
||||
|
||||
void setCreateVHD(boolean createVHD) {
|
||||
this.createVHD = createVHD;
|
||||
}
|
||||
|
||||
List<LogicalImagerRuleSet> getRuleSets() {
|
||||
return ruleSets;
|
||||
}
|
||||
|
@ -46,6 +46,7 @@ class LogicalImagerConfigDeserializer implements JsonDeserializer<LogicalImagerC
|
||||
String version = LogicalImagerConfig.getCurrentVersion();
|
||||
boolean finalizeImageWriter = false;
|
||||
boolean promptBeforeExit = true;
|
||||
boolean createVHD = false;
|
||||
|
||||
final JsonObject jsonObject = je.getAsJsonObject();
|
||||
final JsonElement jsonVersion = jsonObject.get("version"); // NON-NLS
|
||||
@ -63,6 +64,11 @@ class LogicalImagerConfigDeserializer implements JsonDeserializer<LogicalImagerC
|
||||
promptBeforeExit = jsonPromptBeforeExit.getAsBoolean();
|
||||
}
|
||||
|
||||
final JsonElement jsonCreateVHD = jsonObject.get("create-VHD"); // NON-NLS
|
||||
if (jsonCreateVHD != null) {
|
||||
createVHD = jsonCreateVHD.getAsBoolean();
|
||||
}
|
||||
|
||||
JsonArray asJsonArray = jsonObject.get("rule-sets").getAsJsonArray(); // NON-NLS
|
||||
if (asJsonArray == null) {
|
||||
throw new JsonParseException(Bundle.LogicalImagerConfigDeserializer_missingRuleSetException());
|
||||
@ -80,7 +86,7 @@ class LogicalImagerConfigDeserializer implements JsonDeserializer<LogicalImagerC
|
||||
LogicalImagerRuleSet ruleSet = new LogicalImagerRuleSet(setName, rules);
|
||||
ruleSets.add(ruleSet);
|
||||
}
|
||||
return new LogicalImagerConfig(version, finalizeImageWriter, promptBeforeExit, ruleSets);
|
||||
return new LogicalImagerConfig(version, finalizeImageWriter, promptBeforeExit, createVHD, ruleSets);
|
||||
}
|
||||
|
||||
private List<LogicalImagerRule> parseRules(JsonArray asJsonArray) {
|
||||
|
@ -23,6 +23,7 @@ import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
@ -31,19 +32,22 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import javax.annotation.concurrent.GuardedBy;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.casemodule.services.Blackboard;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.ingest.IngestServices;
|
||||
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
|
||||
import org.sleuthkit.autopsy.datamodel.utils.LocalFileImporter;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.Blackboard;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.LocalFilesDataSource;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
@ -51,12 +55,16 @@ import org.sleuthkit.datamodel.TskCoreException;
|
||||
* SearchResults.txt and users.txt files to report - add an image data source to the
|
||||
* case database.
|
||||
*/
|
||||
final class AddLogicalImageTask extends AddMultipleImageTask {
|
||||
final class AddLogicalImageTask implements Runnable {
|
||||
|
||||
private final static Logger LOGGER = Logger.getLogger(AddLogicalImageTask.class.getName());
|
||||
private final static String SEARCH_RESULTS_TXT = "SearchResults.txt"; //NON-NLS
|
||||
private final static String USERS_TXT = "users.txt"; //NON-NLS
|
||||
private final static String MODULE_NAME = "Logical Imager"; //NON-NLS
|
||||
private final static String ROOT_STR = "root"; // NON-NLS
|
||||
private final static String VHD_EXTENSION = ".vhd"; // NON-NLS
|
||||
private final String deviceId;
|
||||
private final String timeZone;
|
||||
private final File src;
|
||||
private final File dest;
|
||||
private final DataSourceProcessorCallback callback;
|
||||
@ -64,20 +72,30 @@ final class AddLogicalImageTask extends AddMultipleImageTask {
|
||||
private final Blackboard blackboard;
|
||||
private final Case currentCase;
|
||||
|
||||
private volatile boolean cancelled;
|
||||
private volatile boolean createVHD;
|
||||
private long totalFiles;
|
||||
private Map<String, Long> imagePathToObjIdMap;
|
||||
|
||||
private final Object addMultipleImagesLock;
|
||||
@GuardedBy("addMultipleImagesLock")
|
||||
private AddMultipleImagesTask addMultipleImagesTask = null;
|
||||
|
||||
AddLogicalImageTask(String deviceId,
|
||||
List<String> imagePaths,
|
||||
String timeZone,
|
||||
File src, File dest,
|
||||
DataSourceProcessorProgressMonitor progressMonitor,
|
||||
DataSourceProcessorCallback callback
|
||||
) throws NoCurrentCaseException {
|
||||
super(deviceId, imagePaths, timeZone, progressMonitor, callback);
|
||||
this.deviceId = deviceId;
|
||||
this.timeZone = timeZone;
|
||||
this.src = src;
|
||||
this.dest = dest;
|
||||
this.progressMonitor = progressMonitor;
|
||||
this.callback = callback;
|
||||
this.currentCase = Case.getCurrentCase();
|
||||
this.blackboard = this.currentCase.getServices().getBlackboard();
|
||||
this.blackboard = this.currentCase.getServices().getArtifactsBlackboard();
|
||||
this.addMultipleImagesLock = new Object();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -90,16 +108,43 @@ final class AddLogicalImageTask extends AddMultipleImageTask {
|
||||
"# {0} - src", "# {1} - dest", "AddLogicalImageTask.failedToCopyDirectory=Failed to copy directory {0} to {1}",
|
||||
"# {0} - file", "AddLogicalImageTask.addingToReport=Adding {0} to report",
|
||||
"# {0} - file", "AddLogicalImageTask.doneAddingToReport=Done adding {0} to report",
|
||||
"AddLogicalImageTask.ingestionCancelled=Ingestion cancelled",
|
||||
"# {0} - file", "AddLogicalImageTask.failToGetCanonicalPath=Fail to get canonical path for {0}",
|
||||
"# {0} - sparseImageDirectory", "AddLogicalImageTask.directoryDoesNotContainSparseImage=Directory {0} does not contain any images",
|
||||
"AddLogicalImageTask.noCurrentCase=No current case",
|
||||
"AddLogicalImageTask.addingInterestingFiles=Adding search results as interesting files",
|
||||
"AddLogicalImageTask.doneAddingInterestingFiles=Done adding search results as interesting files",
|
||||
"# {0} - SearchResults.txt", "# {1} - directory", "AddLogicalImageTask.cannotFindFiles=Cannot find {0} in {1}",
|
||||
"# {0} - reason", "AddLogicalImageTask.failedToAddInterestingFiles=Failed to add interesting files: {0}"
|
||||
"# {0} - reason", "AddLogicalImageTask.failedToAddInterestingFiles=Failed to add interesting files: {0}",
|
||||
"AddLogicalImageTask.addingExtractedFiles=Adding extracted files",
|
||||
"AddLogicalImageTask.doneAddingExtractedFiles=Done adding extracted files",
|
||||
"# {0} - reason", "AddLogicalImageTask.failedToGetTotalFilesCount=Failed to get total files count: {0}",
|
||||
"AddLogicalImageTask.addImageCancelled=Add image cancelled"
|
||||
})
|
||||
@Override
|
||||
public void run() {
|
||||
List<String> errorList = new ArrayList<>();
|
||||
List<Content> emptyDataSources = new ArrayList<>();
|
||||
|
||||
try {
|
||||
progressMonitor.setProgressText(Bundle.AddLogicalImageTask_copyingImageFromTo(src.toString(), dest.toString()));
|
||||
FileUtils.copyDirectory(src, dest);
|
||||
progressMonitor.setProgressText(Bundle.AddLogicalImageTask_doneCopying());
|
||||
} catch (IOException ex) {
|
||||
// Copy directory failed
|
||||
String msg = Bundle.AddLogicalImageTask_failedToCopyDirectory(src.toString(), dest.toString());
|
||||
errorList.add(msg);
|
||||
}
|
||||
|
||||
if (cancelled) {
|
||||
// Don't delete destination directory once we started adding interesting files.
|
||||
// At this point the database and destination directory are complete.
|
||||
deleteDestinationDirectory();
|
||||
errorList.add(Bundle.AddLogicalImageTask_addImageCancelled());
|
||||
callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources);
|
||||
return;
|
||||
}
|
||||
|
||||
// Add the SearchResults.txt and users.txt to the case report
|
||||
String resultsFilename;
|
||||
if (Paths.get(dest.toString(), SEARCH_RESULTS_TXT).toFile().exists()) {
|
||||
@ -109,6 +154,7 @@ final class AddLogicalImageTask extends AddMultipleImageTask {
|
||||
callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources);
|
||||
return;
|
||||
}
|
||||
|
||||
progressMonitor.setProgressText(Bundle.AddLogicalImageTask_addingToReport(resultsFilename));
|
||||
String status = addReport(Paths.get(dest.toString(), resultsFilename), resultsFilename + " " + src.getName());
|
||||
if (status != null) {
|
||||
@ -127,17 +173,101 @@ final class AddLogicalImageTask extends AddMultipleImageTask {
|
||||
}
|
||||
progressMonitor.setProgressText(Bundle.AddLogicalImageTask_doneAddingToReport(USERS_TXT));
|
||||
|
||||
super.run();
|
||||
if (super.getResult() == DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS) {
|
||||
callback.done(super.getResult(), super.getErrorMessages(), super.getNewDataSources());
|
||||
// Get all VHD files in the dest directory
|
||||
List<String> imagePaths = new ArrayList<>();
|
||||
for (File f : dest.listFiles()) {
|
||||
if (f.getName().endsWith(VHD_EXTENSION)) {
|
||||
try {
|
||||
imagePaths.add(f.getCanonicalPath());
|
||||
} catch (IOException ioe) {
|
||||
String msg = Bundle.AddLogicalImageTask_failToGetCanonicalPath(f.getName());
|
||||
errorList.add(msg);
|
||||
callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Path resultsPath = Paths.get(dest.toString(), resultsFilename);
|
||||
try {
|
||||
totalFiles = Files.lines(resultsPath).count() - 1; // skip the header line
|
||||
} catch (IOException ex) {
|
||||
errorList.add(Bundle.AddLogicalImageTask_failedToGetTotalFilesCount(ex.getMessage()));
|
||||
callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources);
|
||||
return;
|
||||
}
|
||||
|
||||
List<Content> newDataSources = new ArrayList<>();
|
||||
|
||||
if (imagePaths.isEmpty()) {
|
||||
createVHD = false;
|
||||
// No VHD in src directory, try ingest the root directory as local files
|
||||
File root = Paths.get(dest.toString(), ROOT_STR).toFile();
|
||||
if (root.exists() && root.isDirectory()) {
|
||||
imagePaths.add(root.getAbsolutePath());
|
||||
} else {
|
||||
String msg = Bundle.AddLogicalImageTask_directoryDoesNotContainSparseImage(dest);
|
||||
errorList.add(msg);
|
||||
callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
progressMonitor.setProgressText(Bundle.AddLogicalImageTask_addingExtractedFiles());
|
||||
addExtractedFiles(dest, resultsPath, newDataSources);
|
||||
progressMonitor.setProgressText(Bundle.AddLogicalImageTask_doneAddingExtractedFiles());
|
||||
} catch (IOException | TskCoreException ex) {
|
||||
errorList.add(ex.getMessage());
|
||||
LOGGER.log(Level.SEVERE, String.format("Failed to add datasource: %s", ex.getMessage()), ex); // NON-NLS
|
||||
callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
createVHD = true;
|
||||
// ingest the VHDs
|
||||
try {
|
||||
synchronized (addMultipleImagesLock) {
|
||||
if (cancelled) {
|
||||
LOGGER.log(Level.SEVERE, "Add VHD cancelled"); // NON-NLS
|
||||
errorList.add(Bundle.AddLogicalImageTask_addImageCancelled());
|
||||
callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources);
|
||||
return;
|
||||
}
|
||||
addMultipleImagesTask = new AddMultipleImagesTask(deviceId, imagePaths, timeZone , progressMonitor);
|
||||
}
|
||||
addMultipleImagesTask.run();
|
||||
if (addMultipleImagesTask.getResult() == DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS) {
|
||||
LOGGER.log(Level.SEVERE, "Failed to add VHD datasource"); // NON-NLS
|
||||
callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, addMultipleImagesTask.getErrorMessages(), emptyDataSources);
|
||||
return;
|
||||
}
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
String msg = Bundle.AddLogicalImageTask_noCurrentCase();
|
||||
errorList.add(msg);
|
||||
callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (cancelled) {
|
||||
if (!createVHD) {
|
||||
// TODO: When 5453 is fixed, we should be able to delete it when adding VHD.
|
||||
deleteDestinationDirectory();
|
||||
}
|
||||
errorList.add(Bundle.AddLogicalImageTask_addImageCancelled());
|
||||
callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
progressMonitor.setProgressText(Bundle.AddLogicalImageTask_addingInterestingFiles());
|
||||
addInterestingFiles(dest, Paths.get(dest.toString(), resultsFilename));
|
||||
addInterestingFiles(Paths.get(dest.toString(), resultsFilename), createVHD);
|
||||
progressMonitor.setProgressText(Bundle.AddLogicalImageTask_doneAddingInterestingFiles());
|
||||
callback.done(super.getResult(), super.getErrorMessages(), super.getNewDataSources());
|
||||
if (createVHD) {
|
||||
callback.done(addMultipleImagesTask.getResult(), addMultipleImagesTask.getErrorMessages(), addMultipleImagesTask.getNewDataSources());
|
||||
} else {
|
||||
callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.NO_ERRORS, errorList, newDataSources);
|
||||
}
|
||||
} catch (IOException | TskCoreException ex) {
|
||||
errorList.add(Bundle.AddLogicalImageTask_failedToAddInterestingFiles(ex.getMessage()));
|
||||
LOGGER.log(Level.SEVERE, "Failed to add interesting files", ex); // NON-NLS
|
||||
@ -171,44 +301,58 @@ final class AddLogicalImageTask extends AddMultipleImageTask {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to cancel the processing of the input image files. May result in
|
||||
* partial processing of the input.
|
||||
*/
|
||||
void cancelTask() {
|
||||
LOGGER.log(Level.WARNING, "AddLogicalImageTask cancelled, processing may be incomplete"); // NON-NLS
|
||||
synchronized (addMultipleImagesLock) {
|
||||
cancelled = true;
|
||||
if (addMultipleImagesTask != null) {
|
||||
addMultipleImagesTask.cancelTask();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Long> imagePathsToDataSourceObjId(Map<Long, List<String>> imagePaths) {
|
||||
Map<String, Long> imagePathToObjIdMap = new HashMap<>();
|
||||
Map<String, Long> imagePathToObjId = new HashMap<>();
|
||||
for (Map.Entry<Long, List<String>> entry : imagePaths.entrySet()) {
|
||||
Long key = entry.getKey();
|
||||
List<String> names = entry.getValue();
|
||||
for (String name : names) {
|
||||
imagePathToObjIdMap.put(name, key);
|
||||
imagePathToObjId.put(name, key);
|
||||
}
|
||||
}
|
||||
return imagePathToObjIdMap;
|
||||
return imagePathToObjId;
|
||||
}
|
||||
|
||||
@Messages({
|
||||
"# {0} - line number", "# {1} - fields length", "# {2} - expected length", "AddLogicalImageTask.notEnoughFields=File does not contain enough fields at line {0}, got {1}, expecting {2}",
|
||||
"# {0} - target image path", "AddLogicalImageTask.cannotFindDataSourceObjId=Cannot find obj_id in tsk_image_names for {0}"
|
||||
"# {0} - target image path", "AddLogicalImageTask.cannotFindDataSourceObjId=Cannot find obj_id in tsk_image_names for {0}",
|
||||
"# {0} - file number", "# {1} - total files", "AddLogicalImageTask.addingInterestingFile=Adding interesting files ({0}/{1})"
|
||||
})
|
||||
private void addInterestingFiles(File src, Path resultsPath) throws IOException, TskCoreException {
|
||||
Map<Long, List<String>> imagePaths = currentCase.getSleuthkitCase().getImagePaths();
|
||||
Map<String, Long> imagePathToObjIdMap = imagePathsToDataSourceObjId(imagePaths);
|
||||
private void addInterestingFiles(Path resultsPath, boolean createVHD) throws IOException, TskCoreException {
|
||||
Map<Long, List<String>> objIdToimagePathsMap = currentCase.getSleuthkitCase().getImagePaths();
|
||||
imagePathToObjIdMap = imagePathsToDataSourceObjId(objIdToimagePathsMap);
|
||||
|
||||
try (BufferedReader br = new BufferedReader(new InputStreamReader(
|
||||
new FileInputStream(resultsPath.toFile()), "UTF8"))) { // NON-NLS
|
||||
List<BlackboardArtifact> artifacts = new ArrayList<>();
|
||||
String line;
|
||||
br.readLine(); // skip the header line
|
||||
int lineNumber = 2;
|
||||
while ((line = br.readLine()) != null) {
|
||||
if (cancelled) {
|
||||
// Don't delete destination directory once we started adding interesting files.
|
||||
// At this point the database and destination directory are complete.
|
||||
break;
|
||||
}
|
||||
String[] fields = line.split("\t", -1); // NON-NLS
|
||||
if (fields.length != 9) {
|
||||
throw new IOException(Bundle.AddLogicalImageTask_notEnoughFields(lineNumber, fields.length, 9));
|
||||
if (fields.length != 14) {
|
||||
throw new IOException(Bundle.AddLogicalImageTask_notEnoughFields(lineNumber, fields.length, 14));
|
||||
}
|
||||
String vhdFilename = fields[0];
|
||||
|
||||
String targetImagePath = Paths.get(src.toString(), vhdFilename).toString();
|
||||
Long dataSourceObjId = imagePathToObjIdMap.get(targetImagePath);
|
||||
if (dataSourceObjId == null) {
|
||||
throw new TskCoreException(Bundle.AddLogicalImageTask_cannotFindDataSourceObjId(targetImagePath));
|
||||
}
|
||||
|
||||
// String fileSystemOffsetStr = fields[1];
|
||||
String fileMetaAddressStr = fields[2];
|
||||
// String extractStatusStr = fields[3];
|
||||
@ -216,37 +360,151 @@ final class AddLogicalImageTask extends AddMultipleImageTask {
|
||||
String ruleName = fields[5];
|
||||
// String description = fields[6];
|
||||
String filename = fields[7];
|
||||
// String parentPath = fields[8];
|
||||
String parentPath = fields[8];
|
||||
|
||||
String query = String.format("data_source_obj_id = '%s' AND meta_addr = '%s' AND name = '%s'", // NON-NLS
|
||||
dataSourceObjId.toString(), fileMetaAddressStr, filename);
|
||||
if (lineNumber % 100 == 0) {
|
||||
progressMonitor.setProgressText(Bundle.AddLogicalImageTask_addingInterestingFile(lineNumber, totalFiles));
|
||||
}
|
||||
String query = makeQuery(createVHD, vhdFilename, fileMetaAddressStr, parentPath, filename);
|
||||
|
||||
// TODO - findAllFilesWhere should SQL-escape the query
|
||||
List<AbstractFile> matchedFiles = Case.getCurrentCase().getSleuthkitCase().findAllFilesWhere(query);
|
||||
for (AbstractFile file : matchedFiles) {
|
||||
addInterestingFile(file, ruleSetName, ruleName);
|
||||
addInterestingFileToArtifacts(file, ruleSetName, ruleName, artifacts);
|
||||
}
|
||||
lineNumber++;
|
||||
} // end reading file
|
||||
|
||||
try {
|
||||
// index the artifact for keyword search
|
||||
blackboard.postArtifacts(artifacts, MODULE_NAME);
|
||||
} catch (Blackboard.BlackboardException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Unable to post artifacts to blackboard", ex); //NON-NLS
|
||||
}
|
||||
}
|
||||
IngestServices.getInstance().fireModuleDataEvent(new ModuleDataEvent(MODULE_NAME,
|
||||
BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT));
|
||||
}
|
||||
|
||||
private void addInterestingFile(AbstractFile file, String ruleSetName, String ruleName) throws TskCoreException {
|
||||
private void addInterestingFileToArtifacts(AbstractFile file, String ruleSetName, String ruleName, List<BlackboardArtifact> artifacts) throws TskCoreException {
|
||||
Collection<BlackboardAttribute> attributes = new ArrayList<>();
|
||||
BlackboardAttribute setNameAttribute = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, ruleSetName);
|
||||
attributes.add(setNameAttribute);
|
||||
BlackboardAttribute ruleNameAttribute = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CATEGORY, MODULE_NAME, ruleName);
|
||||
attributes.add(ruleNameAttribute);
|
||||
org.sleuthkit.datamodel.Blackboard tskBlackboard = Case.getCurrentCase().getSleuthkitCase().getBlackboard();
|
||||
if (!tskBlackboard.artifactExists(file, BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT, attributes)) {
|
||||
BlackboardArtifact artifact = file.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT);
|
||||
if (!blackboard.artifactExists(file, BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT, attributes)) {
|
||||
BlackboardArtifact artifact = this.currentCase.getSleuthkitCase().newBlackboardArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT, file.getId());
|
||||
artifact.addAttributes(attributes);
|
||||
artifacts.add(artifact);
|
||||
}
|
||||
}
|
||||
|
||||
@Messages({
|
||||
"# {0} - file number", "# {1} - total files", "AddLogicalImageTask.addingExtractedFile=Adding extracted files ({0}/{1})"
|
||||
})
|
||||
private void addExtractedFiles(File src, Path resultsPath, List<Content> newDataSources) throws TskCoreException, IOException {
|
||||
SleuthkitCase skCase = Case.getCurrentCase().getSleuthkitCase();
|
||||
SleuthkitCase.CaseDbTransaction trans = null;
|
||||
|
||||
try {
|
||||
trans = skCase.beginTransaction();
|
||||
LocalFilesDataSource localFilesDataSource = skCase.addLocalFilesDataSource(deviceId, this.src.getName(), timeZone, trans);
|
||||
LocalFileImporter fileImporter = new LocalFileImporter(skCase, trans);
|
||||
|
||||
try (BufferedReader br = new BufferedReader(new InputStreamReader(
|
||||
new FileInputStream(resultsPath.toFile()), "UTF8"))) { // NON-NLS
|
||||
String line;
|
||||
br.readLine(); // skip the header line
|
||||
int lineNumber = 2;
|
||||
while ((line = br.readLine()) != null) {
|
||||
if (cancelled) {
|
||||
rollbackTransaction(trans);
|
||||
return;
|
||||
}
|
||||
String[] fields = line.split("\t", -1); // NON-NLS
|
||||
if (fields.length != 14) {
|
||||
rollbackTransaction(trans);
|
||||
throw new IOException(Bundle.AddLogicalImageTask_notEnoughFields(lineNumber, fields.length, 14));
|
||||
}
|
||||
String vhdFilename = fields[0];
|
||||
// String fileSystemOffsetStr = fields[1];
|
||||
// String fileMetaAddressStr = fields[2];
|
||||
// String extractStatusStr = fields[3];
|
||||
// String ruleSetName = fields[4];
|
||||
// String ruleName = fields[5];
|
||||
// String description = fields[6];
|
||||
String filename = fields[7];
|
||||
String parentPath = fields[8];
|
||||
String extractedFilePath = fields[9];
|
||||
String crtime = fields[10];
|
||||
String mtime = fields[11];
|
||||
String atime = fields[12];
|
||||
String ctime = fields[13];
|
||||
parentPath = ROOT_STR + "/" + vhdFilename + "/" + parentPath;
|
||||
|
||||
if (lineNumber % 100 == 0) {
|
||||
progressMonitor.setProgressText(Bundle.AddLogicalImageTask_addingExtractedFile(lineNumber, totalFiles));
|
||||
}
|
||||
|
||||
//addLocalFile here
|
||||
fileImporter.addLocalFile(
|
||||
Paths.get(src.toString(), extractedFilePath).toFile(),
|
||||
filename,
|
||||
parentPath,
|
||||
Long.parseLong(ctime),
|
||||
Long.parseLong(crtime),
|
||||
Long.parseLong(atime),
|
||||
Long.parseLong(mtime),
|
||||
localFilesDataSource);
|
||||
|
||||
lineNumber++;
|
||||
} // end reading file
|
||||
}
|
||||
trans.commit();
|
||||
newDataSources.add(localFilesDataSource);
|
||||
|
||||
} catch (NumberFormatException | TskCoreException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Error adding extracted files", ex); // NON-NLS
|
||||
rollbackTransaction(trans);
|
||||
throw new TskCoreException("Error adding extracted files", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void rollbackTransaction(SleuthkitCase.CaseDbTransaction trans) throws TskCoreException {
|
||||
if (null != trans) {
|
||||
try {
|
||||
// index the artifact for keyword search
|
||||
blackboard.indexArtifact(artifact);
|
||||
} catch (Blackboard.BlackboardException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Unable to index blackboard artifact " + artifact.getArtifactID(), ex); //NON-NLS
|
||||
trans.rollback();
|
||||
} catch (TskCoreException ex) {
|
||||
LOGGER.log(Level.SEVERE, String.format("Failed to rollback transaction: %s", ex.getMessage()), ex); // NON-NLS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean deleteDestinationDirectory() {
|
||||
try {
|
||||
FileUtils.deleteDirectory(dest);
|
||||
LOGGER.log(Level.INFO, String.format("Cancellation: Deleted directory %s", dest.toString())); // NON-NLS
|
||||
return true;
|
||||
} catch (IOException ex) {
|
||||
LOGGER.log(Level.WARNING, String.format("Cancellation: Failed to delete directory %s", dest.toString()), ex); // NON-NLS
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
String makeQuery(boolean createVHD, String vhdFilename, String fileMetaAddressStr, String parentPath, String filename) throws TskCoreException {
|
||||
String query;
|
||||
if (createVHD) {
|
||||
String targetImagePath = Paths.get(dest.toString(), vhdFilename).toString();
|
||||
Long dataSourceObjId = imagePathToObjIdMap.get(targetImagePath);
|
||||
if (dataSourceObjId == null) {
|
||||
throw new TskCoreException(Bundle.AddLogicalImageTask_cannotFindDataSourceObjId(targetImagePath));
|
||||
}
|
||||
query = String.format("data_source_obj_id = '%s' AND meta_addr = '%s' AND name = '%s'", // NON-NLS
|
||||
dataSourceObjId.toString(), fileMetaAddressStr, filename.replace("'", "''"));
|
||||
} else {
|
||||
String newParentPath = "/" + ROOT_STR + "/" + vhdFilename + "/" + parentPath;
|
||||
query = String.format("name = '%s' AND parent_path = '%s'", // NON-NLS
|
||||
filename.replace("'", "''"), newParentPath.replace("'", "''"));
|
||||
}
|
||||
return query;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -21,10 +21,10 @@ package org.sleuthkit.autopsy.logicalimager.dsp;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import javax.annotation.concurrent.GuardedBy;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback.DataSourceProcessorResult;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
@ -42,27 +42,34 @@ import org.sleuthkit.datamodel.TskFileRange;
|
||||
*
|
||||
*/
|
||||
@Messages({
|
||||
"AddMultipleImageTask.fsTypeUnknownErr=Cannot determine file system type"
|
||||
"AddMultipleImagesTask.fsTypeUnknownErr=Cannot determine file system type"
|
||||
})
|
||||
class AddMultipleImageTask implements Runnable {
|
||||
class AddMultipleImagesTask implements Runnable {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(AddMultipleImageTask.class.getName());
|
||||
public static final String TSK_FS_TYPE_UNKNOWN_ERR_MSG = Bundle.AddMultipleImageTask_fsTypeUnknownErr();
|
||||
private static final Logger LOGGER = Logger.getLogger(AddMultipleImagesTask.class.getName());
|
||||
public static final String TSK_FS_TYPE_UNKNOWN_ERR_MSG = Bundle.AddMultipleImagesTask_fsTypeUnknownErr();
|
||||
private static final long TWO_GB = 2000000000L;
|
||||
private final String deviceId;
|
||||
private final List<String> imageFilePaths;
|
||||
private final String timeZone;
|
||||
private final long chunkSize = TWO_GB;
|
||||
private final DataSourceProcessorProgressMonitor progressMonitor;
|
||||
private final DataSourceProcessorCallback callback;
|
||||
private final Case currentCase;
|
||||
|
||||
private boolean criticalErrorOccurred;
|
||||
private volatile boolean cancelled;
|
||||
|
||||
private List<Content> newDataSources;
|
||||
private List<String> errorMessages;
|
||||
private SleuthkitJNI.CaseDbHandle.AddImageProcess addImageProcess = null;
|
||||
private List<String> errorMessages = new ArrayList<>();
|
||||
private DataSourceProcessorResult result;
|
||||
private List<Content> newDataSources = new ArrayList<>();
|
||||
|
||||
/*
|
||||
* The cancellation requested flag and SleuthKit add image process are
|
||||
* guarded by a lock to synchronize cancelling the process (setting the flag
|
||||
* and calling its stop method) and calling either its commit or revert
|
||||
* method.
|
||||
*/
|
||||
private final Object tskAddImageProcessLock;
|
||||
@GuardedBy("tskAddImageProcessLock")
|
||||
private boolean tskAddImageProcessStopped;
|
||||
|
||||
/**
|
||||
* Constructs a runnable that adds multiple image files to a case database.
|
||||
@ -78,41 +85,57 @@ class AddMultipleImageTask implements Runnable {
|
||||
* java.util.TimeZone.getID.
|
||||
* @param progressMonitor Progress monitor for reporting progress during
|
||||
* processing.
|
||||
* @param callback Callback to call when processing is done.
|
||||
*
|
||||
* @throws NoCurrentCaseException The exception if there is no open case.
|
||||
*/
|
||||
@Messages({
|
||||
"# {0} - file", "AddMultipleImageTask.addingFileAsLogicalFile=Adding: {0} as an unallocated space file.",
|
||||
"# {0} - file", "AddMultipleImagesTask.addingFileAsLogicalFile=Adding: {0} as an unallocated space file.",
|
||||
"# {0} - deviceId", "# {1} - exceptionMessage",
|
||||
"AddMultipleImageTask.errorAddingImgWithoutFileSystem=Error adding images without file systems for device %s: %s",})
|
||||
AddMultipleImageTask(String deviceId, List<String> imageFilePaths, String timeZone,
|
||||
DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) throws NoCurrentCaseException {
|
||||
"AddMultipleImagesTask.errorAddingImgWithoutFileSystem=Error adding images without file systems for device {0}: {1}",})
|
||||
AddMultipleImagesTask(String deviceId, List<String> imageFilePaths, String timeZone,
|
||||
DataSourceProcessorProgressMonitor progressMonitor) throws NoCurrentCaseException {
|
||||
this.deviceId = deviceId;
|
||||
this.imageFilePaths = imageFilePaths;
|
||||
this.timeZone = timeZone;
|
||||
this.callback = callback;
|
||||
this.progressMonitor = progressMonitor;
|
||||
currentCase = Case.getCurrentCaseThrows();
|
||||
this.criticalErrorOccurred = false;
|
||||
this.result = DataSourceProcessorResult.NO_ERRORS;
|
||||
tskAddImageProcessLock = new Object();
|
||||
}
|
||||
|
||||
@Messages({
|
||||
"AddMultipleImagesTask.cancelled=Cancellation: Add image process reverted",
|
||||
})
|
||||
@Override
|
||||
public void run() {
|
||||
newDataSources = new ArrayList<>();
|
||||
errorMessages = new ArrayList<>();
|
||||
newDataSources = new ArrayList<>();
|
||||
List<Content> emptyDataSources = new ArrayList<>();
|
||||
|
||||
/*
|
||||
* Try to add the input image files as images.
|
||||
*/
|
||||
List<String> corruptedImageFilePaths = new ArrayList<>();
|
||||
currentCase.getSleuthkitCase().acquireSingleUserCaseWriteLock();
|
||||
try {
|
||||
currentCase.getSleuthkitCase().acquireSingleUserCaseWriteLock();
|
||||
progressMonitor.setIndeterminate(true);
|
||||
for (String imageFilePath : imageFilePaths) {
|
||||
if (!cancelled) {
|
||||
addImageToCase(imageFilePath, newDataSources, corruptedImageFilePaths, errorMessages);
|
||||
synchronized (tskAddImageProcessLock) {
|
||||
if (!tskAddImageProcessStopped) {
|
||||
addImageProcess = currentCase.getSleuthkitCase().makeAddImageProcess(timeZone, false, false, "");
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
run(imageFilePath, corruptedImageFilePaths, errorMessages);
|
||||
commitOrRevertAddImageProcess(imageFilePath, errorMessages, newDataSources);
|
||||
synchronized (tskAddImageProcessLock) {
|
||||
if (tskAddImageProcessStopped) {
|
||||
errorMessages.add(Bundle.AddMultipleImagesTask_cancelled());
|
||||
result = DataSourceProcessorResult.CRITICAL_ERRORS;
|
||||
newDataSources = emptyDataSources;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
@ -124,11 +147,11 @@ class AddMultipleImageTask implements Runnable {
|
||||
* single an unallocated space file with the device id as the root virtual
|
||||
* directory name.
|
||||
*/
|
||||
if (!cancelled && !corruptedImageFilePaths.isEmpty()) {
|
||||
if (!tskAddImageProcessStopped && !corruptedImageFilePaths.isEmpty()) {
|
||||
SleuthkitCase caseDatabase;
|
||||
caseDatabase = currentCase.getSleuthkitCase();
|
||||
try {
|
||||
progressMonitor.setProgressText(Bundle.AddMultipleImageTask_addingFileAsLogicalFile(corruptedImageFilePaths.toString()));
|
||||
progressMonitor.setProgressText(Bundle.AddMultipleImagesTask_addingFileAsLogicalFile(corruptedImageFilePaths.toString()));
|
||||
|
||||
caseDatabase.acquireSingleUserCaseWriteLock();
|
||||
|
||||
@ -146,14 +169,13 @@ class AddMultipleImageTask implements Runnable {
|
||||
start += TWO_GB;
|
||||
sequence++;
|
||||
}
|
||||
|
||||
}
|
||||
double leftoverSize = imageSize - sequence * TWO_GB;
|
||||
fileRanges.add(new TskFileRange(start, (long)leftoverSize, sequence));
|
||||
|
||||
caseDatabase.addLayoutFiles(dataSource, fileRanges);
|
||||
} catch (TskCoreException ex) {
|
||||
errorMessages.add(Bundle.AddMultipleImageTask_errorAddingImgWithoutFileSystem(deviceId, ex.getLocalizedMessage()));
|
||||
errorMessages.add(Bundle.AddMultipleImagesTask_errorAddingImgWithoutFileSystem(deviceId, ex.getLocalizedMessage()));
|
||||
criticalErrorOccurred = true;
|
||||
} finally {
|
||||
caseDatabase.releaseSingleUserCaseWriteLock();
|
||||
@ -167,9 +189,6 @@ class AddMultipleImageTask implements Runnable {
|
||||
progressMonitor.setProgress(0);
|
||||
progressMonitor.setProgress(100);
|
||||
|
||||
/*
|
||||
* Pass the results back via the callback.
|
||||
*/
|
||||
if (criticalErrorOccurred) {
|
||||
result = DataSourceProcessorResult.CRITICAL_ERRORS;
|
||||
} else if (!errorMessages.isEmpty()) {
|
||||
@ -184,38 +203,50 @@ class AddMultipleImageTask implements Runnable {
|
||||
* partial processing of the input.
|
||||
*/
|
||||
void cancelTask() {
|
||||
LOGGER.log(Level.WARNING, "AddMultipleImageTask cancelled, processing may be incomplete"); // NON-NLS
|
||||
cancelled = true;
|
||||
LOGGER.log(Level.WARNING, "AddMultipleImagesTask cancelled, processing may be incomplete"); // NON-NLS
|
||||
synchronized (tskAddImageProcessLock) {
|
||||
tskAddImageProcessStopped = true;
|
||||
if (addImageProcess != null) {
|
||||
try {
|
||||
/*
|
||||
* All this does is set a flag that will make the TSK add
|
||||
* image process exit when the flag is checked between
|
||||
* processing steps. The state of the flag is not
|
||||
* accessible, so record it here so that it is known that
|
||||
* the revert method of the process object needs to be
|
||||
* called.
|
||||
*/
|
||||
addImageProcess.stop();
|
||||
} catch (TskCoreException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Cancellation: addImagePRocess.stop failed", ex); // NON-NLS
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to add an input image to the case.
|
||||
*
|
||||
* @param imageFilePath The image file path.
|
||||
* @param newDataSources If the image is added, a data source is
|
||||
* added to this list for eventual return to
|
||||
* the caller via the callback.
|
||||
* @param corruptedImageFilePaths If the image cannot be added because
|
||||
* Sleuth Kit cannot detect a filesystem,
|
||||
* the image file path is added to this list
|
||||
* for later addition as an unallocated space file.
|
||||
* @param errorMessages If there are any error messages, the
|
||||
* error messages are added to this list for
|
||||
* eventual return to the caller via the
|
||||
* callback.
|
||||
* eventual return to the caller via the getter
|
||||
* method.
|
||||
*/
|
||||
@Messages({
|
||||
"# {0} - imageFilePath", "AddMultipleImageTask.adding=Adding: {0}",
|
||||
"# {0} - imageFilePath", "# {1} - deviceId", "# {2} - exceptionMessage", "AddMultipleImageTask.criticalErrorAdding=Critical error adding {0} for device {1}: {2}",
|
||||
"# {0} - imageFilePath", "# {1} - deviceId", "# {2} - exceptionMessage", "AddMultipleImageTask.criticalErrorReverting=Critical error reverting add image process for {0} for device {1}: {2}",
|
||||
"# {0} - imageFilePath", "# {1} - deviceId", "# {2} - exceptionMessage", "AddMultipleImageTask.nonCriticalErrorAdding=Non-critical error adding {0} for device {1}: {2}",})
|
||||
private void addImageToCase(String imageFilePath, List<Content> newDataSources, List<String> corruptedImageFilePaths, List<String> errorMessages) {
|
||||
"# {0} - imageFilePath", "AddMultipleImagesTask.adding=Adding: {0}",
|
||||
"# {0} - imageFilePath", "# {1} - deviceId", "# {2} - exceptionMessage", "AddMultipleImagesTask.criticalErrorAdding=Critical error adding {0} for device {1}: {2}",
|
||||
"# {0} - imageFilePath", "# {1} - deviceId", "# {2} - exceptionMessage", "AddMultipleImagesTask.criticalErrorReverting=Critical error reverting add image process for {0} for device {1}: {2}",
|
||||
"# {0} - imageFilePath", "# {1} - deviceId", "# {2} - exceptionMessage", "AddMultipleImagesTask.nonCriticalErrorAdding=Non-critical error adding {0} for device {1}: {2}",})
|
||||
private void run(String imageFilePath, List<String> corruptedImageFilePaths, List<String> errorMessages) {
|
||||
/*
|
||||
* Try to add the image to the case database as a data source.
|
||||
*/
|
||||
progressMonitor.setProgressText(Bundle.AddMultipleImageTask_adding(imageFilePath));
|
||||
SleuthkitCase caseDatabase = currentCase.getSleuthkitCase();
|
||||
SleuthkitJNI.CaseDbHandle.AddImageProcess addImageProcess = caseDatabase.makeAddImageProcess(timeZone, false, false, "");
|
||||
progressMonitor.setProgressText(Bundle.AddMultipleImagesTask_adding(imageFilePath));
|
||||
try {
|
||||
addImageProcess.run(deviceId, new String[]{imageFilePath});
|
||||
} catch (TskCoreException ex) {
|
||||
@ -228,62 +259,89 @@ class AddMultipleImageTask implements Runnable {
|
||||
*/
|
||||
corruptedImageFilePaths.add(imageFilePath);
|
||||
} else {
|
||||
errorMessages.add(Bundle.AddMultipleImageTask_criticalErrorAdding(imageFilePath, deviceId, ex.getLocalizedMessage()));
|
||||
errorMessages.add(Bundle.AddMultipleImagesTask_criticalErrorAdding(imageFilePath, deviceId, ex.getLocalizedMessage()));
|
||||
criticalErrorOccurred = true;
|
||||
}
|
||||
} catch (TskDataException ex) {
|
||||
errorMessages.add(Bundle.AddMultipleImagesTask_nonCriticalErrorAdding(imageFilePath, deviceId, ex.getLocalizedMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Commits or reverts the results of the TSK add image process. If the
|
||||
* process was stopped before it completed or there was a critical error the
|
||||
* results are reverted, otherwise they are committed.
|
||||
*
|
||||
* @param imageFilePath The image file path.
|
||||
* @param errorMessages Error messages, if any, are added to this list for
|
||||
* eventual return via the getter method.
|
||||
* @param newDataSources If the new image is successfully committed, it is
|
||||
* added to this list for eventual return via the
|
||||
* getter method.
|
||||
*/
|
||||
private void commitOrRevertAddImageProcess(String imageFilePath, List<String> errorMessages, List<Content> newDataSources) {
|
||||
synchronized (tskAddImageProcessLock) {
|
||||
if (tskAddImageProcessStopped || criticalErrorOccurred) {
|
||||
try {
|
||||
addImageProcess.revert();
|
||||
} catch (TskCoreException ex) {
|
||||
errorMessages.add(Bundle.AddMultipleImagesTask_criticalErrorReverting(imageFilePath, deviceId, ex.getLocalizedMessage()));
|
||||
criticalErrorOccurred = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Either way, the add image process needs to be reverted.
|
||||
* Try to commit the results of the add image process, retrieve the new
|
||||
* image from the case database, and add it to the list of new data
|
||||
* sources to be returned via the getter method.
|
||||
*/
|
||||
try {
|
||||
addImageProcess.revert();
|
||||
} catch (TskCoreException e) {
|
||||
errorMessages.add(Bundle.AddMultipleImageTask_criticalErrorReverting(imageFilePath, deviceId, e.getLocalizedMessage()));
|
||||
long imageId = addImageProcess.commit();
|
||||
Image dataSource = currentCase.getSleuthkitCase().getImageById(imageId);
|
||||
newDataSources.add(dataSource);
|
||||
|
||||
/*
|
||||
* Verify the size of the new image. Note that it may not be what is
|
||||
* expected, but at least part of it was added to the case.
|
||||
*/
|
||||
String verificationError = dataSource.verifyImageSize();
|
||||
if (!verificationError.isEmpty()) {
|
||||
errorMessages.add(Bundle.AddMultipleImagesTask_nonCriticalErrorAdding(imageFilePath, deviceId, verificationError));
|
||||
}
|
||||
} catch (TskCoreException ex) {
|
||||
/*
|
||||
* The add image process commit failed or querying the case database
|
||||
* for the newly added image failed. Either way, this is a critical
|
||||
* error.
|
||||
*/
|
||||
errorMessages.add(Bundle.AddMultipleImagesTask_criticalErrorAdding(imageFilePath, deviceId, ex.getLocalizedMessage()));
|
||||
criticalErrorOccurred = true;
|
||||
}
|
||||
return;
|
||||
} catch (TskDataException ex) {
|
||||
errorMessages.add(Bundle.AddMultipleImageTask_nonCriticalErrorAdding(imageFilePath, deviceId, ex.getLocalizedMessage()));
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to commit the results of the add image process, retrieve the new
|
||||
* image from the case database, and add it to the list of new data
|
||||
* sources to be returned via the callback.
|
||||
*/
|
||||
try {
|
||||
long imageId = addImageProcess.commit();
|
||||
Image dataSource = caseDatabase.getImageById(imageId);
|
||||
newDataSources.add(dataSource);
|
||||
|
||||
/*
|
||||
* Verify the size of the new image. Note that it may not be what is
|
||||
* expected, but at least part of it was added to the case.
|
||||
*/
|
||||
String verificationError = dataSource.verifyImageSize();
|
||||
if (!verificationError.isEmpty()) {
|
||||
errorMessages.add(Bundle.AddMultipleImageTask_nonCriticalErrorAdding(imageFilePath, deviceId, verificationError));
|
||||
}
|
||||
} catch (TskCoreException ex) {
|
||||
/*
|
||||
* The add image process commit failed or querying the case database
|
||||
* for the newly added image failed. Either way, this is a critical
|
||||
* error.
|
||||
*/
|
||||
errorMessages.add(Bundle.AddMultipleImageTask_criticalErrorAdding(imageFilePath, deviceId, ex.getLocalizedMessage()));
|
||||
criticalErrorOccurred = true;
|
||||
}
|
||||
}
|
||||
|
||||
public List<Content> getNewDataSources() {
|
||||
return newDataSources;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the error messages from the AddMultipleImagesTask run
|
||||
* @return List of error message
|
||||
*/
|
||||
public List<String> getErrorMessages() {
|
||||
return errorMessages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the result the AddMultipleImagesTask run
|
||||
* @return The result of the run
|
||||
*/
|
||||
public DataSourceProcessorResult getResult() {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the new data sources the AddMultipleImagesTask run
|
||||
* @return The new data sources of the run
|
||||
*/
|
||||
public List<Content> getNewDataSources() {
|
||||
return newDataSources;
|
||||
}
|
||||
}
|
@ -2,15 +2,28 @@
|
||||
# To change this template file, choose Tools | Templates
|
||||
# and open the template in the editor.
|
||||
|
||||
AddLogicalImageTask.addImageCancelled=Add image cancelled
|
||||
# {0} - file number
|
||||
# {1} - total files
|
||||
AddLogicalImageTask.addingExtractedFile=Adding extracted files ({0}/{1})
|
||||
AddLogicalImageTask.addingExtractedFiles=Adding extracted files
|
||||
# {0} - file number
|
||||
# {1} - total files
|
||||
AddLogicalImageTask.addingInterestingFile=Adding interesting files ({0}/{1})
|
||||
AddLogicalImageTask.addingInterestingFiles=Adding search results as interesting files
|
||||
# {0} - file
|
||||
AddLogicalImageTask.addingToReport=Adding {0} to report
|
||||
# {0} - target image path
|
||||
AddLogicalImageTask.cannotFindDataSourceObjId=Cannot find obj_id in tsk_image_names for {0}
|
||||
# {0} - SearchResults.txt
|
||||
# {1} - directory
|
||||
AddLogicalImageTask.cannotFindFiles=Cannot find {0} in {1}
|
||||
# {0} - src
|
||||
# {1} - dest
|
||||
AddLogicalImageTask.copyingImageFromTo=Copying image from {0} to {1}
|
||||
# {0} - sparseImageDirectory
|
||||
AddLogicalImageTask.directoryDoesNotContainSparseImage=Directory {0} does not contain any images
|
||||
AddLogicalImageTask.doneAddingExtractedFiles=Done adding extracted files
|
||||
AddLogicalImageTask.doneAddingInterestingFiles=Done adding search results as interesting files
|
||||
# {0} - file
|
||||
AddLogicalImageTask.doneAddingToReport=Done adding {0} to report
|
||||
@ -23,37 +36,45 @@ AddLogicalImageTask.failedToAddReport=Failed to add report {0}. Reason= {1}
|
||||
# {0} - src
|
||||
# {1} - dest
|
||||
AddLogicalImageTask.failedToCopyDirectory=Failed to copy directory {0} to {1}
|
||||
# {0} - reason
|
||||
AddLogicalImageTask.failedToGetTotalFilesCount=Failed to get total files count: {0}
|
||||
# {0} - file
|
||||
AddLogicalImageTask.failToGetCanonicalPath=Fail to get canonical path for {0}
|
||||
AddLogicalImageTask.ingestionCancelled=Ingestion cancelled
|
||||
AddLogicalImageTask.noCurrentCase=No current case
|
||||
# {0} - line number
|
||||
# {1} - fields length
|
||||
# {2} - expected length
|
||||
AddLogicalImageTask.notEnoughFields=File does not contain enough fields at line {0}, got {1}, expecting {2}
|
||||
# {0} - imageFilePath
|
||||
AddMultipleImageTask.adding=Adding: {0}
|
||||
AddMultipleImagesTask.adding=Adding: {0}
|
||||
# {0} - file
|
||||
AddMultipleImageTask.addingFileAsLogicalFile=Adding: {0} as an unallocated space file.
|
||||
AddMultipleImagesTask.addingFileAsLogicalFile=Adding: {0} as an unallocated space file.
|
||||
AddMultipleImagesTask.cancelled=Cancellation: Add image process reverted
|
||||
# {0} - imageFilePath
|
||||
# {1} - deviceId
|
||||
# {2} - exceptionMessage
|
||||
AddMultipleImageTask.criticalErrorAdding=Critical error adding {0} for device {1}: {2}
|
||||
AddMultipleImagesTask.criticalErrorAdding=Critical error adding {0} for device {1}: {2}
|
||||
# {0} - imageFilePath
|
||||
# {1} - deviceId
|
||||
# {2} - exceptionMessage
|
||||
AddMultipleImageTask.criticalErrorReverting=Critical error reverting add image process for {0} for device {1}: {2}
|
||||
AddMultipleImagesTask.criticalErrorReverting=Critical error reverting add image process for {0} for device {1}: {2}
|
||||
# {0} - deviceId
|
||||
# {1} - exceptionMessage
|
||||
AddMultipleImageTask.errorAddingImgWithoutFileSystem=Error adding images without file systems for device %s: %s
|
||||
AddMultipleImageTask.fsTypeUnknownErr=Cannot determine file system type
|
||||
AddMultipleImagesTask.errorAddingImgWithoutFileSystem=Error adding images without file systems for device {0}: {1}
|
||||
AddMultipleImagesTask.fsTypeUnknownErr=Cannot determine file system type
|
||||
# {0} - imageFilePath
|
||||
# {1} - deviceId
|
||||
# {2} - exceptionMessage
|
||||
AddMultipleImageTask.nonCriticalErrorAdding=Non-critical error adding {0} for device {1}: {2}
|
||||
AddMultipleImagesTask.nonCriticalErrorAdding=Non-critical error adding {0} for device {1}: {2}
|
||||
LogicalImagerDSProcessor.dataSourceType=Autopsy Logical Imager Results
|
||||
LogicalImagerDSProcessor.destinationDirectoryConfirmation=Destination directory confirmation
|
||||
# {0} - directory
|
||||
LogicalImagerDSProcessor.destinationDirectoryConfirmationMsg=The logical imager folder {0} already exists,\ndo you want to add it again using a new folder name?
|
||||
# {0} - directory
|
||||
LogicalImagerDSProcessor.directoryAlreadyExists=Directory {0} already exists
|
||||
# {0} - directory
|
||||
LogicalImagerDSProcessor.failToCreateDirectory=Failed to create directory {0}
|
||||
# {0} - file
|
||||
LogicalImagerDSProcessor.failToGetCanonicalPath=Fail to get canonical path for {0}
|
||||
# {0} - imageDirPath
|
||||
LogicalImagerDSProcessor.imageDirPathNotFound={0} not found.\nUSB drive has been ejected.
|
||||
LogicalImagerDSProcessor.noCurrentCase=No current case
|
||||
|
@ -19,15 +19,15 @@
|
||||
package org.sleuthkit.autopsy.logicalimager.dsp;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import javax.swing.JOptionPane;
|
||||
import static javax.swing.JOptionPane.YES_OPTION;
|
||||
import javax.swing.JPanel;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.openide.util.lookup.ServiceProvider;
|
||||
import org.openide.util.lookup.ServiceProviders;
|
||||
@ -36,6 +36,7 @@ import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor;
|
||||
import org.sleuthkit.autopsy.coreutils.TimeStampUtils;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
|
||||
/**
|
||||
@ -131,8 +132,10 @@ public final class LogicalImagerDSProcessor implements DataSourceProcessor {
|
||||
"# {0} - imageDirPath", "LogicalImagerDSProcessor.imageDirPathNotFound={0} not found.\nUSB drive has been ejected.",
|
||||
"# {0} - directory", "LogicalImagerDSProcessor.failToCreateDirectory=Failed to create directory {0}",
|
||||
"# {0} - directory", "LogicalImagerDSProcessor.directoryAlreadyExists=Directory {0} already exists",
|
||||
"# {0} - file", "LogicalImagerDSProcessor.failToGetCanonicalPath=Fail to get canonical path for {0}",
|
||||
"LogicalImagerDSProcessor.noCurrentCase=No current case",})
|
||||
"LogicalImagerDSProcessor.destinationDirectoryConfirmation=Destination directory confirmation",
|
||||
"# {0} - directory", "LogicalImagerDSProcessor.destinationDirectoryConfirmationMsg=The logical imager folder {0} already exists,\ndo you want to add it again using a new folder name?",
|
||||
"LogicalImagerDSProcessor.noCurrentCase=No current case",
|
||||
})
|
||||
@Override
|
||||
public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||
configPanel.storeSettings();
|
||||
@ -164,44 +167,27 @@ public final class LogicalImagerDSProcessor implements DataSourceProcessor {
|
||||
File dest = Paths.get(logicalImagerDir.toString(), imageDirPath.getFileName().toString()).toFile();
|
||||
if (dest.exists()) {
|
||||
// Destination directory already exists
|
||||
String msg = Bundle.LogicalImagerDSProcessor_directoryAlreadyExists(dest.toString());
|
||||
errorList.add(msg);
|
||||
callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources);
|
||||
return;
|
||||
int showConfirmDialog = JOptionPane.showConfirmDialog(configPanel,
|
||||
Bundle.LogicalImagerDSProcessor_destinationDirectoryConfirmationMsg(dest.toString()),
|
||||
Bundle.LogicalImagerDSProcessor_destinationDirectoryConfirmation(),
|
||||
JOptionPane.YES_NO_OPTION);
|
||||
if (showConfirmDialog == YES_OPTION) {
|
||||
// Get unique dest directory
|
||||
String uniqueDirectory = imageDirPath.getFileName() + "_" + TimeStampUtils.createTimeStamp();
|
||||
dest = Paths.get(logicalImagerDir.toString(), uniqueDirectory).toFile();
|
||||
} else {
|
||||
String msg = Bundle.LogicalImagerDSProcessor_directoryAlreadyExists(dest.toString());
|
||||
errorList.add(msg);
|
||||
callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources);
|
||||
return;
|
||||
}
|
||||
}
|
||||
File src = imageDirPath.toFile();
|
||||
|
||||
try {
|
||||
progressMonitor.setProgressText(Bundle.AddLogicalImageTask_copyingImageFromTo(src.toString(), dest.toString()));
|
||||
FileUtils.copyDirectory(src, dest);
|
||||
progressMonitor.setProgressText(Bundle.AddLogicalImageTask_doneCopying());
|
||||
} catch (IOException ex) {
|
||||
// Copy directory failed
|
||||
String msg = Bundle.AddLogicalImageTask_failedToCopyDirectory(src.toString(), dest.toString());
|
||||
errorList.add(msg);
|
||||
callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get all VHD files in the src directory
|
||||
List<String> imagePaths = new ArrayList<>();
|
||||
for (File f : dest.listFiles()) {
|
||||
if (f.getName().endsWith(".vhd")) {
|
||||
try {
|
||||
imagePaths.add(f.getCanonicalPath());
|
||||
} catch (IOException ex) {
|
||||
String msg = Bundle.LogicalImagerDSProcessor_failToGetCanonicalPath(f.getName());
|
||||
errorList.add(msg);
|
||||
callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
String deviceId = UUID.randomUUID().toString();
|
||||
String timeZone = Calendar.getInstance().getTimeZone().getID();
|
||||
run(deviceId, imagePaths,
|
||||
timeZone, src, dest,
|
||||
run(deviceId, timeZone, src, dest,
|
||||
progressMonitor, callback);
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
String msg = Bundle.LogicalImagerDSProcessor_noCurrentCase();
|
||||
@ -220,7 +206,6 @@ public final class LogicalImagerDSProcessor implements DataSourceProcessor {
|
||||
* @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 imagePaths Paths to the image files.
|
||||
* @param timeZone The time zone to use when processing dates and
|
||||
* times for the image, obtained from
|
||||
* java.util.TimeZone.getID.
|
||||
@ -230,13 +215,14 @@ public final class LogicalImagerDSProcessor implements DataSourceProcessor {
|
||||
* processing.
|
||||
* @param callback Callback to call when processing is done.
|
||||
*/
|
||||
private void run(String deviceId, List<String> imagePaths, String timeZone,
|
||||
private void run(String deviceId, String timeZone,
|
||||
File src, File dest,
|
||||
DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback
|
||||
) throws NoCurrentCaseException {
|
||||
addLogicalImageTask = new AddLogicalImageTask(deviceId, imagePaths, timeZone, src, dest,
|
||||
addLogicalImageTask = new AddLogicalImageTask(deviceId, timeZone, src, dest,
|
||||
progressMonitor, callback);
|
||||
new Thread(addLogicalImageTask).start();
|
||||
Thread thread = new Thread(addLogicalImageTask);
|
||||
thread.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -333,9 +333,19 @@ final class LogicalImagerPanel extends JPanel implements DocumentListener {
|
||||
}
|
||||
});
|
||||
if (vhdFiles.length == 0) {
|
||||
setErrorMessage(Bundle.LogicalImagerPanel_messageLabel_directoryDoesNotContainSparseImage(path));
|
||||
firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), true, false);
|
||||
return;
|
||||
// No VHD files, try directories for individual files
|
||||
String[] directories = dir.list(new FilenameFilter() {
|
||||
@Override
|
||||
public boolean accept(File dir, String name) {
|
||||
return Paths.get(dir.toString(), name).toFile().isDirectory();
|
||||
}
|
||||
});
|
||||
if (directories.length == 0) {
|
||||
// No directories, bail
|
||||
setErrorMessage(Bundle.LogicalImagerPanel_messageLabel_directoryDoesNotContainSparseImage(path));
|
||||
firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), true, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
manualImageDirPath = Paths.get(path);
|
||||
setNormalMessage(path);
|
||||
@ -360,11 +370,11 @@ final class LogicalImagerPanel extends JPanel implements DocumentListener {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean dirHasVhdFiles(File dir) {
|
||||
File[] fList = dir.listFiles(new FilenameFilter() {
|
||||
private boolean dirHasImagerResult(File dir) {
|
||||
String[] fList = dir.list(new FilenameFilter() {
|
||||
@Override
|
||||
public boolean accept(File dir, String name) {
|
||||
return name.endsWith(".vhd");
|
||||
return name.endsWith(".vhd") || Paths.get(dir.toString(), name).toFile().isDirectory();
|
||||
}
|
||||
});
|
||||
return (fList != null && fList.length != 0);
|
||||
@ -382,9 +392,9 @@ final class LogicalImagerPanel extends JPanel implements DocumentListener {
|
||||
if (fList != null) {
|
||||
imageTableModel = new ImageTableModel();
|
||||
// Find all directories with name like Logical_Imager_HOSTNAME_yyyymmdd_HH_MM_SS
|
||||
// and has vhd files in it
|
||||
// and has Logical Imager result in it
|
||||
for (File file : fList) {
|
||||
if (file.isDirectory() && dirHasVhdFiles(file)) {
|
||||
if (file.isDirectory() && dirHasImagerResult(file)) {
|
||||
String dir = file.getName();
|
||||
Matcher m = regex.matcher(dir);
|
||||
if (m.find()) {
|
||||
@ -508,7 +518,7 @@ final class LogicalImagerPanel extends JPanel implements DocumentListener {
|
||||
}
|
||||
} catch (IOException ignored) {
|
||||
//unable to get this removable drive for default selection will try and select next removable drive by default
|
||||
logger.log(Level.INFO, "Unable to select first removable drive found", ignored);
|
||||
logger.log(Level.INFO, String.format("Unable to select first removable drive found: %s", ignored.getMessage()));
|
||||
}
|
||||
}
|
||||
i++;
|
||||
|
@ -31,6 +31,9 @@ PortableCaseReportModule.generateReport.errorCopyingFiles=Error copying tagged f
|
||||
PortableCaseReportModule.generateReport.errorCopyingInterestingFiles=Error copying interesting files
|
||||
PortableCaseReportModule.generateReport.errorCopyingInterestingResults=Error copying interesting results
|
||||
PortableCaseReportModule.generateReport.errorCopyingTags=Error copying tags
|
||||
PortableCaseReportModule.generateReport.errorCreatingImageTagTable=Error creating image tags table
|
||||
PortableCaseReportModule.generateReport.errorCreatingReportFolder=Could not make report folder
|
||||
PortableCaseReportModule.generateReport.errorGeneratingUCOreport=Problem while generating CASE-UCO report
|
||||
# {0} - attribute type name
|
||||
PortableCaseReportModule.generateReport.errorLookingUpAttrType=Error looking up attribute type {0}
|
||||
PortableCaseReportModule.generateReport.interestingItemError=Error loading intersting items
|
||||
|
@ -206,7 +206,7 @@ class PortableCaseInterestingItemsListPanel extends javax.swing.JPanel {
|
||||
*/
|
||||
private static class GetInterestingItemSetNamesCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback {
|
||||
|
||||
private static final java.util.logging.Logger logger = java.util.logging.Logger.getLogger(GetInterestingItemSetNamesCallback.class.getName());
|
||||
private static final Logger logger = Logger.getLogger(GetInterestingItemSetNamesCallback.class.getName());
|
||||
private final Map<String, Long> setCounts = new HashMap<>();
|
||||
|
||||
@Override
|
||||
|
@ -36,6 +36,7 @@ import org.openide.modules.InstalledFileLocator;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.casemodule.services.contentviewertags.ContentViewerTagManager;
|
||||
import org.sleuthkit.autopsy.coreutils.FileUtil;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
||||
@ -179,9 +180,12 @@ class PortableCaseReportModule implements ReportModule {
|
||||
"PortableCaseReportModule.generateReport.errorCopyingArtifacts=Error copying tagged artifacts",
|
||||
"PortableCaseReportModule.generateReport.errorCopyingInterestingFiles=Error copying interesting files",
|
||||
"PortableCaseReportModule.generateReport.errorCopyingInterestingResults=Error copying interesting results",
|
||||
"PortableCaseReportModule.generateReport.errorCreatingImageTagTable=Error creating image tags table",
|
||||
"# {0} - attribute type name",
|
||||
"PortableCaseReportModule.generateReport.errorLookingUpAttrType=Error looking up attribute type {0}",
|
||||
"PortableCaseReportModule.generateReport.compressingCase=Compressing case...",
|
||||
"PortableCaseReportModule.generateReport.errorCreatingReportFolder=Could not make report folder",
|
||||
"PortableCaseReportModule.generateReport.errorGeneratingUCOreport=Problem while generating CASE-UCO report"
|
||||
})
|
||||
|
||||
void generateReport(String reportPath, PortableCaseOptions options, ReportProgressPanel progressPanel) {
|
||||
@ -240,6 +244,14 @@ class PortableCaseReportModule implements ReportModule {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set up the table for the image tags
|
||||
try {
|
||||
initializeImageTags(progressPanel);
|
||||
} catch (TskCoreException ex) {
|
||||
handleError("Error creating image tag table", Bundle.PortableCaseReportModule_generateReport_errorCreatingImageTagTable(), ex, progressPanel); // NON-NLS
|
||||
return;
|
||||
}
|
||||
|
||||
// Copy the selected tags
|
||||
progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateReport_copyingTags());
|
||||
try {
|
||||
@ -358,7 +370,7 @@ class PortableCaseReportModule implements ReportModule {
|
||||
|
||||
File reportsFolder = Paths.get(caseFolder.toString(), "Reports").toFile();
|
||||
if(!reportsFolder.mkdir()) {
|
||||
handleError("Could not make report folder", "Could not make report folder", null, progressPanel); // NON-NLS
|
||||
handleError("Could not make report folder", Bundle.PortableCaseReportModule_generateReport_errorCreatingReportFolder(), null, progressPanel); // NON-NLS
|
||||
return;
|
||||
}
|
||||
|
||||
@ -366,7 +378,7 @@ class PortableCaseReportModule implements ReportModule {
|
||||
CaseUcoFormatExporter.export(tagNames, setNames, reportsFolder, progressPanel);
|
||||
} catch (IOException | SQLException | NoCurrentCaseException | TskCoreException ex) {
|
||||
handleError("Problem while generating CASE-UCO report",
|
||||
"Problem while generating CASE-UCO report", ex, progressPanel); // NON-NLS
|
||||
Bundle.PortableCaseReportModule_generateReport_errorGeneratingUCOreport(), ex, progressPanel); // NON-NLS
|
||||
}
|
||||
|
||||
// Compress the case (if desired)
|
||||
@ -484,6 +496,22 @@ class PortableCaseReportModule implements ReportModule {
|
||||
currentCaseDbManager.select("max(examiner_id) as max_id from tsk_examiners", new StoreMaxIdCallback("tsk_examiners")); // NON-NLS
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the image tag table in the portable case
|
||||
*
|
||||
* @param progressPanel
|
||||
*
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
private void initializeImageTags(ReportProgressPanel progressPanel) throws TskCoreException {
|
||||
|
||||
// Create the image tags table in the portable case
|
||||
CaseDbAccessManager portableDbAccessManager = portableSkCase.getCaseDbAccessManager();
|
||||
if (! portableDbAccessManager.tableExists(ContentViewerTagManager.TABLE_NAME)) {
|
||||
portableDbAccessManager.createTable(ContentViewerTagManager.TABLE_NAME, ContentViewerTagManager.TABLE_SCHEMA_SQLITE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add all files with a given tag to the portable case.
|
||||
*
|
||||
@ -507,17 +535,89 @@ class PortableCaseReportModule implements ReportModule {
|
||||
|
||||
Content content = tag.getContent();
|
||||
if (content instanceof AbstractFile) {
|
||||
|
||||
long newFileId = copyContentToPortableCase(content, progressPanel);
|
||||
|
||||
// Tag the file
|
||||
if (! oldTagNameToNewTagName.containsKey(tag.getName())) {
|
||||
throw new TskCoreException("TagName map is missing entry for ID " + tag.getName().getId() + " with display name " + tag.getName().getDisplayName()); // NON-NLS
|
||||
}
|
||||
portableSkCase.addContentTag(newIdToContent.get(newFileId), oldTagNameToNewTagName.get(tag.getName()), tag.getComment(), tag.getBeginByteOffset(), tag.getEndByteOffset());
|
||||
ContentTag newContentTag = portableSkCase.addContentTag(newIdToContent.get(newFileId), oldTagNameToNewTagName.get(tag.getName()), tag.getComment(), tag.getBeginByteOffset(), tag.getEndByteOffset());
|
||||
|
||||
// Get the image tag data associated with this tag (empty string if there is none)
|
||||
// and save it if present
|
||||
String appData = getImageTagDataForContentTag(tag);
|
||||
if (! appData.isEmpty()) {
|
||||
addImageTagToPortableCase(newContentTag, appData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the image tag data for a given content tag
|
||||
*
|
||||
* @param tag The ContentTag in the current case
|
||||
*
|
||||
* @return The app_data string for this content tag or an empty string if there was none
|
||||
*
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
private String getImageTagDataForContentTag(ContentTag tag) throws TskCoreException {
|
||||
|
||||
GetImageTagCallback callback = new GetImageTagCallback();
|
||||
String query = "* FROM " + ContentViewerTagManager.TABLE_NAME + " WHERE content_tag_id = " + tag.getId();
|
||||
currentCase.getSleuthkitCase().getCaseDbAccessManager().select(query, callback);
|
||||
return callback.getAppData();
|
||||
}
|
||||
|
||||
/**
|
||||
* CaseDbAccessManager callback to get the app_data string for the image tag
|
||||
*/
|
||||
private static class GetImageTagCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(PortableCaseReportModule.class.getName());
|
||||
private String appData = "";
|
||||
|
||||
@Override
|
||||
public void process(ResultSet rs) {
|
||||
try {
|
||||
while (rs.next()) {
|
||||
try {
|
||||
appData = rs.getString("app_data"); // NON-NLS
|
||||
} catch (SQLException ex) {
|
||||
logger.log(Level.WARNING, "Unable to get app_data from result set", ex); // NON-NLS
|
||||
}
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
logger.log(Level.WARNING, "Failed to get next result for app_data", ex); // NON-NLS
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the app_data string
|
||||
*
|
||||
* @return the app_data string
|
||||
*/
|
||||
String getAppData() {
|
||||
return appData;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an image tag to the portable case.
|
||||
*
|
||||
* @param newContentTag The content tag in the portable case
|
||||
* @param appData The string to copy into app_data
|
||||
*
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
private void addImageTagToPortableCase(ContentTag newContentTag, String appData) throws TskCoreException {
|
||||
String insert = "(content_tag_id, app_data) VALUES (" + newContentTag.getId() + ", '" + appData + "')";
|
||||
portableSkCase.getCaseDbAccessManager().insert(ContentViewerTagManager.TABLE_NAME, insert);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add all artifacts with a given tag to the portable case.
|
||||
*
|
||||
|
@ -205,7 +205,7 @@ class PortableCaseTagsListPanel extends javax.swing.JPanel {
|
||||
*/
|
||||
static class GetTagCountsCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback {
|
||||
|
||||
private static final java.util.logging.Logger logger = java.util.logging.Logger.getLogger(GetTagCountsCallback.class.getName());
|
||||
private static final Logger logger = Logger.getLogger(GetTagCountsCallback.class.getName());
|
||||
private final Map<Long, Long> tagCounts = new HashMap<>();
|
||||
|
||||
@Override
|
||||
|
@ -136,6 +136,7 @@ final class TikaTextExtractor implements TextExtractor {
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", //NON-NLS
|
||||
"application/pdf"); //NON-NLS
|
||||
|
||||
// Used to log to the tika file that is why it uses the java.util.logging.logger class instead of the Autopsy one
|
||||
private static final java.util.logging.Logger TIKA_LOGGER = java.util.logging.Logger.getLogger("Tika"); //NON-NLS
|
||||
private static final Logger AUTOPSY_LOGGER = Logger.getLogger(TikaTextExtractor.class.getName());
|
||||
|
||||
|
@ -356,12 +356,8 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer
|
||||
scene.addEventFilter(KeyEvent.KEY_PRESSED, keyEvent -> {
|
||||
if (new KeyCodeCombination(KeyCode.LEFT, KeyCodeCombination.ALT_DOWN).match(keyEvent)) {
|
||||
new Back(controller).handle(null);
|
||||
} else if (new KeyCodeCombination(KeyCode.BACK_SPACE).match(keyEvent)) {
|
||||
new Back(controller).handle(null);
|
||||
} else if (new KeyCodeCombination(KeyCode.RIGHT, KeyCodeCombination.ALT_DOWN).match(keyEvent)) {
|
||||
new Forward(controller).handle(null);
|
||||
} else if (new KeyCodeCombination(KeyCode.BACK_SPACE, KeyCodeCombination.SHIFT_DOWN).match(keyEvent)) {
|
||||
new Forward(controller).handle(null);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2018 Basis Technology Corp.
|
||||
* Copyright 2018-2019 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -22,7 +22,9 @@ import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.sleuthkit.autopsy.events.AutopsyEvent;
|
||||
import org.sleuthkit.autopsy.ingest.IngestJobSettings;
|
||||
import org.sleuthkit.autopsy.ingest.IngestJobStartResult;
|
||||
@ -35,6 +37,8 @@ import org.sleuthkit.datamodel.Content;
|
||||
*/
|
||||
public final class IngestJobRunner {
|
||||
|
||||
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED);
|
||||
|
||||
/**
|
||||
* Runs an ingest job, blocking until the job is completed.
|
||||
*
|
||||
@ -51,7 +55,7 @@ public final class IngestJobRunner {
|
||||
Object ingestMonitor = new Object();
|
||||
IngestJobCompletiontListener completiontListener = new IngestJobCompletiontListener(ingestMonitor);
|
||||
IngestManager ingestManager = IngestManager.getInstance();
|
||||
ingestManager.addIngestJobEventListener(completiontListener);
|
||||
ingestManager.addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, completiontListener);
|
||||
try {
|
||||
synchronized (ingestMonitor) {
|
||||
IngestJobStartResult jobStartResult = ingestManager.beginIngestJob(dataSources, settings);
|
||||
|
@ -28,6 +28,7 @@ import org.opencv.core.Core;
|
||||
*/
|
||||
public final class OpenCvLoader {
|
||||
|
||||
// Uses java logger since the Autopsy class logger (Autopsy-core) is not part of this module
|
||||
private static final Logger logger = Logger.getLogger(OpenCvLoader.class.getName());
|
||||
private static boolean openCvLoaded;
|
||||
private static UnsatisfiedLinkError exception = null; // Deprecated
|
||||
|
@ -142,6 +142,7 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen
|
||||
ControlEventType.SHUTDOWN.toString(),
|
||||
Event.CANCEL_JOB.toString(),
|
||||
Event.REPROCESS_JOB.toString()}));
|
||||
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED);
|
||||
private static final long JOB_STATUS_EVENT_INTERVAL_SECONDS = 10;
|
||||
private static final String JOB_STATUS_PUBLISHING_THREAD_NAME = "AIM-job-status-event-publisher-%d";
|
||||
private static final long MAX_MISSED_JOB_STATUS_UPDATES = 10;
|
||||
@ -2670,7 +2671,7 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen
|
||||
Path caseDirectoryPath = currentJob.getCaseDirectoryPath();
|
||||
AutoIngestJobLogger jobLogger = new AutoIngestJobLogger(manifestPath, manifest.getDataSourceFileName(), caseDirectoryPath);
|
||||
IngestJobEventListener ingestJobEventListener = new IngestJobEventListener();
|
||||
IngestManager.getInstance().addIngestJobEventListener(ingestJobEventListener);
|
||||
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, ingestJobEventListener);
|
||||
try {
|
||||
synchronized (ingestLock) {
|
||||
IngestJobSettings ingestJobSettings = new IngestJobSettings(AutoIngestUserPreferences.getAutoModeIngestModuleContextString());
|
||||
|
@ -26,7 +26,9 @@ import java.nio.charset.Charset;
|
||||
import java.nio.file.Paths;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
@ -68,6 +70,7 @@ class MultiUserTestTool {
|
||||
private static final Logger LOGGER = Logger.getLogger(MultiUserTestTool.class.getName());
|
||||
private static final String TEST_FILE_NAME = "AutopsyTempFile";
|
||||
private static final Object INGEST_LOCK = new Object();
|
||||
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED);
|
||||
static final String MULTI_USER_TEST_SUCCESSFUL = NbBundle.getMessage(AutoIngestSettingsPanel.class, "AutoIngestSettingsPanel.Success");
|
||||
|
||||
private MultiUserTestTool() {
|
||||
@ -225,10 +228,12 @@ class MultiUserTestTool {
|
||||
/**
|
||||
* Creates a new multi user case.
|
||||
*
|
||||
* @param baseCaseName Case name (will get time stamp appended to it)
|
||||
* @param baseCaseName Case name (will get time stamp appended to it)
|
||||
* @param rootOutputDirectory Full path to directory in which the case will
|
||||
* be created
|
||||
* be created
|
||||
*
|
||||
* @return Case object
|
||||
*
|
||||
* @throws CaseActionException
|
||||
*/
|
||||
private static Case createCase(String baseCaseName, String rootOutputDirectory) throws CaseActionException {
|
||||
@ -251,10 +256,11 @@ class MultiUserTestTool {
|
||||
* @param dataSource The data source.
|
||||
*
|
||||
* @return Error String if there was an error, empty string if the data
|
||||
* source was added successfully
|
||||
* source was added successfully
|
||||
*
|
||||
* @throws InterruptedException if the thread running the job processing
|
||||
* task is interrupted while blocked, i.e., if ingest is shutting down.
|
||||
* task is interrupted while blocked, i.e., if
|
||||
* ingest is shutting down.
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"MultiUserTestTool.noContent=Test data source failed to produce content",
|
||||
@ -298,10 +304,11 @@ class MultiUserTestTool {
|
||||
* @param dataSource The data source to analyze.
|
||||
*
|
||||
* @return Error String if there was an error, empty string if the data
|
||||
* source was analyzed successfully
|
||||
* source was analyzed successfully
|
||||
*
|
||||
* @throws InterruptedException if the thread running the job processing
|
||||
* task is interrupted while blocked, i.e., if auto ingest is shutting down.
|
||||
* task is interrupted while blocked, i.e., if
|
||||
* auto ingest is shutting down.
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"# {0} - cancellationReason",
|
||||
@ -314,7 +321,7 @@ class MultiUserTestTool {
|
||||
|
||||
LOGGER.log(Level.INFO, "Starting ingest modules analysis for {0} ", dataSource.getPath());
|
||||
IngestJobEventListener ingestJobEventListener = new IngestJobEventListener();
|
||||
IngestManager.getInstance().addIngestJobEventListener(ingestJobEventListener);
|
||||
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, ingestJobEventListener);
|
||||
try {
|
||||
synchronized (INGEST_LOCK) {
|
||||
IngestJobSettings ingestJobSettings = new IngestJobSettings(AutoIngestUserPreferences.getAutoModeIngestModuleContextString());
|
||||
@ -324,9 +331,9 @@ class MultiUserTestTool {
|
||||
IngestJob ingestJob = ingestJobStartResult.getJob();
|
||||
if (null != ingestJob) {
|
||||
/*
|
||||
* Block until notified by the ingest job event
|
||||
* listener or until interrupted because auto ingest
|
||||
* is shutting down.
|
||||
* Block until notified by the ingest job event listener
|
||||
* or until interrupted because auto ingest is shutting
|
||||
* down.
|
||||
*/
|
||||
INGEST_LOCK.wait();
|
||||
LOGGER.log(Level.INFO, "Finished ingest modules analysis for {0} ", dataSource.getPath());
|
||||
@ -381,7 +388,7 @@ class MultiUserTestTool {
|
||||
* @return True if the service is running, false otherwise.
|
||||
*
|
||||
* @throws ServicesMonitorException if there is an error querying the
|
||||
* services monitor.
|
||||
* services monitor.
|
||||
*/
|
||||
private static boolean isServiceUp(String serviceName) throws ServicesMonitor.ServicesMonitorException {
|
||||
return (ServicesMonitor.getInstance().getServiceStatus(serviceName).equals(ServicesMonitor.ServiceStatus.UP.toString()));
|
||||
|
@ -89,7 +89,8 @@ import org.sleuthkit.datamodel.TskData;
|
||||
public final class ImageGalleryController {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(ImageGalleryController.class.getName());
|
||||
|
||||
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.DATA_SOURCE_ANALYSIS_STARTED, IngestManager.IngestJobEvent.DATA_SOURCE_ANALYSIS_COMPLETED);
|
||||
private static final Set<IngestManager.IngestModuleEvent> INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestModuleEvent.DATA_ADDED, IngestManager.IngestModuleEvent.FILE_DONE);
|
||||
/*
|
||||
* The file limit for image gallery. If the selected data source (or all
|
||||
* data sources, if that option is selected) has more than this many files
|
||||
@ -267,8 +268,8 @@ public final class ImageGalleryController {
|
||||
dbTaskQueueSize.addListener(obs -> this.updateRegroupDisabled());
|
||||
|
||||
Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, caseEventListener);
|
||||
IngestManager.getInstance().addIngestJobEventListener(ingestJobEventListener);
|
||||
IngestManager.getInstance().addIngestModuleEventListener(ingestModuleEventListener);
|
||||
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, ingestJobEventListener);
|
||||
IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, ingestModuleEventListener);
|
||||
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
topComponent = ImageGalleryTopComponent.getTopComponent();
|
||||
|
@ -35,6 +35,8 @@ OpenAction.multiUserDialog.Header=Multi-user Image Gallery
|
||||
OpenAction.noControllerDialog.header=Cannot open Image Gallery
|
||||
OpenAction.noControllerDialog.text=An initialization error ocurred.\nPlease see the log for details.
|
||||
OpenAction.notAnalyzedDlg.msg=No image/video files available to display yet.\nPlease run FileType and EXIF ingest modules.
|
||||
OpenAction.openTopComponent.error.message=An error occurred while attempting to open Image Gallery.
|
||||
OpenAction.openTopComponent.error.title=Failed to open Image Gallery
|
||||
OpenAction.stale.confDlg.msg=The image / video database may be out of date. Do you want to update and listen for further ingest results?\nChoosing 'yes' will update the database and enable listening to future ingests.
|
||||
OpenAction.stale.confDlg.title=Image Gallery
|
||||
OpenExternalViewerAction.displayName=External Viewer
|
||||
|
@ -34,6 +34,7 @@ import javafx.stage.Modality;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.SwingUtilities;
|
||||
import org.openide.awt.ActionID;
|
||||
import org.openide.awt.ActionReference;
|
||||
@ -43,6 +44,7 @@ import org.openide.util.HelpCtx;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.openide.util.actions.CallableSystemAction;
|
||||
import org.openide.windows.WindowManager;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.core.Installer;
|
||||
@ -292,13 +294,15 @@ public final class OpenAction extends CallableSystemAction {
|
||||
);
|
||||
}
|
||||
|
||||
@Messages({"OpenAction.openTopComponent.error.message=An error occurred while attempting to open Image Gallery.",
|
||||
"OpenAction.openTopComponent.error.title=Failed to open Image Gallery"})
|
||||
private void openTopComponent() {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
try {
|
||||
ImageGalleryTopComponent.openTopComponent();
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Failed to open Image Gallery top component", ex); //NON-NLS}
|
||||
// TODO (JIRA-5217): Give the user some feedback here
|
||||
JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), Bundle.OpenAction_openTopComponent_error_message(), Bundle.OpenAction_openTopComponent_error_title(), JOptionPane.PLAIN_MESSAGE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -572,12 +572,12 @@ public final class DrawableDB {
|
||||
String sql = "CREATE TABLE if not exists drawable_files " //NON-NLS
|
||||
+ "( obj_id BIGINT PRIMARY KEY, " //NON-NLS
|
||||
+ " data_source_obj_id BIGINT NOT NULL, "
|
||||
+ " path VARCHAR(255), " //NON-NLS
|
||||
+ " name VARCHAR(255), " //NON-NLS
|
||||
+ " path TEXT, " //NON-NLS
|
||||
+ " name TEXT, " //NON-NLS
|
||||
+ " created_time integer, " //NON-NLS
|
||||
+ " modified_time integer, " //NON-NLS
|
||||
+ " make VARCHAR(255) DEFAULT NULL, " //NON-NLS
|
||||
+ " model VARCHAR(255) DEFAULT NULL, " //NON-NLS
|
||||
+ " make TEXT DEFAULT NULL, " //NON-NLS
|
||||
+ " model TEXT DEFAULT NULL, " //NON-NLS
|
||||
+ " analyzed integer DEFAULT 0)"; //NON-NLS
|
||||
stmt.execute(sql);
|
||||
} catch (SQLException ex) {
|
||||
@ -588,7 +588,7 @@ public final class DrawableDB {
|
||||
try {
|
||||
String sql = "CREATE TABLE if not exists hash_sets " //NON-NLS
|
||||
+ "( hash_set_id INTEGER primary key," //NON-NLS
|
||||
+ " hash_set_name VARCHAR(255) UNIQUE NOT NULL)"; //NON-NLS
|
||||
+ " hash_set_name TEXT UNIQUE NOT NULL)"; //NON-NLS
|
||||
stmt.execute(sql);
|
||||
} catch (SQLException ex) {
|
||||
logger.log(Level.SEVERE, "Failed to create hash_sets table", ex); //NON-NLS
|
||||
@ -692,8 +692,8 @@ public final class DrawableDB {
|
||||
String tableSchema
|
||||
= "( group_id " + autogenKeyType + " PRIMARY KEY, " //NON-NLS
|
||||
+ " data_source_obj_id BIGINT DEFAULT 0, "
|
||||
+ " value VARCHAR(255) not null, " //NON-NLS
|
||||
+ " attribute VARCHAR(255) not null, " //NON-NLS
|
||||
+ " value TEXT not null, " //NON-NLS
|
||||
+ " attribute TEXT not null, " //NON-NLS
|
||||
+ " is_analyzed integer DEFAULT 0, "
|
||||
+ " UNIQUE(data_source_obj_id, value, attribute) )"; //NON-NLS
|
||||
|
||||
|
143
InternalPythonModules/android/imo.py
Normal file
143
InternalPythonModules/android/imo.py
Normal file
@ -0,0 +1,143 @@
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
|
||||
from java.io import File
|
||||
from java.lang import Class
|
||||
from java.lang import ClassNotFoundException
|
||||
from java.lang import Long
|
||||
from java.lang import String
|
||||
from java.sql import ResultSet
|
||||
from java.sql import SQLException
|
||||
from java.sql import Statement
|
||||
from java.util.logging import Level
|
||||
from java.util import ArrayList
|
||||
from org.apache.commons.codec.binary import Base64
|
||||
from org.sleuthkit.autopsy.casemodule import Case
|
||||
from org.sleuthkit.autopsy.coreutils import Logger
|
||||
from org.sleuthkit.autopsy.coreutils import MessageNotifyUtil
|
||||
from org.sleuthkit.autopsy.coreutils import AppSQLiteDB
|
||||
from org.sleuthkit.autopsy.coreutils import AppDBParserHelper
|
||||
from org.sleuthkit.autopsy.coreutils.AppDBParserHelper import MessageReadStatusEnum
|
||||
from org.sleuthkit.autopsy.coreutils.AppDBParserHelper import CommunicationDirection
|
||||
from org.sleuthkit.autopsy.datamodel import ContentUtils
|
||||
from org.sleuthkit.autopsy.ingest import IngestJobContext
|
||||
from org.sleuthkit.datamodel import AbstractFile
|
||||
from org.sleuthkit.datamodel import BlackboardArtifact
|
||||
from org.sleuthkit.datamodel import BlackboardAttribute
|
||||
from org.sleuthkit.datamodel import Content
|
||||
from org.sleuthkit.datamodel import TskCoreException
|
||||
from org.sleuthkit.datamodel import Account
|
||||
|
||||
import traceback
|
||||
import general
|
||||
|
||||
"""
|
||||
Finds the SQLite DB for IMO, parses the DB for contacts & messages,
|
||||
and adds artifacts to the case.
|
||||
"""
|
||||
class IMOAnalyzer(general.AndroidComponentAnalyzer):
|
||||
def __init__(self):
|
||||
self._logger = Logger.getLogger(self.__class__.__name__)
|
||||
|
||||
def analyze(self, dataSource, fileManager, context):
|
||||
selfAccountAddress = None
|
||||
accountDbs = AppSQLiteDB.findAppDatabases(dataSource, "accountdb.db", True, "com.imo.android.imous")
|
||||
for accountDb in accountDbs:
|
||||
try:
|
||||
accountResultSet = accountDb.runQuery("SELECT uid, name FROM account")
|
||||
if accountResultSet:
|
||||
# We can determine the IMO user ID of the device owner.
|
||||
# Therefore we can create and use a app account and use that
|
||||
# as a 'self' account instead of a Device account
|
||||
if not selfAccountAddress:
|
||||
selfAccountAddress = Account.Address(accountResultSet.getString("uid"), accountResultSet.getString("name"))
|
||||
|
||||
except SQLException as ex:
|
||||
self._logger.log(Level.SEVERE, "Error processing query result for account", ex)
|
||||
finally:
|
||||
accountDb.close()
|
||||
|
||||
friendsDbs = AppSQLiteDB.findAppDatabases(dataSource, "imofriends.db", True, "com.imo.android.imous")
|
||||
for friendsDb in friendsDbs:
|
||||
try:
|
||||
friendsDBHelper = AppDBParserHelper("IMO Parser", friendsDb.getDBFile(),
|
||||
Account.Type.IMO, Account.Type.IMO, selfAccountAddress )
|
||||
contactsResultSet = friendsDb.runQuery("SELECT buid, name FROM friends")
|
||||
if contactsResultSet is not None:
|
||||
while contactsResultSet.next():
|
||||
friendsDBHelper.addContact( contactsResultSet.getString("buid"), ## unique id for account
|
||||
contactsResultSet.getString("name"), ## contact name
|
||||
"", ## phone
|
||||
"", ## home phone
|
||||
"", ## mobile
|
||||
"") ## email
|
||||
queryString = "SELECT messages.buid AS buid, imdata, last_message, timestamp, message_type, message_read, name FROM messages "\
|
||||
"INNER JOIN friends ON friends.buid = messages.buid"
|
||||
messagesResultSet = friendsDb.runQuery(queryString)
|
||||
if messagesResultSet is not None:
|
||||
while messagesResultSet.next():
|
||||
direction = ""
|
||||
fromAddress = None
|
||||
toAddress = None
|
||||
name = messagesResultSet.getString("name")
|
||||
uniqueId = messagesResultSet.getString("buid")
|
||||
|
||||
if (messagesResultSet.getInt("message_type") == 1):
|
||||
direction = CommunicationDirection.INCOMING
|
||||
fromAddress = Account.Address(uniqueId, name)
|
||||
else:
|
||||
direction = CommunicationDirection.OUTGOING
|
||||
toAddress = Account.Address(uniqueId, name)
|
||||
|
||||
|
||||
message_read = messagesResultSet.getInt("message_read")
|
||||
if (message_read == 1):
|
||||
msgReadStatus = MessageReadStatusEnum.READ
|
||||
elif (message_read == 0):
|
||||
msgReadStatus = MessageReadStatusEnum.UNREAD
|
||||
else:
|
||||
msgReadStatus = MessageReadStatusEnum.UNKNOWN
|
||||
|
||||
timeStamp = messagesResultSet.getLong("timestamp") / 1000000000
|
||||
|
||||
|
||||
messageArtifact = friendsDBHelper.addMessage(
|
||||
"IMO Message",
|
||||
direction,
|
||||
fromAddress,
|
||||
toAddress,
|
||||
timeStamp,
|
||||
msgReadStatus,
|
||||
"", # subject
|
||||
messagesResultSet.getString("last_message"),
|
||||
"") # thread id
|
||||
|
||||
# TBD: parse the imdata JSON structure to figure out if there is an attachment.
|
||||
# If one exists, add the attachment as a derived file and a child of the message artifact.
|
||||
|
||||
|
||||
except SQLException as ex:
|
||||
self._logger.log(Level.WARNING, "Error processing query result for IMO friends", ex)
|
||||
except TskCoreException as ex:
|
||||
self._logger.log(Level.WARNING, "Failed to create AppDBParserHelper for adding artifacts.", ex)
|
||||
finally:
|
||||
friendsDb.close()
|
||||
|
||||
|
||||
|
@ -46,6 +46,7 @@ import googlemaplocation
|
||||
import tangomessage
|
||||
import textmessage
|
||||
import wwfmessage
|
||||
import imo
|
||||
|
||||
class AndroidModuleFactory(IngestModuleFactoryAdapter):
|
||||
|
||||
@ -87,7 +88,10 @@ class AndroidIngestModule(DataSourceIngestModule):
|
||||
|
||||
errors = []
|
||||
fileManager = Case.getCurrentCase().getServices().getFileManager()
|
||||
analyzers = [contact.ContactAnalyzer(), calllog.CallLogAnalyzer(), textmessage.TextMessageAnalyzer(), tangomessage.TangoMessageAnalyzer(), wwfmessage.WWFMessageAnalyzer(), googlemaplocation.GoogleMapLocationAnalyzer(), browserlocation.BrowserLocationAnalyzer(), cachelocation.CacheLocationAnalyzer()]
|
||||
analyzers = [contact.ContactAnalyzer(), calllog.CallLogAnalyzer(), textmessage.TextMessageAnalyzer(),
|
||||
tangomessage.TangoMessageAnalyzer(), wwfmessage.WWFMessageAnalyzer(),
|
||||
googlemaplocation.GoogleMapLocationAnalyzer(), browserlocation.BrowserLocationAnalyzer(),
|
||||
cachelocation.CacheLocationAnalyzer(), imo.IMOAnalyzer()]
|
||||
self.log(Level.INFO, "running " + str(len(analyzers)) + " analyzers")
|
||||
progressBar.switchToDeterminate(len(analyzers))
|
||||
|
||||
|
@ -483,6 +483,7 @@ final class IngestSearchRunner {
|
||||
if (progressGroup != null) {
|
||||
progressGroup.setDisplayName(displayName + " " + NbBundle.getMessage(this.getClass(), "SearchRunner.doInBackGround.cancelMsg"));
|
||||
}
|
||||
progressGroup.finish();
|
||||
return IngestSearchRunner.Searcher.this.cancel(true);
|
||||
}
|
||||
}, null);
|
||||
|
45
NEWS.txt
45
NEWS.txt
@ -1,3 +1,48 @@
|
||||
---------------- VERSION 4.12.0 --------------
|
||||
Collection
|
||||
- Added ability to configure a USB drive to use new logical imager tool.
|
||||
- Added logical imager tool that runs on a live Windows computer and saves results to a USB drive.
|
||||
- Added ability to import logical imager results into Autopsy as a data source.
|
||||
|
||||
Ingest Modules:
|
||||
- Changed file type detection so that Tika does not rely only on extension.
|
||||
- Email ingest module assigns thread IDs to messages
|
||||
- Android ingest modules store thread ID from their databases.
|
||||
|
||||
Content Viewers (lower right of UI):
|
||||
- New “Text” viewer that consolidates previous Strings and “Indexed Text” viewers.
|
||||
- New “Translation” panel was added to the new “Text” viewer.
|
||||
- Added integration with Google and Bing translation (credentials required)
|
||||
- Redesigned “Other Occurrences” viewer to have 4th column with details of selected item.
|
||||
- Added Willi Ballentin’s “Registry Hive Viewer” panel to the “Application” viewer.
|
||||
- Improved HTML viewer to use style sheets and better layout.
|
||||
- Added ability to draw a box on a picture while tagging it.
|
||||
|
||||
Result Table (upper right of UI)
|
||||
- Added paging to all views for faster loading of large data sets.
|
||||
- Improved speed of displaying results when a column was sorted.
|
||||
|
||||
Reporting
|
||||
- Portable cases can contain files marked as Interesting Items
|
||||
- Portable cases can be compressed and chunked
|
||||
- “Files - Text” report can use either tabs or commas as the delimiter
|
||||
- “Files - Text” report better handles Unicode text.
|
||||
- Added ability to create a CSV report for the contents of a table
|
||||
- HTML report for tagged pictures includes a copy with the overlay box
|
||||
|
||||
Communications:
|
||||
- Added Account Summary view
|
||||
- Added Contacts panel to show all contacts associated with an account.
|
||||
- Added Media panel to show media attachments associated with an account
|
||||
- Added filter to show accounts if they involved with the most recent messages.
|
||||
- Messages can be grouped by thread.
|
||||
|
||||
Auto Ingest
|
||||
- New Test button was added to help diagnose permission and configuration issues.
|
||||
|
||||
Documentation:
|
||||
- Created new Triage Standard Operating Procedure (SOP) section to the User Docs
|
||||
|
||||
---------------- VERSION 4.11.0 --------------
|
||||
New Features:
|
||||
|
||||
|
@ -236,6 +236,10 @@ abstract class Extract {
|
||||
return moduleName;
|
||||
}
|
||||
|
||||
protected String getRAModuleName() {
|
||||
return RecentActivityExtracterModuleFactory.getModuleName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the state of foundData
|
||||
* @return
|
||||
|
@ -54,6 +54,7 @@ import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.SAXException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Collection;
|
||||
@ -66,8 +67,6 @@ import static java.util.TimeZone.getTimeZone;
|
||||
import org.openide.util.Lookup;
|
||||
import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress;
|
||||
import org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException;
|
||||
import org.sleuthkit.autopsy.ingest.IngestServices;
|
||||
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
|
||||
import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchService;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
@ -90,6 +89,36 @@ import org.sleuthkit.datamodel.TskCoreException;
|
||||
})
|
||||
class ExtractRegistry extends Extract {
|
||||
|
||||
private static final String USERNAME_KEY = "Username"; //NON-NLS
|
||||
private static final String SID_KEY = "SID"; //NON-NLS
|
||||
private static final String RID_KEY = "RID"; //NON-NLS
|
||||
private static final String ACCOUNT_CREATED_KEY = "Account Created"; //NON-NLS
|
||||
private static final String LAST_LOGIN_KEY = "Last Login Date"; //NON-NLS
|
||||
private static final String LOGIN_COUNT_KEY = "Login Count"; //NON-NLS
|
||||
private static final String FULL_NAME_KEY = "Full Name"; //NON-NLS
|
||||
private static final String USER_COMMENT_KEY = "User Comment"; //NON-NLS
|
||||
private static final String ACCOUNT_TYPE_KEY = "Account Type"; //NON-NLS
|
||||
private static final String NAME_KEY = "Name"; //NON-NLS
|
||||
private static final String PWD_RESET_KEY = "Pwd Rest Date"; //NON-NLS
|
||||
private static final String PWD_FAILE_KEY = "Pwd Fail Date"; //NON-NLS
|
||||
private static final String INTERNET_NAME_KEY = "InternetName"; //NON-NLS
|
||||
private static final String PWD_DOES_NOT_EXPIRE_KEY = "Password does not expire"; //NON-NLS
|
||||
private static final String ACCOUNT_DISABLED_KEY = "Account Disabled"; //NON-NLS
|
||||
private static final String PWD_NOT_REQUIRED_KEY = "Password not required"; //NON-NLS
|
||||
private static final String NORMAL_ACCOUNT_KEY = "Normal user account"; //NON-NLS
|
||||
private static final String HOME_DIRECTORY_REQUIRED_KEY = "Home directory required";
|
||||
private static final String TEMPORARY_DUPLICATE_ACCOUNT = "Temporary duplicate account";
|
||||
private static final String MNS_LOGON_ACCOUNT_KEY = "MNS logon user account";
|
||||
private static final String INTERDOMAIN_TRUST_ACCOUNT_KEY = "Interdomain trust account";
|
||||
private static final String WORKSTATION_TRUST_ACCOUNT = "Workstation trust account";
|
||||
private static final String SERVER_TRUST_ACCOUNT = "Server trust account";
|
||||
private static final String ACCOUNT_AUTO_LOCKED = "Account auto locked";
|
||||
private static final String PASSWORD_HINT = "Password Hint";
|
||||
|
||||
private static final String[] PASSWORD_SETTINGS_FLAGS = {PWD_DOES_NOT_EXPIRE_KEY, PWD_NOT_REQUIRED_KEY};
|
||||
private static final String[] ACCOUNT_SETTINGS_FLAGS = {ACCOUNT_AUTO_LOCKED, HOME_DIRECTORY_REQUIRED_KEY, ACCOUNT_DISABLED_KEY};
|
||||
private static final String[] ACCOUNT_TYPE_FLAGS = {NORMAL_ACCOUNT_KEY, SERVER_TRUST_ACCOUNT, WORKSTATION_TRUST_ACCOUNT, INTERDOMAIN_TRUST_ACCOUNT_KEY, MNS_LOGON_ACCOUNT_KEY, TEMPORARY_DUPLICATE_ACCOUNT};
|
||||
|
||||
final private static UsbDeviceIdMapper USB_MAPPER = new UsbDeviceIdMapper();
|
||||
final private static String RIP_EXE = "rip.exe";
|
||||
final private static String RIP_PL = "rip.pl";
|
||||
@ -840,27 +869,29 @@ class ExtractRegistry extends Extract {
|
||||
*/
|
||||
private boolean parseSamPluginOutput(String regFilePath, AbstractFile regAbstractFile) {
|
||||
File regfile = new File(regFilePath);
|
||||
String parentModuleName = RecentActivityExtracterModuleFactory.getModuleName();
|
||||
SimpleDateFormat regRipperTimeFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy 'Z'");
|
||||
regRipperTimeFormat.setTimeZone(getTimeZone("GMT"));
|
||||
try (BufferedReader bufferedReader = new BufferedReader(new FileReader(regfile))) {
|
||||
// Read the file in and create a Document and elements
|
||||
String userInfoSection = "User Information";
|
||||
String previousLine = null;
|
||||
String line = bufferedReader.readLine();
|
||||
Set<UserInfo> userSet = new HashSet<>();
|
||||
Set<Map<String, String>> userSet = new HashSet<>();
|
||||
Map<String, List<String>> groupMap = null;
|
||||
while (line != null) {
|
||||
if (line.contains(SECTION_DIVIDER) && previousLine != null && previousLine.contains(userInfoSection)) {
|
||||
readUsers(bufferedReader, userSet);
|
||||
|
||||
}
|
||||
|
||||
if (line.contains(SECTION_DIVIDER) && previousLine != null && previousLine.contains("Group Membership Information")) {
|
||||
groupMap = readGroups(bufferedReader);
|
||||
}
|
||||
|
||||
previousLine = line;
|
||||
line = bufferedReader.readLine();
|
||||
}
|
||||
Map<String, UserInfo> userInfoMap = new HashMap<>();
|
||||
Map<String, Map<String, String>> userInfoMap = new HashMap<>();
|
||||
//load all the user info which was read into a map
|
||||
for (UserInfo userInfo : userSet) {
|
||||
userInfoMap.put(userInfo.getUserSid(), userInfo);
|
||||
for (Map<String, String> userInfo : userSet) {
|
||||
userInfoMap.put(userInfo.get(SID_KEY), userInfo);
|
||||
}
|
||||
//get all existing OS account artifacts
|
||||
List<BlackboardArtifact> existingOsAccounts = tskCase.getBlackboardArtifacts(ARTIFACT_TYPE.TSK_OS_ACCOUNT);
|
||||
@ -869,45 +900,19 @@ class ExtractRegistry extends Extract {
|
||||
if (osAccount.getDataSource().getId() == regAbstractFile.getDataSourceObjectId()) {
|
||||
BlackboardAttribute existingUserId = osAccount.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_USER_ID));
|
||||
if (existingUserId != null) {
|
||||
UserInfo userInfo = userInfoMap.remove(existingUserId.getValueString().trim());
|
||||
String userID = existingUserId.getValueString().trim();
|
||||
Map<String, String> userInfo = userInfoMap.remove(userID);
|
||||
//if the existing user id matches a user id which we parsed information for check if that information exists and if it doesn't add it
|
||||
if (userInfo != null) {
|
||||
Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
|
||||
if (userInfo.getAccountCreatedDate() != null && !userInfo.getAccountCreatedDate().equals(NEVER_DATE)) {
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
|
||||
parentModuleName, regRipperTimeFormat.parse(userInfo.getAccountCreatedDate()).getTime() / MS_IN_SEC));
|
||||
}
|
||||
if (userInfo.getLastLoginDate() != null && !userInfo.getLastLoginDate().equals(NEVER_DATE)) {
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
|
||||
parentModuleName, regRipperTimeFormat.parse(userInfo.getLastLoginDate()).getTime() / MS_IN_SEC));
|
||||
}
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COUNT,
|
||||
parentModuleName, userInfo.getLoginCount()));
|
||||
osAccount.addAttributes(bbattributes);
|
||||
osAccount.addAttributes(getAttributesForAccount(userInfo, groupMap.get(userID), true));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//add remaining userinfos as accounts;
|
||||
for (String userId : userInfoMap.keySet()) {
|
||||
UserInfo userInfo = userInfoMap.get(userId);
|
||||
Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
|
||||
for (Map<String, String> userInfo : userInfoMap.values()) {
|
||||
BlackboardArtifact bbart = regAbstractFile.newArtifact(ARTIFACT_TYPE.TSK_OS_ACCOUNT);
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_NAME,
|
||||
parentModuleName, userInfo.getUserName()));
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_ID,
|
||||
parentModuleName, userId));
|
||||
if (userInfo.getAccountCreatedDate() != null && !userInfo.getAccountCreatedDate().equals(NEVER_DATE)) {
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
|
||||
parentModuleName, regRipperTimeFormat.parse(userInfo.getAccountCreatedDate()).getTime() / MS_IN_SEC));
|
||||
}
|
||||
if (userInfo.getLastLoginDate() != null && !userInfo.getLastLoginDate().equals(NEVER_DATE)) {
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
|
||||
parentModuleName, regRipperTimeFormat.parse(userInfo.getLastLoginDate()).getTime() / MS_IN_SEC));
|
||||
}
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COUNT,
|
||||
parentModuleName, userInfo.getLoginCount()));
|
||||
bbart.addAttributes(bbattributes);
|
||||
bbart.addAttributes(getAttributesForAccount(userInfo, groupMap.get(userInfo.get(SID_KEY)), false));
|
||||
// index the artifact for keyword search
|
||||
postArtifact(bbart);
|
||||
}
|
||||
@ -925,6 +930,149 @@ class ExtractRegistry extends Extract {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the attribute list for the given user information and group list.
|
||||
*
|
||||
* @param userInfo Map of key\value pairs of user information
|
||||
* @param groupList List of the groups that user belongs
|
||||
* @param existingUser
|
||||
*
|
||||
* @return List
|
||||
*
|
||||
* @throws ParseException
|
||||
*/
|
||||
Collection<BlackboardAttribute> getAttributesForAccount(Map<String, String> userInfo, List<String> groupList, boolean existingUser) throws ParseException {
|
||||
Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
|
||||
|
||||
SimpleDateFormat regRipperTimeFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy 'Z'");
|
||||
regRipperTimeFormat.setTimeZone(getTimeZone("GMT"));
|
||||
|
||||
if (!existingUser) {
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_ID,
|
||||
getRAModuleName(), userInfo.get(SID_KEY)));
|
||||
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_NAME,
|
||||
this.moduleName, userInfo.get(USERNAME_KEY)));
|
||||
}
|
||||
|
||||
String value = userInfo.get(ACCOUNT_CREATED_KEY);
|
||||
if (value != null && !value.isEmpty() && !value.equals(NEVER_DATE)) {
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
|
||||
getRAModuleName(), regRipperTimeFormat.parse(value).getTime() / MS_IN_SEC));
|
||||
}
|
||||
|
||||
value = userInfo.get(LAST_LOGIN_KEY);
|
||||
if (value != null && !value.isEmpty() && !value.equals(NEVER_DATE)) {
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
|
||||
getRAModuleName(), regRipperTimeFormat.parse(value).getTime() / MS_IN_SEC));
|
||||
}
|
||||
|
||||
value = userInfo.get(LOGIN_COUNT_KEY);
|
||||
if (value != null && !value.isEmpty()) {
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COUNT,
|
||||
getRAModuleName(), Integer.parseInt(value)));
|
||||
}
|
||||
|
||||
value = userInfo.get(ACCOUNT_TYPE_KEY);
|
||||
if (value != null && !value.isEmpty()) {
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE,
|
||||
getRAModuleName(), value));
|
||||
}
|
||||
|
||||
value = userInfo.get(USER_COMMENT_KEY);
|
||||
if (value != null && !value.isEmpty()) {
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DESCRIPTION,
|
||||
getRAModuleName(), value));
|
||||
}
|
||||
|
||||
value = userInfo.get(NAME_KEY);
|
||||
if (value != null && !value.isEmpty()) {
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME,
|
||||
getRAModuleName(), value));
|
||||
}
|
||||
|
||||
value = userInfo.get(INTERNET_NAME_KEY);
|
||||
if (value != null && !value.isEmpty()) {
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_EMAIL,
|
||||
getRAModuleName(), value));
|
||||
}
|
||||
|
||||
value = userInfo.get(FULL_NAME_KEY);
|
||||
if (value != null && !value.isEmpty()) {
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DISPLAY_NAME,
|
||||
getRAModuleName(), value));
|
||||
}
|
||||
|
||||
value = userInfo.get(PWD_RESET_KEY);
|
||||
if (value != null && !value.isEmpty() && !value.equals(NEVER_DATE)) {
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_PASSWORD_RESET,
|
||||
getRAModuleName(), regRipperTimeFormat.parse(value).getTime() / MS_IN_SEC));
|
||||
}
|
||||
|
||||
value = userInfo.get(PASSWORD_HINT);
|
||||
if (value != null && !value.isEmpty()) {
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PASSWORD_HINT,
|
||||
getRAModuleName(), value));
|
||||
}
|
||||
|
||||
value = userInfo.get(PWD_FAILE_KEY);
|
||||
if (value != null && !value.isEmpty() && !value.equals(NEVER_DATE)) {
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_PASSWORD_FAIL,
|
||||
getRAModuleName(), regRipperTimeFormat.parse(value).getTime() / MS_IN_SEC));
|
||||
}
|
||||
|
||||
String settingString = "";
|
||||
for (String setting : PASSWORD_SETTINGS_FLAGS) {
|
||||
if (userInfo.containsKey(setting)) {
|
||||
settingString += setting + ", ";
|
||||
}
|
||||
}
|
||||
|
||||
if (!settingString.isEmpty()) {
|
||||
settingString = settingString.substring(0, settingString.length() - 2);
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PASSWORD_SETTINGS,
|
||||
getRAModuleName(), settingString));
|
||||
}
|
||||
|
||||
settingString = "";
|
||||
for (String setting : ACCOUNT_SETTINGS_FLAGS) {
|
||||
if (userInfo.containsKey(setting)) {
|
||||
settingString += setting + ", ";
|
||||
}
|
||||
}
|
||||
|
||||
if (!settingString.isEmpty()) {
|
||||
settingString = settingString.substring(0, settingString.length() - 2);
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_ACCOUNT_SETTINGS,
|
||||
getRAModuleName(), settingString));
|
||||
}
|
||||
|
||||
settingString = "";
|
||||
for (String setting : ACCOUNT_TYPE_FLAGS) {
|
||||
if (userInfo.containsKey(setting)) {
|
||||
settingString += setting + ", ";
|
||||
}
|
||||
}
|
||||
|
||||
if (!settingString.isEmpty()) {
|
||||
settingString = settingString.substring(0, settingString.length() - 2);
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_FLAG,
|
||||
getRAModuleName(), settingString));
|
||||
}
|
||||
|
||||
if (groupList != null && groupList.isEmpty()) {
|
||||
String groups = "";
|
||||
for (String group : groupList) {
|
||||
groups += group + ", ";
|
||||
}
|
||||
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_GROUPS,
|
||||
getRAModuleName(), groups.substring(0, groups.length() - 2)));
|
||||
}
|
||||
|
||||
return bbattributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the User Information section of the SAM regripper plugin's output
|
||||
* and collect user account information from the file.
|
||||
@ -936,42 +1084,125 @@ class ExtractRegistry extends Extract {
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
private void readUsers(BufferedReader bufferedReader, Set<UserInfo> users) throws IOException {
|
||||
String userNameLabel = "Username :";
|
||||
String sidLabel = "SID :";
|
||||
String accountCreatedLabel = "Account Created :";
|
||||
String loginCountLabel = "Login Count :";
|
||||
String lastLoginLabel = "Last Login Date :";
|
||||
private void readUsers(BufferedReader bufferedReader, Set<Map<String, String>> users) throws IOException {
|
||||
String line = bufferedReader.readLine();
|
||||
//read until end of file or next section divider
|
||||
String userName = "";
|
||||
String user_rid = "";
|
||||
while (line != null && !line.contains(SECTION_DIVIDER)) {
|
||||
//when a user name field exists read the name and id number
|
||||
if (line.contains(userNameLabel)) {
|
||||
String userNameAndIdString = line.replace(userNameLabel, "");
|
||||
if (line.contains(USERNAME_KEY)) {
|
||||
String regx = USERNAME_KEY + "\\s*?:";
|
||||
String userNameAndIdString = line.replaceAll(regx, "");
|
||||
userName = userNameAndIdString.substring(0, userNameAndIdString.lastIndexOf('[')).trim();
|
||||
} else if (line.contains(sidLabel) && !userName.isEmpty()) {
|
||||
String sid = line.replace(sidLabel, "").trim();
|
||||
UserInfo userInfo = new UserInfo(userName, sid);
|
||||
user_rid = userNameAndIdString.substring(userNameAndIdString.lastIndexOf('['), userNameAndIdString.lastIndexOf(']'));
|
||||
} else if (line.contains(SID_KEY) && !userName.isEmpty()) {
|
||||
Map.Entry<String, String> entry = getSAMKeyValue(line);
|
||||
|
||||
HashMap<String, String> userInfo = new HashMap<>();
|
||||
userInfo.put(USERNAME_KEY, userName);
|
||||
userInfo.put(RID_KEY, user_rid);
|
||||
userInfo.put(entry.getKey(), entry.getValue());
|
||||
|
||||
//continue reading this users information until end of file or a blank line between users
|
||||
line = bufferedReader.readLine();
|
||||
while (line != null && !line.isEmpty()) {
|
||||
if (line.contains(accountCreatedLabel)) {
|
||||
userInfo.setAccountCreatedDate(line.replace(accountCreatedLabel, "").trim());
|
||||
} else if (line.contains(loginCountLabel)) {
|
||||
userInfo.setLoginCount(Integer.parseInt(line.replace(loginCountLabel, "").trim()));
|
||||
} else if (line.contains(lastLoginLabel)) {
|
||||
userInfo.setLastLoginDate(line.replace(lastLoginLabel, "").trim());
|
||||
}
|
||||
entry = getSAMKeyValue(line);
|
||||
userInfo.put(entry.getKey(), entry.getValue());
|
||||
line = bufferedReader.readLine();
|
||||
}
|
||||
users.add(userInfo);
|
||||
|
||||
userName = "";
|
||||
}
|
||||
line = bufferedReader.readLine();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps the user groups to the sid that are a part of them.
|
||||
*
|
||||
* @param bufferedReader
|
||||
*
|
||||
* @return A map if sid and the groups they map too
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
Map<String, List<String>> readGroups(BufferedReader bufferedReader) throws IOException {
|
||||
Map<String, List<String>> groupMap = new HashMap<>();
|
||||
|
||||
String line = bufferedReader.readLine();
|
||||
|
||||
int userCount = 0;
|
||||
String groupName = null;
|
||||
|
||||
while (line != null && !line.contains(SECTION_DIVIDER)) {
|
||||
|
||||
if (line.contains("Group Name")) {
|
||||
String value = line.replaceAll("Group Name\\s*?:", "").trim();
|
||||
groupName = (value.replaceAll("\\[\\d*?\\]", "")).trim();
|
||||
int startIndex = value.indexOf('[');
|
||||
int endIndex = value.indexOf(']');
|
||||
|
||||
if (startIndex != -1 && endIndex != -1) {
|
||||
String countStr = value.substring(startIndex + 1, endIndex);
|
||||
userCount = Integer.parseInt(countStr);
|
||||
}
|
||||
} else if (line.matches("Users\\s*?:")) {
|
||||
for (int i = 0; i < userCount; i++) {
|
||||
line = bufferedReader.readLine();
|
||||
if (line != null) {
|
||||
String sid = line.trim();
|
||||
List<String> groupList = groupMap.get(sid);
|
||||
if (groupList == null) {
|
||||
groupList = new ArrayList<>();
|
||||
groupMap.put(sid, groupList);
|
||||
}
|
||||
groupList.add(groupName);
|
||||
}
|
||||
}
|
||||
groupName = null;
|
||||
}
|
||||
line = bufferedReader.readLine();
|
||||
}
|
||||
return groupMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the key value from user account strings of the format
|
||||
* key:value or
|
||||
* --> value
|
||||
*
|
||||
* @param line String to parse
|
||||
*
|
||||
* @return key value pair
|
||||
*/
|
||||
private Map.Entry<String, String> getSAMKeyValue(String line) {
|
||||
int index = line.indexOf(':');
|
||||
Map.Entry<String, String> returnValue = null;
|
||||
String key = null;
|
||||
String value = null;
|
||||
|
||||
if (index != -1) {
|
||||
key = line.substring(0, index).trim();
|
||||
if (index + 1 < line.length()) {
|
||||
value = line.substring(index + 1).trim();
|
||||
} else {
|
||||
value = "";
|
||||
}
|
||||
|
||||
} else if (line.contains("-->")) {
|
||||
key = line.replace("-->", "").trim();
|
||||
value = "true";
|
||||
}
|
||||
|
||||
if (key != null) {
|
||||
returnValue = new AbstractMap.SimpleEntry<>(key, value);
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(Content dataSource, IngestJobContext context, DataSourceIngestModuleProgress progressBar) {
|
||||
this.dataSource = dataSource;
|
||||
@ -990,101 +1221,4 @@ class ExtractRegistry extends Extract {
|
||||
public String autopsyPlugins = "";
|
||||
public String fullPlugins = "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Class for organizing information associated with a TSK_OS_ACCOUNT before
|
||||
* the artifact is created.
|
||||
*/
|
||||
private class UserInfo {
|
||||
|
||||
private final String userName;
|
||||
private final String userSid;
|
||||
private String lastLoginDate;
|
||||
private String accountCreatedDate;
|
||||
private int loginCount = 0;
|
||||
|
||||
/**
|
||||
* Create a UserInfo object
|
||||
*
|
||||
* @param name - the os user account name
|
||||
* @param userSidString - the SID for the user account
|
||||
*/
|
||||
private UserInfo(String name, String userSidString) {
|
||||
userName = name;
|
||||
userSid = userSidString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user name.
|
||||
*
|
||||
* @return the userName
|
||||
*/
|
||||
String getUserName() {
|
||||
return userName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user SID.
|
||||
*
|
||||
* @return the user SID
|
||||
*/
|
||||
String getUserSid() {
|
||||
return userSid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last login date for the user
|
||||
*
|
||||
* @return the lastLoginDate
|
||||
*/
|
||||
String getLastLoginDate() {
|
||||
return lastLoginDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the last login date for the users
|
||||
*
|
||||
* @param lastLoginDate the lastLoginDate to set
|
||||
*/
|
||||
void setLastLoginDate(String lastLoginDate) {
|
||||
this.lastLoginDate = lastLoginDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the account creation date.
|
||||
*
|
||||
* @return the accountCreatedDate
|
||||
*/
|
||||
String getAccountCreatedDate() {
|
||||
return accountCreatedDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the account creation date.
|
||||
*
|
||||
* @param accountCreatedDate the accountCreatedDate to set
|
||||
*/
|
||||
void setAccountCreatedDate(String accountCreatedDate) {
|
||||
this.accountCreatedDate = accountCreatedDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of times the user logged in.
|
||||
*
|
||||
* @return the loginCount
|
||||
*/
|
||||
int getLoginCount() {
|
||||
return loginCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the number of times the user logged in.
|
||||
*
|
||||
* @param loginCount the loginCount to set
|
||||
*/
|
||||
void setLoginCount(int loginCount) {
|
||||
this.loginCount = loginCount;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -31,8 +31,8 @@ import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.logging.Level;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.text.JTextComponent;
|
||||
@ -66,7 +66,7 @@ import org.sleuthkit.datamodel.TskData;
|
||||
|
||||
public class AutopsyTestCases {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(AutopsyTestCases.class.getName());
|
||||
private static final Logger logger = Logger.getLogger(AutopsyTestCases.class.getName()); // DO NOT USE AUTOPSY LOGGER
|
||||
private long start;
|
||||
|
||||
/**
|
||||
|
@ -40,7 +40,7 @@ import org.netbeans.junit.NbModuleSuite;
|
||||
*/
|
||||
public class RegressionTest extends TestCase {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(RegressionTest.class.getName());
|
||||
private static final Logger logger = Logger.getLogger(RegressionTest.class.getName()); // DO NOT USE AUTOPSY LOGGER
|
||||
private static final AutopsyTestCases autopsyTests = new AutopsyTestCases(Boolean.parseBoolean(System.getProperty("isMultiUser")));
|
||||
|
||||
/**
|
||||
|
@ -77,7 +77,7 @@
|
||||
</delete>
|
||||
|
||||
<delete includeemptydirs="true" failonerror="false">
|
||||
<fileset dir="${basedir}/docs/doxygen-dev/dev-docs" includes="**/*"/>
|
||||
<fileset dir="${basedir}/docs/doxygen-dev/build-docs" includes="**/*"/>
|
||||
</delete>
|
||||
</target>
|
||||
|
||||
@ -265,7 +265,7 @@
|
||||
<fileset dir="${basedir}/docs/doxygen-user/user-docs" includes="**/*"/>
|
||||
</delete>
|
||||
<delete includeemptydirs="true" failonerror="false">
|
||||
<fileset dir="${basedir}/docs/doxygen-dev/dev-docs" includes="**/*"/>
|
||||
<fileset dir="${basedir}/docs/doxygen-dev/build-docs" includes="**/*"/>
|
||||
</delete>
|
||||
|
||||
<!-- Generate new -->
|
||||
|
@ -58,7 +58,7 @@ PROJECT_LOGO =
|
||||
# entered, it will be relative to the location where doxygen was started. If
|
||||
# left blank the current directory will be used.
|
||||
|
||||
OUTPUT_DIRECTORY = dev-docs
|
||||
OUTPUT_DIRECTORY = build-docs
|
||||
|
||||
# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-
|
||||
# directories (in 2 levels) under the output directory of each output format and
|
||||
|
@ -322,6 +322,7 @@ class TskDbDiff(object):
|
||||
id_fs_info_table = build_id_fs_info_table(conn.cursor(), isMultiUser)
|
||||
id_objects_table = build_id_objects_table(conn.cursor(), isMultiUser)
|
||||
id_artifact_types_table = build_id_artifact_types_table(conn.cursor(), isMultiUser)
|
||||
id_legacy_artifact_types = build_id_legacy_artifact_types_table(conn.cursor(), isMultiUser)
|
||||
id_reports_table = build_id_reports_table(conn.cursor(), isMultiUser)
|
||||
id_images_table = build_id_image_names_table(conn.cursor(), isMultiUser)
|
||||
id_obj_path_table = build_id_obj_path_table(id_files_table, id_objects_table, id_artifact_types_table, id_reports_table, id_images_table)
|
||||
@ -347,7 +348,7 @@ class TskDbDiff(object):
|
||||
if 'INSERT INTO image_gallery_groups_seen' in dump_line:
|
||||
dump_line = ''
|
||||
continue;
|
||||
dump_line = normalize_db_entry(dump_line, id_obj_path_table, id_vs_parts_table, id_vs_info_table, id_fs_info_table, id_objects_table, id_reports_table, id_images_table)
|
||||
dump_line = normalize_db_entry(dump_line, id_obj_path_table, id_vs_parts_table, id_vs_info_table, id_fs_info_table, id_objects_table, id_reports_table, id_images_table, id_legacy_artifact_types)
|
||||
db_log.write('%s\n' % dump_line)
|
||||
dump_line = ''
|
||||
postgreSQL_db.close()
|
||||
@ -361,7 +362,7 @@ class TskDbDiff(object):
|
||||
for line in conn.iterdump():
|
||||
if 'INSERT INTO "image_gallery_groups_seen"' in line:
|
||||
continue
|
||||
line = normalize_db_entry(line, id_obj_path_table, id_vs_parts_table, id_vs_info_table, id_fs_info_table, id_objects_table, id_reports_table, id_images_table)
|
||||
line = normalize_db_entry(line, id_obj_path_table, id_vs_parts_table, id_vs_info_table, id_fs_info_table, id_objects_table, id_reports_table, id_images_table, id_legacy_artifact_types)
|
||||
db_log.write('%s\n' % line)
|
||||
# Now sort the file
|
||||
srtcmdlst = ["sort", dump_file, "-o", dump_file]
|
||||
@ -414,7 +415,7 @@ class PGSettings(object):
|
||||
return self.password
|
||||
|
||||
|
||||
def normalize_db_entry(line, files_table, vs_parts_table, vs_info_table, fs_info_table, objects_table, reports_table, images_table):
|
||||
def normalize_db_entry(line, files_table, vs_parts_table, vs_info_table, fs_info_table, objects_table, reports_table, images_table, artifact_table):
|
||||
""" Make testing more consistent and reasonable by doctoring certain db entries.
|
||||
|
||||
Args:
|
||||
@ -591,10 +592,15 @@ def normalize_db_entry(line, files_table, vs_parts_table, vs_info_table, fs_info
|
||||
# replace object ids with information that is deterministic
|
||||
file_obj_id = int(fields_list[5])
|
||||
object_id = int(fields_list[4])
|
||||
legacy_artifact_id = 'NULL'
|
||||
if (fields_list[6] != 'NULL'):
|
||||
legacy_artifact_id = int(fields_list[6])
|
||||
if file_obj_id != 'NULL' and file_obj_id in files_table.keys():
|
||||
fields_list[5] = files_table[file_obj_id]
|
||||
if object_id != 'NULL' and object_id in files_table.keys():
|
||||
fields_list[4] = files_table[object_id]
|
||||
if legacy_artifact_id != 'NULL' and legacy_artifact_id in artifact_table.keys():
|
||||
fields_list[6] = artifact_table[legacy_artifact_id]
|
||||
newLine = ('INSERT INTO "tsk_event_descriptions" VALUES(' + ','.join(fields_list[1:]) + ');') # remove report_id
|
||||
return newLine
|
||||
else:
|
||||
@ -689,6 +695,17 @@ def build_id_artifact_types_table(db_cursor, isPostgreSQL):
|
||||
mapping = dict([(row[0], row[1]) for row in sql_select_execute(db_cursor, isPostgreSQL, "SELECT blackboard_artifacts.artifact_obj_id, blackboard_artifact_types.type_name FROM blackboard_artifacts INNER JOIN blackboard_artifact_types ON blackboard_artifact_types.artifact_type_id = blackboard_artifacts.artifact_type_id ")])
|
||||
return mapping
|
||||
|
||||
def build_id_legacy_artifact_types_table(db_cursor, isPostgreSQL):
|
||||
"""Build the map of legacy artifact ids to artifact type.
|
||||
|
||||
Args:
|
||||
db_cursor: the database cursor
|
||||
"""
|
||||
# for each row in the db, take the legacy artifact id then create a tuple in the dictionary
|
||||
# with the artifact id as the key and artifact type as the value
|
||||
mapping = dict([(row[0], row[1]) for row in sql_select_execute(db_cursor, isPostgreSQL, "SELECT blackboard_artifacts.artifact_id, blackboard_artifact_types.type_name FROM blackboard_artifacts INNER JOIN blackboard_artifact_types ON blackboard_artifact_types.artifact_type_id = blackboard_artifacts.artifact_type_id ")])
|
||||
return mapping
|
||||
|
||||
def build_id_reports_table(db_cursor, isPostgreSQL):
|
||||
"""Build the map of report object ids to report path.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
MboxParser.handleAttch.noOpenCase.errMsg=Exception while getting open case.
|
||||
MimeJ4MessageParser.handleAttch.noOpenCase.errMsg=Exception while getting open case.
|
||||
OpenIDE-Module-Display-Category=Ingest Module
|
||||
OpenIDE-Module-Long-Description=Email Parser ingest module.\n\nThe module extracts MBOX and PST e-mail files and posts the results to the blackboard.\nIt knows about the Thunderbird folder structure for MBOX files.
|
||||
OpenIDE-Module-Name=Email Parser
|
||||
|
72
thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/EMLParser.java
Executable file
72
thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/EMLParser.java
Executable file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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.thunderbirdparser;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import org.apache.james.mime4j.MimeException;
|
||||
import org.apache.james.mime4j.dom.Message;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.ReadContentInputStream;
|
||||
|
||||
/**
|
||||
* EML file parser. An .eml file contains a single email message.
|
||||
*
|
||||
*/
|
||||
class EMLParser extends MimeJ4MessageParser {
|
||||
|
||||
/**
|
||||
* If the extention of the AbstractFile is eml and 'To:' is found close to
|
||||
* the beginning of the file, then its probably an eml file.
|
||||
*
|
||||
* @param abFile AbstractFile to test
|
||||
* @param buffer A byte buffer of the beginning of the file.
|
||||
*
|
||||
* @return True, if we think this is an eml file, false otherwise.
|
||||
*/
|
||||
static boolean isEMLFile(AbstractFile abFile, byte[] buffer) {
|
||||
String ext = abFile.getNameExtension();
|
||||
boolean isEMLFile = ext != null && ext.equals("eml");
|
||||
if (isEMLFile) {
|
||||
isEMLFile = (new String(buffer)).contains("To:"); //NON-NLS
|
||||
}
|
||||
|
||||
return isEMLFile;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param sourceFile AbstractFile source file for eml message
|
||||
* @param localPath The local path to the eml file
|
||||
*
|
||||
* @return EmailMessage object for message in eml file
|
||||
*
|
||||
* @throws FileNotFoundException
|
||||
* @throws IOException
|
||||
* @throws MimeException
|
||||
*/
|
||||
static EmailMessage parse(AbstractFile sourceFile) throws FileNotFoundException, IOException, MimeException {
|
||||
try (ReadContentInputStream fis = new ReadContentInputStream(sourceFile)) {
|
||||
EMLParser parser = new EMLParser();
|
||||
parser.setLocalPath(sourceFile.getParentPath());
|
||||
Message mimeMsg = parser.getMessageBuilder().parseMessage(fis);
|
||||
return parser.extractEmail(mimeMsg, "", sourceFile.getId());
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user