mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-19 11:07:43 +00:00
Merge remote-tracking branch 'upstream/accounts_relationships' into build_out_CVT_GUI
This commit is contained in:
commit
1add071855
29
Core/src/org/sleuthkit/autopsy/casemodule/AutoIngestCasePanelInterface.java
Executable file
29
Core/src/org/sleuthkit/autopsy/casemodule/AutoIngestCasePanelInterface.java
Executable file
@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2011-2017 Basis Technology Corp.
|
||||||
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.sleuthkit.autopsy.casemodule;
|
||||||
|
|
||||||
|
import javax.swing.JDialog;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for startup window implementations
|
||||||
|
*/
|
||||||
|
public interface AutoIngestCasePanelInterface {
|
||||||
|
|
||||||
|
public void addWindowStateListener(JDialog parent);
|
||||||
|
}
|
@ -17,13 +17,7 @@ NewCaseVisualPanel1.jLabel2.text_1=Case data will be stored in the following dir
|
|||||||
NewCaseVisualPanel1.caseParentDirTextField.text=
|
NewCaseVisualPanel1.caseParentDirTextField.text=
|
||||||
NewCaseVisualPanel1.caseDirTextField.text_1=
|
NewCaseVisualPanel1.caseDirTextField.text_1=
|
||||||
CueBannerPanel.autopsyLogo.text=
|
CueBannerPanel.autopsyLogo.text=
|
||||||
CueBannerPanel.createNewLabel.text=Create New Case
|
|
||||||
CueBannerPanel.openLabel.text=Open Existing Case
|
|
||||||
CueBannerPanel.closeButton.text=Close
|
CueBannerPanel.closeButton.text=Close
|
||||||
CueBannerPanel.openRecentLabel.text=Open Recent Case
|
|
||||||
CueBannerPanel.newCaseButton.text=
|
|
||||||
CueBannerPanel.openCaseButton.text=
|
|
||||||
CueBannerPanel.openRecentButton.text=
|
|
||||||
OpenRecentCasePanel.cancelButton.text=Cancel
|
OpenRecentCasePanel.cancelButton.text=Cancel
|
||||||
OpenRecentCasePanel.jLabel1.text=Recent Cases
|
OpenRecentCasePanel.jLabel1.text=Recent Cases
|
||||||
NewCaseVisualPanel2.caseNumberTextField.text=
|
NewCaseVisualPanel2.caseNumberTextField.text=
|
||||||
@ -225,3 +219,11 @@ CasePropertiesPanel.lbDbType.text=Case Type:
|
|||||||
CasePropertiesPanel.examinerLabel.text=Examiner:
|
CasePropertiesPanel.examinerLabel.text=Examiner:
|
||||||
CasePropertiesPanel.caseNumberLabel.text=Case Number:
|
CasePropertiesPanel.caseNumberLabel.text=Case Number:
|
||||||
LocalDiskPanel.changeDatabasePathCheckbox.text=Update case to use VHD file upon completion
|
LocalDiskPanel.changeDatabasePathCheckbox.text=Update case to use VHD file upon completion
|
||||||
|
CueBannerPanel.openAutoIngestCaseButton.text=
|
||||||
|
CueBannerPanel.openExistingCaseButton.text=
|
||||||
|
CueBannerPanel.openRecentCaseButton.text=
|
||||||
|
CueBannerPanel.createNewCaseButton.text=
|
||||||
|
CueBannerPanel.createNewCaseLabel.text=Create New Case
|
||||||
|
CueBannerPanel.openRecentCaseLabel.text=Open Recent Case
|
||||||
|
CueBannerPanel.openExistingCaseLabel.text=Open Existing Case
|
||||||
|
CueBannerPanel.openAutoIngestCaseLabel.text=Open Auto Ingest Case
|
||||||
|
@ -12,10 +12,7 @@ NewCaseVisualPanel1.caseNameLabel.text_1=\u30b1\u30fc\u30b9\u540d\uff1a
|
|||||||
NewCaseVisualPanel1.caseDirLabel.text=\u30d9\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\uff1a
|
NewCaseVisualPanel1.caseDirLabel.text=\u30d9\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\uff1a
|
||||||
NewCaseVisualPanel1.caseDirBrowseButton.text=\u95b2\u89a7
|
NewCaseVisualPanel1.caseDirBrowseButton.text=\u95b2\u89a7
|
||||||
NewCaseVisualPanel1.jLabel2.text_1=\u30b1\u30fc\u30b9\u30c7\u30fc\u30bf\u306f\u6b21\u306e\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u306b\u4fdd\u5b58\u3055\u308c\u307e\u3059\uff1a
|
NewCaseVisualPanel1.jLabel2.text_1=\u30b1\u30fc\u30b9\u30c7\u30fc\u30bf\u306f\u6b21\u306e\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u306b\u4fdd\u5b58\u3055\u308c\u307e\u3059\uff1a
|
||||||
CueBannerPanel.createNewLabel.text=\u65b0\u898f\u30b1\u30fc\u30b9\u3092\u4f5c\u6210
|
|
||||||
CueBannerPanel.openLabel.text=\u65e2\u5b58\u30b1\u30fc\u30b9\u3092\u958b\u304f
|
|
||||||
CueBannerPanel.closeButton.text=\u9589\u3058\u308b
|
CueBannerPanel.closeButton.text=\u9589\u3058\u308b
|
||||||
CueBannerPanel.openRecentLabel.text=\u6700\u8fd1\u958b\u3044\u305f\u30b1\u30fc\u30b9\u3092\u958b\u304f
|
|
||||||
OpenRecentCasePanel.cancelButton.text=\u30ad\u30e3\u30f3\u30bb\u30eb
|
OpenRecentCasePanel.cancelButton.text=\u30ad\u30e3\u30f3\u30bb\u30eb
|
||||||
OpenRecentCasePanel.jLabel1.text=\u6700\u8fd1\u958b\u3044\u305f\u30d5\u30a1\u30a4\u30eb
|
OpenRecentCasePanel.jLabel1.text=\u6700\u8fd1\u958b\u3044\u305f\u30d5\u30a1\u30a4\u30eb
|
||||||
NewCaseVisualPanel2.examinerLabel.text=\u8abf\u67fb\u62c5\u5f53\u8005\uff1a
|
NewCaseVisualPanel2.examinerLabel.text=\u8abf\u67fb\u62c5\u5f53\u8005\uff1a
|
||||||
@ -196,3 +193,7 @@ CasePropertiesPanel.lbDbName.text=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u540d\uff
|
|||||||
CasePropertiesPanel.lbDbType.text=\u30b1\u30fc\u30b9\u30bf\u30a4\u30d7\uff1a
|
CasePropertiesPanel.lbDbType.text=\u30b1\u30fc\u30b9\u30bf\u30a4\u30d7\uff1a
|
||||||
CasePropertiesPanel.examinerLabel.text=\u8abf\u67fb\u62c5\u5f53\u8005\uff1a
|
CasePropertiesPanel.examinerLabel.text=\u8abf\u67fb\u62c5\u5f53\u8005\uff1a
|
||||||
CasePropertiesPanel.caseNumberLabel.text=\u30b1\u30fc\u30b9\u756a\u53f7\uff1a
|
CasePropertiesPanel.caseNumberLabel.text=\u30b1\u30fc\u30b9\u756a\u53f7\uff1a
|
||||||
|
CueBannerPanel.createNewCaseLabel.text=\u65b0\u898f\u30b1\u30fc\u30b9\u3092\u4f5c\u6210
|
||||||
|
CueBannerPanel.openRecentCaseLabel.text=\u6700\u8fd1\u958b\u3044\u305f\u30b1\u30fc\u30b9\u3092\u958b\u304f
|
||||||
|
CueBannerPanel.openExistingCaseLabel.text=\u65e2\u5b58\u30b1\u30fc\u30b9\u3092\u958b\u304f
|
||||||
|
CueBannerPanel.openAutoIngestCaseLabel.text=\u65e2\u5b58\u30b1\u30fc\u30b9\u3092\u958b\u304f
|
||||||
|
@ -16,23 +16,27 @@
|
|||||||
<Layout>
|
<Layout>
|
||||||
<DimensionLayout dim="0">
|
<DimensionLayout dim="0">
|
||||||
<Group type="103" groupAlignment="0" attributes="0">
|
<Group type="103" groupAlignment="0" attributes="0">
|
||||||
<Group type="102" alignment="0" attributes="0">
|
<Group type="102" attributes="0">
|
||||||
<EmptySpace max="-2" attributes="0"/>
|
|
||||||
<Component id="autopsyLogo" min="-2" max="-2" attributes="0"/>
|
|
||||||
<EmptySpace max="-2" attributes="0"/>
|
|
||||||
<Component id="jSeparator1" min="-2" pref="5" max="-2" attributes="0"/>
|
|
||||||
<EmptySpace max="-2" attributes="0"/>
|
|
||||||
<Group type="103" groupAlignment="2" attributes="0">
|
|
||||||
<Component id="newCaseButton" alignment="2" min="-2" max="-2" attributes="1"/>
|
|
||||||
<Component id="openRecentButton" alignment="2" min="-2" pref="70" max="-2" attributes="1"/>
|
|
||||||
<Component id="openCaseButton" alignment="2" min="-2" max="-2" attributes="1"/>
|
|
||||||
</Group>
|
|
||||||
<EmptySpace max="-2" attributes="0"/>
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
<Group type="103" groupAlignment="0" attributes="0">
|
<Group type="103" groupAlignment="0" attributes="0">
|
||||||
<Group type="103" alignment="1" groupAlignment="0" attributes="0">
|
<Group type="102" alignment="0" attributes="0">
|
||||||
<Component id="createNewLabel" alignment="0" min="-2" max="-2" attributes="0"/>
|
<Component id="autopsyLogo" min="-2" max="-2" attributes="0"/>
|
||||||
<Component id="openRecentLabel" alignment="0" min="-2" max="-2" attributes="0"/>
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
<Component id="openLabel" alignment="0" min="-2" max="-2" attributes="0"/>
|
<Component id="jSeparator1" min="-2" pref="5" max="-2" attributes="0"/>
|
||||||
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
|
<Group type="103" groupAlignment="2" attributes="0">
|
||||||
|
<Component id="createNewCaseButton" alignment="2" min="-2" max="-2" attributes="1"/>
|
||||||
|
<Component id="openRecentCaseButton" alignment="2" min="-2" pref="70" max="-2" attributes="1"/>
|
||||||
|
<Component id="openExistingCaseButton" alignment="2" min="-2" max="-2" attributes="1"/>
|
||||||
|
<Component id="openAutoIngestCaseButton" alignment="2" min="-2" max="-2" attributes="1"/>
|
||||||
|
</Group>
|
||||||
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
|
<Group type="103" groupAlignment="0" attributes="0">
|
||||||
|
<Component id="createNewCaseLabel" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||||
|
<Component id="openRecentCaseLabel" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||||
|
<Component id="openExistingCaseLabel" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||||
|
<Component id="openAutoIngestCaseLabel" min="-2" max="-2" attributes="0"/>
|
||||||
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
<Component id="closeButton" alignment="1" min="-2" pref="73" max="-2" attributes="0"/>
|
<Component id="closeButton" alignment="1" min="-2" pref="73" max="-2" attributes="0"/>
|
||||||
</Group>
|
</Group>
|
||||||
@ -47,25 +51,33 @@
|
|||||||
<Group type="103" groupAlignment="0" max="-2" attributes="0">
|
<Group type="103" groupAlignment="0" max="-2" attributes="0">
|
||||||
<Group type="102" attributes="0">
|
<Group type="102" attributes="0">
|
||||||
<Group type="103" groupAlignment="2" attributes="0">
|
<Group type="103" groupAlignment="2" attributes="0">
|
||||||
<Component id="newCaseButton" alignment="2" min="-2" pref="56" max="-2" attributes="0"/>
|
<Component id="createNewCaseButton" alignment="2" min="-2" pref="56" max="-2" attributes="0"/>
|
||||||
<Component id="createNewLabel" alignment="2" min="-2" max="-2" attributes="0"/>
|
<Component id="createNewCaseLabel" alignment="2" min="-2" max="-2" attributes="0"/>
|
||||||
</Group>
|
</Group>
|
||||||
<EmptySpace max="-2" attributes="0"/>
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
<Group type="103" groupAlignment="2" attributes="0">
|
<Group type="103" groupAlignment="2" attributes="0">
|
||||||
<Component id="openRecentButton" alignment="2" min="-2" pref="70" max="-2" attributes="0"/>
|
<Component id="openRecentCaseButton" alignment="2" min="-2" pref="70" max="-2" attributes="0"/>
|
||||||
<Component id="openRecentLabel" alignment="2" min="-2" max="-2" attributes="0"/>
|
<Component id="openRecentCaseLabel" alignment="2" min="-2" max="-2" attributes="0"/>
|
||||||
</Group>
|
</Group>
|
||||||
<EmptySpace max="-2" attributes="0"/>
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
<Group type="103" groupAlignment="2" attributes="0">
|
<Group type="103" groupAlignment="2" attributes="0">
|
||||||
<Component id="openCaseButton" alignment="2" min="-2" pref="58" max="-2" attributes="0"/>
|
<Component id="openExistingCaseButton" alignment="2" min="-2" pref="58" max="-2" attributes="0"/>
|
||||||
<Component id="openLabel" alignment="2" min="-2" max="-2" attributes="0"/>
|
<Component id="openExistingCaseLabel" alignment="2" min="-2" max="-2" attributes="0"/>
|
||||||
</Group>
|
</Group>
|
||||||
<EmptySpace max="32767" attributes="0"/>
|
<EmptySpace max="32767" attributes="0"/>
|
||||||
<Component id="closeButton" min="-2" max="-2" attributes="0"/>
|
<Group type="103" groupAlignment="0" attributes="0">
|
||||||
|
<Component id="openAutoIngestCaseButton" alignment="1" min="-2" pref="58" max="-2" attributes="0"/>
|
||||||
|
<Group type="102" alignment="1" attributes="0">
|
||||||
|
<Component id="openAutoIngestCaseLabel" min="-2" max="-2" attributes="0"/>
|
||||||
|
<EmptySpace min="-2" pref="20" max="-2" attributes="0"/>
|
||||||
|
</Group>
|
||||||
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
<Component id="jSeparator1" alignment="0" max="32767" attributes="0"/>
|
<Component id="jSeparator1" alignment="0" max="32767" attributes="0"/>
|
||||||
<Component id="autopsyLogo" alignment="0" min="-2" pref="257" max="-2" attributes="0"/>
|
<Component id="autopsyLogo" alignment="0" min="-2" pref="257" max="-2" attributes="0"/>
|
||||||
</Group>
|
</Group>
|
||||||
|
<EmptySpace pref="10" max="32767" attributes="0"/>
|
||||||
|
<Component id="closeButton" min="-2" max="-2" attributes="0"/>
|
||||||
<EmptySpace max="-2" attributes="0"/>
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
</Group>
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
@ -85,13 +97,13 @@
|
|||||||
<AuxValue name="JavaCodeGenerator_CreateCodePost" type="java.lang.String" value="this.autopsyLogo.setText("");"/>
|
<AuxValue name="JavaCodeGenerator_CreateCodePost" type="java.lang.String" value="this.autopsyLogo.setText("");"/>
|
||||||
</AuxValues>
|
</AuxValues>
|
||||||
</Component>
|
</Component>
|
||||||
<Component class="javax.swing.JButton" name="newCaseButton">
|
<Component class="javax.swing.JButton" name="createNewCaseButton">
|
||||||
<Properties>
|
<Properties>
|
||||||
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||||
<Image iconType="3" name="/org/sleuthkit/autopsy/casemodule/btn_icon_create_new_case.png"/>
|
<Image iconType="3" name="/org/sleuthkit/autopsy/casemodule/btn_icon_create_new_case.png"/>
|
||||||
</Property>
|
</Property>
|
||||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="CueBannerPanel.newCaseButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="CueBannerPanel.createNewCaseButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
</Property>
|
</Property>
|
||||||
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
||||||
<Connection code="null" type="code"/>
|
<Connection code="null" type="code"/>
|
||||||
@ -103,16 +115,16 @@
|
|||||||
</Property>
|
</Property>
|
||||||
</Properties>
|
</Properties>
|
||||||
<Events>
|
<Events>
|
||||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="newCaseButtonActionPerformed"/>
|
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="createNewCaseButtonActionPerformed"/>
|
||||||
</Events>
|
</Events>
|
||||||
</Component>
|
</Component>
|
||||||
<Component class="javax.swing.JButton" name="openRecentButton">
|
<Component class="javax.swing.JButton" name="openRecentCaseButton">
|
||||||
<Properties>
|
<Properties>
|
||||||
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||||
<Image iconType="3" name="/org/sleuthkit/autopsy/casemodule/btn_icon_open_recent.png"/>
|
<Image iconType="3" name="/org/sleuthkit/autopsy/casemodule/btn_icon_open_recent.png"/>
|
||||||
</Property>
|
</Property>
|
||||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="CueBannerPanel.openRecentButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="CueBannerPanel.openRecentCaseButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
</Property>
|
</Property>
|
||||||
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
||||||
<Connection code="null" type="code"/>
|
<Connection code="null" type="code"/>
|
||||||
@ -124,40 +136,40 @@
|
|||||||
</Property>
|
</Property>
|
||||||
</Properties>
|
</Properties>
|
||||||
<Events>
|
<Events>
|
||||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="openRecentButtonActionPerformed"/>
|
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="openRecentCaseButtonActionPerformed"/>
|
||||||
</Events>
|
</Events>
|
||||||
</Component>
|
</Component>
|
||||||
<Component class="javax.swing.JLabel" name="createNewLabel">
|
<Component class="javax.swing.JLabel" name="createNewCaseLabel">
|
||||||
<Properties>
|
<Properties>
|
||||||
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
|
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
|
||||||
<FontInfo relative="true">
|
<FontInfo relative="true">
|
||||||
<Font bold="false" component="createNewLabel" property="font" relativeSize="false" size="13"/>
|
<Font bold="false" component="createNewCaseLabel" property="font" relativeSize="false" size="13"/>
|
||||||
</FontInfo>
|
</FontInfo>
|
||||||
</Property>
|
</Property>
|
||||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="CueBannerPanel.createNewLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="CueBannerPanel.createNewCaseLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
</Property>
|
</Property>
|
||||||
</Properties>
|
</Properties>
|
||||||
</Component>
|
</Component>
|
||||||
<Component class="javax.swing.JLabel" name="openRecentLabel">
|
<Component class="javax.swing.JLabel" name="openRecentCaseLabel">
|
||||||
<Properties>
|
<Properties>
|
||||||
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
|
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
|
||||||
<FontInfo relative="true">
|
<FontInfo relative="true">
|
||||||
<Font bold="false" component="openRecentLabel" property="font" relativeSize="false" size="13"/>
|
<Font bold="false" component="openRecentCaseLabel" property="font" relativeSize="false" size="13"/>
|
||||||
</FontInfo>
|
</FontInfo>
|
||||||
</Property>
|
</Property>
|
||||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="CueBannerPanel.openRecentLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="CueBannerPanel.openRecentCaseLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
</Property>
|
</Property>
|
||||||
</Properties>
|
</Properties>
|
||||||
</Component>
|
</Component>
|
||||||
<Component class="javax.swing.JButton" name="openCaseButton">
|
<Component class="javax.swing.JButton" name="openExistingCaseButton">
|
||||||
<Properties>
|
<Properties>
|
||||||
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||||
<Image iconType="3" name="/org/sleuthkit/autopsy/casemodule/btn_icon_open_existing.png"/>
|
<Image iconType="3" name="/org/sleuthkit/autopsy/casemodule/btn_icon_open_existing.png"/>
|
||||||
</Property>
|
</Property>
|
||||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="CueBannerPanel.openCaseButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="CueBannerPanel.openExistingCaseButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
</Property>
|
</Property>
|
||||||
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
||||||
<Connection code="null" type="code"/>
|
<Connection code="null" type="code"/>
|
||||||
@ -172,18 +184,18 @@
|
|||||||
</Property>
|
</Property>
|
||||||
</Properties>
|
</Properties>
|
||||||
<Events>
|
<Events>
|
||||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="openCaseButtonActionPerformed"/>
|
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="openExistingCaseButtonActionPerformed"/>
|
||||||
</Events>
|
</Events>
|
||||||
</Component>
|
</Component>
|
||||||
<Component class="javax.swing.JLabel" name="openLabel">
|
<Component class="javax.swing.JLabel" name="openExistingCaseLabel">
|
||||||
<Properties>
|
<Properties>
|
||||||
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
|
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
|
||||||
<FontInfo relative="true">
|
<FontInfo relative="true">
|
||||||
<Font bold="false" component="openLabel" property="font" relativeSize="false" size="13"/>
|
<Font bold="false" component="openExistingCaseLabel" property="font" relativeSize="false" size="13"/>
|
||||||
</FontInfo>
|
</FontInfo>
|
||||||
</Property>
|
</Property>
|
||||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="CueBannerPanel.openLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="CueBannerPanel.openExistingCaseLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
</Property>
|
</Property>
|
||||||
</Properties>
|
</Properties>
|
||||||
</Component>
|
</Component>
|
||||||
@ -204,5 +216,41 @@
|
|||||||
<Property name="orientation" type="int" value="1"/>
|
<Property name="orientation" type="int" value="1"/>
|
||||||
</Properties>
|
</Properties>
|
||||||
</Component>
|
</Component>
|
||||||
|
<Component class="javax.swing.JButton" name="openAutoIngestCaseButton">
|
||||||
|
<Properties>
|
||||||
|
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||||
|
<Image iconType="3" name="/org/sleuthkit/autopsy/casemodule/btn_icon_open_existing.png"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="CueBannerPanel.openAutoIngestCaseButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
||||||
|
<Connection code="null" type="code"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="borderPainted" type="boolean" value="false"/>
|
||||||
|
<Property name="contentAreaFilled" type="boolean" value="false"/>
|
||||||
|
<Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor">
|
||||||
|
<Insets value="[1, 1, 1, 1]"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
|
<Dimension value="[64, 64]"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
<Events>
|
||||||
|
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="openAutoIngestCaseButtonActionPerformed"/>
|
||||||
|
</Events>
|
||||||
|
</Component>
|
||||||
|
<Component class="javax.swing.JLabel" name="openAutoIngestCaseLabel">
|
||||||
|
<Properties>
|
||||||
|
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
|
||||||
|
<FontInfo relative="true">
|
||||||
|
<Font bold="false" component="openAutoIngestCaseLabel" property="font" relativeSize="false" size="13"/>
|
||||||
|
</FontInfo>
|
||||||
|
</Property>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="CueBannerPanel.openAutoIngestCaseLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
</Component>
|
||||||
</SubComponents>
|
</SubComponents>
|
||||||
</Form>
|
</Form>
|
||||||
|
@ -26,10 +26,14 @@ import java.awt.event.KeyEvent;
|
|||||||
import javax.swing.ImageIcon;
|
import javax.swing.ImageIcon;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JDialog;
|
import javax.swing.JDialog;
|
||||||
|
import javax.swing.JPanel;
|
||||||
import javax.swing.KeyStroke;
|
import javax.swing.KeyStroke;
|
||||||
import org.openide.util.Lookup;
|
import org.openide.util.Lookup;
|
||||||
import org.openide.util.NbBundle;
|
import org.openide.util.NbBundle;
|
||||||
import org.openide.windows.WindowManager;
|
import org.openide.windows.WindowManager;
|
||||||
|
import org.sleuthkit.autopsy.casemodule.AutoIngestCasePanelInterface;
|
||||||
|
import org.sleuthkit.autopsy.core.UserPreferences;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.NetworkUtils;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The panel in the default Autopsy startup window.
|
* The panel in the default Autopsy startup window.
|
||||||
@ -37,11 +41,14 @@ import org.openide.windows.WindowManager;
|
|||||||
public class CueBannerPanel extends javax.swing.JPanel {
|
public class CueBannerPanel extends javax.swing.JPanel {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
private static final String LOCAL_HOST_NAME = NetworkUtils.getLocalHostName();
|
||||||
|
private static final String REVIEW_MODE_TITLE = "Cases" + " (" + LOCAL_HOST_NAME + ")";
|
||||||
/*
|
/*
|
||||||
* This is field is static for the sake of the closeOpenRecentCasesWindow
|
* This is field is static for the sake of the closeOpenRecentCasesWindow
|
||||||
* method.
|
* method.
|
||||||
*/
|
*/
|
||||||
private static JDialog recentCasesWindow;
|
private static JDialog recentCasesWindow;
|
||||||
|
private static JDialog autoIngestCasePanelWindow;
|
||||||
|
|
||||||
public static void closeOpenRecentCasesWindow() {
|
public static void closeOpenRecentCasesWindow() {
|
||||||
if (null != recentCasesWindow) {
|
if (null != recentCasesWindow) {
|
||||||
@ -49,6 +56,12 @@ public class CueBannerPanel extends javax.swing.JPanel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void closeAutoIngestCasesWindow() {
|
||||||
|
if (null != autoIngestCasePanelWindow) {
|
||||||
|
autoIngestCasePanelWindow.setVisible(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public CueBannerPanel() {
|
public CueBannerPanel() {
|
||||||
initComponents();
|
initComponents();
|
||||||
customizeComponents();
|
customizeComponents();
|
||||||
@ -77,8 +90,13 @@ public class CueBannerPanel extends javax.swing.JPanel {
|
|||||||
public void refresh() {
|
public void refresh() {
|
||||||
enableComponents();
|
enableComponents();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void customizeComponents() {
|
private void customizeComponents() {
|
||||||
|
initRecentCasesWindow();
|
||||||
|
initAutoIngestCasesWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initRecentCasesWindow() {
|
||||||
recentCasesWindow = new JDialog(
|
recentCasesWindow = new JDialog(
|
||||||
WindowManager.getDefault().getMainWindow(),
|
WindowManager.getDefault().getMainWindow(),
|
||||||
NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.title.text"),
|
NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.title.text"),
|
||||||
@ -100,15 +118,39 @@ public class CueBannerPanel extends javax.swing.JPanel {
|
|||||||
recentCasesWindow.pack();
|
recentCasesWindow.pack();
|
||||||
recentCasesWindow.setResizable(false);
|
recentCasesWindow.setResizable(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void initAutoIngestCasesWindow() {
|
||||||
|
autoIngestCasePanelWindow = new JDialog(
|
||||||
|
WindowManager.getDefault().getMainWindow(),
|
||||||
|
REVIEW_MODE_TITLE,
|
||||||
|
Dialog.ModalityType.APPLICATION_MODAL);
|
||||||
|
autoIngestCasePanelWindow.getRootPane().registerKeyboardAction(
|
||||||
|
e -> {
|
||||||
|
autoIngestCasePanelWindow.setVisible(false);
|
||||||
|
},
|
||||||
|
KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_IN_FOCUSED_WINDOW);
|
||||||
|
OpenRecentCasePanel recentCasesPanel = OpenRecentCasePanel.getInstance();
|
||||||
|
recentCasesPanel.setCloseButtonActionListener(new ActionListener() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
autoIngestCasePanelWindow.setVisible(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
AutoIngestCasePanelInterface autoIngestCasePanel = Lookup.getDefault().lookup(AutoIngestCasePanelInterface.class);
|
||||||
|
autoIngestCasePanel.addWindowStateListener(autoIngestCasePanelWindow);
|
||||||
|
autoIngestCasePanelWindow.add((JPanel)autoIngestCasePanel);
|
||||||
|
autoIngestCasePanelWindow.pack();
|
||||||
|
autoIngestCasePanelWindow.setResizable(false);
|
||||||
|
}
|
||||||
|
|
||||||
private void enableComponents() {
|
private void enableComponents() {
|
||||||
if (RecentCases.getInstance().getTotalRecentCases() == 0) {
|
boolean enableOpenRecentCaseButton = (RecentCases.getInstance().getTotalRecentCases() > 0);
|
||||||
openRecentButton.setEnabled(false);
|
openRecentCaseButton.setEnabled(enableOpenRecentCaseButton);
|
||||||
openRecentLabel.setEnabled(false);
|
openRecentCaseLabel.setEnabled(enableOpenRecentCaseButton);
|
||||||
} else {
|
|
||||||
openRecentButton.setEnabled(true);
|
boolean showOpenAutoIngestCaseButton = (UserPreferences.getMode() == UserPreferences.SelectedMode.REVIEW);
|
||||||
openRecentLabel.setEnabled(true);
|
openAutoIngestCaseButton.setVisible(showOpenAutoIngestCaseButton);
|
||||||
}
|
openAutoIngestCaseLabel.setVisible(showOpenAutoIngestCaseButton);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -122,89 +164,110 @@ public class CueBannerPanel extends javax.swing.JPanel {
|
|||||||
|
|
||||||
autopsyLogo = new javax.swing.JLabel();
|
autopsyLogo = new javax.swing.JLabel();
|
||||||
this.autopsyLogo.setText("");
|
this.autopsyLogo.setText("");
|
||||||
newCaseButton = new javax.swing.JButton();
|
createNewCaseButton = new javax.swing.JButton();
|
||||||
openRecentButton = new javax.swing.JButton();
|
openRecentCaseButton = new javax.swing.JButton();
|
||||||
createNewLabel = new javax.swing.JLabel();
|
createNewCaseLabel = new javax.swing.JLabel();
|
||||||
openRecentLabel = new javax.swing.JLabel();
|
openRecentCaseLabel = new javax.swing.JLabel();
|
||||||
openCaseButton = new javax.swing.JButton();
|
openExistingCaseButton = new javax.swing.JButton();
|
||||||
openLabel = new javax.swing.JLabel();
|
openExistingCaseLabel = new javax.swing.JLabel();
|
||||||
closeButton = new javax.swing.JButton();
|
closeButton = new javax.swing.JButton();
|
||||||
jSeparator1 = new javax.swing.JSeparator();
|
jSeparator1 = new javax.swing.JSeparator();
|
||||||
|
openAutoIngestCaseButton = new javax.swing.JButton();
|
||||||
|
openAutoIngestCaseLabel = new javax.swing.JLabel();
|
||||||
|
|
||||||
autopsyLogo.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/casemodule/welcome_logo.png"))); // NOI18N
|
autopsyLogo.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/casemodule/welcome_logo.png"))); // NOI18N
|
||||||
autopsyLogo.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.autopsyLogo.text")); // NOI18N
|
autopsyLogo.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.autopsyLogo.text")); // NOI18N
|
||||||
|
|
||||||
newCaseButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/casemodule/btn_icon_create_new_case.png"))); // NOI18N
|
createNewCaseButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/casemodule/btn_icon_create_new_case.png"))); // NOI18N
|
||||||
newCaseButton.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.newCaseButton.text")); // NOI18N
|
createNewCaseButton.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.createNewCaseButton.text")); // NOI18N
|
||||||
newCaseButton.setBorder(null);
|
createNewCaseButton.setBorder(null);
|
||||||
newCaseButton.setBorderPainted(false);
|
createNewCaseButton.setBorderPainted(false);
|
||||||
newCaseButton.setContentAreaFilled(false);
|
createNewCaseButton.setContentAreaFilled(false);
|
||||||
newCaseButton.setPreferredSize(new java.awt.Dimension(64, 64));
|
createNewCaseButton.setPreferredSize(new java.awt.Dimension(64, 64));
|
||||||
newCaseButton.addActionListener(new java.awt.event.ActionListener() {
|
createNewCaseButton.addActionListener(new java.awt.event.ActionListener() {
|
||||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||||
newCaseButtonActionPerformed(evt);
|
createNewCaseButtonActionPerformed(evt);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
openRecentButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/casemodule/btn_icon_open_recent.png"))); // NOI18N
|
openRecentCaseButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/casemodule/btn_icon_open_recent.png"))); // NOI18N
|
||||||
openRecentButton.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.openRecentButton.text")); // NOI18N
|
openRecentCaseButton.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.openRecentCaseButton.text")); // NOI18N
|
||||||
openRecentButton.setBorder(null);
|
openRecentCaseButton.setBorder(null);
|
||||||
openRecentButton.setBorderPainted(false);
|
openRecentCaseButton.setBorderPainted(false);
|
||||||
openRecentButton.setContentAreaFilled(false);
|
openRecentCaseButton.setContentAreaFilled(false);
|
||||||
openRecentButton.setPreferredSize(new java.awt.Dimension(64, 64));
|
openRecentCaseButton.setPreferredSize(new java.awt.Dimension(64, 64));
|
||||||
openRecentButton.addActionListener(new java.awt.event.ActionListener() {
|
openRecentCaseButton.addActionListener(new java.awt.event.ActionListener() {
|
||||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||||
openRecentButtonActionPerformed(evt);
|
openRecentCaseButtonActionPerformed(evt);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
createNewLabel.setFont(createNewLabel.getFont().deriveFont(createNewLabel.getFont().getStyle() & ~java.awt.Font.BOLD, 13));
|
createNewCaseLabel.setFont(createNewCaseLabel.getFont().deriveFont(createNewCaseLabel.getFont().getStyle() & ~java.awt.Font.BOLD, 13));
|
||||||
createNewLabel.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.createNewLabel.text")); // NOI18N
|
createNewCaseLabel.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.createNewCaseLabel.text")); // NOI18N
|
||||||
|
|
||||||
openRecentLabel.setFont(openRecentLabel.getFont().deriveFont(openRecentLabel.getFont().getStyle() & ~java.awt.Font.BOLD, 13));
|
openRecentCaseLabel.setFont(openRecentCaseLabel.getFont().deriveFont(openRecentCaseLabel.getFont().getStyle() & ~java.awt.Font.BOLD, 13));
|
||||||
openRecentLabel.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.openRecentLabel.text")); // NOI18N
|
openRecentCaseLabel.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.openRecentCaseLabel.text")); // NOI18N
|
||||||
|
|
||||||
openCaseButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/casemodule/btn_icon_open_existing.png"))); // NOI18N
|
openExistingCaseButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/casemodule/btn_icon_open_existing.png"))); // NOI18N
|
||||||
openCaseButton.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.openCaseButton.text")); // NOI18N
|
openExistingCaseButton.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.openExistingCaseButton.text")); // NOI18N
|
||||||
openCaseButton.setBorder(null);
|
openExistingCaseButton.setBorder(null);
|
||||||
openCaseButton.setBorderPainted(false);
|
openExistingCaseButton.setBorderPainted(false);
|
||||||
openCaseButton.setContentAreaFilled(false);
|
openExistingCaseButton.setContentAreaFilled(false);
|
||||||
openCaseButton.setMargin(new java.awt.Insets(1, 1, 1, 1));
|
openExistingCaseButton.setMargin(new java.awt.Insets(1, 1, 1, 1));
|
||||||
openCaseButton.setPreferredSize(new java.awt.Dimension(64, 64));
|
openExistingCaseButton.setPreferredSize(new java.awt.Dimension(64, 64));
|
||||||
openCaseButton.addActionListener(new java.awt.event.ActionListener() {
|
openExistingCaseButton.addActionListener(new java.awt.event.ActionListener() {
|
||||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||||
openCaseButtonActionPerformed(evt);
|
openExistingCaseButtonActionPerformed(evt);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
openLabel.setFont(openLabel.getFont().deriveFont(openLabel.getFont().getStyle() & ~java.awt.Font.BOLD, 13));
|
openExistingCaseLabel.setFont(openExistingCaseLabel.getFont().deriveFont(openExistingCaseLabel.getFont().getStyle() & ~java.awt.Font.BOLD, 13));
|
||||||
openLabel.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.openLabel.text")); // NOI18N
|
openExistingCaseLabel.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.openExistingCaseLabel.text")); // NOI18N
|
||||||
|
|
||||||
closeButton.setFont(closeButton.getFont().deriveFont(closeButton.getFont().getStyle() & ~java.awt.Font.BOLD, 11));
|
closeButton.setFont(closeButton.getFont().deriveFont(closeButton.getFont().getStyle() & ~java.awt.Font.BOLD, 11));
|
||||||
closeButton.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.closeButton.text")); // NOI18N
|
closeButton.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.closeButton.text")); // NOI18N
|
||||||
|
|
||||||
jSeparator1.setOrientation(javax.swing.SwingConstants.VERTICAL);
|
jSeparator1.setOrientation(javax.swing.SwingConstants.VERTICAL);
|
||||||
|
|
||||||
|
openAutoIngestCaseButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/casemodule/btn_icon_open_existing.png"))); // NOI18N
|
||||||
|
openAutoIngestCaseButton.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.openAutoIngestCaseButton.text")); // NOI18N
|
||||||
|
openAutoIngestCaseButton.setBorder(null);
|
||||||
|
openAutoIngestCaseButton.setBorderPainted(false);
|
||||||
|
openAutoIngestCaseButton.setContentAreaFilled(false);
|
||||||
|
openAutoIngestCaseButton.setMargin(new java.awt.Insets(1, 1, 1, 1));
|
||||||
|
openAutoIngestCaseButton.setPreferredSize(new java.awt.Dimension(64, 64));
|
||||||
|
openAutoIngestCaseButton.addActionListener(new java.awt.event.ActionListener() {
|
||||||
|
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||||
|
openAutoIngestCaseButtonActionPerformed(evt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
openAutoIngestCaseLabel.setFont(openAutoIngestCaseLabel.getFont().deriveFont(openAutoIngestCaseLabel.getFont().getStyle() & ~java.awt.Font.BOLD, 13));
|
||||||
|
openAutoIngestCaseLabel.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.openAutoIngestCaseLabel.text")); // NOI18N
|
||||||
|
|
||||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||||
this.setLayout(layout);
|
this.setLayout(layout);
|
||||||
layout.setHorizontalGroup(
|
layout.setHorizontalGroup(
|
||||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
.addGroup(layout.createSequentialGroup()
|
.addGroup(layout.createSequentialGroup()
|
||||||
.addContainerGap()
|
.addContainerGap()
|
||||||
.addComponent(autopsyLogo)
|
|
||||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
|
||||||
.addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, 5, javax.swing.GroupLayout.PREFERRED_SIZE)
|
|
||||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
|
||||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
|
|
||||||
.addComponent(newCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
|
||||||
.addComponent(openRecentButton, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE)
|
|
||||||
.addComponent(openCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
|
|
||||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
|
||||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
.addGroup(layout.createSequentialGroup()
|
||||||
.addComponent(createNewLabel)
|
.addComponent(autopsyLogo)
|
||||||
.addComponent(openRecentLabel)
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
.addComponent(openLabel))
|
.addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, 5, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
|
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
|
||||||
|
.addComponent(createNewCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
|
.addComponent(openRecentCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
|
.addComponent(openExistingCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
|
.addComponent(openAutoIngestCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
|
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
|
.addComponent(createNewCaseLabel)
|
||||||
|
.addComponent(openRecentCaseLabel)
|
||||||
|
.addComponent(openExistingCaseLabel)
|
||||||
|
.addComponent(openAutoIngestCaseLabel)))
|
||||||
.addComponent(closeButton, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, 73, javax.swing.GroupLayout.PREFERRED_SIZE))
|
.addComponent(closeButton, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, 73, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||||
.addContainerGap())
|
.addContainerGap())
|
||||||
);
|
);
|
||||||
@ -215,48 +278,61 @@ public class CueBannerPanel extends javax.swing.JPanel {
|
|||||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
|
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
|
||||||
.addGroup(layout.createSequentialGroup()
|
.addGroup(layout.createSequentialGroup()
|
||||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
|
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
|
||||||
.addComponent(newCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 56, javax.swing.GroupLayout.PREFERRED_SIZE)
|
.addComponent(createNewCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 56, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
.addComponent(createNewLabel))
|
.addComponent(createNewCaseLabel))
|
||||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
|
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
|
||||||
.addComponent(openRecentButton, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE)
|
.addComponent(openRecentCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
.addComponent(openRecentLabel))
|
.addComponent(openRecentCaseLabel))
|
||||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
|
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
|
||||||
.addComponent(openCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 58, javax.swing.GroupLayout.PREFERRED_SIZE)
|
.addComponent(openExistingCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 58, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
.addComponent(openLabel))
|
.addComponent(openExistingCaseLabel))
|
||||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||||
.addComponent(closeButton))
|
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
|
.addComponent(openAutoIngestCaseButton, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, 58, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
|
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
|
||||||
|
.addComponent(openAutoIngestCaseLabel)
|
||||||
|
.addGap(20, 20, 20))))
|
||||||
.addComponent(jSeparator1)
|
.addComponent(jSeparator1)
|
||||||
.addComponent(autopsyLogo, javax.swing.GroupLayout.PREFERRED_SIZE, 257, javax.swing.GroupLayout.PREFERRED_SIZE))
|
.addComponent(autopsyLogo, javax.swing.GroupLayout.PREFERRED_SIZE, 257, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 10, Short.MAX_VALUE)
|
||||||
|
.addComponent(closeButton)
|
||||||
.addContainerGap())
|
.addContainerGap())
|
||||||
);
|
);
|
||||||
}// </editor-fold>//GEN-END:initComponents
|
}// </editor-fold>//GEN-END:initComponents
|
||||||
|
|
||||||
private void newCaseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_newCaseButtonActionPerformed
|
private void createNewCaseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_createNewCaseButtonActionPerformed
|
||||||
Lookup.getDefault().lookup(CaseNewActionInterface.class).actionPerformed(evt);
|
Lookup.getDefault().lookup(CaseNewActionInterface.class).actionPerformed(evt);
|
||||||
}//GEN-LAST:event_newCaseButtonActionPerformed
|
}//GEN-LAST:event_createNewCaseButtonActionPerformed
|
||||||
|
|
||||||
private void openCaseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_openCaseButtonActionPerformed
|
private void openExistingCaseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_openExistingCaseButtonActionPerformed
|
||||||
Lookup.getDefault().lookup(CaseOpenAction.class).actionPerformed(evt);
|
Lookup.getDefault().lookup(CaseOpenAction.class).actionPerformed(evt);
|
||||||
}//GEN-LAST:event_openCaseButtonActionPerformed
|
}//GEN-LAST:event_openExistingCaseButtonActionPerformed
|
||||||
|
|
||||||
private void openRecentButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_openRecentButtonActionPerformed
|
private void openRecentCaseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_openRecentCaseButtonActionPerformed
|
||||||
recentCasesWindow.setLocationRelativeTo(WindowManager.getDefault().getMainWindow());
|
recentCasesWindow.setLocationRelativeTo(WindowManager.getDefault().getMainWindow());
|
||||||
OpenRecentCasePanel.getInstance(); //refreshes the recent cases table
|
OpenRecentCasePanel.getInstance(); //refreshes the recent cases table
|
||||||
recentCasesWindow.setVisible(true);
|
recentCasesWindow.setVisible(true);
|
||||||
}//GEN-LAST:event_openRecentButtonActionPerformed
|
}//GEN-LAST:event_openRecentCaseButtonActionPerformed
|
||||||
|
|
||||||
|
private void openAutoIngestCaseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_openAutoIngestCaseButtonActionPerformed
|
||||||
|
autoIngestCasePanelWindow.setLocationRelativeTo(WindowManager.getDefault().getMainWindow());
|
||||||
|
autoIngestCasePanelWindow.setVisible(true);
|
||||||
|
}//GEN-LAST:event_openAutoIngestCaseButtonActionPerformed
|
||||||
|
|
||||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||||
private javax.swing.JLabel autopsyLogo;
|
private javax.swing.JLabel autopsyLogo;
|
||||||
private javax.swing.JButton closeButton;
|
private javax.swing.JButton closeButton;
|
||||||
private javax.swing.JLabel createNewLabel;
|
private javax.swing.JButton createNewCaseButton;
|
||||||
|
private javax.swing.JLabel createNewCaseLabel;
|
||||||
private javax.swing.JSeparator jSeparator1;
|
private javax.swing.JSeparator jSeparator1;
|
||||||
private javax.swing.JButton newCaseButton;
|
private javax.swing.JButton openAutoIngestCaseButton;
|
||||||
private javax.swing.JButton openCaseButton;
|
private javax.swing.JLabel openAutoIngestCaseLabel;
|
||||||
private javax.swing.JLabel openLabel;
|
private javax.swing.JButton openExistingCaseButton;
|
||||||
private javax.swing.JButton openRecentButton;
|
private javax.swing.JLabel openExistingCaseLabel;
|
||||||
private javax.swing.JLabel openRecentLabel;
|
private javax.swing.JButton openRecentCaseButton;
|
||||||
|
private javax.swing.JLabel openRecentCaseLabel;
|
||||||
// End of variables declaration//GEN-END:variables
|
// End of variables declaration//GEN-END:variables
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -85,10 +85,6 @@ public class EamCaseEditDetailsDialog extends JDialog {
|
|||||||
private void customizeComponents() {
|
private void customizeComponents() {
|
||||||
setTextBoxListeners();
|
setTextBoxListeners();
|
||||||
setTextAreaListeners();
|
setTextAreaListeners();
|
||||||
|
|
||||||
// The organization functions of central repo are not being included in the current release.
|
|
||||||
this.pnOrganization.setVisible(false);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setTextBoxListeners() {
|
private void setTextBoxListeners() {
|
||||||
|
@ -79,7 +79,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
|
|||||||
private static final Logger logger = Logger.getLogger(DataResultViewerTable.class.getName());
|
private static final Logger logger = Logger.getLogger(DataResultViewerTable.class.getName());
|
||||||
@NbBundle.Messages("DataResultViewerTable.firstColLbl=Name")
|
@NbBundle.Messages("DataResultViewerTable.firstColLbl=Name")
|
||||||
static private final String FIRST_COLUMN_LABEL = Bundle.DataResultViewerTable_firstColLbl();
|
static private final String FIRST_COLUMN_LABEL = Bundle.DataResultViewerTable_firstColLbl();
|
||||||
private static final Color TAGGED_COLOR = new Color(200, 210, 220);
|
private static final Color TAGGED_COLOR = new Color(255, 255, 195);
|
||||||
/**
|
/**
|
||||||
* The properties map:
|
* The properties map:
|
||||||
*
|
*
|
||||||
|
@ -1429,7 +1429,14 @@ final public class Accounts implements AutopsyVisitableItem {
|
|||||||
artifacts.forEach(artifact -> {
|
artifacts.forEach(artifact -> {
|
||||||
try {
|
try {
|
||||||
AccountInstance accountInstance = skCase.getCommunicationsManager().getAccountInstance(artifact);
|
AccountInstance accountInstance = skCase.getCommunicationsManager().getAccountInstance(artifact);
|
||||||
accountInstance.setReviewStatus(newStatus);
|
|
||||||
|
if (BlackboardArtifact.ReviewStatus.APPROVED == newStatus) {
|
||||||
|
accountInstance.approveAccount();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
accountInstance.rejectAccount();
|
||||||
|
}
|
||||||
|
|
||||||
} catch (TskCoreException ex) {
|
} catch (TskCoreException ex) {
|
||||||
LOGGER.log(Level.SEVERE, "Error changing artifact review status.", ex); //NON-NLS
|
LOGGER.log(Level.SEVERE, "Error changing artifact review status.", ex); //NON-NLS
|
||||||
}
|
}
|
||||||
|
@ -1,394 +0,0 @@
|
|||||||
/*
|
|
||||||
* Sample module in the public domain. Feel free to use this as a template
|
|
||||||
* for your modules.
|
|
||||||
*
|
|
||||||
* Contact: Brian Carrier [carrier <at> sleuthkit [dot] org]
|
|
||||||
*
|
|
||||||
* This is free and unencumbered software released into the public domain.
|
|
||||||
*
|
|
||||||
* Anyone is free to copy, modify, publish, use, compile, sell, or
|
|
||||||
* distribute this software, either in source code form or as a compiled
|
|
||||||
* binary, for any purpose, commercial or non-commercial, and by any
|
|
||||||
* means.
|
|
||||||
*
|
|
||||||
* In jurisdictions that recognize copyright laws, the author or authors
|
|
||||||
* of this software dedicate any and all copyright interest in the
|
|
||||||
* software to the public domain. We make this dedication for the benefit
|
|
||||||
* of the public at large and to the detriment of our heirs and
|
|
||||||
* successors. We intend this dedication to be an overt act of
|
|
||||||
* relinquishment in perpetuity of all present and future rights to this
|
|
||||||
* software under copyright law.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
||||||
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
||||||
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
||||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
||||||
* OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
package org.sleuthkit.autopsy.examples;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import javax.xml.parsers.DocumentBuilder;
|
|
||||||
import javax.xml.parsers.DocumentBuilderFactory;
|
|
||||||
import javax.xml.parsers.ParserConfigurationException;
|
|
||||||
import javax.xml.transform.Transformer;
|
|
||||||
import javax.xml.transform.TransformerConfigurationException;
|
|
||||||
import javax.xml.transform.TransformerException;
|
|
||||||
import javax.xml.transform.TransformerFactory;
|
|
||||||
import javax.xml.transform.dom.DOMSource;
|
|
||||||
import javax.xml.transform.stream.StreamResult;
|
|
||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
|
||||||
import org.sleuthkit.autopsy.coreutils.ErrorInfo;
|
|
||||||
import org.sleuthkit.autopsy.coreutils.ExecUtil;
|
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
|
||||||
import org.sleuthkit.autopsy.externalresults.ExternalResults;
|
|
||||||
import org.sleuthkit.autopsy.externalresults.ExternalResultsImporter;
|
|
||||||
import org.sleuthkit.autopsy.externalresults.ExternalResultsXMLParser;
|
|
||||||
import org.sleuthkit.autopsy.ingest.DataSourceIngestModule;
|
|
||||||
import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProcessTerminator;
|
|
||||||
import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress;
|
|
||||||
import org.sleuthkit.autopsy.ingest.IngestJobContext;
|
|
||||||
import org.sleuthkit.autopsy.ingest.IngestMessage;
|
|
||||||
import org.sleuthkit.autopsy.ingest.IngestModuleReferenceCounter;
|
|
||||||
import org.sleuthkit.autopsy.ingest.IngestServices;
|
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
|
|
||||||
import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
|
|
||||||
import org.sleuthkit.datamodel.Content;
|
|
||||||
import org.sleuthkit.datamodel.Image;
|
|
||||||
import org.w3c.dom.Document;
|
|
||||||
import org.w3c.dom.Element;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sample data source ingest module that doesn't do much. Demonstrates use of
|
|
||||||
* utility classes: ExecUtils and the org.sleuthkit.autopsy.externalresults
|
|
||||||
* package.
|
|
||||||
*/
|
|
||||||
public class SampleExecutableDataSourceIngestModule implements DataSourceIngestModule {
|
|
||||||
|
|
||||||
private static final IngestModuleReferenceCounter refCounter = new IngestModuleReferenceCounter();
|
|
||||||
private static final String moduleName = SampleExecutableIngestModuleFactory.getModuleName();
|
|
||||||
private final String fileInCaseDatabase = "/WINDOWS/system32/ntmsapi.dll"; // Probably
|
|
||||||
private IngestJobContext context;
|
|
||||||
private String outputDirPath;
|
|
||||||
private String derivedFileInCaseDatabase;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void startUp(IngestJobContext context) throws IngestModuleException {
|
|
||||||
this.context = context;
|
|
||||||
if (refCounter.incrementAndGet(context.getJobId()) == 1) {
|
|
||||||
// Create an output directory for this job.
|
|
||||||
outputDirPath = Case.getCurrentCase().getModuleDirectory() + File.separator + moduleName; //NON-NLS
|
|
||||||
File outputDir = new File(outputDirPath);
|
|
||||||
if (outputDir.exists() == false) {
|
|
||||||
outputDir.mkdirs();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ProcessResult process(Content dataSource, DataSourceIngestModuleProgress progressBar) {
|
|
||||||
if (refCounter.get(context.getJobId()) == 1) {
|
|
||||||
try {
|
|
||||||
// There will be two tasks: data source analysis and import of
|
|
||||||
// the results of the analysis.
|
|
||||||
progressBar.switchToDeterminate(2);
|
|
||||||
|
|
||||||
// Do the analysis. The following sample code could be used to
|
|
||||||
// run an executable. In this case the executable would take
|
|
||||||
// two command line arguments, the path to the data source to be
|
|
||||||
// analyzed and the path to a results file to be generated. The
|
|
||||||
// results file would be an an XML file (see org.sleuthkit.autopsy.externalresults.autopsy_external_results.xsd)
|
|
||||||
// with instructions for the import of blackboard artifacts,
|
|
||||||
// derived files, and reports generated by the analysis. In this
|
|
||||||
// sample ingest module, the generation of the analysis results is
|
|
||||||
// simulated.
|
|
||||||
String resultsFilePath = outputDirPath + File.separator + String.format("job_%d_results.xml", context.getJobId());
|
|
||||||
boolean haveRealExecutable = false;
|
|
||||||
if (haveRealExecutable) {
|
|
||||||
if (dataSource instanceof Image) {
|
|
||||||
Image image = (Image) dataSource;
|
|
||||||
String dataSourcePath = image.getPaths()[0];
|
|
||||||
List<String> commandLine = new ArrayList<>();
|
|
||||||
commandLine.add("some.exe");
|
|
||||||
commandLine.add(dataSourcePath);
|
|
||||||
commandLine.add(resultsFilePath);
|
|
||||||
ProcessBuilder processBuilder = new ProcessBuilder(commandLine);
|
|
||||||
ExecUtil.execute(processBuilder, new DataSourceIngestModuleProcessTerminator(context));
|
|
||||||
} // not a disk image
|
|
||||||
else {
|
|
||||||
return ProcessResult.OK;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
generateSimulatedResults(resultsFilePath);
|
|
||||||
}
|
|
||||||
progressBar.progress(1);
|
|
||||||
|
|
||||||
// Import the results of the analysis.
|
|
||||||
ExternalResultsXMLParser resultsParser = new ExternalResultsXMLParser(dataSource, resultsFilePath);
|
|
||||||
ExternalResults results = resultsParser.parse();
|
|
||||||
List<ErrorInfo> errors = resultsParser.getErrorInfo();
|
|
||||||
ExternalResultsImporter importer = new ExternalResultsImporter();
|
|
||||||
errors.addAll(importer.importResults(results));
|
|
||||||
for (ErrorInfo errorInfo : errors) {
|
|
||||||
IngestServices.getInstance().postMessage(IngestMessage.createErrorMessage(moduleName, "External Results Import Error", errorInfo.getMessage()));
|
|
||||||
}
|
|
||||||
progressBar.progress(2);
|
|
||||||
} catch (ParserConfigurationException | TransformerException | IOException ex) {
|
|
||||||
Logger logger = IngestServices.getInstance().getLogger(moduleName);
|
|
||||||
logger.log(Level.SEVERE, "Failed to simulate analysis and results import", ex); //NON-NLS
|
|
||||||
return ProcessResult.ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ProcessResult.OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void generateSimulatedResults(String resultsFilePath) throws ParserConfigurationException, IOException, TransformerConfigurationException, TransformerException {
|
|
||||||
List<String> derivedFilePaths = generateSimulatedDerivedFiles();
|
|
||||||
List<String> reportFilePaths = generateSimulatedReports();
|
|
||||||
generateSimulatedResultsFile(derivedFilePaths, reportFilePaths, resultsFilePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<String> generateSimulatedDerivedFiles() throws IOException {
|
|
||||||
List<String> filePaths = new ArrayList<>();
|
|
||||||
String fileContents = "This is a simulated derived file.";
|
|
||||||
for (int i = 0; i < 2; ++i) {
|
|
||||||
String fileName = String.format("job_%d_derived_file_%d.txt", context.getJobId(), i);
|
|
||||||
filePaths.add(generateFile(fileName, fileContents.getBytes()));
|
|
||||||
if (i == 0) {
|
|
||||||
this.derivedFileInCaseDatabase = this.fileInCaseDatabase + "/" + fileName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return filePaths;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<String> generateSimulatedReports() throws IOException {
|
|
||||||
List<String> filePaths = new ArrayList<>();
|
|
||||||
String fileContents = "This is a simulated report.";
|
|
||||||
for (int i = 0; i < 2; ++i) {
|
|
||||||
String fileName = String.format("job_%d_report_%d.txt", context.getJobId(), i);
|
|
||||||
filePaths.add(generateFile(fileName, fileContents.getBytes()));
|
|
||||||
}
|
|
||||||
return filePaths;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String generateFile(String fileName, byte[] fileContents) throws IOException {
|
|
||||||
String filePath = outputDirPath + File.separator + fileName;
|
|
||||||
File file = new File(filePath);
|
|
||||||
if (!file.exists()) {
|
|
||||||
file.createNewFile();
|
|
||||||
}
|
|
||||||
try (FileOutputStream fileStream = new FileOutputStream(file)) {
|
|
||||||
fileStream.write(fileContents);
|
|
||||||
fileStream.flush();
|
|
||||||
}
|
|
||||||
return filePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void generateSimulatedResultsFile(List<String> derivedFilePaths, List<String> reportPaths, String resultsFilePath) throws ParserConfigurationException, TransformerConfigurationException, TransformerException {
|
|
||||||
// SAMPLE GENERATED BY THE CODE BELOW:
|
|
||||||
//
|
|
||||||
// <?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
// <autopsy_results>
|
|
||||||
// <derived_files>
|
|
||||||
// <derived_file>
|
|
||||||
// <local_path>C:\cases\Small\ModuleOutput\Sample Executable Ingest Module\job_1_derived_file_0.txt</local_path>
|
|
||||||
// <parent_file>/WINDOWS/system32/ntmsapi.dll</parent_file>
|
|
||||||
// </derived_file>
|
|
||||||
// <derived_file>
|
|
||||||
// <local_path>C:\cases\Small\ModuleOutput\Sample Executable Ingest Module\job_1_derived_file_1.txt</local_path>
|
|
||||||
// <parent_file>/WINDOWS/system32/ntmsapi.dll/job_1_derived_file_0.txt</parent_file>
|
|
||||||
// </derived_file>
|
|
||||||
// </derived_files>
|
|
||||||
// <artifacts>
|
|
||||||
// <artifact type="TSK_INTERESTING_FILE_HIT">
|
|
||||||
// <source_file>/WINDOWS/system32/ntmsapi.dll</source_file>
|
|
||||||
// <attribute type="TSK_SET_NAME">
|
|
||||||
// <value>SampleInterestingFilesSet</value>
|
|
||||||
// <source_module>Sample Executable Ingest Module</source_module>
|
|
||||||
// </attribute>
|
|
||||||
// </artifact>
|
|
||||||
// <artifact type="SampleArtifactType">
|
|
||||||
// <source_file>/WINDOWS/system32/ntmsapi.dll/job_1_derived_file_0.txt</source_file>
|
|
||||||
// <attribute type="SampleArtifactAttributeType">
|
|
||||||
// <value type="text">One</value>
|
|
||||||
// </attribute>
|
|
||||||
// <attribute type="SampleArtifactAttributeType">
|
|
||||||
// <value type="int32">2</value>
|
|
||||||
// </attribute>
|
|
||||||
// <attribute type="SampleArtifactAttributeType">
|
|
||||||
// <value type="int64">3</value>
|
|
||||||
// </attribute>
|
|
||||||
// <attribute type="SampleArtifactAttributeType">
|
|
||||||
// <value type="double">4.0</value>
|
|
||||||
// </attribute>
|
|
||||||
// </artifact>
|
|
||||||
// </artifacts>
|
|
||||||
// <reports>
|
|
||||||
// <report>
|
|
||||||
// <local_path>C:\cases\Small\ModuleOutput\Sample Executable Ingest Module\job_1_report_0.txt</local_path>
|
|
||||||
// <source_module>Sample Executable Ingest Module</source_module>
|
|
||||||
// <report_name>Sample Report</report_name>
|
|
||||||
// </report>
|
|
||||||
// <report>
|
|
||||||
// <local_path>C:\cases\Small\ModuleOutput\Sample Executable Ingest Module\job_1_report_1.txt</local_path>
|
|
||||||
// <source_module>Sample Executable Ingest Module</source_module>
|
|
||||||
// </report>
|
|
||||||
// </reports>
|
|
||||||
// </autopsy_results>
|
|
||||||
|
|
||||||
// Create the XML DOM document and the root element.
|
|
||||||
DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
|
|
||||||
DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
|
|
||||||
Document doc = docBuilder.newDocument();
|
|
||||||
Element rootElement = doc.createElement(ExternalResultsXMLParser.TagNames.ROOT_ELEM.toString());
|
|
||||||
doc.appendChild(rootElement);
|
|
||||||
|
|
||||||
// Add a derived files list element to the root element.
|
|
||||||
Element derivedFilesListElement = doc.createElement(ExternalResultsXMLParser.TagNames.DERIVED_FILES_LIST_ELEM.toString());
|
|
||||||
rootElement.appendChild(derivedFilesListElement);
|
|
||||||
|
|
||||||
// Add derived file elements to the derived files list element. Each
|
|
||||||
// file element gets required local path and parent file child elements.
|
|
||||||
// Note that the local path of the derived file must be to a location in
|
|
||||||
// the case directory or a subdirectory of the case directory and the
|
|
||||||
// parent file must be specified using the path format used in the case
|
|
||||||
// database, e.g., /WINDOWS/system32/ntmsapi.dll, where volume, file
|
|
||||||
// system, etc. are not in the path.
|
|
||||||
for (int i = 0; i < derivedFilePaths.size(); ++i) {
|
|
||||||
String filePath = derivedFilePaths.get(i);
|
|
||||||
Element derivedFileElement = doc.createElement(ExternalResultsXMLParser.TagNames.DERIVED_FILE_ELEM.toString());
|
|
||||||
derivedFilesListElement.appendChild(derivedFileElement);
|
|
||||||
Element localPathElement = doc.createElement(ExternalResultsXMLParser.TagNames.LOCAL_PATH_ELEM.toString());
|
|
||||||
localPathElement.setTextContent(filePath);
|
|
||||||
derivedFileElement.appendChild(localPathElement);
|
|
||||||
Element parentPathElement = doc.createElement(ExternalResultsXMLParser.TagNames.PARENT_FILE_ELEM.toString());
|
|
||||||
if (i == 0) {
|
|
||||||
parentPathElement.setTextContent(this.fileInCaseDatabase);
|
|
||||||
} else {
|
|
||||||
parentPathElement.setTextContent(this.derivedFileInCaseDatabase);
|
|
||||||
}
|
|
||||||
derivedFileElement.appendChild(parentPathElement);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add an artifacts list element to the root element.
|
|
||||||
Element artifactsListElement = doc.createElement(ExternalResultsXMLParser.TagNames.ARTIFACTS_LIST_ELEM.toString());
|
|
||||||
rootElement.appendChild(artifactsListElement);
|
|
||||||
|
|
||||||
// Add an artifact element to the artifacts list element with the required
|
|
||||||
// artifact type attribute. A standard artifact type is used as the type
|
|
||||||
// attribute of this artifact element.
|
|
||||||
Element artifactElement = doc.createElement(ExternalResultsXMLParser.TagNames.ARTIFACT_ELEM.toString());
|
|
||||||
artifactElement.setAttribute(ExternalResultsXMLParser.AttributeNames.TYPE_ATTR.toString(), ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getLabel());
|
|
||||||
artifactsListElement.appendChild(artifactElement);
|
|
||||||
|
|
||||||
// Add the required source file element to the artifact element. Note
|
|
||||||
// that source file must be either the local path of a derived file or a
|
|
||||||
// file in the case database.
|
|
||||||
Element fileElement = doc.createElement(ExternalResultsXMLParser.TagNames.SOURCE_FILE_ELEM.toString());
|
|
||||||
fileElement.setTextContent(this.fileInCaseDatabase);
|
|
||||||
artifactElement.appendChild(fileElement);
|
|
||||||
|
|
||||||
// Add an artifact attribute element to the artifact element. A standard
|
|
||||||
// artifact attribute type is used as the required type XML attribute of
|
|
||||||
// the artifact attribute element.
|
|
||||||
Element artifactAttrElement = doc.createElement(ExternalResultsXMLParser.TagNames.ATTRIBUTE_ELEM.toString());
|
|
||||||
artifactAttrElement.setAttribute(ExternalResultsXMLParser.AttributeNames.TYPE_ATTR.toString(), ATTRIBUTE_TYPE.TSK_SET_NAME.getLabel());
|
|
||||||
artifactElement.appendChild(artifactAttrElement);
|
|
||||||
|
|
||||||
// Add the required value element to the artifact attribute element,
|
|
||||||
// with an optional type XML attribute of ExternalXML.VALUE_TYPE_TEXT,
|
|
||||||
// which is the default.
|
|
||||||
Element artifactAttributeValueElement = doc.createElement(ExternalResultsXMLParser.TagNames.VALUE_ELEM.toString());
|
|
||||||
artifactAttributeValueElement.setTextContent("SampleInterestingFilesSet");
|
|
||||||
artifactAttrElement.appendChild(artifactAttributeValueElement);
|
|
||||||
|
|
||||||
// Add an optional source module element to the artifact attribute
|
|
||||||
// element.
|
|
||||||
Element artifactAttrSourceElement = doc.createElement(ExternalResultsXMLParser.TagNames.SOURCE_MODULE_ELEM.toString());
|
|
||||||
artifactAttrSourceElement.setTextContent(moduleName);
|
|
||||||
artifactAttrElement.appendChild(artifactAttrSourceElement);
|
|
||||||
|
|
||||||
// Add an artifact element with a user-defined type.
|
|
||||||
artifactElement = doc.createElement(ExternalResultsXMLParser.TagNames.ARTIFACT_ELEM.toString());
|
|
||||||
artifactElement.setAttribute(ExternalResultsXMLParser.AttributeNames.TYPE_ATTR.toString(), "SampleArtifactType");
|
|
||||||
artifactsListElement.appendChild(artifactElement);
|
|
||||||
|
|
||||||
// Add the required source file element.
|
|
||||||
fileElement = doc.createElement(ExternalResultsXMLParser.TagNames.SOURCE_FILE_ELEM.toString());
|
|
||||||
fileElement.setTextContent(this.derivedFileInCaseDatabase);
|
|
||||||
artifactElement.appendChild(fileElement);
|
|
||||||
|
|
||||||
// Add artifact attribute elements with user-defined types to the
|
|
||||||
// artifact element, adding value elements of assorted types.
|
|
||||||
for (int i = 0; i < 5; ++i) {
|
|
||||||
artifactAttrElement = doc.createElement(ExternalResultsXMLParser.TagNames.ATTRIBUTE_ELEM.toString());
|
|
||||||
artifactAttrElement.setAttribute(ExternalResultsXMLParser.AttributeNames.TYPE_ATTR.toString(), "SampleArtifactAttributeType" + i);
|
|
||||||
artifactElement.appendChild(artifactAttrElement);
|
|
||||||
artifactAttributeValueElement = doc.createElement(ExternalResultsXMLParser.TagNames.VALUE_ELEM.toString());
|
|
||||||
switch (i) {
|
|
||||||
case 0:
|
|
||||||
artifactAttributeValueElement.setAttribute(ExternalResultsXMLParser.AttributeNames.TYPE_ATTR.toString(), ExternalResultsXMLParser.AttributeValues.VALUE_TYPE_TEXT.toString());
|
|
||||||
artifactAttributeValueElement.setTextContent("One");
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
artifactAttributeValueElement.setAttribute(ExternalResultsXMLParser.AttributeNames.TYPE_ATTR.toString(), ExternalResultsXMLParser.AttributeValues.VALUE_TYPE_INT32.toString());
|
|
||||||
artifactAttributeValueElement.setTextContent("2");
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
artifactAttributeValueElement.setAttribute(ExternalResultsXMLParser.AttributeNames.TYPE_ATTR.toString(), ExternalResultsXMLParser.AttributeValues.VALUE_TYPE_INT64.toString());
|
|
||||||
artifactAttributeValueElement.setTextContent("3");
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
artifactAttributeValueElement.setAttribute(ExternalResultsXMLParser.AttributeNames.TYPE_ATTR.toString(), ExternalResultsXMLParser.AttributeValues.VALUE_TYPE_DOUBLE.toString());
|
|
||||||
artifactAttributeValueElement.setTextContent("4.0");
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
artifactAttributeValueElement.setAttribute(ExternalResultsXMLParser.AttributeNames.TYPE_ATTR.toString(), ExternalResultsXMLParser.AttributeValues.VALUE_TYPE_DATETIME.toString());
|
|
||||||
artifactAttributeValueElement.setTextContent("7023372036854775839");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
artifactAttrElement.appendChild(artifactAttributeValueElement);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add a reports list element to the root element.
|
|
||||||
Element reportsListElement = doc.createElement(ExternalResultsXMLParser.TagNames.REPORTS_LIST_ELEM.toString());
|
|
||||||
rootElement.appendChild(reportsListElement);
|
|
||||||
|
|
||||||
// Add report elements to the reports list element. Each report element
|
|
||||||
// gets required local path and source module child elements. There is
|
|
||||||
// also an optional report name element. Note that the local path of the
|
|
||||||
// report must be to a location in the case directory or a subdirectory
|
|
||||||
// of the case directory and the parent file must be specified using the
|
|
||||||
// path format used in the case database, e.g., /WINDOWS/system32/ntmsapi.dll,
|
|
||||||
// where volume, file system, etc. are not in the path.
|
|
||||||
for (int i = 0; i < reportPaths.size(); ++i) {
|
|
||||||
String reportPath = reportPaths.get(i);
|
|
||||||
Element reportElement = doc.createElement(ExternalResultsXMLParser.TagNames.REPORT_ELEM.toString());
|
|
||||||
reportsListElement.appendChild(reportElement);
|
|
||||||
Element reportPathElement = doc.createElement(ExternalResultsXMLParser.TagNames.LOCAL_PATH_ELEM.toString());
|
|
||||||
reportPathElement.setTextContent(reportPath);
|
|
||||||
reportElement.appendChild(reportPathElement);
|
|
||||||
Element reportSourceModuleElement = doc.createElement(ExternalResultsXMLParser.TagNames.SOURCE_MODULE_ELEM.toString());
|
|
||||||
reportSourceModuleElement.setTextContent(moduleName);
|
|
||||||
reportElement.appendChild(reportSourceModuleElement);
|
|
||||||
if (i == 0) {
|
|
||||||
Element reportNameElement = doc.createElement(ExternalResultsXMLParser.TagNames.REPORT_NAME_ELEM.toString());
|
|
||||||
reportNameElement.setTextContent("Sample Report");
|
|
||||||
reportElement.appendChild(reportNameElement);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TransformerFactory transformerFactory = TransformerFactory.newInstance();
|
|
||||||
Transformer transformer = transformerFactory.newTransformer();
|
|
||||||
DOMSource source = new DOMSource(doc);
|
|
||||||
StreamResult result = new StreamResult(new File(resultsFilePath));
|
|
||||||
transformer.transform(source, result);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,79 +0,0 @@
|
|||||||
/*
|
|
||||||
* Sample ingest module factory in the public domain.
|
|
||||||
* Feel free to use this as a template for your inget module factories.
|
|
||||||
*
|
|
||||||
* Contact: Brian Carrier [carrier <at> sleuthkit [dot] org]
|
|
||||||
*
|
|
||||||
* This is free and unencumbered software released into the public domain.
|
|
||||||
*
|
|
||||||
* Anyone is free to copy, modify, publish, use, compile, sell, or
|
|
||||||
* distribute this software, either in source code form or as a compiled
|
|
||||||
* binary, for any purpose, commercial or non-commercial, and by any
|
|
||||||
* means.
|
|
||||||
*
|
|
||||||
* In jurisdictions that recognize copyright laws, the author or authors
|
|
||||||
* of this software dedicate any and all copyright interest in the
|
|
||||||
* software to the public domain. We make this dedication for the benefit
|
|
||||||
* of the public at large and to the detriment of our heirs and
|
|
||||||
* successors. We intend this dedication to be an overt act of
|
|
||||||
* relinquishment in perpetuity of all present and future rights to this
|
|
||||||
* software under copyright law.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
||||||
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
||||||
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
||||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
||||||
* OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
package org.sleuthkit.autopsy.examples;
|
|
||||||
|
|
||||||
import org.openide.util.NbBundle;
|
|
||||||
import org.openide.util.lookup.ServiceProvider;
|
|
||||||
import static org.sleuthkit.autopsy.examples.SampleIngestModuleFactory.getModuleName;
|
|
||||||
import org.sleuthkit.autopsy.ingest.DataSourceIngestModule;
|
|
||||||
import org.sleuthkit.autopsy.ingest.IngestModuleFactory;
|
|
||||||
import org.sleuthkit.autopsy.ingest.IngestModuleFactoryAdapter;
|
|
||||||
import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A factory that creates sample executable data source ingest modules.
|
|
||||||
*/
|
|
||||||
@ServiceProvider(service = IngestModuleFactory.class) // Sample is discarded at runtime
|
|
||||||
public class SampleExecutableIngestModuleFactory extends IngestModuleFactoryAdapter {
|
|
||||||
|
|
||||||
private static final String VERSION_NUMBER = "1.0.0";
|
|
||||||
|
|
||||||
// This class method allows the ingest module instances created by this
|
|
||||||
// factory to use the same display name that is provided to the Autopsy
|
|
||||||
// ingest framework by the factory.
|
|
||||||
static String getModuleName() {
|
|
||||||
return NbBundle.getMessage(SampleIngestModuleFactory.class, "SampleExecutableIngestModuleFactory.moduleName");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getModuleDisplayName() {
|
|
||||||
return getModuleName();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getModuleDescription() {
|
|
||||||
return NbBundle.getMessage(SampleIngestModuleFactory.class, "SampleExecutableIngestModuleFactory.moduleDescription");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getModuleVersionNumber() {
|
|
||||||
return VERSION_NUMBER;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isDataSourceIngestModuleFactory() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DataSourceIngestModule createDataSourceIngestModule(IngestModuleIngestJobSettings ingestOptions) {
|
|
||||||
return new SampleExecutableDataSourceIngestModule();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
ExternalResultsImporter.importDerivedFiles.errMsg1.text=Could not import derived file at {0}, parent file {1} not found.
|
|
||||||
ExternalResultsImporter.importDerivedFiles.errMsg2.text=Could not import derived file at {0}, file does not exist.
|
|
||||||
ExternalResultsImporter.importDerivedFiles.errMsg3.text=Could not import derived file at {0}, error querying/updating case database.
|
|
||||||
ExternalResultsImporter.importArtifacts.caseErrMsg1.text=Could not import {0} attribute, value \= {1}, for {2} artifact from {3}, unrecognized attribute value type\: {4}.
|
|
||||||
ExternalResultsImporter.importArtifacts.errMsg1.text=Could not import {0} artifact from {1}, source file not found.
|
|
||||||
ExternalResultsImporter.importArtifacts.errMsg2.text=Could not import {0} artifact from {1}, error updating case database.
|
|
||||||
ExternalResultsImporter.importReports.errMsg1.text=Could not import report at {0}, file does not exist.
|
|
||||||
ExternalResultsImporter.importReports.errMsg2.text=Could not import report at {0}, error updating case database.
|
|
||||||
ExternalResultsImporter.findFileInCaseDatabase.errMsg1.text=Parent file path {0} is ambiguous, using first file found.
|
|
||||||
ExternalResultsImporter.getPathRelativeToCaseFolder.errMsg1.text=Did not convert {0} to relative path, not in a subdirectory of case directory {1}.
|
|
||||||
ExternalResultsImporter.getPathRelativeToCaseFolder.errMsg2.text=Expected {0} to be an absolute path to a file in a subdirectory of case directory {1}.
|
|
||||||
ExternalResultsXMLParser.parse.errMsg1.text=Did not find {0} root element of {1}.
|
|
||||||
ExternalResultsXMLParser.parse.errMsg2.text=Error parsing {0}.
|
|
||||||
ExternalResultsXMLParser.parseArtifactAttributes.errMsg1.text=Found {0} element that has no content in {1}.
|
|
||||||
ExternalResultsXMLParser.parseAttributeValueType.errMsg1.text=Found unrecognized value {0} for {1} attribute of {2} element.
|
|
||||||
ExternalResultsXMLParser.getChildElementContent.errMsg1.text=Found {0} element with {1} child element that has no content in {2}.
|
|
||||||
ExternalResultsXMLParser.getChildElementContent.errMsg2.text=Found {0} element missing {1} child element in {2}.
|
|
||||||
ExternalResultsXMLParser.getChildElement.errMsg1.text=Found multiple {0} child elements for {1} element in {2}, ignoring all but first occurrence.
|
|
||||||
ExternalResults.addArtifact.exception.msg1.text=type argument is empty.
|
|
||||||
ExternalResults.addArtifact.exception.msg2.text=source argument is empty.
|
|
||||||
ExternalResults.addReport.exception.msg1.text=localPath argument is empty.
|
|
||||||
ExternalResults.addReport.exception.msg2.text=sourceModuleName argument is empty.
|
|
||||||
ExternalResults.addDerivedFile.exception.msg1.text=localPath argument is empty.
|
|
||||||
ExternalResults.addDerivedFile.exception.msg2.text=parentPath argument is empty.
|
|
||||||
ExternalResults.Artifact.addAttribute.exception.msg1.text=type argument is empty.
|
|
||||||
ExternalResults.Artifact.addAttribute.exception.msg2.text=value argument is empty.
|
|
||||||
ExternalResults.Artifact.addAttribute.exception.msg3.text=valueType argument is empty.
|
|
@ -1,27 +0,0 @@
|
|||||||
ExternalResults.addArtifact.exception.msg1.text=\u5F15\u6570\u30BF\u30A4\u30D7\u304C\u7A7A\u767D\u3067\u3059\u3002
|
|
||||||
ExternalResults.addArtifact.exception.msg2.text=\u30BD\u30FC\u30B9\u5F15\u6570\u304C\u7A7A\u767D\u3067\u3059\u3002
|
|
||||||
ExternalResults.addDerivedFile.exception.msg1.text=\u30ED\u30FC\u30AB\u30EB\u30D1\u30B9\u5F15\u6570\u304C\u7A7A\u767D\u3067\u3059\u3002
|
|
||||||
ExternalResults.addDerivedFile.exception.msg2.text=\u30DA\u30A2\u30EC\u30F3\u30C8\u30D1\u30B9\u5F15\u6570\u304C\u7A7A\u767D\u3067\u3059\u3002
|
|
||||||
ExternalResults.addReport.exception.msg1.text=\u30ED\u30FC\u30AB\u30EB\u30D1\u30B9\u5F15\u6570\u304C\u7A7A\u767D\u3067\u3059\u3002
|
|
||||||
ExternalResults.addReport.exception.msg2.text=\u30BD\u30FC\u30B9\u30E2\u30B8\u30E5\u30FC\u30EB\u540D\u304C\u7A7A\u767D\u3067\u3059\u3002
|
|
||||||
ExternalResults.Artifact.addAttribute.exception.msg1.text=\u5F15\u6570\u30BF\u30A4\u30D7\u304C\u7A7A\u767D\u3067\u3059\u3002
|
|
||||||
ExternalResults.Artifact.addAttribute.exception.msg2.text=\u5F15\u6570\u5024\u304C\u7A7A\u767D\u3067\u3059\u3002
|
|
||||||
ExternalResultsImporter.findFileInCaseDatabase.errMsg1.text=\u30DA\u30A2\u30EC\u30F3\u30C8\u30D5\u30A1\u30A4\u30EB\u30D1\u30B9{0}\u304C\u66D6\u6627\u3067\u3059\u3002\u6700\u521D\u306B\u898B\u3064\u3051\u305F\u30D5\u30A1\u30A4\u30EB\u3092\u5229\u7528\u3057\u307E\u3059\u3002
|
|
||||||
ExternalResultsImporter.getPathRelativeToCaseFolder.errMsg1.text={0}\u3092\u76F8\u5BFE\u30D1\u30B9\u306B\u5909\u63DB\u3057\u307E\u305B\u3093\u3067\u3057\u305F\u3002\u30B1\u30FC\u30B9\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA{1}\u306E\u30B5\u30D6\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306B\u3042\u308A\u307E\u305B\u3093\u3002
|
|
||||||
ExternalResultsImporter.getPathRelativeToCaseFolder.errMsg2.text={0}\u306F\u30B1\u30FC\u30B9\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA{1}\u306E\u30B5\u30D6\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u5185\u306E\u30D5\u30A1\u30A4\u30EB\u3078\u306E\u7D76\u5BFE\u30D1\u30B9\u3068\u4E88\u60F3\u3055\u308C\u3066\u3044\u307E\u3059\u3002
|
|
||||||
ExternalResultsImporter.importArtifacts.caseErrMsg1.text={3}\u306E{2}\u30A2\u30FC\u30C6\u30A3\u30D5\u30A1\u30AF\u30C8\u306E{0}\u5C5E\u6027\u3001\u30D0\u30EA\u30E5\u30FC\uFF1D{1}\u3092\u30A4\u30F3\u30DD\u30FC\u30C8\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\u3002\u8A8D\u8B58\u3055\u308C\u3066\u3044\u306A\u3044\u5C5E\u6027\u5024\u578B\uFF1A{4}
|
|
||||||
ExternalResultsImporter.importDerivedFiles.errMsg1.text={0}\u306B\u6D3E\u751F\u30D5\u30A1\u30A4\u30EB\u3092\u30A4\u30F3\u30DD\u30FC\u30C8\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\u3002\u30DA\u30A2\u30EC\u30F3\u30C8\u30D5\u30A1\u30A4\u30EB{1}\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3067\u3057\u305F\u3002
|
|
||||||
ExternalResultsImporter.importDerivedFiles.errMsg2.text={0}\u306B\u6D3E\u751F\u30D5\u30A1\u30A4\u30EB\u3092\u30A4\u30F3\u30DD\u30FC\u30C8\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\u3002\u30D5\u30A1\u30A4\u30EB\u304C\u5B58\u5728\u3057\u307E\u305B\u3093\u3002
|
|
||||||
ExternalResultsImporter.importDerivedFiles.errMsg3.text={0}\u306B\u6D3E\u751F\u30D5\u30A1\u30A4\u30EB\u3092\u30A4\u30F3\u30DD\u30FC\u30C8\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\u3002\u30B1\u30FC\u30B9\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u3067\u306E\u30AF\u30A8\u30EA\u30FC\uFF0F\u30A2\u30C3\u30D7\u30C7\u30FC\u30C8\u306E\u5B9F\u884C\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F\u3002
|
|
||||||
ExternalResultsImporter.importReports.errMsg1.text={0}\u306B\u30EC\u30DD\u30FC\u30C8\u3092\u30A4\u30F3\u30DD\u30FC\u30C8\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\u3002\u30D5\u30A1\u30A4\u30EB\u304C\u5B58\u5728\u3057\u307E\u305B\u3093\u3002
|
|
||||||
ExternalResultsImporter.importReports.errMsg2.text={0}\u306B\u30EC\u30DD\u30FC\u30C8\u3092\u30A4\u30F3\u30DD\u30FC\u30C8\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\u3002\u30B1\u30FC\u30B9\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u306E\u30A2\u30C3\u30D7\u30C7\u30FC\u30C8\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F\u3002
|
|
||||||
ExternalResultsXMLParser.parse.errMsg2.text={0}\u306E\u30D1\u30FC\u30B9\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F\u3002
|
|
||||||
ExternalResults.Artifact.addAttribute.exception.msg3.text=\u5024\u578B\u5F15\u6570\u304C\u7A7A\u767D\u3067\u3059\u3002
|
|
||||||
ExternalResultsImporter.importArtifacts.errMsg1.text={1}\u306E{0}\u30A2\u30FC\u30C6\u30A3\u30D5\u30A1\u30AF\u30C8\u3092\u30A4\u30F3\u30DD\u30FC\u30C8\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\u3002\u30BD\u30FC\u30B9\u30D5\u30A1\u30A4\u30EB\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3067\u3057\u305F\u3002
|
|
||||||
ExternalResultsImporter.importArtifacts.errMsg2.text={1}\u306E{0}\u30A2\u30FC\u30C6\u30A3\u30D5\u30A1\u30AF\u30C8\u3092\u30A4\u30F3\u30DD\u30FC\u30C8\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\u3002\u30B1\u30FC\u30B9\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u306E\u30A2\u30C3\u30D7\u30C7\u30FC\u30C8\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F\u3002
|
|
||||||
ExternalResultsXMLParser.getChildElement.errMsg1.text={2}\u306B\u3066\u8907\u6570\u306E{1}\u30A8\u30EC\u30E1\u30F3\u30C8\u306E{0}\u30C1\u30E3\u30A4\u30EB\u30C9\u30A8\u30EC\u30E1\u30F3\u30C8\u304C\u898B\u3064\u304B\u308A\u307E\u3057\u305F\u3002\u4E00\u756A\u76EE\u4EE5\u5916\u306F\u7121\u8996\u3057\u307E\u3059\u3002
|
|
||||||
ExternalResultsXMLParser.getChildElementContent.errMsg1.text={2}\u306B\u30B3\u30F3\u30C6\u30F3\u30C4\u304C\u306A\u304F\u3001{1}\u30C1\u30E3\u30A4\u30EB\u30C9\u30A8\u30EC\u30E1\u30F3\u30C8\u304C\u3042\u308B\u3001{0}\u30A8\u30EC\u30E1\u30F3\u30C8\u304C\u898B\u3064\u304B\u308A\u307E\u3057\u305F\u3002
|
|
||||||
ExternalResultsXMLParser.getChildElementContent.errMsg2.text={2}\u306B\u3066{1}\u30C1\u30E3\u30A4\u30EB\u30C9\u30A8\u30EC\u30E1\u30F3\u30C8\u304C\u306A\u3044{0}\u30A8\u30EC\u30E1\u30F3\u30C8\u304C\u898B\u3064\u304B\u308A\u307E\u3057\u305F\u3002
|
|
||||||
ExternalResultsXMLParser.parse.errMsg1.text={1}\u306E{0}\u30EB\u30FC\u30C8\u30A8\u30EC\u30E1\u30F3\u30C8\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3067\u3057\u305F\u3002
|
|
||||||
ExternalResultsXMLParser.parseArtifactAttributes.errMsg1.text={1}\u306B\u30B3\u30F3\u30C6\u30F3\u30C4\u304C\u306A\u3044{0}\u30A8\u30EC\u30E1\u30F3\u30C8\u304C\u898B\u3064\u304B\u308A\u307E\u3057\u305F\u3002
|
|
||||||
ExternalResultsXMLParser.parseAttributeValueType.errMsg1.text={2}\u30A8\u30EC\u30E1\u30F3\u30C8\u306E{1}\u5C5E\u6027\u306B\u5BFE\u3057\u3066\u3001\u8A8D\u8B58\u3055\u308C\u306A\u3044\u30D0\u30EA\u30E5\u30FC{0}\u304C\u898B\u3064\u304B\u308A\u307E\u3057\u305F\u3002
|
|
@ -1,212 +0,0 @@
|
|||||||
/*
|
|
||||||
* Autopsy Forensic Browser
|
|
||||||
*
|
|
||||||
* Copyright 2014 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.externalresults;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.openide.util.NbBundle;
|
|
||||||
import org.sleuthkit.datamodel.Content;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
final public class ExternalResults {
|
|
||||||
|
|
||||||
private final Content dataSource;
|
|
||||||
private final List<Artifact> artifacts = new ArrayList<>();
|
|
||||||
private final List<Report> reports = new ArrayList<>();
|
|
||||||
private final List<DerivedFile> derivedFiles = new ArrayList<>();
|
|
||||||
|
|
||||||
ExternalResults(Content dataSource) {
|
|
||||||
this.dataSource = dataSource;
|
|
||||||
}
|
|
||||||
|
|
||||||
Content getDataSource() {
|
|
||||||
return this.dataSource;
|
|
||||||
}
|
|
||||||
|
|
||||||
Artifact addArtifact(String type, String sourceFilePath) {
|
|
||||||
if (type.isEmpty()) {
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
NbBundle.getMessage(this.getClass(), "ExternalResults.addArtifact.exception.msg1.text"));
|
|
||||||
}
|
|
||||||
if (sourceFilePath.isEmpty()) {
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
NbBundle.getMessage(this.getClass(), "ExternalResults.addArtifact.exception.msg2.text"));
|
|
||||||
}
|
|
||||||
Artifact artifact = new Artifact(type, sourceFilePath);
|
|
||||||
artifacts.add(artifact);
|
|
||||||
return artifact;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Artifact> getArtifacts() {
|
|
||||||
return Collections.unmodifiableList(artifacts);
|
|
||||||
}
|
|
||||||
|
|
||||||
void addReport(String localPath, String sourceModuleName, String reportName) {
|
|
||||||
if (localPath.isEmpty()) {
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
NbBundle.getMessage(this.getClass(), "ExternalResults.addReport.exception.msg1.text"));
|
|
||||||
}
|
|
||||||
if (sourceModuleName.isEmpty()) {
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
NbBundle.getMessage(this.getClass(), "ExternalResults.addReport.exception.msg2.text"));
|
|
||||||
}
|
|
||||||
Report report = new Report(localPath, sourceModuleName, reportName);
|
|
||||||
reports.add(report);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Report> getReports() {
|
|
||||||
return Collections.unmodifiableList(reports);
|
|
||||||
}
|
|
||||||
|
|
||||||
void addDerivedFile(String localPath, String parentPath) {
|
|
||||||
if (localPath.isEmpty()) {
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
NbBundle.getMessage(this.getClass(), "ExternalResults.addDerivedFile.exception.msg1.text"));
|
|
||||||
}
|
|
||||||
if (parentPath.isEmpty()) {
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
NbBundle.getMessage(this.getClass(), "ExternalResults.addDerivedFile.exception.msg2.text"));
|
|
||||||
}
|
|
||||||
DerivedFile file = new DerivedFile(localPath, parentPath);
|
|
||||||
derivedFiles.add(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<DerivedFile> getDerivedFiles() {
|
|
||||||
return Collections.unmodifiableList(derivedFiles);
|
|
||||||
}
|
|
||||||
|
|
||||||
static final class Artifact {
|
|
||||||
|
|
||||||
private final String type;
|
|
||||||
private final String sourceFilePath;
|
|
||||||
private final ArrayList<ArtifactAttribute> attributes = new ArrayList<>();
|
|
||||||
|
|
||||||
Artifact(String type, String sourceFilePath) {
|
|
||||||
this.type = type;
|
|
||||||
this.sourceFilePath = sourceFilePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
String getType() {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
String getSourceFilePath() {
|
|
||||||
return sourceFilePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
void addAttribute(String type, String value, String valueType, String sourceModule) {
|
|
||||||
if (type.isEmpty()) {
|
|
||||||
throw new IllegalArgumentException(NbBundle.getMessage(this.getClass(),
|
|
||||||
"ExternalResults.Artifact.addAttribute.exception.msg1.text"));
|
|
||||||
}
|
|
||||||
if (value.isEmpty()) {
|
|
||||||
throw new IllegalArgumentException(NbBundle.getMessage(this.getClass(),
|
|
||||||
"ExternalResults.Artifact.addAttribute.exception.msg2.text"));
|
|
||||||
}
|
|
||||||
if (valueType.isEmpty()) {
|
|
||||||
throw new IllegalArgumentException(NbBundle.getMessage(this.getClass(),
|
|
||||||
"ExternalResults.Artifact.addAttribute.exception.msg3.text"));
|
|
||||||
}
|
|
||||||
attributes.add(new ArtifactAttribute(type, value, valueType, sourceModule));
|
|
||||||
}
|
|
||||||
|
|
||||||
List<ArtifactAttribute> getAttributes() {
|
|
||||||
return Collections.unmodifiableList(attributes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static final class ArtifactAttribute {
|
|
||||||
|
|
||||||
private final String type;
|
|
||||||
private final String valueType;
|
|
||||||
private final String value;
|
|
||||||
private final String sourceModule;
|
|
||||||
|
|
||||||
private ArtifactAttribute(String type, String value, String valueType, String sourceModule) {
|
|
||||||
this.type = type;
|
|
||||||
this.value = value;
|
|
||||||
this.valueType = valueType;
|
|
||||||
this.sourceModule = sourceModule;
|
|
||||||
}
|
|
||||||
|
|
||||||
String getType() {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
String getValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
String getValueType() {
|
|
||||||
return valueType;
|
|
||||||
}
|
|
||||||
|
|
||||||
String getSourceModule() {
|
|
||||||
return sourceModule;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static final class Report {
|
|
||||||
|
|
||||||
private final String localPath;
|
|
||||||
private final String sourceModuleName;
|
|
||||||
private final String reportName;
|
|
||||||
|
|
||||||
Report(String localPath, String sourceModuleName, String displayName) {
|
|
||||||
this.localPath = localPath;
|
|
||||||
this.sourceModuleName = sourceModuleName;
|
|
||||||
this.reportName = displayName;
|
|
||||||
}
|
|
||||||
|
|
||||||
String getLocalPath() {
|
|
||||||
return localPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
String getSourceModuleName() {
|
|
||||||
return sourceModuleName;
|
|
||||||
}
|
|
||||||
|
|
||||||
String getReportName() {
|
|
||||||
return reportName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static final class DerivedFile {
|
|
||||||
|
|
||||||
private final String localPath;
|
|
||||||
private final String parentPath;
|
|
||||||
|
|
||||||
DerivedFile(String localPath, String parentPath) {
|
|
||||||
this.localPath = localPath;
|
|
||||||
this.parentPath = parentPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
String getLocalPath() {
|
|
||||||
return localPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
String getParentPath() {
|
|
||||||
return parentPath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,308 +0,0 @@
|
|||||||
/*
|
|
||||||
* Autopsy Forensic Browser
|
|
||||||
*
|
|
||||||
* Copyright 2011-2016 Basis Technology Corp.
|
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this localFile 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.externalresults;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import org.openide.util.NbBundle;
|
|
||||||
import org.openide.util.NbBundle.Messages;
|
|
||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
|
||||||
import org.sleuthkit.autopsy.casemodule.services.Blackboard;
|
|
||||||
import org.sleuthkit.autopsy.casemodule.services.FileManager;
|
|
||||||
import org.sleuthkit.autopsy.coreutils.ErrorInfo;
|
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
|
||||||
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
|
||||||
import org.sleuthkit.autopsy.ingest.IngestServices;
|
|
||||||
import org.sleuthkit.autopsy.ingest.ModuleContentEvent;
|
|
||||||
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
|
|
||||||
import org.sleuthkit.datamodel.AbstractFile;
|
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
|
||||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
|
||||||
import org.sleuthkit.datamodel.Content;
|
|
||||||
import org.sleuthkit.datamodel.DerivedFile;
|
|
||||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
|
||||||
import org.sleuthkit.datamodel.TskData;
|
|
||||||
import org.sleuthkit.datamodel.TskDataException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Uses a standard representation of results data (e.g., artifacts, derived
|
|
||||||
* files, reports) to import results generated by a process external to Autopsy
|
|
||||||
* into Autopsy.
|
|
||||||
*/
|
|
||||||
public final class ExternalResultsImporter {
|
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(ExternalResultsImporter.class.getName());
|
|
||||||
private static final HashSet<Integer> standardArtifactTypeIds = new HashSet<>();
|
|
||||||
private final List<ErrorInfo> errors = new ArrayList<>();
|
|
||||||
private Blackboard blackboard;
|
|
||||||
|
|
||||||
static {
|
|
||||||
for (BlackboardArtifact.ARTIFACT_TYPE artifactType : BlackboardArtifact.ARTIFACT_TYPE.values()) {
|
|
||||||
standardArtifactTypeIds.add(artifactType.getTypeID());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Import results generated by a process external to Autopsy into Autopsy.
|
|
||||||
*
|
|
||||||
* @param results A standard representation of results data (e.g.,
|
|
||||||
* artifacts, derived files, reports)from the data source.
|
|
||||||
*
|
|
||||||
* @return A collection of error messages, possibly empty. The error
|
|
||||||
* messages are already logged but are provided to allow the caller
|
|
||||||
* to provide additional user feedback via the Autopsy user
|
|
||||||
* interface.
|
|
||||||
*/
|
|
||||||
public List<ErrorInfo> importResults(ExternalResults results) {
|
|
||||||
blackboard = Case.getCurrentCase().getServices().getBlackboard();
|
|
||||||
// Import files first, they may be artifactData sources.
|
|
||||||
importDerivedFiles(results);
|
|
||||||
importArtifacts(results);
|
|
||||||
importReports(results);
|
|
||||||
List<ErrorInfo> importErrors = new ArrayList<>(this.errors);
|
|
||||||
this.errors.clear();
|
|
||||||
return importErrors;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void importDerivedFiles(ExternalResults results) {
|
|
||||||
FileManager fileManager = Case.getCurrentCase().getServices().getFileManager();
|
|
||||||
for (ExternalResults.DerivedFile fileData : results.getDerivedFiles()) {
|
|
||||||
String localPath = fileData.getLocalPath();
|
|
||||||
try {
|
|
||||||
File localFile = new File(localPath);
|
|
||||||
if (localFile.exists()) {
|
|
||||||
String relativePath = this.getPathRelativeToCaseFolder(localPath);
|
|
||||||
if (!relativePath.isEmpty()) {
|
|
||||||
String parentFilePath = fileData.getParentPath();
|
|
||||||
AbstractFile parentFile = findFileInCaseDatabase(parentFilePath);
|
|
||||||
if (parentFile != null) {
|
|
||||||
DerivedFile derivedFile = fileManager.addDerivedFile(localFile.getName(), relativePath, localFile.length(),
|
|
||||||
0, 0, 0, 0, // Do not currently have file times for derived files from external processes.
|
|
||||||
true, parentFile,
|
|
||||||
"", "", "", "", // Not currently providing derivation info for derived files from external processes.
|
|
||||||
TskData.EncodingType.NONE); // Don't allow external encoded files
|
|
||||||
IngestServices.getInstance().fireModuleContentEvent(new ModuleContentEvent(derivedFile));
|
|
||||||
} else {
|
|
||||||
String errorMessage = NbBundle.getMessage(this.getClass(),
|
|
||||||
"ExternalResultsImporter.importDerivedFiles.errMsg1.text",
|
|
||||||
localPath, parentFilePath);
|
|
||||||
ExternalResultsImporter.logger.log(Level.SEVERE, errorMessage);
|
|
||||||
this.errors.add(new ErrorInfo(ExternalResultsImporter.class.getName(), errorMessage));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
String errorMessage = NbBundle.getMessage(this.getClass(),
|
|
||||||
"ExternalResultsImporter.importDerivedFiles.errMsg2.text",
|
|
||||||
localPath);
|
|
||||||
ExternalResultsImporter.logger.log(Level.SEVERE, errorMessage);
|
|
||||||
this.errors.add(new ErrorInfo(ExternalResultsImporter.class.getName(), errorMessage));
|
|
||||||
}
|
|
||||||
} catch (TskCoreException ex) {
|
|
||||||
String errorMessage = NbBundle.getMessage(this.getClass(),
|
|
||||||
"ExternalResultsImporter.importDerivedFiles.errMsg3.text",
|
|
||||||
localPath);
|
|
||||||
ExternalResultsImporter.logger.log(Level.SEVERE, errorMessage, ex);
|
|
||||||
this.errors.add(new ErrorInfo(ExternalResultsImporter.class.getName(), errorMessage, ex));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Messages({"ExternalResultsImporter.indexError.message=Failed to index imported artifact for keyword search."})
|
|
||||||
private void importArtifacts(ExternalResults results) {
|
|
||||||
SleuthkitCase caseDb = Case.getCurrentCase().getSleuthkitCase();
|
|
||||||
for (ExternalResults.Artifact artifactData : results.getArtifacts()) {
|
|
||||||
try {
|
|
||||||
// Add the artifact to the case database.
|
|
||||||
int artifactTypeId = caseDb.getArtifactType(artifactData.getType()).getTypeID();
|
|
||||||
if (artifactTypeId == -1) {
|
|
||||||
artifactTypeId = caseDb.addBlackboardArtifactType(artifactData.getType(), artifactData.getType()).getTypeID();
|
|
||||||
}
|
|
||||||
Content sourceFile = findFileInCaseDatabase(artifactData.getSourceFilePath());
|
|
||||||
if (sourceFile != null) {
|
|
||||||
BlackboardArtifact artifact = sourceFile.newArtifact(artifactTypeId);
|
|
||||||
|
|
||||||
// Add the artifact's attributes to the case database.
|
|
||||||
Collection<BlackboardAttribute> attributes = new ArrayList<>();
|
|
||||||
for (ExternalResults.ArtifactAttribute attributeData : artifactData.getAttributes()) {
|
|
||||||
BlackboardAttribute.Type attributeType = caseDb.getAttributeType(attributeData.getType());
|
|
||||||
if (attributeType == null) {
|
|
||||||
switch (attributeData.getValueType()) {
|
|
||||||
case "text": //NON-NLS
|
|
||||||
attributeType = caseDb.addArtifactAttributeType(attributeData.getType(), BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.fromLabel("String"), attributeData.getType()); //NON-NLS
|
|
||||||
break;
|
|
||||||
case "int32": //NON-NLS
|
|
||||||
attributeType = caseDb.addArtifactAttributeType(attributeData.getType(), BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.fromLabel("Integer"), attributeData.getType()); //NON-NLS
|
|
||||||
break;
|
|
||||||
case "int64": //NON-NLS
|
|
||||||
attributeType = caseDb.addArtifactAttributeType(attributeData.getType(), BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.fromLabel("Long"), attributeData.getType()); //NON-NLS
|
|
||||||
break;
|
|
||||||
case "double": //NON-NLS
|
|
||||||
attributeType = caseDb.addArtifactAttributeType(attributeData.getType(), BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.fromLabel("Double"), attributeData.getType()); //NON-NLS
|
|
||||||
break;
|
|
||||||
case "datetime": //NON-NLS
|
|
||||||
attributeType = caseDb.addArtifactAttributeType(attributeData.getType(), BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.fromLabel("DateTime"), attributeData.getType()); //NON-NLS
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (attributeData.getValueType()) {
|
|
||||||
case "text": //NON-NLS
|
|
||||||
attributes.add(new BlackboardAttribute(attributeType, attributeData.getSourceModule(), attributeData.getValue()));
|
|
||||||
break;
|
|
||||||
case "int32": //NON-NLS
|
|
||||||
int intValue = Integer.parseInt(attributeData.getValue());
|
|
||||||
attributes.add(new BlackboardAttribute(attributeType, attributeData.getSourceModule(), intValue));
|
|
||||||
break;
|
|
||||||
case "int64": //NON-NLS
|
|
||||||
long longValue = Long.parseLong(attributeData.getValue());
|
|
||||||
attributes.add(new BlackboardAttribute(attributeType, attributeData.getSourceModule(), longValue));
|
|
||||||
break;
|
|
||||||
case "double": //NON-NLS
|
|
||||||
double doubleValue = Double.parseDouble(attributeData.getValue());
|
|
||||||
attributes.add(new BlackboardAttribute(attributeType, attributeData.getSourceModule(), doubleValue));
|
|
||||||
break;
|
|
||||||
case "datetime": //NON-NLS
|
|
||||||
long dateTimeValue = Long.parseLong(attributeData.getValue());
|
|
||||||
attributes.add(new BlackboardAttribute(attributeType, attributeData.getSourceModule(), dateTimeValue));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
String errorMessage = NbBundle.getMessage(this.getClass(),
|
|
||||||
"ExternalResultsImporter.importArtifacts.caseErrMsg1.text",
|
|
||||||
attributeData.getType(), attributeData.getValue(),
|
|
||||||
artifactData.getType(), artifactData.getSourceFilePath(),
|
|
||||||
attributeData.getValueType());
|
|
||||||
ExternalResultsImporter.logger.log(Level.SEVERE, errorMessage);
|
|
||||||
this.errors.add(new ErrorInfo(ExternalResultsImporter.class.getName(), errorMessage));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
artifact.addAttributes(attributes);
|
|
||||||
|
|
||||||
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
|
|
||||||
MessageNotifyUtil.Notify.error(
|
|
||||||
Bundle.ExternalResultsImporter_indexError_message(), artifact.getDisplayName());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (standardArtifactTypeIds.contains(artifactTypeId)) {
|
|
||||||
IngestServices.getInstance().fireModuleDataEvent(new ModuleDataEvent(this.getClass().getSimpleName(), BlackboardArtifact.ARTIFACT_TYPE.fromID(artifactTypeId)));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
String errorMessage = NbBundle.getMessage(this.getClass(),
|
|
||||||
"ExternalResultsImporter.importArtifacts.errMsg1.text",
|
|
||||||
artifactData.getType(), artifactData.getSourceFilePath());
|
|
||||||
ExternalResultsImporter.logger.log(Level.SEVERE, errorMessage);
|
|
||||||
this.errors.add(new ErrorInfo(ExternalResultsImporter.class.getName(), errorMessage));
|
|
||||||
}
|
|
||||||
} catch (TskCoreException | TskDataException ex) {
|
|
||||||
String errorMessage = NbBundle.getMessage(this.getClass(),
|
|
||||||
"ExternalResultsImporter.importArtifacts.errMsg2.text",
|
|
||||||
artifactData.getType(), artifactData.getSourceFilePath());
|
|
||||||
ExternalResultsImporter.logger.log(Level.SEVERE, errorMessage, ex);
|
|
||||||
this.errors.add(new ErrorInfo(ExternalResultsImporter.class.getName(), errorMessage, ex));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void importReports(ExternalResults results) {
|
|
||||||
for (ExternalResults.Report report : results.getReports()) {
|
|
||||||
String reportPath = report.getLocalPath();
|
|
||||||
try {
|
|
||||||
File reportFile = new File(reportPath);
|
|
||||||
if (reportFile.exists()) {
|
|
||||||
Case.getCurrentCase().addReport(reportPath, report.getSourceModuleName(), report.getReportName());
|
|
||||||
} else {
|
|
||||||
String errorMessage = NbBundle.getMessage(this.getClass(), "ExternalResultsImporter.importReports.errMsg1.text", reportPath);
|
|
||||||
ExternalResultsImporter.logger.log(Level.SEVERE, errorMessage);
|
|
||||||
this.errors.add(new ErrorInfo(ExternalResultsImporter.class.getName(), errorMessage));
|
|
||||||
}
|
|
||||||
} catch (TskCoreException ex) {
|
|
||||||
String errorMessage = NbBundle.getMessage(this.getClass(), "ExternalResultsImporter.importReports.errMsg2.text", reportPath);
|
|
||||||
ExternalResultsImporter.logger.log(Level.SEVERE, errorMessage, ex);
|
|
||||||
this.errors.add(new ErrorInfo(ExternalResultsImporter.class.getName(), errorMessage, ex));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private AbstractFile findFileInCaseDatabase(String filePath) throws TskCoreException {
|
|
||||||
AbstractFile file = null;
|
|
||||||
// Split the path into the file name and the parent path.
|
|
||||||
String fileName = filePath;
|
|
||||||
String parentPath = "";
|
|
||||||
int charPos = filePath.lastIndexOf("/");
|
|
||||||
if (charPos >= 0) {
|
|
||||||
fileName = filePath.substring(charPos + 1);
|
|
||||||
parentPath = filePath.substring(0, charPos + 1);
|
|
||||||
}
|
|
||||||
// Find the file.
|
|
||||||
String condition = "name='" + fileName + "' AND parent_path='" + parentPath + "'"; //NON-NLS
|
|
||||||
List<AbstractFile> files = Case.getCurrentCase().getSleuthkitCase().findAllFilesWhere(condition);
|
|
||||||
if (!files.isEmpty()) {
|
|
||||||
file = files.get(0);
|
|
||||||
if (files.size() > 1) {
|
|
||||||
String errorMessage = NbBundle.getMessage(this.getClass(), "ExternalResultsImporter.findFileInCaseDatabase.errMsg1.text", filePath);
|
|
||||||
this.recordError(errorMessage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return file;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getPathRelativeToCaseFolder(String localPath) {
|
|
||||||
String relativePath = "";
|
|
||||||
String caseDirectoryPath = Case.getCurrentCase().getCaseDirectory();
|
|
||||||
Path path = Paths.get(localPath);
|
|
||||||
if (path.isAbsolute()) {
|
|
||||||
Path pathBase = Paths.get(caseDirectoryPath);
|
|
||||||
try {
|
|
||||||
Path pathRelative = pathBase.relativize(path);
|
|
||||||
relativePath = pathRelative.toString();
|
|
||||||
} catch (IllegalArgumentException ex) {
|
|
||||||
String errorMessage = NbBundle.getMessage(this.getClass(),
|
|
||||||
"ExternalResultsImporter.getPathRelativeToCaseFolder.errMsg1.text",
|
|
||||||
localPath, caseDirectoryPath);
|
|
||||||
this.recordError(errorMessage, ex);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
String errorMessage = NbBundle.getMessage(this.getClass(),
|
|
||||||
"ExternalResultsImporter.getPathRelativeToCaseFolder.errMsg2.text",
|
|
||||||
localPath, caseDirectoryPath);
|
|
||||||
this.recordError(errorMessage);
|
|
||||||
}
|
|
||||||
return relativePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void recordError(String errorMessage) {
|
|
||||||
ExternalResultsImporter.logger.log(Level.SEVERE, errorMessage);
|
|
||||||
this.errors.add(new ErrorInfo(this.getClass().getName(), errorMessage));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void recordError(String errorMessage, Exception ex) {
|
|
||||||
ExternalResultsImporter.logger.log(Level.SEVERE, errorMessage, ex);
|
|
||||||
this.errors.add(new ErrorInfo(this.getClass().getName(), errorMessage));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,50 +0,0 @@
|
|||||||
/*
|
|
||||||
* Autopsy Forensic Browser
|
|
||||||
*
|
|
||||||
* Copyright 2014 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.externalresults;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import org.sleuthkit.autopsy.coreutils.ErrorInfo;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface for parsers that convert some representation of results data (e.g.,
|
|
||||||
* artifacts, derived files, reports) generated by a process external to Autopsy
|
|
||||||
* into a form ready for import into Autopsy.
|
|
||||||
*/
|
|
||||||
public interface ExternalResultsParser {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts some representation of results data generated by a process
|
|
||||||
* external to Autopsy and supplied to the parser via its constructor into a
|
|
||||||
* form ready for import into Autopsy.
|
|
||||||
*
|
|
||||||
* @return External results data in a form ready for import into Autopsy.
|
|
||||||
*/
|
|
||||||
ExternalResults parse();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets error information describing any errors encountered while parsing
|
|
||||||
* the input results representation.
|
|
||||||
*
|
|
||||||
* @return A collection of error messages, possibly empty. The error
|
|
||||||
* messages are already logged but are provided to allow the caller
|
|
||||||
* to provide additional user feedback via the Autopsy user
|
|
||||||
* interface.
|
|
||||||
*/
|
|
||||||
List<ErrorInfo> getErrorInfo();
|
|
||||||
}
|
|
@ -1,352 +0,0 @@
|
|||||||
/*
|
|
||||||
* Autopsy Forensic Browser
|
|
||||||
*
|
|
||||||
* Copyright 2011-2016 Basis Technology Corp.
|
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.sleuthkit.autopsy.externalresults;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import org.openide.util.NbBundle;
|
|
||||||
import org.sleuthkit.autopsy.coreutils.ErrorInfo;
|
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
|
||||||
import org.sleuthkit.autopsy.coreutils.XMLUtil;
|
|
||||||
import org.sleuthkit.datamodel.Content;
|
|
||||||
import org.w3c.dom.Document;
|
|
||||||
import org.w3c.dom.Element;
|
|
||||||
import org.w3c.dom.NodeList;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses an XML representation of of results data (e.g., artifacts, derived
|
|
||||||
* files, reports) generated by a process external to Autopsy.
|
|
||||||
*/
|
|
||||||
public final class ExternalResultsXMLParser implements ExternalResultsParser {
|
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(ExternalResultsXMLParser.class.getName());
|
|
||||||
private static final String XSD_FILE = "autopsy_external_results.xsd"; //NON-NLS
|
|
||||||
private final Content dataSource;
|
|
||||||
private final String resultsFilePath;
|
|
||||||
private ExternalResults resultsData;
|
|
||||||
private List<ErrorInfo> errors = new ArrayList<>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tag names for an external results XML file.
|
|
||||||
*/
|
|
||||||
public enum TagNames {
|
|
||||||
|
|
||||||
ROOT_ELEM("autopsy_results"), //NON-NLS
|
|
||||||
DERIVED_FILES_LIST_ELEM("derived_files"), //NON-NLS
|
|
||||||
DERIVED_FILE_ELEM("derived_file"), //NON-NLS
|
|
||||||
LOCAL_PATH_ELEM("local_path"), //NON-NLS
|
|
||||||
PARENT_FILE_ELEM("parent_file"), //NON-NLS
|
|
||||||
ARTIFACTS_LIST_ELEM("artifacts"), //NON-NLS
|
|
||||||
ARTIFACT_ELEM("artifact"), //NON-NLS
|
|
||||||
SOURCE_FILE_ELEM("source_file"), //NON-NLS
|
|
||||||
ATTRIBUTE_ELEM("attribute"), //NON-NLS
|
|
||||||
VALUE_ELEM("value"), //NON-NLS
|
|
||||||
SOURCE_MODULE_ELEM("source_module"), //NON-NLS
|
|
||||||
REPORTS_LIST_ELEM("reports"), //NON-NLS
|
|
||||||
REPORT_ELEM("report"), //NON-NLS
|
|
||||||
REPORT_NAME_ELEM("report_name"); //NON-NLS
|
|
||||||
private final String text;
|
|
||||||
|
|
||||||
private TagNames(final String text) {
|
|
||||||
this.text = text;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return this.text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attribute names for an external results XML file.
|
|
||||||
*/
|
|
||||||
public enum AttributeNames {
|
|
||||||
|
|
||||||
TYPE_ATTR("type"); //NON-NLS
|
|
||||||
private final String text;
|
|
||||||
|
|
||||||
private AttributeNames(final String text) {
|
|
||||||
this.text = text;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return this.text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attribute values for an external results XML file.
|
|
||||||
*/
|
|
||||||
public enum AttributeValues {
|
|
||||||
|
|
||||||
VALUE_TYPE_TEXT("text"), //NON-NLS
|
|
||||||
VALUE_TYPE_INT32("int32"), //NON-NLS
|
|
||||||
VALUE_TYPE_INT64("int64"), //NON-NLS
|
|
||||||
VALUE_TYPE_DOUBLE("double"), //NON-NLS
|
|
||||||
VALUE_TYPE_DATETIME("datetime"); //NON-NLS
|
|
||||||
private final String text;
|
|
||||||
|
|
||||||
private AttributeValues(final String text) {
|
|
||||||
this.text = text;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return this.text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor.
|
|
||||||
*
|
|
||||||
* @param dataSource The data source for the results.
|
|
||||||
* @param resultsFilePath Full path of the results file to be parsed.
|
|
||||||
*/
|
|
||||||
public ExternalResultsXMLParser(Content dataSource, String resultsFilePath) {
|
|
||||||
this.dataSource = dataSource;
|
|
||||||
this.resultsFilePath = resultsFilePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ExternalResults parse() {
|
|
||||||
this.errors.clear();
|
|
||||||
this.resultsData = new ExternalResults(dataSource);
|
|
||||||
try {
|
|
||||||
// Note that XMLUtil.loadDoc() logs a warning if the file does not
|
|
||||||
// conform to the XSD, but still returns a Document object. Until
|
|
||||||
// this behavior is improved, validation is still required. If
|
|
||||||
// XMLUtil.loadDoc() does return null, it failed to load the
|
|
||||||
// document and it logged the error.
|
|
||||||
final Document doc = XMLUtil.loadDoc(ExternalResultsXMLParser.class, this.resultsFilePath, XSD_FILE);
|
|
||||||
if (doc != null) {
|
|
||||||
final Element rootElem = doc.getDocumentElement();
|
|
||||||
if (rootElem != null && rootElem.getNodeName().equals(TagNames.ROOT_ELEM.toString())) {
|
|
||||||
parseDerivedFiles(rootElem);
|
|
||||||
parseArtifacts(rootElem);
|
|
||||||
parseReports(rootElem);
|
|
||||||
} else {
|
|
||||||
String errorMessage = NbBundle.getMessage(this.getClass(),
|
|
||||||
"ExternalResultsXMLParser.parse.errMsg1.text",
|
|
||||||
TagNames.ROOT_ELEM.toString(), this.resultsFilePath);
|
|
||||||
recordError(errorMessage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception ex) {
|
|
||||||
String errorMessage = NbBundle.getMessage(this.getClass(), "ExternalResultsXMLParser.parse.errMsg2.text", this.resultsFilePath);
|
|
||||||
recordError(errorMessage, ex);
|
|
||||||
}
|
|
||||||
return this.resultsData;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<ErrorInfo> getErrorInfo() {
|
|
||||||
return new ArrayList<>(this.errors);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void parseDerivedFiles(Element rootElement) {
|
|
||||||
// Get the derived file lists.
|
|
||||||
NodeList derivedFilesListNodes = rootElement.getElementsByTagName(TagNames.DERIVED_FILES_LIST_ELEM.toString());
|
|
||||||
for (int i = 0; i < derivedFilesListNodes.getLength(); ++i) {
|
|
||||||
Element derivedFilesListElem = (Element) derivedFilesListNodes.item(i);
|
|
||||||
// Get the derived files.
|
|
||||||
NodeList derivedFileNodes = derivedFilesListElem.getElementsByTagName(TagNames.DERIVED_FILE_ELEM.toString());
|
|
||||||
for (int j = 0; j < derivedFileNodes.getLength(); ++j) {
|
|
||||||
Element derivedFileElem = (Element) derivedFileNodes.item(j);
|
|
||||||
// Get the local path of the derived file.
|
|
||||||
String path = getChildElementContent(derivedFileElem, TagNames.LOCAL_PATH_ELEM.toString(), true);
|
|
||||||
if (path.isEmpty()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Get the parent file of the derived file.
|
|
||||||
String parentFile = getChildElementContent((Element) derivedFileNodes.item(j), TagNames.PARENT_FILE_ELEM.toString(), true);
|
|
||||||
if (parentFile.isEmpty()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
this.resultsData.addDerivedFile(path, parentFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void parseArtifacts(final Element root) {
|
|
||||||
// Get the artifact lists.
|
|
||||||
NodeList artifactsListNodes = root.getElementsByTagName(TagNames.ARTIFACTS_LIST_ELEM.toString());
|
|
||||||
for (int i = 0; i < artifactsListNodes.getLength(); ++i) {
|
|
||||||
Element artifactsListElem = (Element) artifactsListNodes.item(i);
|
|
||||||
// Get the artifacts.
|
|
||||||
NodeList artifactNodes = artifactsListElem.getElementsByTagName(TagNames.ARTIFACT_ELEM.toString());
|
|
||||||
for (int j = 0; j < artifactNodes.getLength(); ++j) {
|
|
||||||
Element artifactElem = (Element) artifactNodes.item(j);
|
|
||||||
// Get the artifact type.
|
|
||||||
final String type = getElementAttributeValue(artifactElem, AttributeNames.TYPE_ATTR.toString());
|
|
||||||
if (!type.isEmpty()) {
|
|
||||||
// Get the source file of the artifact and the attributes,
|
|
||||||
// if any.
|
|
||||||
final String sourceFilePath = this.getChildElementContent(artifactElem, TagNames.SOURCE_FILE_ELEM.toString(), true);
|
|
||||||
if (!sourceFilePath.isEmpty()) {
|
|
||||||
ExternalResults.Artifact artifact = this.resultsData.addArtifact(type, sourceFilePath);
|
|
||||||
parseArtifactAttributes(artifactElem, artifact);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void parseArtifactAttributes(final Element artifactElem, ExternalResults.Artifact artifact) {
|
|
||||||
// Get the artifact attributes.
|
|
||||||
NodeList attributeNodesList = artifactElem.getElementsByTagName(TagNames.ATTRIBUTE_ELEM.toString());
|
|
||||||
for (int i = 0; i < attributeNodesList.getLength(); ++i) {
|
|
||||||
Element attributeElem = (Element) attributeNodesList.item(i);
|
|
||||||
final String type = getElementAttributeValue(attributeElem, AttributeNames.TYPE_ATTR.toString());
|
|
||||||
if (type.isEmpty()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Get the value of the artifact attribute.
|
|
||||||
Element valueElem = this.getChildElement(attributeElem, TagNames.VALUE_ELEM.toString());
|
|
||||||
if (valueElem == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
final String value = valueElem.getTextContent();
|
|
||||||
if (value.isEmpty()) {
|
|
||||||
String errorMessage = NbBundle.getMessage(this.getClass(),
|
|
||||||
"ExternalResultsXMLParser.parseArtifactAttributes.errMsg1.text",
|
|
||||||
TagNames.VALUE_ELEM.toString(), this.resultsFilePath);
|
|
||||||
recordError(errorMessage);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Get the value type.
|
|
||||||
String valueType = parseArtifactAttributeValueType(valueElem);
|
|
||||||
if (valueType.isEmpty()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Get the optional source module.
|
|
||||||
String sourceModule = this.getChildElementContent(attributeElem, TagNames.SOURCE_MODULE_ELEM.toString(), false);
|
|
||||||
// Add the attribute to the artifact.
|
|
||||||
artifact.addAttribute(type, value, valueType, sourceModule);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String parseArtifactAttributeValueType(Element valueElem) {
|
|
||||||
String valueType = valueElem.getAttribute(AttributeNames.TYPE_ATTR.toString());
|
|
||||||
if (valueType.isEmpty()) {
|
|
||||||
// Default to text.
|
|
||||||
valueType = AttributeValues.VALUE_TYPE_TEXT.toString();
|
|
||||||
} else if (!valueType.equals(AttributeValues.VALUE_TYPE_TEXT.toString())
|
|
||||||
&& !valueType.equals(AttributeValues.VALUE_TYPE_DOUBLE.toString())
|
|
||||||
&& !valueType.equals(AttributeValues.VALUE_TYPE_INT32.toString())
|
|
||||||
&& !valueType.equals(AttributeValues.VALUE_TYPE_INT64.toString())
|
|
||||||
&& !valueType.equals(AttributeValues.VALUE_TYPE_DATETIME.toString())) {
|
|
||||||
String errorMessage = NbBundle.getMessage(this.getClass(),
|
|
||||||
"ExternalResultsXMLParser.parseAttributeValueType.errMsg1.text",
|
|
||||||
valueType,
|
|
||||||
AttributeNames.TYPE_ATTR.toString(),
|
|
||||||
TagNames.VALUE_ELEM.toString());
|
|
||||||
this.recordError(errorMessage);
|
|
||||||
valueType = "";
|
|
||||||
}
|
|
||||||
return valueType;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void parseReports(Element root) {
|
|
||||||
// Get the report lists.
|
|
||||||
NodeList reportsListNodes = root.getElementsByTagName(TagNames.REPORTS_LIST_ELEM.toString());
|
|
||||||
for (int i = 0; i < reportsListNodes.getLength(); ++i) {
|
|
||||||
Element reportsListElem = (Element) reportsListNodes.item(i);
|
|
||||||
// Get the reports.
|
|
||||||
NodeList reportNodes = reportsListElem.getElementsByTagName(TagNames.REPORT_ELEM.toString());
|
|
||||||
for (int j = 0; j < reportNodes.getLength(); ++j) {
|
|
||||||
Element reportElem = (Element) reportNodes.item(j);
|
|
||||||
// Get the local path.
|
|
||||||
String path = getChildElementContent(reportElem, TagNames.LOCAL_PATH_ELEM.toString(), true);
|
|
||||||
if (path.isEmpty()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Get the source module.
|
|
||||||
String sourceModule = getChildElementContent(reportElem, TagNames.SOURCE_MODULE_ELEM.toString(), true);
|
|
||||||
if (path.isEmpty()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Get the optional report name.
|
|
||||||
String reportName = getChildElementContent(reportElem, TagNames.REPORT_NAME_ELEM.toString(), false);
|
|
||||||
this.resultsData.addReport(path, sourceModule, reportName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getElementAttributeValue(Element element, String attributeName) {
|
|
||||||
final String attributeValue = element.getAttribute(attributeName);
|
|
||||||
if (attributeValue.isEmpty()) {
|
|
||||||
logger.log(Level.SEVERE, "Found {0} element missing {1} attribute in {2}", new Object[]{ //NON-NLS
|
|
||||||
element.getTagName(),
|
|
||||||
attributeName,
|
|
||||||
this.resultsFilePath});
|
|
||||||
}
|
|
||||||
return attributeValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getChildElementContent(Element parentElement, String childElementTagName, boolean required) {
|
|
||||||
String content = "";
|
|
||||||
Element childElement = this.getChildElement(parentElement, childElementTagName);
|
|
||||||
if (childElement != null) {
|
|
||||||
content = childElement.getTextContent();
|
|
||||||
if (content.isEmpty()) {
|
|
||||||
String errorMessage = NbBundle.getMessage(this.getClass(),
|
|
||||||
"ExternalResultsXMLParser.getChildElementContent.errMsg1.text",
|
|
||||||
parentElement.getTagName(),
|
|
||||||
childElementTagName,
|
|
||||||
this.resultsFilePath);
|
|
||||||
this.recordError(errorMessage);
|
|
||||||
}
|
|
||||||
} else if (required) {
|
|
||||||
String errorMessage = NbBundle.getMessage(this.getClass(), "ExternalResultsXMLParser.getChildElementContent.errMsg2.text",
|
|
||||||
parentElement.getTagName(),
|
|
||||||
childElementTagName,
|
|
||||||
this.resultsFilePath);
|
|
||||||
this.recordError(errorMessage);
|
|
||||||
}
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Element getChildElement(Element parentElement, String childElementTagName) {
|
|
||||||
Element childElem = null;
|
|
||||||
NodeList childNodes = parentElement.getElementsByTagName(childElementTagName);
|
|
||||||
if (childNodes.getLength() > 0) {
|
|
||||||
childElem = (Element) childNodes.item(0);
|
|
||||||
if (childNodes.getLength() > 1) {
|
|
||||||
String errorMessage = NbBundle.getMessage(this.getClass(), "ExternalResultsXMLParser.getChildElement.errMsg1.text",
|
|
||||||
childElementTagName,
|
|
||||||
parentElement.getTagName(),
|
|
||||||
this.resultsFilePath);
|
|
||||||
this.recordError(errorMessage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return childElem;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void recordError(String errorMessage) {
|
|
||||||
ExternalResultsXMLParser.logger.log(Level.SEVERE, errorMessage);
|
|
||||||
this.errors.add(new ErrorInfo(this.getClass().getSimpleName(), errorMessage));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void recordError(String errorMessage, Exception ex) {
|
|
||||||
ExternalResultsXMLParser.logger.log(Level.SEVERE, errorMessage, ex);
|
|
||||||
this.errors.add(new ErrorInfo(this.getClass().getSimpleName(), errorMessage, ex));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,70 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
|
||||||
<xs:element name="autopsy_results">
|
|
||||||
<xs:complexType>
|
|
||||||
<xs:sequence minOccurs="0" maxOccurs="1">
|
|
||||||
<xs:element minOccurs="0" maxOccurs="1" name="data_source" type="xs:string" />
|
|
||||||
<xs:element minOccurs="0" maxOccurs="unbounded" name="derived_files">
|
|
||||||
<xs:complexType>
|
|
||||||
<xs:sequence>
|
|
||||||
<xs:element maxOccurs="unbounded" name="derived_file">
|
|
||||||
<xs:complexType>
|
|
||||||
<xs:sequence>
|
|
||||||
<xs:element name="local_path" type="xs:string" />
|
|
||||||
<xs:element minOccurs="0" name="parent_file" type="xs:string" />
|
|
||||||
</xs:sequence>
|
|
||||||
</xs:complexType>
|
|
||||||
</xs:element>
|
|
||||||
</xs:sequence>
|
|
||||||
</xs:complexType>
|
|
||||||
</xs:element>
|
|
||||||
<xs:element minOccurs="0" maxOccurs="unbounded" name="artifacts">
|
|
||||||
<xs:complexType>
|
|
||||||
<xs:sequence>
|
|
||||||
<xs:element minOccurs="0" maxOccurs="unbounded" name="artifact">
|
|
||||||
<xs:complexType>
|
|
||||||
<xs:sequence>
|
|
||||||
<xs:element minOccurs="0" maxOccurs="1" name="source_file" type="xs:string" />
|
|
||||||
<xs:element minOccurs="0" maxOccurs="unbounded" name="attribute">
|
|
||||||
<xs:complexType>
|
|
||||||
<xs:sequence>
|
|
||||||
<xs:element name="value">
|
|
||||||
<xs:complexType>
|
|
||||||
<xs:simpleContent>
|
|
||||||
<xs:extension base="xs:string">
|
|
||||||
<xs:attribute name="type" type="xs:string" use="optional" />
|
|
||||||
</xs:extension>
|
|
||||||
</xs:simpleContent>
|
|
||||||
</xs:complexType>
|
|
||||||
</xs:element>
|
|
||||||
<xs:element minOccurs="0" maxOccurs="1" name="source_module" type="xs:string" />
|
|
||||||
</xs:sequence>
|
|
||||||
<xs:attribute name="type" type="xs:string" use="required" />
|
|
||||||
</xs:complexType>
|
|
||||||
</xs:element>
|
|
||||||
</xs:sequence>
|
|
||||||
<xs:attribute name="type" type="xs:string" use="required" />
|
|
||||||
</xs:complexType>
|
|
||||||
</xs:element>
|
|
||||||
</xs:sequence>
|
|
||||||
</xs:complexType>
|
|
||||||
</xs:element>
|
|
||||||
<xs:element minOccurs="0" maxOccurs="unbounded" name="reports">
|
|
||||||
<xs:complexType>
|
|
||||||
<xs:sequence>
|
|
||||||
<xs:element maxOccurs="unbounded" name="report">
|
|
||||||
<xs:complexType>
|
|
||||||
<xs:sequence>
|
|
||||||
<xs:element name="local_path" type="xs:string" />
|
|
||||||
<xs:element name="source_module" type="xs:string" />
|
|
||||||
<xs:element minOccurs="0" maxOccurs="1" name="report_name" type="xs:string" />
|
|
||||||
</xs:sequence>
|
|
||||||
</xs:complexType>
|
|
||||||
</xs:element>
|
|
||||||
</xs:sequence>
|
|
||||||
</xs:complexType>
|
|
||||||
</xs:element>
|
|
||||||
</xs:sequence>
|
|
||||||
</xs:complexType>
|
|
||||||
</xs:element>
|
|
||||||
</xs:schema>
|
|
@ -30,6 +30,7 @@ import java.util.concurrent.TimeUnit;
|
|||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import org.netbeans.api.progress.ProgressHandle;
|
import org.netbeans.api.progress.ProgressHandle;
|
||||||
|
import org.openide.util.NbBundle.Messages;
|
||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
import org.sleuthkit.autopsy.core.RuntimeProperties;
|
import org.sleuthkit.autopsy.core.RuntimeProperties;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
@ -130,6 +131,10 @@ class ImageWriter implements PropertyChangeListener{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Messages({
|
||||||
|
"# {0} - data source name",
|
||||||
|
"ImageWriter.progressBar.message=Finishing acquisition of {0}"
|
||||||
|
})
|
||||||
private void startFinishImage(String dataSourceName){
|
private void startFinishImage(String dataSourceName){
|
||||||
|
|
||||||
synchronized(currentTasksLock){
|
synchronized(currentTasksLock){
|
||||||
@ -166,7 +171,7 @@ class ImageWriter implements PropertyChangeListener{
|
|||||||
|
|
||||||
if(doUI){
|
if(doUI){
|
||||||
periodicTasksExecutor = new ScheduledThreadPoolExecutor(1, new ThreadFactoryBuilder().setNameFormat("image-writer-progress-update-%d").build()); //NON-NLS
|
periodicTasksExecutor = new ScheduledThreadPoolExecutor(1, new ThreadFactoryBuilder().setNameFormat("image-writer-progress-update-%d").build()); //NON-NLS
|
||||||
progressHandle = ProgressHandle.createHandle("Image writer - " + dataSourceName);
|
progressHandle = ProgressHandle.createHandle(Bundle.ImageWriter_progressBar_message(dataSourceName));
|
||||||
progressHandle.start(100);
|
progressHandle.start(100);
|
||||||
progressUpdateTask = periodicTasksExecutor.scheduleAtFixedRate(
|
progressUpdateTask = periodicTasksExecutor.scheduleAtFixedRate(
|
||||||
new ProgressUpdateTask(progressHandle, imageHandle), 0, 250, TimeUnit.MILLISECONDS);
|
new ProgressUpdateTask(progressHandle, imageHandle), 0, 250, TimeUnit.MILLISECONDS);
|
||||||
|
@ -29,7 +29,6 @@ import org.openide.NotifyDescriptor;
|
|||||||
import org.openide.util.Lookup;
|
import org.openide.util.Lookup;
|
||||||
import org.openide.util.NbBundle;
|
import org.openide.util.NbBundle;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.autopsy.examples.SampleExecutableIngestModuleFactory;
|
|
||||||
import org.sleuthkit.autopsy.examples.SampleIngestModuleFactory;
|
import org.sleuthkit.autopsy.examples.SampleIngestModuleFactory;
|
||||||
import org.sleuthkit.autopsy.modules.e01verify.E01VerifierModuleFactory;
|
import org.sleuthkit.autopsy.modules.e01verify.E01VerifierModuleFactory;
|
||||||
import org.sleuthkit.autopsy.modules.exif.ExifParserModuleFactory;
|
import org.sleuthkit.autopsy.modules.exif.ExifParserModuleFactory;
|
||||||
@ -48,7 +47,6 @@ final class IngestModuleFactoryLoader {
|
|||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(IngestModuleFactoryLoader.class.getName());
|
private static final Logger logger = Logger.getLogger(IngestModuleFactoryLoader.class.getName());
|
||||||
private static final String SAMPLE_MODULE_FACTORY_CLASS_NAME = SampleIngestModuleFactory.class.getCanonicalName();
|
private static final String SAMPLE_MODULE_FACTORY_CLASS_NAME = SampleIngestModuleFactory.class.getCanonicalName();
|
||||||
private static final String SAMPLE_EXECUTABLE_MODULE_FACTORY_CLASS_NAME = SampleExecutableIngestModuleFactory.class.getCanonicalName();
|
|
||||||
private static final ArrayList<String> coreModuleOrdering = new ArrayList<String>() {
|
private static final ArrayList<String> coreModuleOrdering = new ArrayList<String>() {
|
||||||
{
|
{
|
||||||
// The ordering of the core ingest module factories implemented
|
// The ordering of the core ingest module factories implemented
|
||||||
@ -141,8 +139,7 @@ final class IngestModuleFactoryLoader {
|
|||||||
private static void addFactory(IngestModuleFactory factory, HashSet<String> moduleDisplayNames, HashMap<String, IngestModuleFactory> javaFactoriesByClass) {
|
private static void addFactory(IngestModuleFactory factory, HashSet<String> moduleDisplayNames, HashMap<String, IngestModuleFactory> javaFactoriesByClass) {
|
||||||
// Ignore the sample ingest module factories implemented in Java.
|
// Ignore the sample ingest module factories implemented in Java.
|
||||||
String className = factory.getClass().getCanonicalName();
|
String className = factory.getClass().getCanonicalName();
|
||||||
if (className.equals(IngestModuleFactoryLoader.SAMPLE_MODULE_FACTORY_CLASS_NAME)
|
if (className.equals(IngestModuleFactoryLoader.SAMPLE_MODULE_FACTORY_CLASS_NAME)) {
|
||||||
|| className.equals(IngestModuleFactoryLoader.SAMPLE_EXECUTABLE_MODULE_FACTORY_CLASS_NAME)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,8 +192,6 @@ public final class IngestMonitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
logMemoryUsage();
|
logMemoryUsage();
|
||||||
logDiskSpaceUsage();
|
|
||||||
|
|
||||||
if (!enoughDiskSpace()) {
|
if (!enoughDiskSpace()) {
|
||||||
/*
|
/*
|
||||||
* Shut down ingest by cancelling all ingest jobs.
|
* Shut down ingest by cancelling all ingest jobs.
|
||||||
|
@ -0,0 +1,122 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2011-2017 Basis Technology Corp.
|
||||||
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.sleuthkit.autopsy.experimental.autoingest;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import org.apache.commons.io.FilenameUtils;
|
||||||
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
|
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback;
|
||||||
|
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
|
import org.sleuthkit.datamodel.Content;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A runnable that adds an archive data source as well as data sources
|
||||||
|
* contained in the archive to the case database.
|
||||||
|
*/
|
||||||
|
class AddArchiveTask implements Runnable {
|
||||||
|
|
||||||
|
private final Logger logger = Logger.getLogger(AddArchiveTask.class.getName());
|
||||||
|
private final String deviceId;
|
||||||
|
private final String archivePath;
|
||||||
|
private final DataSourceProcessorProgressMonitor progressMonitor;
|
||||||
|
private final DataSourceProcessorCallback callback;
|
||||||
|
private boolean criticalErrorOccurred;
|
||||||
|
|
||||||
|
private static final String ARCHIVE_EXTRACTOR_MODULE_OUTPUT_DIR = "Archive Extractor";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a runnable task that adds an archive as well as data sources
|
||||||
|
* contained in the archive to the case database.
|
||||||
|
*
|
||||||
|
* @param deviceId An ASCII-printable identifier for the device associated
|
||||||
|
* with the data source that is intended to be unique across multiple cases
|
||||||
|
* (e.g., a UUID).
|
||||||
|
* @param archivePath Path to the archive file.
|
||||||
|
* @param progressMonitor Progress monitor to report progress during
|
||||||
|
* processing.
|
||||||
|
* @param callback Callback to call when processing is done.
|
||||||
|
*/
|
||||||
|
AddArchiveTask(String deviceId, String archivePath, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||||
|
this.deviceId = deviceId;
|
||||||
|
this.archivePath = archivePath;
|
||||||
|
this.callback = callback;
|
||||||
|
this.progressMonitor = progressMonitor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the archive to the case database.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
List<String> errorMessages = new ArrayList<>();
|
||||||
|
List<Content> newDataSources = new ArrayList<>();
|
||||||
|
DataSourceProcessorCallback.DataSourceProcessorResult result;
|
||||||
|
if (!ArchiveUtil.isArchive(Paths.get(archivePath))) {
|
||||||
|
criticalErrorOccurred = true;
|
||||||
|
logger.log(Level.SEVERE, String.format("Input data source is not a valid datasource: %s", archivePath)); //NON-NLS
|
||||||
|
errorMessages.add("Input data source is not a valid datasource: " + archivePath);
|
||||||
|
result = DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS;
|
||||||
|
callback.done(result, errorMessages, newDataSources);
|
||||||
|
}
|
||||||
|
|
||||||
|
// extract the archive and pass the extracted folder as input
|
||||||
|
Path destinationFolder = Paths.get("");
|
||||||
|
try {
|
||||||
|
Case currentCase = Case.getCurrentCase();
|
||||||
|
|
||||||
|
// get file name without full path or extension
|
||||||
|
String dataSourceFileNameNoExt = FilenameUtils.getBaseName(archivePath);
|
||||||
|
|
||||||
|
// create folder to extract archive to
|
||||||
|
destinationFolder = Paths.get(currentCase.getModuleDirectory(), ARCHIVE_EXTRACTOR_MODULE_OUTPUT_DIR, dataSourceFileNameNoExt + "_" + TimeStampUtils.createTimeStamp());
|
||||||
|
destinationFolder.toFile().mkdirs();
|
||||||
|
|
||||||
|
// extract contents of ZIP archive into destination folder
|
||||||
|
//ArchiveUtil.unpackArchiveFile(archivePath, destinationFolder.toString());
|
||||||
|
|
||||||
|
// do processing
|
||||||
|
|
||||||
|
} catch (Exception ex) {
|
||||||
|
criticalErrorOccurred = true;
|
||||||
|
errorMessages.add(ex.getMessage());
|
||||||
|
logger.log(Level.SEVERE, String.format("Critical error occurred while extracting archive %s", archivePath), ex); //NON-NLS
|
||||||
|
} finally {
|
||||||
|
if (criticalErrorOccurred) {
|
||||||
|
result = DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS;
|
||||||
|
} else if (!errorMessages.isEmpty()) {
|
||||||
|
result = DataSourceProcessorCallback.DataSourceProcessorResult.NONCRITICAL_ERRORS;
|
||||||
|
} else {
|
||||||
|
result = DataSourceProcessorCallback.DataSourceProcessorResult.NO_ERRORS;
|
||||||
|
}
|
||||||
|
callback.done(result, errorMessages, newDataSources);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Attempts to cancel adding the archive to the case database.
|
||||||
|
*/
|
||||||
|
public void cancelTask() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2011-2017 Basis Technology Corp.
|
||||||
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.sleuthkit.autopsy.experimental.autoingest;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
|
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback;
|
||||||
|
import org.sleuthkit.datamodel.Content;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A "callback" that collects the results of running a data source processor on
|
||||||
|
* a data source and unblocks the job processing thread when the data source
|
||||||
|
* processor finishes running in its own thread.
|
||||||
|
*/
|
||||||
|
@Immutable
|
||||||
|
class AddDataSourceCallback extends DataSourceProcessorCallback {
|
||||||
|
|
||||||
|
private final Case caseForJob;
|
||||||
|
private final DataSource dataSourceInfo;
|
||||||
|
private final UUID taskId;
|
||||||
|
private final Object lock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a "callback" that collects the results of running a data
|
||||||
|
* source processor on a data source and unblocks the job processing thread
|
||||||
|
* when the data source processor finishes running in its own thread.
|
||||||
|
*
|
||||||
|
* @param caseForJob The case for the current job.
|
||||||
|
* @param dataSourceInfo The data source
|
||||||
|
* @param taskId The task id to associate with ingest job events.
|
||||||
|
*/
|
||||||
|
AddDataSourceCallback(Case caseForJob, DataSource dataSourceInfo, UUID taskId, Object lock) {
|
||||||
|
this.caseForJob = caseForJob;
|
||||||
|
this.dataSourceInfo = dataSourceInfo;
|
||||||
|
this.taskId = taskId;
|
||||||
|
this.lock = lock;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by the data source processor when it finishes running in its own
|
||||||
|
* thread.
|
||||||
|
*
|
||||||
|
* @param result The result code for the processing of the data source.
|
||||||
|
* @param errorMessages Any error messages generated during the processing
|
||||||
|
* of the data source.
|
||||||
|
* @param dataSourceContent The content produced by processing the data
|
||||||
|
* source.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void done(DataSourceProcessorCallback.DataSourceProcessorResult result, List<String> errorMessages, List<Content> dataSourceContent) {
|
||||||
|
if (!dataSourceContent.isEmpty()) {
|
||||||
|
caseForJob.notifyDataSourceAdded(dataSourceContent.get(0), taskId);
|
||||||
|
} else {
|
||||||
|
caseForJob.notifyFailedAddingDataSource(taskId);
|
||||||
|
}
|
||||||
|
dataSourceInfo.setDataSourceProcessorOutput(result, errorMessages, dataSourceContent);
|
||||||
|
dataSourceContent.addAll(dataSourceContent);
|
||||||
|
synchronized (lock) {
|
||||||
|
lock.notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by the data source processor when it finishes running in its own
|
||||||
|
* thread, if that thread is the AWT (Abstract Window Toolkit) event
|
||||||
|
* dispatch thread (EDT).
|
||||||
|
*
|
||||||
|
* @param result The result code for the processing of the data source.
|
||||||
|
* @param errorMessages Any error messages generated during the processing
|
||||||
|
* of the data source.
|
||||||
|
* @param dataSourceContent The content produced by processing the data
|
||||||
|
* source.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void doneEDT(DataSourceProcessorCallback.DataSourceProcessorResult result, List<String> errorMessages, List<Content> dataSources) {
|
||||||
|
done(result, errorMessages, dataSources);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,224 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2011-2017 Basis Technology Corp.
|
||||||
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.sleuthkit.autopsy.experimental.autoingest;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.UUID;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import org.openide.util.NbBundle;
|
||||||
|
import org.openide.util.lookup.ServiceProvider;
|
||||||
|
import org.openide.util.lookup.ServiceProviders;
|
||||||
|
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor;
|
||||||
|
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback;
|
||||||
|
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor;
|
||||||
|
import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A data source processor that handles archive files. Implements the
|
||||||
|
* DataSourceProcessor service provider interface to allow integration with the
|
||||||
|
* add data source wizard. It also provides a run method overload to allow it to
|
||||||
|
* be used independently of the wizard.
|
||||||
|
*/
|
||||||
|
//@ServiceProviders(value={
|
||||||
|
// @ServiceProvider(service=DataSourceProcessor.class),
|
||||||
|
// @ServiceProvider(service=AutoIngestDataSourceProcessor.class)}
|
||||||
|
//)
|
||||||
|
@NbBundle.Messages({
|
||||||
|
"ArchiveDSP.dsType.text=Archive file"})
|
||||||
|
public class ArchiveExtractorDSProcessor implements DataSourceProcessor, AutoIngestDataSourceProcessor {
|
||||||
|
|
||||||
|
private final static String DATA_SOURCE_TYPE = Bundle.ArchiveDSP_dsType_text();
|
||||||
|
|
||||||
|
private final ArchiveFilePanel configPanel;
|
||||||
|
private String deviceId;
|
||||||
|
private String archivePath;
|
||||||
|
private boolean setDataSourceOptionsCalled;
|
||||||
|
|
||||||
|
private AddArchiveTask addArchiveTask;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an archive data source processor that
|
||||||
|
* implements the DataSourceProcessor service provider interface to allow
|
||||||
|
* integration with the add data source wizard. It also provides a run
|
||||||
|
* method overload to allow it to be used independently of the wizard.
|
||||||
|
*/
|
||||||
|
public ArchiveExtractorDSProcessor() {
|
||||||
|
configPanel = ArchiveFilePanel.createInstance(ArchiveExtractorDSProcessor.class.getName(), ArchiveUtil.getArchiveFilters());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int canProcess(Path dataSourcePath) throws AutoIngestDataSourceProcessorException {
|
||||||
|
// check whether this is an archive
|
||||||
|
if (ArchiveUtil.isArchive(dataSourcePath)){
|
||||||
|
// return "high confidence" value
|
||||||
|
return 100;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void process(String deviceId, Path dataSourcePath, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callBack) throws AutoIngestDataSourceProcessorException {
|
||||||
|
run(deviceId, dataSourcePath.toString(), progressMonitor, callBack);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDataSourceType() {
|
||||||
|
return DATA_SOURCE_TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the panel that allows a user to select a data source and do any
|
||||||
|
* configuration required by the data source. The panel is less than 544
|
||||||
|
* pixels wide and less than 173 pixels high.
|
||||||
|
*
|
||||||
|
* @return A selection and configuration panel for this data source
|
||||||
|
* processor.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public JPanel getPanel() {
|
||||||
|
configPanel.readSettings();
|
||||||
|
configPanel.select();
|
||||||
|
return configPanel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether the settings in the selection and configuration panel
|
||||||
|
* are valid and complete.
|
||||||
|
*
|
||||||
|
* @return True if the settings are valid and complete and the processor is
|
||||||
|
* ready to have its run method called, false otherwise.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean isPanelValid() {
|
||||||
|
return configPanel.validatePanel();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a data source to the case database using a background task in a
|
||||||
|
* separate thread and the settings provided by the selection and
|
||||||
|
* configuration panel. Returns as soon as the background task is started.
|
||||||
|
* The background task uses a callback object to signal task completion and
|
||||||
|
* return results.
|
||||||
|
*
|
||||||
|
* This method should not be called unless isPanelValid returns true.
|
||||||
|
*
|
||||||
|
* @param progressMonitor Progress monitor that will be used by the
|
||||||
|
* background task to report progress.
|
||||||
|
* @param callback Callback that will be used by the background task
|
||||||
|
* to return results.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||||
|
if (!setDataSourceOptionsCalled) {
|
||||||
|
configPanel.storeSettings();
|
||||||
|
deviceId = UUID.randomUUID().toString();
|
||||||
|
archivePath = configPanel.getContentPaths();
|
||||||
|
}
|
||||||
|
run(deviceId, archivePath, progressMonitor, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a data source to the case database using a background task in a
|
||||||
|
* separate thread and the given settings instead of those provided by the
|
||||||
|
* selection and configuration panel. Returns as soon as the background task
|
||||||
|
* is started and uses the callback object to signal task completion and
|
||||||
|
* return results.
|
||||||
|
*
|
||||||
|
* @param deviceId An ASCII-printable identifier for the device
|
||||||
|
* associated with the data source that is
|
||||||
|
* intended to be unique across multiple cases
|
||||||
|
* (e.g., a UUID).
|
||||||
|
* @param archivePath Path to the archive file.
|
||||||
|
* @param progressMonitor Progress monitor for reporting progress
|
||||||
|
* during processing.
|
||||||
|
* @param callback Callback to call when processing is done.
|
||||||
|
*/
|
||||||
|
public void run(String deviceId, String archivePath, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||||
|
addArchiveTask = new AddArchiveTask(deviceId, archivePath, progressMonitor, callback);
|
||||||
|
new Thread(addArchiveTask).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requests cancellation of the background task that adds a data source to
|
||||||
|
* the case database, after the task is started using the run method. This
|
||||||
|
* is a "best effort" cancellation, with no guarantees that the case
|
||||||
|
* database will be unchanged. If cancellation succeeded, the list of new
|
||||||
|
* data sources returned by the background task will be empty.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void cancel() {
|
||||||
|
if (null != addArchiveTask) {
|
||||||
|
addArchiveTask.cancelTask();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reset() {
|
||||||
|
deviceId = null;
|
||||||
|
archivePath = null;
|
||||||
|
configPanel.reset();
|
||||||
|
setDataSourceOptionsCalled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts the contents of a ZIP archive submitted as a data source to a
|
||||||
|
* subdirectory of the auto ingest module output directory.
|
||||||
|
*
|
||||||
|
* @throws IOException if there is a problem extracting the data source from
|
||||||
|
* the archive.
|
||||||
|
|
||||||
|
private static Path extractDataSource(Path outputDirectoryPath, Path dataSourcePath) throws IOException {
|
||||||
|
String dataSourceFileNameNoExt = FilenameUtils.removeExtension(dataSourcePath.getFileName().toString());
|
||||||
|
Path destinationFolder = Paths.get(outputDirectoryPath.toString(),
|
||||||
|
AUTO_INGEST_MODULE_OUTPUT_DIR,
|
||||||
|
dataSourceFileNameNoExt + "_" + TimeStampUtils.createTimeStamp());
|
||||||
|
Files.createDirectories(destinationFolder);
|
||||||
|
|
||||||
|
int BUFFER_SIZE = 524288; // Read/write 500KB at a time
|
||||||
|
File sourceZipFile = dataSourcePath.toFile();
|
||||||
|
ZipFile zipFile;
|
||||||
|
zipFile = new ZipFile(sourceZipFile, ZipFile.OPEN_READ);
|
||||||
|
Enumeration<? extends ZipEntry> zipFileEntries = zipFile.entries();
|
||||||
|
try {
|
||||||
|
while (zipFileEntries.hasMoreElements()) {
|
||||||
|
ZipEntry entry = zipFileEntries.nextElement();
|
||||||
|
String currentEntry = entry.getName();
|
||||||
|
File destFile = new File(destinationFolder.toString(), currentEntry);
|
||||||
|
destFile = new File(destinationFolder.toString(), destFile.getName());
|
||||||
|
File destinationParent = destFile.getParentFile();
|
||||||
|
destinationParent.mkdirs();
|
||||||
|
if (!entry.isDirectory()) {
|
||||||
|
BufferedInputStream is = new BufferedInputStream(zipFile.getInputStream(entry));
|
||||||
|
int currentByte;
|
||||||
|
byte data[] = new byte[BUFFER_SIZE];
|
||||||
|
try (FileOutputStream fos = new FileOutputStream(destFile); BufferedOutputStream dest = new BufferedOutputStream(fos, BUFFER_SIZE)) {
|
||||||
|
currentByte = is.read(data, 0, BUFFER_SIZE);
|
||||||
|
while (currentByte != -1) {
|
||||||
|
dest.write(data, 0, currentByte);
|
||||||
|
currentByte = is.read(data, 0, BUFFER_SIZE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
zipFile.close();
|
||||||
|
}
|
||||||
|
return destinationFolder;
|
||||||
|
} */
|
||||||
|
}
|
@ -0,0 +1,94 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
|
||||||
|
<Form version="1.5" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
|
||||||
|
<Properties>
|
||||||
|
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
|
<Dimension value="[0, 65]"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
|
<Dimension value="[403, 65]"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
<AuxValues>
|
||||||
|
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
|
||||||
|
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
|
||||||
|
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
|
||||||
|
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
|
||||||
|
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
|
||||||
|
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
|
||||||
|
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
|
||||||
|
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
|
||||||
|
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
|
||||||
|
</AuxValues>
|
||||||
|
|
||||||
|
<Layout>
|
||||||
|
<DimensionLayout dim="0">
|
||||||
|
<Group type="103" groupAlignment="0" attributes="0">
|
||||||
|
<Group type="102" attributes="0">
|
||||||
|
<Component id="pathTextField" max="32767" attributes="0"/>
|
||||||
|
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||||
|
<Component id="browseButton" min="-2" max="-2" attributes="0"/>
|
||||||
|
<EmptySpace min="-2" pref="2" max="-2" attributes="0"/>
|
||||||
|
</Group>
|
||||||
|
<Group type="102" attributes="0">
|
||||||
|
<Group type="103" groupAlignment="0" attributes="0">
|
||||||
|
<Component id="pathLabel" min="-2" max="-2" attributes="0"/>
|
||||||
|
<Component id="errorLabel" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||||
|
</Group>
|
||||||
|
<EmptySpace min="0" pref="277" max="32767" attributes="0"/>
|
||||||
|
</Group>
|
||||||
|
</Group>
|
||||||
|
</DimensionLayout>
|
||||||
|
<DimensionLayout dim="1">
|
||||||
|
<Group type="103" groupAlignment="0" attributes="0">
|
||||||
|
<Group type="102" attributes="0">
|
||||||
|
<Component id="pathLabel" min="-2" max="-2" attributes="0"/>
|
||||||
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
|
<Group type="103" groupAlignment="3" attributes="0">
|
||||||
|
<Component id="browseButton" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||||
|
<Component id="pathTextField" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||||
|
</Group>
|
||||||
|
<EmptySpace min="-2" pref="3" max="-2" attributes="0"/>
|
||||||
|
<Component id="errorLabel" min="-2" max="-2" attributes="0"/>
|
||||||
|
<EmptySpace max="32767" attributes="0"/>
|
||||||
|
</Group>
|
||||||
|
</Group>
|
||||||
|
</DimensionLayout>
|
||||||
|
</Layout>
|
||||||
|
<SubComponents>
|
||||||
|
<Component class="javax.swing.JLabel" name="pathLabel">
|
||||||
|
<Properties>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="ArchiveFilePanel.pathLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
</Component>
|
||||||
|
<Component class="javax.swing.JButton" name="browseButton">
|
||||||
|
<Properties>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="ArchiveFilePanel.browseButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
<Events>
|
||||||
|
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="browseButtonActionPerformed"/>
|
||||||
|
</Events>
|
||||||
|
</Component>
|
||||||
|
<Component class="javax.swing.JTextField" name="pathTextField">
|
||||||
|
<Properties>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="ArchiveFilePanel.pathTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
</Component>
|
||||||
|
<Component class="javax.swing.JLabel" name="errorLabel">
|
||||||
|
<Properties>
|
||||||
|
<Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
|
||||||
|
<Color blue="0" green="0" red="ff" type="rgb"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="ArchiveFilePanel.errorLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
</Component>
|
||||||
|
</SubComponents>
|
||||||
|
</Form>
|
@ -0,0 +1,280 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2011-2017 Basis Technology Corp.
|
||||||
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.sleuthkit.autopsy.experimental.autoingest;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import javax.swing.JFileChooser;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.event.DocumentEvent;
|
||||||
|
import javax.swing.event.DocumentListener;
|
||||||
|
import javax.swing.filechooser.FileFilter;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.openide.util.NbBundle;
|
||||||
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
|
import static org.sleuthkit.autopsy.experimental.autoingest.Bundle.*;
|
||||||
|
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.DriveUtils;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.PathValidator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Panel for adding an archive file which is supported by 7zip library (e.g.
|
||||||
|
* "zip", "rar", "arj", "7z", "7zip", "gzip, etc). Allows the user to select a
|
||||||
|
* file.
|
||||||
|
*/
|
||||||
|
class ArchiveFilePanel extends JPanel implements DocumentListener {
|
||||||
|
|
||||||
|
private static final Logger logger = Logger.getLogger(ArchiveFilePanel.class.getName());
|
||||||
|
private static final String PROP_LAST_ARCHIVE_PATH = "LBL_LastImage_PATH"; //NON-NLS
|
||||||
|
|
||||||
|
private final JFileChooser fileChooser = new JFileChooser();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Externally supplied name is used to store settings
|
||||||
|
*/
|
||||||
|
private final String contextName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates new form ArchiveFilePanel
|
||||||
|
*
|
||||||
|
* @param context A string context name used to read/store last
|
||||||
|
* used settings.
|
||||||
|
* @param fileChooserFilters A list of filters to be used with the
|
||||||
|
* FileChooser.
|
||||||
|
*/
|
||||||
|
private ArchiveFilePanel(String context, List<FileFilter> fileChooserFilters) {
|
||||||
|
this.contextName = context;
|
||||||
|
initComponents();
|
||||||
|
|
||||||
|
errorLabel.setVisible(false);
|
||||||
|
|
||||||
|
fileChooser.setDragEnabled(false);
|
||||||
|
fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
|
||||||
|
fileChooser.setMultiSelectionEnabled(false);
|
||||||
|
fileChooserFilters.forEach(fileChooser::addChoosableFileFilter);
|
||||||
|
if (fileChooserFilters.isEmpty() == false) {
|
||||||
|
fileChooser.setFileFilter(fileChooserFilters.get(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates and returns an instance of a ArchiveFilePanel.
|
||||||
|
*
|
||||||
|
* @param context A string context name used to read/store last
|
||||||
|
* used settings.
|
||||||
|
* @param fileChooserFilters A list of filters to be used with the
|
||||||
|
* FileChooser.
|
||||||
|
*
|
||||||
|
* @return instance of the ArchiveFilePanel
|
||||||
|
*/
|
||||||
|
public static synchronized ArchiveFilePanel createInstance(String context, List<FileFilter> fileChooserFilters) {
|
||||||
|
ArchiveFilePanel instance = new ArchiveFilePanel(context, fileChooserFilters);
|
||||||
|
// post-constructor initialization of listener support without leaking references of uninitialized objects
|
||||||
|
instance.pathTextField.getDocument().addDocumentListener(instance);
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called from within the constructor to initialize the form.
|
||||||
|
* WARNING: Do NOT modify this code. The content of this method is always
|
||||||
|
* regenerated by the Form Editor.
|
||||||
|
*/
|
||||||
|
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||||
|
private void initComponents() {
|
||||||
|
|
||||||
|
pathLabel = new javax.swing.JLabel();
|
||||||
|
browseButton = new javax.swing.JButton();
|
||||||
|
pathTextField = new javax.swing.JTextField();
|
||||||
|
errorLabel = new javax.swing.JLabel();
|
||||||
|
|
||||||
|
setMinimumSize(new java.awt.Dimension(0, 65));
|
||||||
|
setPreferredSize(new java.awt.Dimension(403, 65));
|
||||||
|
|
||||||
|
org.openide.awt.Mnemonics.setLocalizedText(pathLabel, org.openide.util.NbBundle.getMessage(ArchiveFilePanel.class, "ArchiveFilePanel.pathLabel.text")); // NOI18N
|
||||||
|
|
||||||
|
org.openide.awt.Mnemonics.setLocalizedText(browseButton, org.openide.util.NbBundle.getMessage(ArchiveFilePanel.class, "ArchiveFilePanel.browseButton.text")); // NOI18N
|
||||||
|
browseButton.addActionListener(new java.awt.event.ActionListener() {
|
||||||
|
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||||
|
browseButtonActionPerformed(evt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
pathTextField.setText(org.openide.util.NbBundle.getMessage(ArchiveFilePanel.class, "ArchiveFilePanel.pathTextField.text")); // NOI18N
|
||||||
|
|
||||||
|
errorLabel.setForeground(new java.awt.Color(255, 0, 0));
|
||||||
|
org.openide.awt.Mnemonics.setLocalizedText(errorLabel, org.openide.util.NbBundle.getMessage(ArchiveFilePanel.class, "ArchiveFilePanel.errorLabel.text")); // NOI18N
|
||||||
|
|
||||||
|
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||||
|
this.setLayout(layout);
|
||||||
|
layout.setHorizontalGroup(
|
||||||
|
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
|
.addGroup(layout.createSequentialGroup()
|
||||||
|
.addComponent(pathTextField)
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||||
|
.addComponent(browseButton)
|
||||||
|
.addGap(2, 2, 2))
|
||||||
|
.addGroup(layout.createSequentialGroup()
|
||||||
|
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
|
.addComponent(pathLabel)
|
||||||
|
.addComponent(errorLabel))
|
||||||
|
.addGap(0, 277, Short.MAX_VALUE))
|
||||||
|
);
|
||||||
|
layout.setVerticalGroup(
|
||||||
|
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
|
.addGroup(layout.createSequentialGroup()
|
||||||
|
.addComponent(pathLabel)
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
|
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||||
|
.addComponent(browseButton)
|
||||||
|
.addComponent(pathTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||||
|
.addGap(3, 3, 3)
|
||||||
|
.addComponent(errorLabel)
|
||||||
|
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||||
|
);
|
||||||
|
}// </editor-fold>//GEN-END:initComponents
|
||||||
|
|
||||||
|
private void browseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_browseButtonActionPerformed
|
||||||
|
String oldText = getContentPaths();
|
||||||
|
// set the current directory of the FileChooser if the ArchivePath Field is valid
|
||||||
|
File currentDir = new File(oldText);
|
||||||
|
if (currentDir.exists()) {
|
||||||
|
fileChooser.setCurrentDirectory(currentDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
|
||||||
|
String path = fileChooser.getSelectedFile().getPath();
|
||||||
|
setContentPath(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateHelper();
|
||||||
|
}//GEN-LAST:event_browseButtonActionPerformed
|
||||||
|
|
||||||
|
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||||
|
private javax.swing.JButton browseButton;
|
||||||
|
private javax.swing.JLabel errorLabel;
|
||||||
|
private javax.swing.JLabel pathLabel;
|
||||||
|
private javax.swing.JTextField pathTextField;
|
||||||
|
// End of variables declaration//GEN-END:variables
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the path of the user selected archive.
|
||||||
|
*
|
||||||
|
* @return the archive path
|
||||||
|
*/
|
||||||
|
public String getContentPaths() {
|
||||||
|
return pathTextField.getText();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the path of the archive file.
|
||||||
|
*
|
||||||
|
* @param s path of the archive file
|
||||||
|
*/
|
||||||
|
public void setContentPath(String s) {
|
||||||
|
pathTextField.setText(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
//reset the UI elements to default
|
||||||
|
pathTextField.setText(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should we enable the next button of the wizard?
|
||||||
|
*
|
||||||
|
* @return true if a proper archive has been selected, false otherwise
|
||||||
|
*/
|
||||||
|
@NbBundle.Messages("DataSourceOnCDriveError.text=Warning: Path to multi-user data source is on \"C:\" drive")
|
||||||
|
public boolean validatePanel() {
|
||||||
|
errorLabel.setVisible(false);
|
||||||
|
String path = getContentPaths();
|
||||||
|
if (StringUtils.isBlank(path)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// display warning if there is one (but don't disable "next" button)
|
||||||
|
if (false == PathValidator.isValid(path, Case.getCurrentCase().getCaseType())) {
|
||||||
|
errorLabel.setVisible(true);
|
||||||
|
errorLabel.setText(Bundle.DataSourceOnCDriveError_text());
|
||||||
|
}
|
||||||
|
|
||||||
|
return new File(path).isFile()
|
||||||
|
|| DriveUtils.isPhysicalDrive(path)
|
||||||
|
|| DriveUtils.isPartition(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void storeSettings() {
|
||||||
|
String archivePathName = getContentPaths();
|
||||||
|
if (null != archivePathName) {
|
||||||
|
String archivePath = archivePathName.substring(0, archivePathName.lastIndexOf(File.separator) + 1);
|
||||||
|
ModuleSettings.setConfigSetting(contextName, PROP_LAST_ARCHIVE_PATH, archivePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void readSettings() {
|
||||||
|
String lastArchivePath = ModuleSettings.getConfigSetting(contextName, PROP_LAST_ARCHIVE_PATH);
|
||||||
|
if (StringUtils.isNotBlank(lastArchivePath)) {
|
||||||
|
setContentPath(lastArchivePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void insertUpdate(DocumentEvent e) {
|
||||||
|
updateHelper();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeUpdate(DocumentEvent e) {
|
||||||
|
updateHelper();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void changedUpdate(DocumentEvent e) {
|
||||||
|
updateHelper();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update functions are called by the pathTextField which has this set as
|
||||||
|
* it's DocumentEventListener. Each update function fires a property change
|
||||||
|
* to be caught by the parent panel.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@NbBundle.Messages({"ArchiveFilePanel.moduleErr=Module Error",
|
||||||
|
"ArchiveFilePanel.moduleErr.msg=A module caused an error listening to ArchiveFilePanel updates."
|
||||||
|
+ " See log to determine which module. Some data could be incomplete.\n"})
|
||||||
|
private void updateHelper() {
|
||||||
|
try {
|
||||||
|
firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), false, true);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.log(Level.SEVERE, "ArchiveFilePanel listener threw exception", e); //NON-NLS
|
||||||
|
MessageNotifyUtil.Notify.error(ArchiveFilePanel_moduleErr(), ArchiveFilePanel_moduleErr_msg());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the focus to the pathTextField.
|
||||||
|
*/
|
||||||
|
public void select() {
|
||||||
|
pathTextField.requestFocusInWindow();
|
||||||
|
}
|
||||||
|
}
|
@ -26,7 +26,9 @@ import java.io.RandomAccessFile;
|
|||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import javax.swing.filechooser.FileFilter;
|
||||||
import net.sf.sevenzipjbinding.ISequentialOutStream;
|
import net.sf.sevenzipjbinding.ISequentialOutStream;
|
||||||
import net.sf.sevenzipjbinding.ISevenZipInArchive;
|
import net.sf.sevenzipjbinding.ISevenZipInArchive;
|
||||||
import net.sf.sevenzipjbinding.SevenZip;
|
import net.sf.sevenzipjbinding.SevenZip;
|
||||||
@ -35,18 +37,46 @@ import net.sf.sevenzipjbinding.SevenZipNativeInitializationException;
|
|||||||
import net.sf.sevenzipjbinding.impl.RandomAccessFileInStream;
|
import net.sf.sevenzipjbinding.impl.RandomAccessFileInStream;
|
||||||
import net.sf.sevenzipjbinding.simple.ISimpleInArchive;
|
import net.sf.sevenzipjbinding.simple.ISimpleInArchive;
|
||||||
import net.sf.sevenzipjbinding.simple.ISimpleInArchiveItem;
|
import net.sf.sevenzipjbinding.simple.ISimpleInArchiveItem;
|
||||||
|
import org.openide.util.NbBundle;
|
||||||
|
import org.sleuthkit.autopsy.casemodule.GeneralFilter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set of utilities that handles archive file extraction. Uses 7zip library.
|
* Set of utilities that handles archive file extraction. Uses 7zip library.
|
||||||
*/
|
*/
|
||||||
final class ArchiveUtil {
|
final class ArchiveUtil {
|
||||||
|
|
||||||
static final String[] SUPPORTED_EXTENSIONS = {"zip", "rar", "arj", "7z", "7zip", "gzip", "gz", "bzip2", "tar", "tgz",}; // NON-NLS
|
private static final String[] SUPPORTED_EXTENSIONS = {"zip", "rar", "arj", "7z", "7zip", "gzip", "gz", "bzip2", "tar", "tgz",}; // NON-NLS
|
||||||
|
private static final List<String> ARCHIVE_EXTS = Arrays.asList(".zip", ".rar", ".arj", ".7z", ".7zip", ".gzip", ".gz", ".bzip2", ".tar", ".tgz"); //NON-NLS
|
||||||
|
@NbBundle.Messages("GeneralFilter.archiveDesc.text=Archive Files (.zip, .rar, .arj, .7z, .7zip, .gzip, .gz, .bzip2, .tar, .tgz)")
|
||||||
|
private static final String ARCHIVE_DESC = Bundle.GeneralFilter_archiveDesc_text();
|
||||||
|
private static final GeneralFilter SEVEN_ZIP_FILTER = new GeneralFilter(ARCHIVE_EXTS, ARCHIVE_DESC);
|
||||||
|
private static final List<FileFilter> ARCHIVE_FILTERS = new ArrayList<>();
|
||||||
|
static {
|
||||||
|
ARCHIVE_FILTERS.add(SEVEN_ZIP_FILTER);
|
||||||
|
}
|
||||||
|
|
||||||
private ArchiveUtil() {
|
private ArchiveUtil() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static List<FileFilter> getArchiveFilters() {
|
||||||
|
return ARCHIVE_FILTERS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean isArchive(Path dataSourcePath) {
|
||||||
|
String fileName = dataSourcePath.getFileName().toString();
|
||||||
|
// check whether it's a zip archive file that can be extracted
|
||||||
|
return isAcceptedByFiler(new File(fileName), ARCHIVE_FILTERS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isAcceptedByFiler(File file, List<FileFilter> filters) {
|
||||||
|
for (FileFilter filter : filters) {
|
||||||
|
if (filter.accept(file)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enum of mime types which support archive extraction
|
* Enum of mime types which support archive extraction
|
||||||
*/
|
*/
|
||||||
|
@ -18,37 +18,32 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.experimental.autoingest;
|
package org.sleuthkit.autopsy.experimental.autoingest;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.logging.Level;
|
|
||||||
import javax.swing.SwingUtilities;
|
|
||||||
import org.openide.filesystems.FileObject;
|
|
||||||
import org.openide.filesystems.FileUtil;
|
|
||||||
import org.openide.util.actions.CallableSystemAction;
|
|
||||||
import org.sleuthkit.autopsy.casemodule.AddImageAction;
|
|
||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
import org.sleuthkit.autopsy.casemodule.CaseActionException;
|
import org.sleuthkit.autopsy.casemodule.CaseActionException;
|
||||||
import org.sleuthkit.autopsy.casemodule.CaseNewAction;
|
import org.sleuthkit.autopsy.coordinationservice.CoordinationService;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CoordinationServiceException;
|
||||||
import org.sleuthkit.autopsy.experimental.configuration.AutoIngestUserPreferences;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles locating and opening cases created by auto ingest.
|
* Handles locating and opening cases created by auto ingest.
|
||||||
*/
|
*/
|
||||||
final class AutoIngestCaseManager {
|
final class AutoIngestCaseManager {
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(AutoIngestCaseManager.class.getName());
|
|
||||||
private static AutoIngestCaseManager instance;
|
private static AutoIngestCaseManager instance;
|
||||||
|
|
||||||
|
private CoordinationService coordinationService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the auto ingest case manager.
|
* Gets the auto ingest case manager.
|
||||||
*
|
*
|
||||||
* @return The auto ingest case manager singleton.
|
* @return The auto ingest case manager singleton.
|
||||||
|
*
|
||||||
|
* @throws AutoIngestCaseManagerException
|
||||||
*/
|
*/
|
||||||
synchronized static AutoIngestCaseManager getInstance() {
|
synchronized static AutoIngestCaseManager getInstance() throws AutoIngestCaseManager.AutoIngestCaseManagerException {
|
||||||
if (null == instance) {
|
if (null == instance) {
|
||||||
instance = new AutoIngestCaseManager();
|
instance = new AutoIngestCaseManager();
|
||||||
}
|
}
|
||||||
@ -58,38 +53,67 @@ final class AutoIngestCaseManager {
|
|||||||
/**
|
/**
|
||||||
* Constructs an object that handles locating and opening cases created by
|
* Constructs an object that handles locating and opening cases created by
|
||||||
* auto ingest.
|
* auto ingest.
|
||||||
|
*
|
||||||
|
* @throws AutoIngestCaseManagerException
|
||||||
*/
|
*/
|
||||||
private AutoIngestCaseManager() {
|
private AutoIngestCaseManager() throws AutoIngestCaseManagerException {
|
||||||
|
try {
|
||||||
/*
|
coordinationService = CoordinationService.getInstance();
|
||||||
* Permanently delete the "Open Recent Cases" item in the "File" menu.
|
} catch (CoordinationServiceException ex) {
|
||||||
* This is quite drastic, as it also affects Autopsy standalone mode on
|
throw new AutoIngestCaseManager.AutoIngestCaseManagerException("Failed to get the coordination service.", ex);
|
||||||
* this machine, but review mode is only for looking at cases created by
|
|
||||||
* automated ingest.
|
|
||||||
*/
|
|
||||||
FileObject root = FileUtil.getConfigRoot();
|
|
||||||
FileObject openRecentCasesMenu = root.getFileObject("Menu/Case/OpenRecentCase");
|
|
||||||
if (openRecentCasesMenu != null) {
|
|
||||||
try {
|
|
||||||
openRecentCasesMenu.delete();
|
|
||||||
} catch (IOException ex) {
|
|
||||||
AutoIngestCaseManager.LOGGER.log(Level.WARNING, "Unable to remove Open Recent Cases file menu item", ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Gets a list of the cases in the top level case folder used by auto
|
* Gets a list of the cases in the top level case folder used by auto
|
||||||
* ingest.
|
* ingest.
|
||||||
|
*
|
||||||
|
* @return List of cases.
|
||||||
|
*
|
||||||
|
* @throws AutoIngestCaseManagerException
|
||||||
*/
|
*/
|
||||||
List<AutoIngestCase> getCases() {
|
List<AutoIngestCase> getCases() throws AutoIngestCaseManagerException {
|
||||||
List<AutoIngestCase> cases = new ArrayList<>();
|
List<AutoIngestCase> cases = new ArrayList<>();
|
||||||
List<Path> caseFolders = PathUtils.findCaseFolders(Paths.get(AutoIngestUserPreferences.getAutoModeResultsFolder()));
|
List<Path> casePathList = getCasePaths();
|
||||||
for (Path caseFolderPath : caseFolders) {
|
for (Path casePath : casePathList) {
|
||||||
cases.add(new AutoIngestCase(caseFolderPath));
|
cases.add(new AutoIngestCase(casePath));
|
||||||
}
|
}
|
||||||
return cases;
|
return cases;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve all of the case nodes and filter for only those that represent
|
||||||
|
* case paths.
|
||||||
|
*
|
||||||
|
* @return List of case paths.
|
||||||
|
*
|
||||||
|
* @throws AutoIngestCaseManagerException
|
||||||
|
*/
|
||||||
|
private List<Path> getCasePaths() throws AutoIngestCaseManagerException {
|
||||||
|
try {
|
||||||
|
List<String> nodeList = coordinationService.getNodeList(CoordinationService.CategoryNode.CASES);
|
||||||
|
List<Path> casePathList = new ArrayList<Path>(0);
|
||||||
|
for (String node : nodeList) {
|
||||||
|
if(node.indexOf('\\') >= 0 || node.indexOf('/') >= 0) {
|
||||||
|
/*
|
||||||
|
* This is not a case name lock (name specifies a path).
|
||||||
|
*/
|
||||||
|
String nodeUpperCase = node.toUpperCase();
|
||||||
|
if(!nodeUpperCase.endsWith("_RESOURCES") && !nodeUpperCase.endsWith("AUTO_INGEST_LOG.TXT")) {
|
||||||
|
/*
|
||||||
|
* This is not a case resource lock, nor a case auto
|
||||||
|
* ingest log lock. Collect the path.
|
||||||
|
*/
|
||||||
|
casePathList.add(Paths.get(node));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return casePathList;
|
||||||
|
|
||||||
|
} catch (CoordinationServiceException ex) {
|
||||||
|
throw new AutoIngestCaseManager.AutoIngestCaseManagerException("Failed to get node list from coordination service.", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens an auto ingest case case.
|
* Opens an auto ingest case case.
|
||||||
@ -104,4 +128,35 @@ final class AutoIngestCaseManager {
|
|||||||
*/
|
*/
|
||||||
Case.openAsCurrentCase(caseMetadataFilePath.toString());
|
Case.openAsCurrentCase(caseMetadataFilePath.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception type thrown when there is an error completing an auto ingest
|
||||||
|
* case manager operation.
|
||||||
|
*/
|
||||||
|
static final class AutoIngestCaseManagerException extends Exception {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an instance of the exception type thrown when there is an
|
||||||
|
* error completing an auto ingest case manager operation.
|
||||||
|
*
|
||||||
|
* @param message The exception message.
|
||||||
|
*/
|
||||||
|
private AutoIngestCaseManagerException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an instance of the exception type thrown when there is an
|
||||||
|
* error completing an auto ingest case manager operation.
|
||||||
|
*
|
||||||
|
* @param message The exception message.
|
||||||
|
* @param cause A Throwable cause for the error.
|
||||||
|
*/
|
||||||
|
private AutoIngestCaseManagerException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.experimental.autoingest;
|
|||||||
import java.awt.Cursor;
|
import java.awt.Cursor;
|
||||||
import java.awt.Desktop;
|
import java.awt.Desktop;
|
||||||
import java.awt.EventQueue;
|
import java.awt.EventQueue;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
import java.awt.event.WindowAdapter;
|
import java.awt.event.WindowAdapter;
|
||||||
import java.awt.event.WindowEvent;
|
import java.awt.event.WindowEvent;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -39,16 +40,24 @@ import javax.swing.SwingWorker;
|
|||||||
import javax.swing.event.ListSelectionEvent;
|
import javax.swing.event.ListSelectionEvent;
|
||||||
import javax.swing.table.DefaultTableModel;
|
import javax.swing.table.DefaultTableModel;
|
||||||
import javax.swing.table.TableColumn;
|
import javax.swing.table.TableColumn;
|
||||||
|
import org.openide.util.NbBundle;
|
||||||
|
import org.openide.util.lookup.ServiceProvider;
|
||||||
|
import org.openide.windows.WindowManager;
|
||||||
import org.sleuthkit.autopsy.casemodule.CaseActionCancelledException;
|
import org.sleuthkit.autopsy.casemodule.CaseActionCancelledException;
|
||||||
import org.sleuthkit.autopsy.casemodule.CaseMetadata;
|
import org.sleuthkit.autopsy.casemodule.CaseMetadata;
|
||||||
import org.sleuthkit.autopsy.casemodule.StartupWindowProvider;
|
import org.sleuthkit.autopsy.casemodule.StartupWindowProvider;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
||||||
|
import org.sleuthkit.autopsy.casemodule.AutoIngestCasePanelInterface;
|
||||||
|
import org.sleuthkit.autopsy.casemodule.CueBannerPanel;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.NetworkUtils;
|
||||||
|
import org.sleuthkit.autopsy.experimental.configuration.StartupWindow;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A panel that allows a user to open cases created by auto ingest.
|
* A panel that allows a user to open cases created by auto ingest.
|
||||||
*/
|
*/
|
||||||
public final class AutoIngestCasePanel extends JPanel {
|
@ServiceProvider(service = AutoIngestCasePanelInterface.class)
|
||||||
|
public final class AutoIngestCasePanel extends JPanel implements AutoIngestCasePanelInterface {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
private static final Logger logger = Logger.getLogger(AutoIngestCasePanel.class.getName());
|
private static final Logger logger = Logger.getLogger(AutoIngestCasePanel.class.getName());
|
||||||
@ -90,6 +99,34 @@ public final class AutoIngestCasePanel extends JPanel {
|
|||||||
private final String[] columnNames = {CASE_HEADER, CREATEDTIME_HEADER, COMPLETEDTIME_HEADER, STATUS_ICON_HEADER, OUTPUT_FOLDER_HEADER};
|
private final String[] columnNames = {CASE_HEADER, CREATEDTIME_HEADER, COMPLETEDTIME_HEADER, STATUS_ICON_HEADER, OUTPUT_FOLDER_HEADER};
|
||||||
private DefaultTableModel caseTableModel;
|
private DefaultTableModel caseTableModel;
|
||||||
private Path currentlySelectedCase = null;
|
private Path currentlySelectedCase = null;
|
||||||
|
|
||||||
|
public AutoIngestCasePanel() {
|
||||||
|
init(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addWindowStateListener(JDialog parent) {
|
||||||
|
/*
|
||||||
|
* Add a window state listener that starts and stops refreshing of the
|
||||||
|
* cases table.
|
||||||
|
*/
|
||||||
|
parent.addWindowListener(new WindowAdapter() {
|
||||||
|
@Override
|
||||||
|
public void windowClosing(WindowEvent e) {
|
||||||
|
stopCasesTableRefreshes();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void windowActivated(WindowEvent e) {
|
||||||
|
startCasesTableRefreshes();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void windowClosed(WindowEvent e) {
|
||||||
|
stopCasesTableRefreshes();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a panel that allows a user to open cases created by automated
|
* Constructs a panel that allows a user to open cases created by automated
|
||||||
@ -98,6 +135,10 @@ public final class AutoIngestCasePanel extends JPanel {
|
|||||||
* @param parent The parent dialog for this panel.
|
* @param parent The parent dialog for this panel.
|
||||||
*/
|
*/
|
||||||
public AutoIngestCasePanel(JDialog parent) {
|
public AutoIngestCasePanel(JDialog parent) {
|
||||||
|
init(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init(JDialog parent) {
|
||||||
caseTableModel = new DefaultTableModel(columnNames, 0) {
|
caseTableModel = new DefaultTableModel(columnNames, 0) {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
@ -226,7 +267,8 @@ public final class AutoIngestCasePanel extends JPanel {
|
|||||||
private void refreshCasesTable() {
|
private void refreshCasesTable() {
|
||||||
try {
|
try {
|
||||||
currentlySelectedCase = getSelectedCase();
|
currentlySelectedCase = getSelectedCase();
|
||||||
List<AutoIngestCase> theModel = AutoIngestCaseManager.getInstance().getCases();
|
AutoIngestCaseManager manager = AutoIngestCaseManager.getInstance();
|
||||||
|
List<AutoIngestCase> theModel = manager.getCases();
|
||||||
EventQueue.invokeLater(new CaseTableRefreshTask(theModel));
|
EventQueue.invokeLater(new CaseTableRefreshTask(theModel));
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
logger.log(Level.SEVERE, "Unexpected exception in refreshCasesTable", ex); //NON-NLS
|
logger.log(Level.SEVERE, "Unexpected exception in refreshCasesTable", ex); //NON-NLS
|
||||||
@ -297,6 +339,7 @@ public final class AutoIngestCasePanel extends JPanel {
|
|||||||
AutoIngestCaseManager.getInstance().openCase(caseMetadataFilePath);
|
AutoIngestCaseManager.getInstance().openCase(caseMetadataFilePath);
|
||||||
stopCasesTableRefreshes();
|
stopCasesTableRefreshes();
|
||||||
StartupWindowProvider.getInstance().close();
|
StartupWindowProvider.getInstance().close();
|
||||||
|
CueBannerPanel.closeAutoIngestCasesWindow();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,30 +89,34 @@ public final class AutoIngestJob implements Comparable<AutoIngestJob>, Serializa
|
|||||||
*
|
*
|
||||||
* @param manifest The manifest for an automated ingest job.
|
* @param manifest The manifest for an automated ingest job.
|
||||||
*/
|
*/
|
||||||
AutoIngestJob(Manifest manifest) {
|
AutoIngestJob(Manifest manifest) throws AutoIngestJobException {
|
||||||
/*
|
try {
|
||||||
* Version 0 fields.
|
/*
|
||||||
*/
|
* Version 0 fields.
|
||||||
this.manifest = manifest;
|
*/
|
||||||
this.nodeName = "";
|
this.manifest = manifest;
|
||||||
this.caseDirectoryPath = "";
|
this.nodeName = "";
|
||||||
this.priority = DEFAULT_PRIORITY;
|
this.caseDirectoryPath = "";
|
||||||
this.stage = Stage.PENDING;
|
this.priority = DEFAULT_PRIORITY;
|
||||||
this.stageStartDate = manifest.getDateFileCreated();
|
this.stage = Stage.PENDING;
|
||||||
this.dataSourceProcessor = null;
|
this.stageStartDate = manifest.getDateFileCreated();
|
||||||
this.ingestJob = null;
|
this.dataSourceProcessor = null;
|
||||||
this.cancelled = false;
|
this.ingestJob = null;
|
||||||
this.completed = false;
|
this.cancelled = false;
|
||||||
this.completedDate = new Date(0);
|
this.completed = false;
|
||||||
this.errorsOccurred = false;
|
this.completedDate = new Date(0);
|
||||||
|
this.errorsOccurred = false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Version 1 fields.
|
* Version 1 fields.
|
||||||
*/
|
*/
|
||||||
this.version = CURRENT_VERSION;
|
this.version = CURRENT_VERSION;
|
||||||
this.processingStatus = ProcessingStatus.PENDING;
|
this.processingStatus = ProcessingStatus.PENDING;
|
||||||
this.numberOfCrashes = 0;
|
this.numberOfCrashes = 0;
|
||||||
this.stageDetails = this.getProcessingStageDetails();
|
this.stageDetails = this.getProcessingStageDetails();
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throw new AutoIngestJobException(String.format("Error creating automated ingest job"), ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -122,30 +126,34 @@ public final class AutoIngestJob implements Comparable<AutoIngestJob>, Serializa
|
|||||||
* @param nodeData The coordination service node data for an automated
|
* @param nodeData The coordination service node data for an automated
|
||||||
* ingest job.
|
* ingest job.
|
||||||
*/
|
*/
|
||||||
AutoIngestJob(AutoIngestJobNodeData nodeData) {
|
AutoIngestJob(AutoIngestJobNodeData nodeData) throws AutoIngestJobException {
|
||||||
/*
|
try {
|
||||||
* Version 0 fields.
|
/*
|
||||||
*/
|
* Version 0 fields.
|
||||||
this.manifest = new Manifest(nodeData.getManifestFilePath(), nodeData.getManifestFileDate(), nodeData.getCaseName(), nodeData.getDeviceId(), nodeData.getDataSourcePath(), Collections.emptyMap());
|
*/
|
||||||
this.nodeName = nodeData.getProcessingHostName();
|
this.manifest = new Manifest(nodeData.getManifestFilePath(), nodeData.getManifestFileDate(), nodeData.getCaseName(), nodeData.getDeviceId(), nodeData.getDataSourcePath(), Collections.emptyMap());
|
||||||
this.caseDirectoryPath = nodeData.getCaseDirectoryPath().toString();
|
this.nodeName = nodeData.getProcessingHostName();
|
||||||
this.priority = nodeData.getPriority();
|
this.caseDirectoryPath = nodeData.getCaseDirectoryPath().toString();
|
||||||
this.stage = nodeData.getProcessingStage();
|
this.priority = nodeData.getPriority();
|
||||||
this.stageStartDate = nodeData.getProcessingStageStartDate();
|
this.stage = nodeData.getProcessingStage();
|
||||||
this.dataSourceProcessor = null; // Transient data not in node data.
|
this.stageStartDate = nodeData.getProcessingStageStartDate();
|
||||||
this.ingestJob = null; // Transient data not in node data.
|
this.dataSourceProcessor = null; // Transient data not in node data.
|
||||||
this.cancelled = false; // Transient data not in node data.
|
this.ingestJob = null; // Transient data not in node data.
|
||||||
this.completed = false; // Transient data not in node data.
|
this.cancelled = false; // Transient data not in node data.
|
||||||
this.completedDate = nodeData.getCompletedDate();
|
this.completed = false; // Transient data not in node data.
|
||||||
this.errorsOccurred = nodeData.getErrorsOccurred();
|
this.completedDate = nodeData.getCompletedDate();
|
||||||
|
this.errorsOccurred = nodeData.getErrorsOccurred();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Version 1 fields.
|
* Version 1 fields.
|
||||||
*/
|
*/
|
||||||
this.version = CURRENT_VERSION;
|
this.version = CURRENT_VERSION;
|
||||||
this.processingStatus = nodeData.getProcessingStatus();
|
this.processingStatus = nodeData.getProcessingStatus();
|
||||||
this.numberOfCrashes = nodeData.getNumberOfCrashes();
|
this.numberOfCrashes = nodeData.getNumberOfCrashes();
|
||||||
this.stageDetails = this.getProcessingStageDetails();
|
this.stageDetails = this.getProcessingStageDetails();
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throw new AutoIngestJobException(String.format("Error creating automated ingest job"), ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -622,5 +630,33 @@ public final class AutoIngestJob implements Comparable<AutoIngestJob>, Serializa
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception thrown when there is a problem creating auto ingest job.
|
||||||
|
*/
|
||||||
|
final static class AutoIngestJobException extends Exception {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an exception to throw when there is a problem creating
|
||||||
|
* auto ingest job.
|
||||||
|
*
|
||||||
|
* @param message The exception message.
|
||||||
|
*/
|
||||||
|
private AutoIngestJobException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an exception to throw when there is a problem creating
|
||||||
|
* auto ingest job.
|
||||||
|
*
|
||||||
|
* @param message The exception message.
|
||||||
|
* @param cause The cause of the exception, if it was an exception.
|
||||||
|
*/
|
||||||
|
private AutoIngestJobException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,6 @@ import java.time.Duration;
|
|||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
@ -59,8 +58,6 @@ import java.util.concurrent.TimeUnit;
|
|||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import javax.annotation.concurrent.GuardedBy;
|
import javax.annotation.concurrent.GuardedBy;
|
||||||
import javax.annotation.concurrent.Immutable;
|
|
||||||
import javax.annotation.concurrent.ThreadSafe;
|
|
||||||
import org.openide.util.Lookup;
|
import org.openide.util.Lookup;
|
||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
import org.sleuthkit.autopsy.casemodule.Case.CaseType;
|
import org.sleuthkit.autopsy.casemodule.Case.CaseType;
|
||||||
@ -93,13 +90,13 @@ import org.sleuthkit.autopsy.experimental.configuration.SharedConfiguration;
|
|||||||
import org.sleuthkit.autopsy.experimental.configuration.SharedConfiguration.SharedConfigurationException;
|
import org.sleuthkit.autopsy.experimental.configuration.SharedConfiguration.SharedConfigurationException;
|
||||||
import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor;
|
import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor;
|
||||||
import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException;
|
import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException;
|
||||||
|
import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestJob.AutoIngestJobException;
|
||||||
import org.sleuthkit.autopsy.ingest.IngestJob;
|
import org.sleuthkit.autopsy.ingest.IngestJob;
|
||||||
import org.sleuthkit.autopsy.ingest.IngestJob.CancellationReason;
|
import org.sleuthkit.autopsy.ingest.IngestJob.CancellationReason;
|
||||||
import org.sleuthkit.autopsy.ingest.IngestJobSettings;
|
import org.sleuthkit.autopsy.ingest.IngestJobSettings;
|
||||||
import org.sleuthkit.autopsy.ingest.IngestJobStartResult;
|
import org.sleuthkit.autopsy.ingest.IngestJobStartResult;
|
||||||
import org.sleuthkit.autopsy.ingest.IngestManager;
|
import org.sleuthkit.autopsy.ingest.IngestManager;
|
||||||
import org.sleuthkit.autopsy.ingest.IngestModuleError;
|
import org.sleuthkit.autopsy.ingest.IngestModuleError;
|
||||||
import org.sleuthkit.datamodel.Content;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An auto ingest manager is responsible for processing auto ingest jobs defined
|
* An auto ingest manager is responsible for processing auto ingest jobs defined
|
||||||
@ -759,7 +756,7 @@ public final class AutoIngestManager extends Observable implements PropertyChang
|
|||||||
AutoIngestJob deletedJob = new AutoIngestJob(nodeData);
|
AutoIngestJob deletedJob = new AutoIngestJob(nodeData);
|
||||||
deletedJob.setProcessingStatus(AutoIngestJob.ProcessingStatus.DELETED);
|
deletedJob.setProcessingStatus(AutoIngestJob.ProcessingStatus.DELETED);
|
||||||
this.updateCoordinationServiceNode(deletedJob);
|
this.updateCoordinationServiceNode(deletedJob);
|
||||||
} catch (AutoIngestJobNodeData.InvalidDataException ex) {
|
} catch (AutoIngestJobNodeData.InvalidDataException | AutoIngestJobException ex) {
|
||||||
SYS_LOGGER.log(Level.WARNING, String.format("Invalid auto ingest job node data for %s", manifestPath), ex);
|
SYS_LOGGER.log(Level.WARNING, String.format("Invalid auto ingest job node data for %s", manifestPath), ex);
|
||||||
return CaseDeletionResult.PARTIALLY_DELETED;
|
return CaseDeletionResult.PARTIALLY_DELETED;
|
||||||
} catch (InterruptedException | CoordinationServiceException ex) {
|
} catch (InterruptedException | CoordinationServiceException ex) {
|
||||||
@ -1015,92 +1012,103 @@ public final class AutoIngestManager extends Observable implements PropertyChang
|
|||||||
* @return TERMINATE if auto ingest is shutting down, CONTINUE if it has
|
* @return TERMINATE if auto ingest is shutting down, CONTINUE if it has
|
||||||
* not.
|
* not.
|
||||||
*
|
*
|
||||||
* @throws IOException if an I/O error occurs, but this implementation
|
|
||||||
* does not throw.
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public FileVisitResult visitFile(Path filePath, BasicFileAttributes attrs) throws IOException {
|
public FileVisitResult visitFile(Path filePath, BasicFileAttributes attrs) {
|
||||||
if (Thread.currentThread().isInterrupted()) {
|
if (Thread.currentThread().isInterrupted()) {
|
||||||
return TERMINATE;
|
return TERMINATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
Manifest manifest = null;
|
try {
|
||||||
for (ManifestFileParser parser : Lookup.getDefault().lookupAll(ManifestFileParser.class)) {
|
Manifest manifest = null;
|
||||||
if (parser.fileIsManifest(filePath)) {
|
for (ManifestFileParser parser : Lookup.getDefault().lookupAll(ManifestFileParser.class)) {
|
||||||
try {
|
if (parser.fileIsManifest(filePath)) {
|
||||||
manifest = parser.parse(filePath);
|
try {
|
||||||
break;
|
manifest = parser.parse(filePath);
|
||||||
} catch (ManifestFileParserException ex) {
|
break;
|
||||||
SYS_LOGGER.log(Level.SEVERE, String.format("Error attempting to parse %s with parser %s", filePath, parser.getClass().getCanonicalName()), ex);
|
} catch (ManifestFileParserException ex) {
|
||||||
|
SYS_LOGGER.log(Level.SEVERE, String.format("Error attempting to parse %s with parser %s", filePath, parser.getClass().getCanonicalName()), ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Thread.currentThread().isInterrupted()) {
|
||||||
|
return TERMINATE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Thread.currentThread().isInterrupted()) {
|
if (Thread.currentThread().isInterrupted()) {
|
||||||
return TERMINATE;
|
return TERMINATE;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (Thread.currentThread().isInterrupted()) {
|
if (null != manifest) {
|
||||||
return TERMINATE;
|
/*
|
||||||
}
|
|
||||||
|
|
||||||
if (null != manifest) {
|
|
||||||
/*
|
|
||||||
* Update the mapping of case names to manifest paths that is
|
* Update the mapping of case names to manifest paths that is
|
||||||
* used for case deletion.
|
* used for case deletion.
|
||||||
*/
|
*/
|
||||||
String caseName = manifest.getCaseName();
|
String caseName = manifest.getCaseName();
|
||||||
Path manifestPath = manifest.getFilePath();
|
Path manifestPath = manifest.getFilePath();
|
||||||
if (casesToManifests.containsKey(caseName)) {
|
if (casesToManifests.containsKey(caseName)) {
|
||||||
Set<Path> manifestPaths = casesToManifests.get(caseName);
|
Set<Path> manifestPaths = casesToManifests.get(caseName);
|
||||||
manifestPaths.add(manifestPath);
|
manifestPaths.add(manifestPath);
|
||||||
} else {
|
} else {
|
||||||
Set<Path> manifestPaths = new HashSet<>();
|
Set<Path> manifestPaths = new HashSet<>();
|
||||||
manifestPaths.add(manifestPath);
|
manifestPaths.add(manifestPath);
|
||||||
casesToManifests.put(caseName, manifestPaths);
|
casesToManifests.put(caseName, manifestPaths);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Add a job to the pending jobs queue, the completed jobs list,
|
* Add a job to the pending jobs queue, the completed jobs list,
|
||||||
* or do crashed job recovery, as required.
|
* or do crashed job recovery, as required.
|
||||||
*/
|
*/
|
||||||
try {
|
try {
|
||||||
byte[] rawData = coordinationService.getNodeData(CoordinationService.CategoryNode.MANIFESTS, manifestPath.toString());
|
byte[] rawData = coordinationService.getNodeData(CoordinationService.CategoryNode.MANIFESTS, manifestPath.toString());
|
||||||
if (null != rawData && rawData.length > 0) {
|
if (null != rawData && rawData.length > 0) {
|
||||||
try {
|
try {
|
||||||
AutoIngestJobNodeData nodeData = new AutoIngestJobNodeData(rawData);
|
AutoIngestJobNodeData nodeData = new AutoIngestJobNodeData(rawData);
|
||||||
AutoIngestJob.ProcessingStatus processingStatus = nodeData.getProcessingStatus();
|
AutoIngestJob.ProcessingStatus processingStatus = nodeData.getProcessingStatus();
|
||||||
switch (processingStatus) {
|
switch (processingStatus) {
|
||||||
case PENDING:
|
case PENDING:
|
||||||
addPendingJob(manifest, nodeData);
|
addPendingJob(manifest, nodeData);
|
||||||
break;
|
break;
|
||||||
case PROCESSING:
|
case PROCESSING:
|
||||||
doRecoveryIfCrashed(manifest, nodeData);
|
doRecoveryIfCrashed(manifest, nodeData);
|
||||||
break;
|
break;
|
||||||
case COMPLETED:
|
case COMPLETED:
|
||||||
addCompletedJob(manifest, nodeData);
|
addCompletedJob(manifest, nodeData);
|
||||||
break;
|
break;
|
||||||
case DELETED:
|
case DELETED:
|
||||||
/*
|
/*
|
||||||
* Ignore jobs marked as "deleted."
|
* Ignore jobs marked as "deleted."
|
||||||
*/
|
*/
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
SYS_LOGGER.log(Level.SEVERE, "Unknown ManifestNodeData.ProcessingStatus");
|
SYS_LOGGER.log(Level.SEVERE, "Unknown ManifestNodeData.ProcessingStatus");
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
} catch (AutoIngestJobNodeData.InvalidDataException | AutoIngestJobException ex) {
|
||||||
|
SYS_LOGGER.log(Level.SEVERE, String.format("Invalid auto ingest job node data for %s", manifestPath), ex);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
addNewPendingJob(manifest);
|
||||||
|
} catch (AutoIngestJobException ex) {
|
||||||
|
SYS_LOGGER.log(Level.SEVERE, String.format("Invalid manifest data for %s", manifestPath), ex);
|
||||||
}
|
}
|
||||||
} catch (AutoIngestJobNodeData.InvalidDataException ex) {
|
|
||||||
SYS_LOGGER.log(Level.WARNING, String.format("Invalid auto ingest job node data for %s", manifestPath), ex);
|
|
||||||
}
|
}
|
||||||
} else {
|
} catch (CoordinationServiceException ex) {
|
||||||
addNewPendingJob(manifest);
|
SYS_LOGGER.log(Level.SEVERE, String.format("Error transmitting node data for %s", manifestPath), ex);
|
||||||
|
return CONTINUE;
|
||||||
|
} catch (InterruptedException ex) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
return TERMINATE;
|
||||||
}
|
}
|
||||||
} catch (CoordinationServiceException ex) {
|
|
||||||
SYS_LOGGER.log(Level.SEVERE, String.format("Error transmitting node data for %s", manifestPath), ex);
|
|
||||||
return CONTINUE;
|
|
||||||
} catch (InterruptedException ex) {
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
return TERMINATE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} catch (Exception ex) {
|
||||||
|
// Catch all unhandled and unexpected exceptions. Otherwise one bad file
|
||||||
|
// can stop the entire input folder scanning. Given that the exception is unexpected,
|
||||||
|
// I'm hesitant to add logging which requires accessing or de-referencing data.
|
||||||
|
SYS_LOGGER.log(Level.SEVERE, "Unexpected exception in file visitor", ex);
|
||||||
|
return CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Thread.currentThread().isInterrupted()) {
|
if (!Thread.currentThread().isInterrupted()) {
|
||||||
@ -1122,7 +1130,7 @@ public final class AutoIngestManager extends Observable implements PropertyChang
|
|||||||
* blocked, i.e., if auto ingest is
|
* blocked, i.e., if auto ingest is
|
||||||
* shutting down.
|
* shutting down.
|
||||||
*/
|
*/
|
||||||
private void addPendingJob(Manifest manifest, AutoIngestJobNodeData nodeData) throws InterruptedException {
|
private void addPendingJob(Manifest manifest, AutoIngestJobNodeData nodeData) throws InterruptedException, AutoIngestJobException {
|
||||||
AutoIngestJob job;
|
AutoIngestJob job;
|
||||||
if (nodeData.getVersion() == AutoIngestJobNodeData.getCurrentVersion()) {
|
if (nodeData.getVersion() == AutoIngestJobNodeData.getCurrentVersion()) {
|
||||||
job = new AutoIngestJob(nodeData);
|
job = new AutoIngestJob(nodeData);
|
||||||
@ -1176,7 +1184,7 @@ public final class AutoIngestManager extends Observable implements PropertyChang
|
|||||||
* blocked, i.e., if auto ingest is
|
* blocked, i.e., if auto ingest is
|
||||||
* shutting down.
|
* shutting down.
|
||||||
*/
|
*/
|
||||||
private void addNewPendingJob(Manifest manifest) throws InterruptedException {
|
private void addNewPendingJob(Manifest manifest) throws InterruptedException, AutoIngestJobException {
|
||||||
/*
|
/*
|
||||||
* Create the coordination service node data for the job. Note that
|
* Create the coordination service node data for the job. Note that
|
||||||
* getting the lock will create the node for the job (with no data)
|
* getting the lock will create the node for the job (with no data)
|
||||||
@ -1218,7 +1226,7 @@ public final class AutoIngestManager extends Observable implements PropertyChang
|
|||||||
* blocked, i.e., if auto ingest is
|
* blocked, i.e., if auto ingest is
|
||||||
* shutting down.
|
* shutting down.
|
||||||
*/
|
*/
|
||||||
private void doRecoveryIfCrashed(Manifest manifest, AutoIngestJobNodeData nodeData) throws InterruptedException {
|
private void doRecoveryIfCrashed(Manifest manifest, AutoIngestJobNodeData nodeData) throws InterruptedException, AutoIngestJobException {
|
||||||
/*
|
/*
|
||||||
* Try to get an exclusive lock on the coordination service node for
|
* Try to get an exclusive lock on the coordination service node for
|
||||||
* the job. If the lock cannot be obtained, another host in the auto
|
* the job. If the lock cannot be obtained, another host in the auto
|
||||||
@ -1314,7 +1322,7 @@ public final class AutoIngestManager extends Observable implements PropertyChang
|
|||||||
* @throws CoordinationServiceException
|
* @throws CoordinationServiceException
|
||||||
* @throws InterruptedException
|
* @throws InterruptedException
|
||||||
*/
|
*/
|
||||||
private void addCompletedJob(Manifest manifest, AutoIngestJobNodeData nodeData) throws CoordinationServiceException, InterruptedException {
|
private void addCompletedJob(Manifest manifest, AutoIngestJobNodeData nodeData) throws CoordinationServiceException, InterruptedException, AutoIngestJobException {
|
||||||
Path caseDirectoryPath = PathUtils.findCaseDirectory(rootOutputDirectory, manifest.getCaseName());
|
Path caseDirectoryPath = PathUtils.findCaseDirectory(rootOutputDirectory, manifest.getCaseName());
|
||||||
if (null != caseDirectoryPath) {
|
if (null != caseDirectoryPath) {
|
||||||
AutoIngestJob job;
|
AutoIngestJob job;
|
||||||
@ -1441,7 +1449,6 @@ public final class AutoIngestManager extends Observable implements PropertyChang
|
|||||||
*/
|
*/
|
||||||
private final class JobProcessingTask implements Runnable {
|
private final class JobProcessingTask implements Runnable {
|
||||||
|
|
||||||
private static final String AUTO_INGEST_MODULE_OUTPUT_DIR = "AutoIngest";
|
|
||||||
private final Object ingestLock;
|
private final Object ingestLock;
|
||||||
private final Object pauseLock;
|
private final Object pauseLock;
|
||||||
@GuardedBy("pauseLock")
|
@GuardedBy("pauseLock")
|
||||||
@ -2204,7 +2211,7 @@ public final class AutoIngestManager extends Observable implements PropertyChang
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
DataSource dataSource = identifyDataSource(caseForJob);
|
DataSource dataSource = identifyDataSource();
|
||||||
if (null == dataSource) {
|
if (null == dataSource) {
|
||||||
currentJob.setProcessingStage(AutoIngestJob.Stage.COMPLETED, Date.from(Instant.now()));
|
currentJob.setProcessingStage(AutoIngestJob.Stage.COMPLETED, Date.from(Instant.now()));
|
||||||
return;
|
return;
|
||||||
@ -2257,7 +2264,7 @@ public final class AutoIngestManager extends Observable implements PropertyChang
|
|||||||
* interrupted while blocked, i.e.,
|
* interrupted while blocked, i.e.,
|
||||||
* if auto ingest is shutting down.
|
* if auto ingest is shutting down.
|
||||||
*/
|
*/
|
||||||
private DataSource identifyDataSource(Case caseForJob) throws AutoIngestAlertFileException, AutoIngestJobLoggerException, InterruptedException {
|
private DataSource identifyDataSource() throws AutoIngestAlertFileException, AutoIngestJobLoggerException, InterruptedException {
|
||||||
Manifest manifest = currentJob.getManifest();
|
Manifest manifest = currentJob.getManifest();
|
||||||
Path manifestPath = manifest.getFilePath();
|
Path manifestPath = manifest.getFilePath();
|
||||||
SYS_LOGGER.log(Level.INFO, "Identifying data source for {0} ", manifestPath);
|
SYS_LOGGER.log(Level.INFO, "Identifying data source for {0} ", manifestPath);
|
||||||
@ -2276,7 +2283,7 @@ public final class AutoIngestManager extends Observable implements PropertyChang
|
|||||||
String deviceId = manifest.getDeviceId();
|
String deviceId = manifest.getDeviceId();
|
||||||
return new DataSource(deviceId, dataSourcePath);
|
return new DataSource(deviceId, dataSourcePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Passes the data source for the current job through a data source
|
* Passes the data source for the current job through a data source
|
||||||
* processor that adds it to the case database.
|
* processor that adds it to the case database.
|
||||||
@ -2299,28 +2306,21 @@ public final class AutoIngestManager extends Observable implements PropertyChang
|
|||||||
SYS_LOGGER.log(Level.INFO, "Adding data source for {0} ", manifestPath);
|
SYS_LOGGER.log(Level.INFO, "Adding data source for {0} ", manifestPath);
|
||||||
currentJob.setProcessingStage(AutoIngestJob.Stage.ADDING_DATA_SOURCE, Date.from(Instant.now()));
|
currentJob.setProcessingStage(AutoIngestJob.Stage.ADDING_DATA_SOURCE, Date.from(Instant.now()));
|
||||||
UUID taskId = UUID.randomUUID();
|
UUID taskId = UUID.randomUUID();
|
||||||
DataSourceProcessorCallback callBack = new AddDataSourceCallback(caseForJob, dataSource, taskId);
|
DataSourceProcessorCallback callBack = new AddDataSourceCallback(caseForJob, dataSource, taskId, ingestLock);
|
||||||
DataSourceProcessorProgressMonitor progressMonitor = new DoNothingDSPProgressMonitor();
|
DataSourceProcessorProgressMonitor progressMonitor = new DoNothingDSPProgressMonitor();
|
||||||
Path caseDirectoryPath = currentJob.getCaseDirectoryPath();
|
Path caseDirectoryPath = currentJob.getCaseDirectoryPath();
|
||||||
AutoIngestJobLogger jobLogger = new AutoIngestJobLogger(manifestPath, manifest.getDataSourceFileName(), caseDirectoryPath);
|
AutoIngestJobLogger jobLogger = new AutoIngestJobLogger(manifestPath, manifest.getDataSourceFileName(), caseDirectoryPath);
|
||||||
try {
|
try {
|
||||||
caseForJob.notifyAddingDataSource(taskId);
|
caseForJob.notifyAddingDataSource(taskId);
|
||||||
|
|
||||||
// lookup all AutomatedIngestDataSourceProcessors
|
Map<AutoIngestDataSourceProcessor, Integer> validDataSourceProcessorsMap;
|
||||||
Collection<? extends AutoIngestDataSourceProcessor> processorCandidates = Lookup.getDefault().lookupAll(AutoIngestDataSourceProcessor.class);
|
try {
|
||||||
|
// lookup all AutomatedIngestDataSourceProcessors and poll which ones are able to process the current data source
|
||||||
Map<AutoIngestDataSourceProcessor, Integer> validDataSourceProcessorsMap = new HashMap<>();
|
validDataSourceProcessorsMap = DataSourceProcessorUtility.getDataSourceProcessor(dataSource.getPath());
|
||||||
for (AutoIngestDataSourceProcessor processor : processorCandidates) {
|
} catch (AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException ex) {
|
||||||
try {
|
SYS_LOGGER.log(Level.SEVERE, "Exception while determining best data source processor for {0}", dataSource.getPath());
|
||||||
int confidence = processor.canProcess(dataSource.getPath());
|
// rethrow the exception. It will get caught & handled upstream and will result in AIM auto-pause.
|
||||||
if (confidence > 0) {
|
throw ex;
|
||||||
validDataSourceProcessorsMap.put(processor, confidence);
|
|
||||||
}
|
|
||||||
} catch (AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException ex) {
|
|
||||||
SYS_LOGGER.log(Level.SEVERE, "Exception while determining whether data source processor {0} can process {1}", new Object[]{processor.getDataSourceType(), dataSource.getPath()});
|
|
||||||
// rethrow the exception. It will get caught & handled upstream and will result in AIM auto-pause.
|
|
||||||
throw ex;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// did we find a data source processor that can process the data source
|
// did we find a data source processor that can process the data source
|
||||||
@ -2577,80 +2577,7 @@ public final class AutoIngestManager extends Observable implements PropertyChang
|
|||||||
jobLogger.logFileExportError();
|
jobLogger.logFileExportError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* A "callback" that collects the results of running a data source
|
|
||||||
* processor on a data source and unblocks the job processing thread
|
|
||||||
* when the data source processor finishes running in its own thread.
|
|
||||||
*/
|
|
||||||
@Immutable
|
|
||||||
class AddDataSourceCallback extends DataSourceProcessorCallback {
|
|
||||||
|
|
||||||
private final Case caseForJob;
|
|
||||||
private final DataSource dataSourceInfo;
|
|
||||||
private final UUID taskId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs a "callback" that collects the results of running a
|
|
||||||
* data source processor on a data source and unblocks the job
|
|
||||||
* processing thread when the data source processor finishes running
|
|
||||||
* in its own thread.
|
|
||||||
*
|
|
||||||
* @param caseForJob The case for the current job.
|
|
||||||
* @param dataSourceInfo The data source
|
|
||||||
* @param taskId The task id to associate with ingest job
|
|
||||||
* events.
|
|
||||||
*/
|
|
||||||
AddDataSourceCallback(Case caseForJob, DataSource dataSourceInfo, UUID taskId) {
|
|
||||||
this.caseForJob = caseForJob;
|
|
||||||
this.dataSourceInfo = dataSourceInfo;
|
|
||||||
this.taskId = taskId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called by the data source processor when it finishes running in
|
|
||||||
* its own thread.
|
|
||||||
*
|
|
||||||
* @param result The result code for the processing of
|
|
||||||
* the data source.
|
|
||||||
* @param errorMessages Any error messages generated during the
|
|
||||||
* processing of the data source.
|
|
||||||
* @param dataSourceContent The content produced by processing the
|
|
||||||
* data source.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void done(DataSourceProcessorCallback.DataSourceProcessorResult result, List<String> errorMessages, List<Content> dataSourceContent) {
|
|
||||||
if (!dataSourceContent.isEmpty()) {
|
|
||||||
caseForJob.notifyDataSourceAdded(dataSourceContent.get(0), taskId);
|
|
||||||
} else {
|
|
||||||
caseForJob.notifyFailedAddingDataSource(taskId);
|
|
||||||
}
|
|
||||||
dataSourceInfo.setDataSourceProcessorOutput(result, errorMessages, dataSourceContent);
|
|
||||||
dataSourceContent.addAll(dataSourceContent);
|
|
||||||
synchronized (ingestLock) {
|
|
||||||
ingestLock.notify();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called by the data source processor when it finishes running in
|
|
||||||
* its own thread, if that thread is the AWT (Abstract Window
|
|
||||||
* Toolkit) event dispatch thread (EDT).
|
|
||||||
*
|
|
||||||
* @param result The result code for the processing of
|
|
||||||
* the data source.
|
|
||||||
* @param errorMessages Any error messages generated during the
|
|
||||||
* processing of the data source.
|
|
||||||
* @param dataSourceContent The content produced by processing the
|
|
||||||
* data source.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void doneEDT(DataSourceProcessorCallback.DataSourceProcessorResult result, List<String> errorMessages, List<Content> dataSources) {
|
|
||||||
done(result, errorMessages, dataSources);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A data source processor progress monitor does nothing. There is
|
* A data source processor progress monitor does nothing. There is
|
||||||
* currently no mechanism for showing or recording data source processor
|
* currently no mechanism for showing or recording data source processor
|
||||||
@ -2990,49 +2917,7 @@ public final class AutoIngestManager extends Observable implements PropertyChang
|
|||||||
PARTIALLY_DELETED,
|
PARTIALLY_DELETED,
|
||||||
FULLY_DELETED
|
FULLY_DELETED
|
||||||
}
|
}
|
||||||
|
|
||||||
@ThreadSafe
|
|
||||||
private static final class DataSource {
|
|
||||||
|
|
||||||
private final String deviceId;
|
|
||||||
private final Path path;
|
|
||||||
private DataSourceProcessorResult resultCode;
|
|
||||||
private List<String> errorMessages;
|
|
||||||
private List<Content> content;
|
|
||||||
|
|
||||||
DataSource(String deviceId, Path path) {
|
|
||||||
this.deviceId = deviceId;
|
|
||||||
this.path = path;
|
|
||||||
}
|
|
||||||
|
|
||||||
String getDeviceId() {
|
|
||||||
return deviceId;
|
|
||||||
}
|
|
||||||
|
|
||||||
Path getPath() {
|
|
||||||
return this.path;
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized void setDataSourceProcessorOutput(DataSourceProcessorResult result, List<String> errorMessages, List<Content> content) {
|
|
||||||
this.resultCode = result;
|
|
||||||
this.errorMessages = new ArrayList<>(errorMessages);
|
|
||||||
this.content = new ArrayList<>(content);
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized DataSourceProcessorResult getResultDataSourceProcessorResultCode() {
|
|
||||||
return resultCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized List<String> getDataSourceProcessorErrorMessages() {
|
|
||||||
return new ArrayList<>(errorMessages);
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized List<Content> getContent() {
|
|
||||||
return new ArrayList<>(content);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static final class AutoIngestManagerException extends Exception {
|
static final class AutoIngestManagerException extends Exception {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
@ -265,7 +265,7 @@ public final class AutoIngestMonitor extends Observable implements PropertyChang
|
|||||||
}
|
}
|
||||||
} catch (InterruptedException ex) {
|
} catch (InterruptedException ex) {
|
||||||
LOGGER.log(Level.SEVERE, String.format("Unexpected interrupt while retrieving coordination service node data for '%s'", node), ex);
|
LOGGER.log(Level.SEVERE, String.format("Unexpected interrupt while retrieving coordination service node data for '%s'", node), ex);
|
||||||
} catch (AutoIngestJobNodeData.InvalidDataException ex) {
|
} catch (AutoIngestJobNodeData.InvalidDataException | AutoIngestJob.AutoIngestJobException ex) {
|
||||||
LOGGER.log(Level.SEVERE, String.format("Unable to use node data for '%s'", node), ex);
|
LOGGER.log(Level.SEVERE, String.format("Unable to use node data for '%s'", node), ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -247,3 +247,7 @@ AutoIngestDashboard.prioritizeJobButton.toolTipText=Move the selected job to the
|
|||||||
AutoIngestDashboard.prioritizeJobButton.text=Prioritize &Job
|
AutoIngestDashboard.prioritizeJobButton.text=Prioritize &Job
|
||||||
AutoIngestDashboard.prioritizeCaseButton.toolTipText=Move all images associated with a case to top of Pending queue.
|
AutoIngestDashboard.prioritizeCaseButton.toolTipText=Move all images associated with a case to top of Pending queue.
|
||||||
AutoIngestDashboard.prioritizeCaseButton.text=Prioritize &Case
|
AutoIngestDashboard.prioritizeCaseButton.text=Prioritize &Case
|
||||||
|
ArchiveFilePanel.pathLabel.text=Browse for an archive file:
|
||||||
|
ArchiveFilePanel.browseButton.text=Browse
|
||||||
|
ArchiveFilePanel.pathTextField.text=
|
||||||
|
ArchiveFilePanel.errorLabel.text=Error Label
|
@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2011-2017 Basis Technology Corp.
|
||||||
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.sleuthkit.autopsy.experimental.autoingest;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.annotation.concurrent.ThreadSafe;
|
||||||
|
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback.DataSourceProcessorResult;
|
||||||
|
import org.sleuthkit.datamodel.Content;
|
||||||
|
|
||||||
|
@ThreadSafe
|
||||||
|
class DataSource {
|
||||||
|
|
||||||
|
private final String deviceId;
|
||||||
|
private final Path path;
|
||||||
|
private DataSourceProcessorResult resultCode;
|
||||||
|
private List<String> errorMessages;
|
||||||
|
private List<Content> content;
|
||||||
|
|
||||||
|
DataSource(String deviceId, Path path) {
|
||||||
|
this.deviceId = deviceId;
|
||||||
|
this.path = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getDeviceId() {
|
||||||
|
return deviceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
Path getPath() {
|
||||||
|
return this.path;
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized void setDataSourceProcessorOutput(DataSourceProcessorResult result, List<String> errorMessages, List<Content> content) {
|
||||||
|
this.resultCode = result;
|
||||||
|
this.errorMessages = new ArrayList<>(errorMessages);
|
||||||
|
this.content = new ArrayList<>(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized DataSourceProcessorResult getResultDataSourceProcessorResultCode() {
|
||||||
|
return resultCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized List<String> getDataSourceProcessorErrorMessages() {
|
||||||
|
return new ArrayList<>(errorMessages);
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized List<Content> getContent() {
|
||||||
|
return new ArrayList<>(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2011-2017 Basis Technology Corp.
|
||||||
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.sleuthkit.autopsy.experimental.autoingest;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.openide.util.Lookup;
|
||||||
|
import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor;
|
||||||
|
import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A utility class to find Data Source Processors
|
||||||
|
*/
|
||||||
|
class DataSourceProcessorUtility {
|
||||||
|
|
||||||
|
private DataSourceProcessorUtility() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A utility method to find all Data Source Processors (DSP) that are able
|
||||||
|
* to process the input data source. Only the DSPs that implement
|
||||||
|
* AutoIngestDataSourceProcessor interface are used.
|
||||||
|
*
|
||||||
|
* @param dataSourcePath Full path to the data source
|
||||||
|
* @return Hash map of all DSPs that can process the data source along with
|
||||||
|
* their confidence score
|
||||||
|
* @throws
|
||||||
|
* org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException
|
||||||
|
*/
|
||||||
|
static Map<AutoIngestDataSourceProcessor, Integer> getDataSourceProcessor(Path dataSourcePath) throws AutoIngestDataSourceProcessorException {
|
||||||
|
|
||||||
|
// lookup all AutomatedIngestDataSourceProcessors
|
||||||
|
Collection<? extends AutoIngestDataSourceProcessor> processorCandidates = Lookup.getDefault().lookupAll(AutoIngestDataSourceProcessor.class);
|
||||||
|
|
||||||
|
Map<AutoIngestDataSourceProcessor, Integer> validDataSourceProcessorsMap = new HashMap<>();
|
||||||
|
for (AutoIngestDataSourceProcessor processor : processorCandidates) {
|
||||||
|
int confidence = processor.canProcess(dataSourcePath);
|
||||||
|
if (confidence > 0) {
|
||||||
|
validDataSourceProcessorsMap.put(processor, confidence);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return validDataSourceProcessorsMap;
|
||||||
|
}
|
||||||
|
}
|
@ -126,11 +126,7 @@
|
|||||||
</Group>
|
</Group>
|
||||||
<Group type="102" attributes="0">
|
<Group type="102" attributes="0">
|
||||||
<Group type="103" groupAlignment="0" attributes="0">
|
<Group type="103" groupAlignment="0" attributes="0">
|
||||||
<Group type="102" alignment="0" attributes="0">
|
<Component id="jLabelSelectMode" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||||
<Component id="jLabelSelectMode" min="-2" max="-2" attributes="0"/>
|
|
||||||
<EmptySpace max="-2" attributes="0"/>
|
|
||||||
<Component id="restartRequiredNodeLabel" min="-2" max="-2" attributes="0"/>
|
|
||||||
</Group>
|
|
||||||
<Component id="jRadioButtonReview" alignment="0" min="-2" max="-2" attributes="0"/>
|
<Component id="jRadioButtonReview" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||||
<Component id="jRadioButtonAutomated" alignment="0" min="-2" max="-2" attributes="0"/>
|
<Component id="jRadioButtonAutomated" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||||
</Group>
|
</Group>
|
||||||
@ -159,10 +155,7 @@
|
|||||||
<Group type="103" groupAlignment="0" attributes="0">
|
<Group type="103" groupAlignment="0" attributes="0">
|
||||||
<Group type="102" attributes="0">
|
<Group type="102" attributes="0">
|
||||||
<EmptySpace max="-2" attributes="0"/>
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
<Group type="103" groupAlignment="3" attributes="0">
|
<Component id="jLabelSelectMode" min="-2" max="-2" attributes="0"/>
|
||||||
<Component id="jLabelSelectMode" alignment="3" min="-2" max="-2" attributes="0"/>
|
|
||||||
<Component id="restartRequiredNodeLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
|
||||||
</Group>
|
|
||||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||||
<Component id="jRadioButtonAutomated" min="-2" max="-2" attributes="0"/>
|
<Component id="jRadioButtonAutomated" min="-2" max="-2" attributes="0"/>
|
||||||
<EmptySpace max="-2" attributes="0"/>
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
@ -203,16 +196,6 @@
|
|||||||
</Property>
|
</Property>
|
||||||
</Properties>
|
</Properties>
|
||||||
</Component>
|
</Component>
|
||||||
<Component class="javax.swing.JLabel" name="restartRequiredNodeLabel">
|
|
||||||
<Properties>
|
|
||||||
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
|
||||||
<Image iconType="3" name="/org/sleuthkit/autopsy/experimental/images/warning16.png"/>
|
|
||||||
</Property>
|
|
||||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
|
||||||
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AutoIngestSettingsPanel.restartRequiredNodeLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
|
||||||
</Property>
|
|
||||||
</Properties>
|
|
||||||
</Component>
|
|
||||||
<Component class="javax.swing.JRadioButton" name="jRadioButtonAutomated">
|
<Component class="javax.swing.JRadioButton" name="jRadioButtonAutomated">
|
||||||
<Properties>
|
<Properties>
|
||||||
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
|
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
|
||||||
|
@ -210,35 +210,16 @@ public class AutoIngestSettingsPanel extends javax.swing.JPanel {
|
|||||||
boolean needsRestart = false;
|
boolean needsRestart = false;
|
||||||
UserPreferences.SelectedMode storedMode = UserPreferences.getMode();
|
UserPreferences.SelectedMode storedMode = UserPreferences.getMode();
|
||||||
|
|
||||||
if (AutoIngestUserPreferences.getJoinAutoModeCluster() != cbJoinAutoIngestCluster.isSelected()) {
|
|
||||||
needsRestart = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
AutoIngestUserPreferences.setJoinAutoModeCluster(cbJoinAutoIngestCluster.isSelected());
|
AutoIngestUserPreferences.setJoinAutoModeCluster(cbJoinAutoIngestCluster.isSelected());
|
||||||
if (!cbJoinAutoIngestCluster.isSelected()) {
|
if (!cbJoinAutoIngestCluster.isSelected()) {
|
||||||
|
if(storedMode == UserPreferences.SelectedMode.AUTOINGEST) {
|
||||||
|
needsRestart = true;
|
||||||
|
}
|
||||||
|
|
||||||
UserPreferences.setMode(UserPreferences.SelectedMode.STANDALONE);
|
UserPreferences.setMode(UserPreferences.SelectedMode.STANDALONE);
|
||||||
//before return popup the message
|
|
||||||
if (needsRestart) {
|
|
||||||
SwingUtilities.invokeLater(() -> {
|
|
||||||
JOptionPane.showMessageDialog(null,
|
|
||||||
NbBundle.getMessage(AutoIngestSettingsPanel.class, "AutoIngestSettingsPanel.MustRestart"),
|
|
||||||
NbBundle.getMessage(AutoIngestSettingsPanel.class, "AutoIngestSettingsPanel.restartRequiredLabel.text"),
|
|
||||||
JOptionPane.WARNING_MESSAGE);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
else if (jRadioButtonAutomated.isSelected()) {
|
||||||
if (jRadioButtonAutomated.isSelected()) {
|
if (storedMode == UserPreferences.SelectedMode.REVIEW) {
|
||||||
if (storedMode != UserPreferences.SelectedMode.AUTOINGEST) {
|
|
||||||
needsRestart = true;
|
|
||||||
}
|
|
||||||
String thePath = AutoIngestUserPreferences.getAutoModeImageFolder();
|
|
||||||
if (thePath != null && 0 != inputPathTextField.getText().compareTo(thePath)) {
|
|
||||||
needsRestart = true;
|
|
||||||
}
|
|
||||||
thePath = AutoIngestUserPreferences.getAutoModeResultsFolder();
|
|
||||||
if (thePath != null && 0 != outputPathTextField.getText().compareTo(thePath)) {
|
|
||||||
needsRestart = true;
|
needsRestart = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -254,11 +235,7 @@ public class AutoIngestSettingsPanel extends javax.swing.JPanel {
|
|||||||
AutoIngestUserPreferences.setSharedConfigMaster(masterNodeCheckBox.isSelected());
|
AutoIngestUserPreferences.setSharedConfigMaster(masterNodeCheckBox.isSelected());
|
||||||
}
|
}
|
||||||
} else if (jRadioButtonReview.isSelected()) {
|
} else if (jRadioButtonReview.isSelected()) {
|
||||||
if (storedMode != UserPreferences.SelectedMode.REVIEW) {
|
if (storedMode == UserPreferences.SelectedMode.AUTOINGEST) {
|
||||||
needsRestart = true;
|
|
||||||
}
|
|
||||||
String thePath = AutoIngestUserPreferences.getAutoModeResultsFolder();
|
|
||||||
if (thePath != null && 0 != outputPathTextField.getText().compareTo(thePath)) {
|
|
||||||
needsRestart = true;
|
needsRestart = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -661,7 +638,6 @@ public class AutoIngestSettingsPanel extends javax.swing.JPanel {
|
|||||||
nodePanel = new javax.swing.JPanel();
|
nodePanel = new javax.swing.JPanel();
|
||||||
jPanelNodeType = new javax.swing.JPanel();
|
jPanelNodeType = new javax.swing.JPanel();
|
||||||
jLabelSelectMode = new javax.swing.JLabel();
|
jLabelSelectMode = new javax.swing.JLabel();
|
||||||
restartRequiredNodeLabel = new javax.swing.JLabel();
|
|
||||||
jRadioButtonAutomated = new javax.swing.JRadioButton();
|
jRadioButtonAutomated = new javax.swing.JRadioButton();
|
||||||
jRadioButtonReview = new javax.swing.JRadioButton();
|
jRadioButtonReview = new javax.swing.JRadioButton();
|
||||||
jLabelSelectInputFolder = new javax.swing.JLabel();
|
jLabelSelectInputFolder = new javax.swing.JLabel();
|
||||||
@ -703,9 +679,6 @@ public class AutoIngestSettingsPanel extends javax.swing.JPanel {
|
|||||||
|
|
||||||
org.openide.awt.Mnemonics.setLocalizedText(jLabelSelectMode, org.openide.util.NbBundle.getMessage(AutoIngestSettingsPanel.class, "AutoIngestSettingsPanel.jLabelSelectMode.text")); // NOI18N
|
org.openide.awt.Mnemonics.setLocalizedText(jLabelSelectMode, org.openide.util.NbBundle.getMessage(AutoIngestSettingsPanel.class, "AutoIngestSettingsPanel.jLabelSelectMode.text")); // NOI18N
|
||||||
|
|
||||||
restartRequiredNodeLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/experimental/images/warning16.png"))); // NOI18N
|
|
||||||
org.openide.awt.Mnemonics.setLocalizedText(restartRequiredNodeLabel, org.openide.util.NbBundle.getMessage(AutoIngestSettingsPanel.class, "AutoIngestSettingsPanel.restartRequiredNodeLabel.text")); // NOI18N
|
|
||||||
|
|
||||||
modeRadioButtons.add(jRadioButtonAutomated);
|
modeRadioButtons.add(jRadioButtonAutomated);
|
||||||
jRadioButtonAutomated.setSelected(true);
|
jRadioButtonAutomated.setSelected(true);
|
||||||
org.openide.awt.Mnemonics.setLocalizedText(jRadioButtonAutomated, org.openide.util.NbBundle.getMessage(AutoIngestSettingsPanel.class, "AutoIngestSettingsPanel.jRadioButtonAutomated.text")); // NOI18N
|
org.openide.awt.Mnemonics.setLocalizedText(jRadioButtonAutomated, org.openide.util.NbBundle.getMessage(AutoIngestSettingsPanel.class, "AutoIngestSettingsPanel.jRadioButtonAutomated.text")); // NOI18N
|
||||||
@ -777,10 +750,7 @@ public class AutoIngestSettingsPanel extends javax.swing.JPanel {
|
|||||||
.addComponent(browseOutputFolderButton, javax.swing.GroupLayout.Alignment.TRAILING)))
|
.addComponent(browseOutputFolderButton, javax.swing.GroupLayout.Alignment.TRAILING)))
|
||||||
.addGroup(jPanelNodeTypeLayout.createSequentialGroup()
|
.addGroup(jPanelNodeTypeLayout.createSequentialGroup()
|
||||||
.addGroup(jPanelNodeTypeLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
.addGroup(jPanelNodeTypeLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
.addGroup(jPanelNodeTypeLayout.createSequentialGroup()
|
.addComponent(jLabelSelectMode)
|
||||||
.addComponent(jLabelSelectMode)
|
|
||||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
|
||||||
.addComponent(restartRequiredNodeLabel))
|
|
||||||
.addComponent(jRadioButtonReview)
|
.addComponent(jRadioButtonReview)
|
||||||
.addComponent(jRadioButtonAutomated))
|
.addComponent(jRadioButtonAutomated))
|
||||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||||
@ -802,9 +772,7 @@ public class AutoIngestSettingsPanel extends javax.swing.JPanel {
|
|||||||
.addGroup(jPanelNodeTypeLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
.addGroup(jPanelNodeTypeLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
.addGroup(jPanelNodeTypeLayout.createSequentialGroup()
|
.addGroup(jPanelNodeTypeLayout.createSequentialGroup()
|
||||||
.addContainerGap()
|
.addContainerGap()
|
||||||
.addGroup(jPanelNodeTypeLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
.addComponent(jLabelSelectMode)
|
||||||
.addComponent(jLabelSelectMode)
|
|
||||||
.addComponent(restartRequiredNodeLabel))
|
|
||||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||||
.addComponent(jRadioButtonAutomated)
|
.addComponent(jRadioButtonAutomated)
|
||||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
@ -1407,7 +1375,6 @@ public class AutoIngestSettingsPanel extends javax.swing.JPanel {
|
|||||||
jRadioButtonAutomated.setEnabled(enabled);
|
jRadioButtonAutomated.setEnabled(enabled);
|
||||||
jRadioButtonReview.setEnabled(enabled);
|
jRadioButtonReview.setEnabled(enabled);
|
||||||
outputPathTextField.setEnabled(enabled);
|
outputPathTextField.setEnabled(enabled);
|
||||||
restartRequiredNodeLabel.setEnabled(enabled);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||||
@ -1442,7 +1409,6 @@ public class AutoIngestSettingsPanel extends javax.swing.JPanel {
|
|||||||
private javax.swing.JScrollPane nodeScrollPane;
|
private javax.swing.JScrollPane nodeScrollPane;
|
||||||
private javax.swing.JTextField outputPathTextField;
|
private javax.swing.JTextField outputPathTextField;
|
||||||
private javax.swing.JProgressBar pbTaskInProgress;
|
private javax.swing.JProgressBar pbTaskInProgress;
|
||||||
private javax.swing.JLabel restartRequiredNodeLabel;
|
|
||||||
private javax.swing.JCheckBox sharedConfigCheckbox;
|
private javax.swing.JCheckBox sharedConfigCheckbox;
|
||||||
private javax.swing.JTextField sharedSettingsErrorTextField;
|
private javax.swing.JTextField sharedSettingsErrorTextField;
|
||||||
private javax.swing.JTextField sharedSettingsTextField;
|
private javax.swing.JTextField sharedSettingsTextField;
|
||||||
|
@ -59,7 +59,7 @@ public final class AutoIngestUserPreferences {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get "Join Automated Ingest Cluster" setting from persistent storage.
|
* Get "Join auto ingest cluster" setting from persistent storage.
|
||||||
*
|
*
|
||||||
* @return SelectedMode Selected setting.
|
* @return SelectedMode Selected setting.
|
||||||
*/
|
*/
|
||||||
@ -71,7 +71,7 @@ public final class AutoIngestUserPreferences {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set "Join Automated Ingest Cluster" setting to persistent storage.
|
* Set "Join auto ingest cluster" setting to persistent storage.
|
||||||
*
|
*
|
||||||
* @param join boolean value of whether to join auto ingest cluster or not
|
* @param join boolean value of whether to join auto ingest cluster or not
|
||||||
*/
|
*/
|
||||||
|
@ -25,7 +25,7 @@ AutoIngestSettingsPanel.AdvancedAutoIngestSettingsPanel.Title=Advanced Settings
|
|||||||
AutoIngestSettingsPanel.browseGlobalSettingsButton.text=Browse
|
AutoIngestSettingsPanel.browseGlobalSettingsButton.text=Browse
|
||||||
AutoIngestSettingsPanel.browseSharedSettingsButton.text=Browse
|
AutoIngestSettingsPanel.browseSharedSettingsButton.text=Browse
|
||||||
AutoIngestSettingsPanel.CannotAccess=Cannot access
|
AutoIngestSettingsPanel.CannotAccess=Cannot access
|
||||||
AutoIngestSettingsPanel.cbJoinAutoIngestCluster.text=Join Automated Ingest Cluster
|
AutoIngestSettingsPanel.cbJoinAutoIngestCluster.text=Join auto ingest cluster
|
||||||
AutoIngestSettingsPanel.CheckPermissions=Check permissions.
|
AutoIngestSettingsPanel.CheckPermissions=Check permissions.
|
||||||
AutoIngestSettingsPanel.configButtonErrorTextField.text=configButtonErrorTextField
|
AutoIngestSettingsPanel.configButtonErrorTextField.text=configButtonErrorTextField
|
||||||
AutoIngestSettingsPanel.downloadButton.text=Download Config
|
AutoIngestSettingsPanel.downloadButton.text=Download Config
|
||||||
@ -89,7 +89,6 @@ OptionsDialog.jLabel1.text=jLabel1
|
|||||||
StartupWindow.AutoIngestMode=Automated Ingest Node
|
StartupWindow.AutoIngestMode=Automated Ingest Node
|
||||||
StartupWindow.CaseImportMode=Single User Case Import
|
StartupWindow.CaseImportMode=Single User Case Import
|
||||||
StartupWindow.CopyAndImportMode=Utilities
|
StartupWindow.CopyAndImportMode=Utilities
|
||||||
StartupWindow.ReviewMode=Cases
|
|
||||||
StartupWindow.title.text=Welcome
|
StartupWindow.title.text=Welcome
|
||||||
AdvancedAutoIngestSettingsPanel.lbInputScanIntervalMinutes.text=minutes
|
AdvancedAutoIngestSettingsPanel.lbInputScanIntervalMinutes.text=minutes
|
||||||
AdvancedAutoIngestSettingsPanel.lbTimeoutHours.text=hour(s)
|
AdvancedAutoIngestSettingsPanel.lbTimeoutHours.text=hour(s)
|
||||||
@ -133,10 +132,9 @@ AutoIngestSettingsPanel.inputPathTextField.toolTipText=Input folder for automate
|
|||||||
AutoIngestSettingsPanel.inputPathTextField.text=
|
AutoIngestSettingsPanel.inputPathTextField.text=
|
||||||
AutoIngestSettingsPanel.jLabelSelectInputFolder.text=Select shared images folder:
|
AutoIngestSettingsPanel.jLabelSelectInputFolder.text=Select shared images folder:
|
||||||
AutoIngestSettingsPanel.jRadioButtonReview.toolTipText=Review cases created in automated processing mode
|
AutoIngestSettingsPanel.jRadioButtonReview.toolTipText=Review cases created in automated processing mode
|
||||||
AutoIngestSettingsPanel.jRadioButtonReview.text=Examiner Node
|
AutoIngestSettingsPanel.jRadioButtonReview.text=Examiner node
|
||||||
AutoIngestSettingsPanel.jRadioButtonAutomated.toolTipText=Automatically detect new data sources and create cases.
|
AutoIngestSettingsPanel.jRadioButtonAutomated.toolTipText=Automatically detect new data sources and create cases.
|
||||||
AutoIngestSettingsPanel.jRadioButtonAutomated.text=Auto Ingest Node
|
AutoIngestSettingsPanel.jRadioButtonAutomated.text=Auto ingest node (application restart required)
|
||||||
AutoIngestSettingsPanel.restartRequiredNodeLabel.text=Application restart required
|
|
||||||
AutoIngestSettingsPanel.jLabelSelectMode.text=Select mode:
|
AutoIngestSettingsPanel.jLabelSelectMode.text=Select mode:
|
||||||
AutoIngestSettingsPanel.jPanelIngestSettings.border.title=Automated Ingest Settings
|
AutoIngestSettingsPanel.jPanelIngestSettings.border.title=Automated Ingest Settings
|
||||||
AutoIngestSettingsPanel.bnLogging.text=Node Status Logging
|
AutoIngestSettingsPanel.bnLogging.text=Node Status Logging
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.experimental.configuration;
|
package org.sleuthkit.autopsy.experimental.configuration;
|
||||||
|
|
||||||
import java.awt.Cursor;
|
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Toolkit;
|
import java.awt.Toolkit;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
@ -36,7 +35,6 @@ import org.sleuthkit.autopsy.casemodule.StartupWindowInterface;
|
|||||||
import org.sleuthkit.autopsy.core.UserPreferences;
|
import org.sleuthkit.autopsy.core.UserPreferences;
|
||||||
import org.sleuthkit.autopsy.coreutils.NetworkUtils;
|
import org.sleuthkit.autopsy.coreutils.NetworkUtils;
|
||||||
import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestControlPanel;
|
import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestControlPanel;
|
||||||
import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestCasePanel;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The default implementation of the Autopsy startup window
|
* The default implementation of the Autopsy startup window
|
||||||
@ -48,7 +46,6 @@ public final class StartupWindow extends JDialog implements StartupWindowInterfa
|
|||||||
private static Dimension DIMENSIONS = new Dimension(750, 400);
|
private static Dimension DIMENSIONS = new Dimension(750, 400);
|
||||||
private static CueBannerPanel welcomeWindow;
|
private static CueBannerPanel welcomeWindow;
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
private AutoIngestCasePanel caseManagementPanel = null;
|
|
||||||
private static final String LOCAL_HOST_NAME = NetworkUtils.getLocalHostName();
|
private static final String LOCAL_HOST_NAME = NetworkUtils.getLocalHostName();
|
||||||
|
|
||||||
public StartupWindow() {
|
public StartupWindow() {
|
||||||
@ -78,12 +75,6 @@ public final class StartupWindow extends JDialog implements StartupWindowInterfa
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void open() {
|
public void open() {
|
||||||
|
|
||||||
if (caseManagementPanel != null) {
|
|
||||||
caseManagementPanel.updateView();
|
|
||||||
caseManagementPanel.setCursor(Cursor.getDefaultCursor());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (welcomeWindow != null) {
|
if (welcomeWindow != null) {
|
||||||
welcomeWindow.refresh();
|
welcomeWindow.refresh();
|
||||||
}
|
}
|
||||||
@ -104,38 +95,27 @@ public final class StartupWindow extends JDialog implements StartupWindowInterfa
|
|||||||
* user.
|
* user.
|
||||||
*/
|
*/
|
||||||
private void addPanelForMode() {
|
private void addPanelForMode() {
|
||||||
UserPreferences.SelectedMode mode = UserPreferences.getMode();
|
if(UserPreferences.getMode() == UserPreferences.SelectedMode.AUTOINGEST) {
|
||||||
|
this.setTitle(NbBundle.getMessage(StartupWindow.class, "StartupWindow.AutoIngestMode") + " (" + LOCAL_HOST_NAME + ")");
|
||||||
switch (mode) {
|
setIconImage(ImageUtilities.loadImage("org/sleuthkit/autopsy/experimental/images/frame.gif", false)); //NON-NLS
|
||||||
case AUTOINGEST:
|
this.addWindowListener(new WindowAdapter() {
|
||||||
this.setTitle(NbBundle.getMessage(StartupWindow.class, "StartupWindow.AutoIngestMode") + " (" + LOCAL_HOST_NAME + ")");
|
@Override
|
||||||
setIconImage(ImageUtilities.loadImage("org/sleuthkit/autopsy/experimental/images/frame.gif", false)); //NON-NLS
|
public void windowClosing(WindowEvent e) {
|
||||||
this.addWindowListener(new WindowAdapter() {
|
AutoIngestControlPanel.getInstance().shutdown();
|
||||||
@Override
|
}
|
||||||
public void windowClosing(WindowEvent e) {
|
});
|
||||||
AutoIngestControlPanel.getInstance().shutdown();
|
setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
|
||||||
}
|
add(AutoIngestControlPanel.getInstance());
|
||||||
});
|
} else {
|
||||||
setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
|
welcomeWindow = new CueBannerPanel();
|
||||||
add(AutoIngestControlPanel.getInstance());
|
// add the command to close the window to the button on the Volume Detail Panel
|
||||||
break;
|
welcomeWindow.setCloseButtonActionListener(new ActionListener() {
|
||||||
case REVIEW:
|
@Override
|
||||||
this.setTitle(NbBundle.getMessage(StartupWindow.class, "StartupWindow.ReviewMode") + " (" + LOCAL_HOST_NAME + ")");
|
public void actionPerformed(ActionEvent e) {
|
||||||
caseManagementPanel = new AutoIngestCasePanel(this);
|
close();
|
||||||
setIconImage(ImageUtilities.loadImage("org/sleuthkit/autopsy/experimental/images/frame.gif", false)); //NON-NLS
|
}
|
||||||
add(caseManagementPanel);
|
});
|
||||||
break;
|
add(welcomeWindow);
|
||||||
default:
|
|
||||||
welcomeWindow = new CueBannerPanel();
|
|
||||||
// add the command to close the window to the button on the Volume Detail Panel
|
|
||||||
welcomeWindow.setCloseButtonActionListener(new ActionListener() {
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
add(welcomeWindow);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -138,12 +138,14 @@ final class GlobalListSettingsPanel extends javax.swing.JPanel implements Option
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addPropertyChangeListener(PropertyChangeListener l) {
|
public void addPropertyChangeListener(PropertyChangeListener l) {
|
||||||
|
super.addPropertyChangeListener(l);
|
||||||
listsManagementPanel.addPropertyChangeListener(l);
|
listsManagementPanel.addPropertyChangeListener(l);
|
||||||
editListPanel.addPropertyChangeListener(l);
|
editListPanel.addPropertyChangeListener(l);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removePropertyChangeListener(PropertyChangeListener l) {
|
public void removePropertyChangeListener(PropertyChangeListener l) {
|
||||||
|
super.removePropertyChangeListener(l);
|
||||||
listsManagementPanel.removePropertyChangeListener(l);
|
listsManagementPanel.removePropertyChangeListener(l);
|
||||||
editListPanel.removePropertyChangeListener(l);
|
editListPanel.removePropertyChangeListener(l);
|
||||||
}
|
}
|
||||||
|
@ -104,13 +104,13 @@ class HighlightedText implements IndexedText {
|
|||||||
/**
|
/**
|
||||||
* This constructor is used when keyword hits are accessed from the ad-hoc
|
* This constructor is used when keyword hits are accessed from the ad-hoc
|
||||||
* search results. In that case we have the entire QueryResults object and
|
* search results. In that case we have the entire QueryResults object and
|
||||||
* need to arrange the paging.
|
need to arrange the paging.
|
||||||
*
|
*
|
||||||
* @param objectId The objectID of the content whose text will be
|
* @param objectId The objectID of the content whose text will be
|
||||||
* highlighted.
|
* highlighted.
|
||||||
* @param QueryResults The QueryResults for the ad-hoc search from whose
|
* @param QueryResults The QueryResults for the ad-hoc search from whose
|
||||||
* results a selection was made leading to this
|
results a selection was made leading to this
|
||||||
* HighlightedText.
|
HighlightedText.
|
||||||
*/
|
*/
|
||||||
HighlightedText(long objectId, QueryResults hits) {
|
HighlightedText(long objectId, QueryResults hits) {
|
||||||
this.objectId = objectId;
|
this.objectId = objectId;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2011-2016 Basis Technology Corp.
|
* Copyright 2011-2017 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -28,11 +28,12 @@ import org.sleuthkit.autopsy.ingest.IngestModuleGlobalSettingsPanel;
|
|||||||
*/
|
*/
|
||||||
final class KeywordSearchGlobalSettingsPanel extends IngestModuleGlobalSettingsPanel implements OptionsPanel {
|
final class KeywordSearchGlobalSettingsPanel extends IngestModuleGlobalSettingsPanel implements OptionsPanel {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
private GlobalListSettingsPanel listsPanel;
|
private GlobalListSettingsPanel listsPanel;
|
||||||
private KeywordSearchGlobalLanguageSettingsPanel languagesPanel;
|
private KeywordSearchGlobalLanguageSettingsPanel languagesPanel;
|
||||||
private KeywordSearchGlobalSearchSettingsPanel generalPanel;
|
private KeywordSearchGlobalSearchSettingsPanel generalPanel;
|
||||||
|
|
||||||
public KeywordSearchGlobalSettingsPanel() {
|
KeywordSearchGlobalSettingsPanel() {
|
||||||
initComponents();
|
initComponents();
|
||||||
customizeComponents();
|
customizeComponents();
|
||||||
}
|
}
|
||||||
@ -53,6 +54,7 @@ final class KeywordSearchGlobalSettingsPanel extends IngestModuleGlobalSettingsP
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addPropertyChangeListener(PropertyChangeListener l) {
|
public void addPropertyChangeListener(PropertyChangeListener l) {
|
||||||
|
super.addPropertyChangeListener(l);
|
||||||
listsPanel.addPropertyChangeListener(l);
|
listsPanel.addPropertyChangeListener(l);
|
||||||
languagesPanel.addPropertyChangeListener(l);
|
languagesPanel.addPropertyChangeListener(l);
|
||||||
generalPanel.addPropertyChangeListener(l);
|
generalPanel.addPropertyChangeListener(l);
|
||||||
@ -60,6 +62,7 @@ final class KeywordSearchGlobalSettingsPanel extends IngestModuleGlobalSettingsP
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removePropertyChangeListener(PropertyChangeListener l) {
|
public void removePropertyChangeListener(PropertyChangeListener l) {
|
||||||
|
super.removePropertyChangeListener(l);
|
||||||
listsPanel.removePropertyChangeListener(l);
|
listsPanel.removePropertyChangeListener(l);
|
||||||
languagesPanel.removePropertyChangeListener(l);
|
languagesPanel.removePropertyChangeListener(l);
|
||||||
generalPanel.removePropertyChangeListener(l);
|
generalPanel.removePropertyChangeListener(l);
|
||||||
|
@ -101,20 +101,20 @@ interface KeywordSearchQuery {
|
|||||||
String getEscapedQueryString();
|
String getEscapedQueryString();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts the keyword hits for a given search term into artifacts.
|
* Posts a keyword hit artifact to the blackboard for a given keyword hit.
|
||||||
*
|
*
|
||||||
* @param content The Content object associated with the hit.
|
* @param content The text source object for the hit.
|
||||||
* @param foundKeyword The keyword that was found by the search, this may be
|
* @param foundKeyword The keyword that was found by the search, this may be
|
||||||
* different than the Keyword that was searched if, for
|
* different than the Keyword that was searched if, for
|
||||||
* example, it was a RegexQuery.
|
* example, it was a RegexQuery.
|
||||||
* @param hit The keyword hit.
|
* @param hit The keyword hit.
|
||||||
* @param snippet The document snippet that contains the hit.
|
* @param snippet A snippet from the text that contains the hit.
|
||||||
* @param listName The name of the keyword list that contained the
|
* @param listName The name of the keyword list that contained the
|
||||||
* keyword for which the hit was found.
|
* keyword for which the hit was found.
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* @return The newly created artifact or Null if there was a problem
|
* @return The newly created artifact or null if there was a problem
|
||||||
* creating it.
|
* creating it.
|
||||||
*/
|
*/
|
||||||
BlackboardArtifact writeSingleFileHitsToBlackBoard(Content content, Keyword foundKeyword, KeywordHit hit, String snippet, String listName);
|
BlackboardArtifact postKeywordHitToBlackboard(Content content, Keyword foundKeyword, KeywordHit hit, String snippet, String listName);
|
||||||
}
|
}
|
||||||
|
@ -331,7 +331,7 @@ class KeywordSearchResultFactory extends ChildFactory<KeyValueQueryContent> {
|
|||||||
final String queryDisp = queryStr.length() > QUERY_DISPLAY_LEN ? queryStr.substring(0, QUERY_DISPLAY_LEN - 1) + " ..." : queryStr;
|
final String queryDisp = queryStr.length() > QUERY_DISPLAY_LEN ? queryStr.substring(0, QUERY_DISPLAY_LEN - 1) + " ..." : queryStr;
|
||||||
try {
|
try {
|
||||||
progress = ProgressHandle.createHandle(NbBundle.getMessage(this.getClass(), "KeywordSearchResultFactory.progress.saving", queryDisp), () -> BlackboardResultWriter.this.cancel(true));
|
progress = ProgressHandle.createHandle(NbBundle.getMessage(this.getClass(), "KeywordSearchResultFactory.progress.saving", queryDisp), () -> BlackboardResultWriter.this.cancel(true));
|
||||||
hits.writeAllHitsToBlackBoard(progress, null, this, false);
|
hits.process(progress, null, this, false);
|
||||||
} finally {
|
} finally {
|
||||||
finalizeWorker();
|
finalizeWorker();
|
||||||
}
|
}
|
||||||
|
@ -52,8 +52,8 @@ class LuceneQuery implements KeywordSearchQuery {
|
|||||||
private static final Logger logger = Logger.getLogger(LuceneQuery.class.getName());
|
private static final Logger logger = Logger.getLogger(LuceneQuery.class.getName());
|
||||||
private String keywordStringEscaped;
|
private String keywordStringEscaped;
|
||||||
private boolean isEscaped;
|
private boolean isEscaped;
|
||||||
private final Keyword originalKeyword ;
|
private final Keyword originalKeyword;
|
||||||
private final KeywordList keywordList ;
|
private final KeywordList keywordList;
|
||||||
private final List<KeywordQueryFilter> filters = new ArrayList<>();
|
private final List<KeywordQueryFilter> filters = new ArrayList<>();
|
||||||
private String field = null;
|
private String field = null;
|
||||||
private static final int MAX_RESULTS_PER_CURSOR_MARK = 512;
|
private static final int MAX_RESULTS_PER_CURSOR_MARK = 512;
|
||||||
@ -70,7 +70,7 @@ class LuceneQuery implements KeywordSearchQuery {
|
|||||||
LuceneQuery(KeywordList keywordList, Keyword keyword) {
|
LuceneQuery(KeywordList keywordList, Keyword keyword) {
|
||||||
this.keywordList = keywordList;
|
this.keywordList = keywordList;
|
||||||
this.originalKeyword = keyword;
|
this.originalKeyword = keyword;
|
||||||
this.keywordStringEscaped = this.originalKeyword.getSearchTerm();
|
this.keywordStringEscaped = this.originalKeyword.getSearchTerm();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -191,8 +191,24 @@ class LuceneQuery implements KeywordSearchQuery {
|
|||||||
return StringUtils.isNotBlank(originalKeyword.getSearchTerm());
|
return StringUtils.isNotBlank(originalKeyword.getSearchTerm());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Posts a keyword hit artifact to the blackboard for a given keyword hit.
|
||||||
|
*
|
||||||
|
* @param content The text source object for the hit.
|
||||||
|
* @param foundKeyword The keyword that was found by the search, this may be
|
||||||
|
* different than the Keyword that was searched if, for
|
||||||
|
* example, it was a RegexQuery.
|
||||||
|
* @param hit The keyword hit.
|
||||||
|
* @param snippet A snippet from the text that contains the hit.
|
||||||
|
* @param listName The name of the keyword list that contained the
|
||||||
|
* keyword for which the hit was found.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @return The newly created artifact or null if there was a problem
|
||||||
|
* creating it.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public BlackboardArtifact writeSingleFileHitsToBlackBoard(Content content, Keyword foundKeyword, KeywordHit hit, String snippet, String listName) {
|
public BlackboardArtifact postKeywordHitToBlackboard(Content content, Keyword foundKeyword, KeywordHit hit, String snippet, String listName) {
|
||||||
final String MODULE_NAME = KeywordSearchModuleFactory.getModuleName();
|
final String MODULE_NAME = KeywordSearchModuleFactory.getModuleName();
|
||||||
|
|
||||||
Collection<BlackboardAttribute> attributes = new ArrayList<>();
|
Collection<BlackboardAttribute> attributes = new ArrayList<>();
|
||||||
@ -225,11 +241,9 @@ class LuceneQuery implements KeywordSearchQuery {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
hit.getArtifactID().ifPresent(artifactID
|
hit.getArtifactID().ifPresent(artifactID
|
||||||
-> attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, artifactID))
|
-> attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, artifactID))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
bba.addAttributes(attributes); //write out to bb
|
bba.addAttributes(attributes); //write out to bb
|
||||||
@ -398,10 +412,10 @@ class LuceneQuery implements KeywordSearchQuery {
|
|||||||
return EscapeUtil.unEscapeHtml(contentHighlights.get(0)).trim();
|
return EscapeUtil.unEscapeHtml(contentHighlights.get(0)).trim();
|
||||||
}
|
}
|
||||||
} catch (NoOpenCoreException ex) {
|
} catch (NoOpenCoreException ex) {
|
||||||
logger.log(Level.SEVERE, "Error executing Lucene Solr Query: " + query +". Solr doc id " + solrObjectId + ", chunkID " + chunkID , ex); //NON-NLS
|
logger.log(Level.SEVERE, "Error executing Lucene Solr Query: " + query + ". Solr doc id " + solrObjectId + ", chunkID " + chunkID, ex); //NON-NLS
|
||||||
throw ex;
|
throw ex;
|
||||||
} catch (KeywordSearchModuleException ex) {
|
} catch (KeywordSearchModuleException ex) {
|
||||||
logger.log(Level.SEVERE, "Error executing Lucene Solr Query: " + query +". Solr doc id " + solrObjectId + ", chunkID " + chunkID , ex); //NON-NLS
|
logger.log(Level.SEVERE, "Error executing Lucene Solr Query: " + query + ". Solr doc id " + solrObjectId + ", chunkID " + chunkID, ex); //NON-NLS
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,195 +45,274 @@ import org.sleuthkit.datamodel.SleuthkitCase;
|
|||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores the results from running a Solr query (which could contain multiple
|
* Stores and processes the results of a keyword search query. Processing
|
||||||
* keywords).
|
* includes posting keyword hit artifacts to the blackboard, sending messages
|
||||||
*
|
* about the search hits to the ingest inbox, and publishing an event to notify
|
||||||
|
* subscribers of the blackboard posts.
|
||||||
*/
|
*/
|
||||||
class QueryResults {
|
class QueryResults {
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(QueryResults.class.getName());
|
private static final Logger logger = Logger.getLogger(QueryResults.class.getName());
|
||||||
private static final String MODULE_NAME = KeywordSearchModuleFactory.getModuleName();
|
private static final String MODULE_NAME = KeywordSearchModuleFactory.getModuleName();
|
||||||
/**
|
private final KeywordSearchQuery query;
|
||||||
* The query that generated the results.
|
|
||||||
*/
|
|
||||||
private final KeywordSearchQuery keywordSearchQuery;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A map of keywords to keyword hits.
|
|
||||||
*/
|
|
||||||
private final Map<Keyword, List<KeywordHit>> results = new HashMap<>();
|
private final Map<Keyword, List<KeywordHit>> results = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a object that stores and processes the results of a keyword
|
||||||
|
* search query. Processing includes posting keyword hit artifacts to the
|
||||||
|
* blackboard, sending messages about the search hits to the ingest inbox,
|
||||||
|
* and publishing an event to notify subscribers of the blackboard posts.
|
||||||
|
*
|
||||||
|
* The KeywordSearchQuery is used to do the blackboard posts.
|
||||||
|
*
|
||||||
|
* @param query The query.
|
||||||
|
*/
|
||||||
QueryResults(KeywordSearchQuery query) {
|
QueryResults(KeywordSearchQuery query) {
|
||||||
this.keywordSearchQuery = query;
|
this.query = query;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the keyword search query that generated the results stored in this
|
||||||
|
* object.
|
||||||
|
*
|
||||||
|
* @return The query.
|
||||||
|
*/
|
||||||
|
KeywordSearchQuery getQuery() {
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the keyword hits for a keyword to the hits that are stored in this
|
||||||
|
* object. All calls to this method MUST be completed before calling the
|
||||||
|
* process method.
|
||||||
|
*
|
||||||
|
* @param keyword The keyword,
|
||||||
|
* @param hits The hits.
|
||||||
|
*/
|
||||||
void addResult(Keyword keyword, List<KeywordHit> hits) {
|
void addResult(Keyword keyword, List<KeywordHit> hits) {
|
||||||
results.put(keyword, hits);
|
results.put(keyword, hits);
|
||||||
}
|
}
|
||||||
|
|
||||||
KeywordSearchQuery getQuery() {
|
/**
|
||||||
return keywordSearchQuery;
|
* Gets the keyword hits stored in this object for a given keyword.
|
||||||
}
|
*
|
||||||
|
* @param keyword The keyword.
|
||||||
|
*
|
||||||
|
* @return The keyword hits.
|
||||||
|
*/
|
||||||
List<KeywordHit> getResults(Keyword keyword) {
|
List<KeywordHit> getResults(Keyword keyword) {
|
||||||
return results.get(keyword);
|
return results.get(keyword);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the set of unique keywords for which keyword hits have been stored
|
||||||
|
* in this object.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
Set<Keyword> getKeywords() {
|
Set<Keyword> getKeywords() {
|
||||||
return results.keySet();
|
return results.keySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes the keyword hits encapsulated in this query result to the
|
* Processes the keyword hits stored in this object by osting keyword hit
|
||||||
* blackboard. Makes one artifact per keyword per searched object (file or
|
* artifacts to the blackboard, sending messages about the search hits to
|
||||||
* artifact), i.e., if a keyword is found several times in the object, only
|
* the ingest inbox, and publishing an event to notify subscribers of the
|
||||||
* one artifact is created.
|
* blackboard posts.
|
||||||
*
|
*
|
||||||
* @param progress Can be null.
|
* Makes one artifact per keyword per searched text source object (file or
|
||||||
* @param subProgress Can be null.
|
* artifact), i.e., if a keyword is found several times in the text
|
||||||
* @param worker The Swing worker that is writing the hits, needed to
|
* extracted from the source object, only one artifact is created.
|
||||||
* support cancellation.
|
*
|
||||||
* @param notifyInbox Whether or not write a message to the ingest messages
|
* This method ASSUMES that the processing is being done using a SwingWorker
|
||||||
* inbox.
|
* that should be checked for task cancellation.
|
||||||
|
*
|
||||||
|
* All calls to the addResult method MUST be completed before calling this
|
||||||
|
* method.
|
||||||
|
*
|
||||||
|
* @param progress A progress indicator that reports the number of
|
||||||
|
* keywords processed. Can be null.
|
||||||
|
* @param subProgress A progress contributor that reports the keyword
|
||||||
|
* currently being processed. Can be null.
|
||||||
|
* @param worker The SwingWorker that is being used to do the
|
||||||
|
* processing, will be checked for task cancellation
|
||||||
|
* before processing each keyword.
|
||||||
|
* @param notifyInbox Whether or not to write a message to the ingest
|
||||||
|
* messages inbox if there is a keyword hit in the text
|
||||||
|
* exrtacted from the text source object.
|
||||||
*
|
*
|
||||||
* @return The artifacts that were created.
|
|
||||||
*/
|
*/
|
||||||
Collection<BlackboardArtifact> writeAllHitsToBlackBoard(ProgressHandle progress, ProgressContributor subProgress, SwingWorker<?, ?> worker, boolean notifyInbox) {
|
void process(ProgressHandle progress, ProgressContributor subProgress, SwingWorker<?, ?> worker, boolean notifyInbox) {
|
||||||
final Collection<BlackboardArtifact> newArtifacts = new ArrayList<>();
|
/*
|
||||||
if (progress != null) {
|
* Initialize the progress indicator to the number of keywords that will
|
||||||
|
* be processed.
|
||||||
|
*/
|
||||||
|
if (null != progress) {
|
||||||
progress.start(getKeywords().size());
|
progress.start(getKeywords().size());
|
||||||
}
|
}
|
||||||
int unitProgress = 0;
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Process the keyword hits for each keyword.
|
||||||
|
*/
|
||||||
|
int keywordsProcessed = 0;
|
||||||
|
final Collection<BlackboardArtifact> hitArtifacts = new ArrayList<>();
|
||||||
for (final Keyword keyword : getKeywords()) {
|
for (final Keyword keyword : getKeywords()) {
|
||||||
|
/*
|
||||||
|
* Cancellation check.
|
||||||
|
*/
|
||||||
if (worker.isCancelled()) {
|
if (worker.isCancelled()) {
|
||||||
logger.log(Level.INFO, "Cancel detected, bailing before new keyword processed: {0}", keyword.getSearchTerm()); //NON-NLS
|
logger.log(Level.INFO, "Processing cancelled, exiting before processing search term {0}", keyword.getSearchTerm()); //NON-NLS
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update progress object(s), if any
|
/*
|
||||||
|
* Update the progress indicator and the show the current keyword
|
||||||
|
* via the progress contributor.
|
||||||
|
*/
|
||||||
if (progress != null) {
|
if (progress != null) {
|
||||||
progress.progress(keyword.toString(), unitProgress);
|
progress.progress(keyword.toString(), keywordsProcessed);
|
||||||
}
|
}
|
||||||
if (subProgress != null) {
|
if (subProgress != null) {
|
||||||
String hitDisplayStr = keyword.getSearchTerm();
|
String hitDisplayStr = keyword.getSearchTerm();
|
||||||
if (hitDisplayStr.length() > 50) {
|
if (hitDisplayStr.length() > 50) {
|
||||||
hitDisplayStr = hitDisplayStr.substring(0, 49) + "...";
|
hitDisplayStr = hitDisplayStr.substring(0, 49) + "...";
|
||||||
}
|
}
|
||||||
subProgress.progress(keywordSearchQuery.getKeywordList().getName() + ": " + hitDisplayStr, unitProgress);
|
subProgress.progress(query.getKeywordList().getName() + ": " + hitDisplayStr, keywordsProcessed);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (KeywordHit hit : getOneHitPerObject(keyword)) {
|
/*
|
||||||
String termString = keyword.getSearchTerm();
|
* Reduce the hits for this keyword to one hit per text source
|
||||||
|
* object so that only one hit artifact is generated per text source
|
||||||
|
* object, no matter how many times the keyword was actually found.
|
||||||
|
*/
|
||||||
|
for (KeywordHit hit : getOneHitPerTextSourceObject(keyword)) {
|
||||||
|
/*
|
||||||
|
* Get a snippet (preview) for the hit. Regex queries always
|
||||||
|
* have snippets made from the content_str pulled back from Solr
|
||||||
|
* for executing the search. Other types of queries may or may
|
||||||
|
* not have snippets yet.
|
||||||
|
*/
|
||||||
String snippet = hit.getSnippet();
|
String snippet = hit.getSnippet();
|
||||||
if (StringUtils.isBlank(snippet)) {
|
if (StringUtils.isBlank(snippet)) {
|
||||||
final String snippetQuery = KeywordSearchUtil.escapeLuceneQuery(termString);
|
final String snippetQuery = KeywordSearchUtil.escapeLuceneQuery(keyword.getSearchTerm());
|
||||||
try {
|
try {
|
||||||
/*
|
snippet = LuceneQuery.querySnippet(snippetQuery, hit.getSolrObjectId(), hit.getChunkId(), !query.isLiteral(), true);
|
||||||
* this doesn't work for regex queries... But that is
|
|
||||||
* okay because regex queries always have snippets made
|
|
||||||
* from the content_str field we pull back from Solr
|
|
||||||
*/
|
|
||||||
snippet = LuceneQuery.querySnippet(snippetQuery, hit.getSolrObjectId(), hit.getChunkId(), !keywordSearchQuery.isLiteral(), true);
|
|
||||||
} catch (NoOpenCoreException e) {
|
} catch (NoOpenCoreException e) {
|
||||||
logger.log(Level.WARNING, "Error querying snippet: " + snippetQuery, e); //NON-NLS
|
logger.log(Level.SEVERE, "Solr core closed while executing snippet query " + snippetQuery, e); //NON-NLS
|
||||||
//no reason to continue
|
break; // Stop processing.
|
||||||
break;
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.log(Level.WARNING, "Error querying snippet: " + snippetQuery, e); //NON-NLS
|
logger.log(Level.SEVERE, "Error executing snippet query " + snippetQuery, e); //NON-NLS
|
||||||
continue;
|
continue; // Try processing the next hit.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the content (file or artifact) that is the text source
|
||||||
|
* for the hit.
|
||||||
|
*/
|
||||||
Content content = null;
|
Content content = null;
|
||||||
try {
|
try {
|
||||||
SleuthkitCase tskCase = Case.getCurrentCase().getSleuthkitCase();
|
SleuthkitCase tskCase = Case.getCurrentCase().getSleuthkitCase();
|
||||||
content = tskCase.getContentById(hit.getContentID());
|
content = tskCase.getContentById(hit.getContentID());
|
||||||
} catch (TskCoreException | IllegalStateException tskCoreException) {
|
} catch (TskCoreException | IllegalStateException tskCoreException) {
|
||||||
logger.log(Level.SEVERE, "Error adding artifact for keyword hit to blackboard", tskCoreException); //NON-NLS
|
logger.log(Level.SEVERE, "Failed to get text source object for ", tskCoreException); //NON-NLS
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
BlackboardArtifact writeResult = keywordSearchQuery.writeSingleFileHitsToBlackBoard(content, keyword, hit, snippet, keywordSearchQuery.getKeywordList().getName());
|
|
||||||
if (writeResult != null) {
|
/*
|
||||||
newArtifacts.add(writeResult);
|
* Post an artifact for the hit to the blackboard.
|
||||||
|
*/
|
||||||
|
BlackboardArtifact artifact = query.postKeywordHitToBlackboard(content, keyword, hit, snippet, query.getKeywordList().getName());
|
||||||
|
if (null == artifact) {
|
||||||
|
logger.log(Level.SEVERE, "Error posting keyword hit artifact for keyword {0} in {1} to the blackboard", new Object[]{keyword.toString(), content}); //NON-NLS
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Send an ingest inbox message for the hit.
|
||||||
|
*/
|
||||||
|
if (null != artifact) {
|
||||||
|
hitArtifacts.add(artifact);
|
||||||
if (notifyInbox) {
|
if (notifyInbox) {
|
||||||
try {
|
try {
|
||||||
writeSingleFileInboxMessage(writeResult, content);
|
writeSingleFileInboxMessage(artifact, content);
|
||||||
} catch (TskCoreException ex) {
|
} catch (TskCoreException ex) {
|
||||||
logger.log(Level.WARNING, "Error posting message to Ingest Inbox", ex); //NON-NLS
|
logger.log(Level.SEVERE, "Error sending message to ingest messages inbox", ex); //NON-NLS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
logger.log(Level.WARNING, "BB artifact for keyword hit not written, file: {0}, hit: {1}", new Object[]{content, keyword.toString()}); //NON-NLS
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
++unitProgress;
|
|
||||||
|
++keywordsProcessed;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update artifact browser
|
/*
|
||||||
if (!newArtifacts.isEmpty()) {
|
* Publish an event to notify subscribers of the blackboard posts. The
|
||||||
newArtifacts.stream()
|
* artifacts are grouped by type, since they may contain both
|
||||||
//group artifacts by type
|
* TSK_KEYWORD_HIT artifacts and TSK_ACCOUNT artifacts (for credit card
|
||||||
|
* account number hits).
|
||||||
|
*/
|
||||||
|
if (!hitArtifacts.isEmpty()) {
|
||||||
|
hitArtifacts.stream()
|
||||||
|
// Group artifacts by type
|
||||||
.collect(Collectors.groupingBy(BlackboardArtifact::getArtifactTypeID))
|
.collect(Collectors.groupingBy(BlackboardArtifact::getArtifactTypeID))
|
||||||
//for each type send an event
|
// For each type send an event
|
||||||
.forEach((typeID, artifacts)
|
.forEach((typeID, artifacts)
|
||||||
-> IngestServices.getInstance().fireModuleDataEvent(new ModuleDataEvent(MODULE_NAME, BlackboardArtifact.ARTIFACT_TYPE.fromID(typeID), artifacts)));
|
-> IngestServices.getInstance().fireModuleDataEvent(new ModuleDataEvent(MODULE_NAME, BlackboardArtifact.ARTIFACT_TYPE.fromID(typeID), artifacts)));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return newArtifacts;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the first hit of the keyword.
|
* Reduce the hits for a given keyword to one hit per text source object so
|
||||||
|
* that only one hit artifact is generated per text source object, no matter
|
||||||
|
* how many times the keyword was actually found.
|
||||||
*
|
*
|
||||||
* @param keyword
|
* @param keyword The keyword.
|
||||||
*
|
*
|
||||||
* @return Collection<KeywordHit> containing KeywordHits with lowest
|
* @return Collection<KeywordHit> The reduced set of keyword hits.
|
||||||
* SolrObjectID-ChunkID pairs.
|
|
||||||
*/
|
*/
|
||||||
private Collection<KeywordHit> getOneHitPerObject(Keyword keyword) {
|
private Collection<KeywordHit> getOneHitPerTextSourceObject(Keyword keyword) {
|
||||||
HashMap<Long, KeywordHit> hits = new HashMap<>();
|
/*
|
||||||
|
* For each Solr document (chunk) for a text source object, return only
|
||||||
// create a list of KeywordHits. KeywordHits with lowest chunkID is added the the list.
|
* a single keyword hit from the first chunk of text (the one with the
|
||||||
for (KeywordHit hit : getResults(keyword)) {
|
* lowest chunk id).
|
||||||
|
*/
|
||||||
|
HashMap< Long, KeywordHit> hits = new HashMap<>();
|
||||||
|
getResults(keyword).forEach((hit) -> {
|
||||||
if (!hits.containsKey(hit.getSolrObjectId())) {
|
if (!hits.containsKey(hit.getSolrObjectId())) {
|
||||||
hits.put(hit.getSolrObjectId(), hit);
|
hits.put(hit.getSolrObjectId(), hit);
|
||||||
} else if (hit.getChunkId() < hits.get(hit.getSolrObjectId()).getChunkId()) {
|
} else if (hit.getChunkId() < hits.get(hit.getSolrObjectId()).getChunkId()) {
|
||||||
hits.put(hit.getSolrObjectId(), hit);
|
hits.put(hit.getSolrObjectId(), hit);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
return hits.values();
|
return hits.values();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate and post an ingest inbox message for the given keyword in the
|
* Send an ingest inbox message indicating that there was a keyword hit in
|
||||||
* given content.
|
* the given text source object.
|
||||||
*
|
*
|
||||||
* @param artifact The keyword hit artifact.
|
* @param artifact The keyword hit artifact for the hit.
|
||||||
* @param hitContent The content that the hit is in.
|
* @param hitContent The text source object.
|
||||||
*
|
*
|
||||||
* @throws TskCoreException If there is a problem generating or posting the
|
* @throws TskCoreException If there is a problem generating or send the
|
||||||
* inbox message.
|
* inbox message.
|
||||||
*/
|
*/
|
||||||
private void writeSingleFileInboxMessage(BlackboardArtifact artifact, Content hitContent) throws TskCoreException {
|
private void writeSingleFileInboxMessage(BlackboardArtifact artifact, Content hitContent) throws TskCoreException {
|
||||||
StringBuilder subjectSb = new StringBuilder();
|
StringBuilder subjectSb = new StringBuilder(1024);
|
||||||
StringBuilder detailsSb = new StringBuilder();
|
if (!query.isLiteral()) {
|
||||||
|
|
||||||
if (!keywordSearchQuery.isLiteral()) {
|
|
||||||
subjectSb.append(NbBundle.getMessage(this.getClass(), "KeywordSearchIngestModule.regExpHitLbl"));
|
subjectSb.append(NbBundle.getMessage(this.getClass(), "KeywordSearchIngestModule.regExpHitLbl"));
|
||||||
} else {
|
} else {
|
||||||
subjectSb.append(NbBundle.getMessage(this.getClass(), "KeywordSearchIngestModule.kwHitLbl"));
|
subjectSb.append(NbBundle.getMessage(this.getClass(), "KeywordSearchIngestModule.kwHitLbl"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StringBuilder detailsSb = new StringBuilder(1024);
|
||||||
String uniqueKey = null;
|
String uniqueKey = null;
|
||||||
BlackboardAttribute attr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD));
|
BlackboardAttribute attr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD));
|
||||||
if (attr != null) {
|
if (attr != null) {
|
||||||
final String keyword = attr.getValueString();
|
final String keyword = attr.getValueString();
|
||||||
subjectSb.append(keyword);
|
subjectSb.append(keyword);
|
||||||
uniqueKey = keyword.toLowerCase();
|
uniqueKey = keyword.toLowerCase();
|
||||||
//details
|
|
||||||
detailsSb.append("<table border='0' cellpadding='4' width='280'>"); //NON-NLS
|
detailsSb.append("<table border='0' cellpadding='4' width='280'>"); //NON-NLS
|
||||||
//hit
|
|
||||||
detailsSb.append("<tr>"); //NON-NLS
|
detailsSb.append("<tr>"); //NON-NLS
|
||||||
detailsSb.append(NbBundle.getMessage(this.getClass(), "KeywordSearchIngestModule.kwHitThLbl"));
|
detailsSb.append(NbBundle.getMessage(this.getClass(), "KeywordSearchIngestModule.kwHitThLbl"));
|
||||||
detailsSb.append("<td>").append(EscapeUtil.escapeHtml(keyword)).append("</td>"); //NON-NLS
|
detailsSb.append("<td>").append(EscapeUtil.escapeHtml(keyword)).append("</td>"); //NON-NLS
|
||||||
@ -270,7 +349,7 @@ class QueryResults {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//regex
|
//regex
|
||||||
if (!keywordSearchQuery.isLiteral()) {
|
if (!query.isLiteral()) {
|
||||||
attr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP));
|
attr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP));
|
||||||
if (attr != null) {
|
if (attr != null) {
|
||||||
detailsSb.append("<tr>"); //NON-NLS
|
detailsSb.append("<tr>"); //NON-NLS
|
||||||
@ -279,7 +358,6 @@ class QueryResults {
|
|||||||
detailsSb.append("</tr>"); //NON-NLS
|
detailsSb.append("</tr>"); //NON-NLS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
detailsSb.append("</table>"); //NON-NLS
|
detailsSb.append("</table>"); //NON-NLS
|
||||||
|
|
||||||
IngestServices.getInstance().postMessage(IngestMessage.createDataMessage(MODULE_NAME, subjectSb.toString(), detailsSb.toString(), uniqueKey, artifact));
|
IngestServices.getInstance().postMessage(IngestMessage.createDataMessage(MODULE_NAME, subjectSb.toString(), detailsSb.toString(), uniqueKey, artifact));
|
||||||
|
@ -403,8 +403,24 @@ final class RegexQuery implements KeywordSearchQuery {
|
|||||||
return escapedQuery;
|
return escapedQuery;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Posts a keyword hit artifact to the blackboard for a given keyword hit.
|
||||||
|
*
|
||||||
|
* @param content The text source object for the hit.
|
||||||
|
* @param foundKeyword The keyword that was found by the search, this may be
|
||||||
|
* different than the Keyword that was searched if, for
|
||||||
|
* example, it was a RegexQuery.
|
||||||
|
* @param hit The keyword hit.
|
||||||
|
* @param snippet A snippet from the text that contains the hit.
|
||||||
|
* @param listName The name of the keyword list that contained the
|
||||||
|
* keyword for which the hit was found.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @return The newly created artifact or null if there was a problem
|
||||||
|
* creating it.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public BlackboardArtifact writeSingleFileHitsToBlackBoard(Content content, Keyword foundKeyword, KeywordHit hit, String snippet, String listName) {
|
public BlackboardArtifact postKeywordHitToBlackboard(Content content, Keyword foundKeyword, KeywordHit hit, String snippet, String listName) {
|
||||||
final String MODULE_NAME = KeywordSearchModuleFactory.getModuleName();
|
final String MODULE_NAME = KeywordSearchModuleFactory.getModuleName();
|
||||||
|
|
||||||
if (content == null) {
|
if (content == null) {
|
||||||
@ -413,96 +429,30 @@ final class RegexQuery implements KeywordSearchQuery {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create either a "plain vanilla" keyword hit artifact with keyword and
|
* Credit Card number hits are handled differently
|
||||||
* regex attributes, or a credit card account artifact with attributes
|
*/
|
||||||
* parsed from from the snippet for the hit and looked up based on the
|
if (originalKeyword.getArtifactAttributeType() == ATTRIBUTE_TYPE.TSK_CARD_NUMBER) {
|
||||||
* parsed bank identifcation number.
|
createCCNAccount(content, foundKeyword, hit, snippet, listName);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create a "plain vanilla" keyword hit artifact with keyword and
|
||||||
|
* regex attributes
|
||||||
*/
|
*/
|
||||||
BlackboardArtifact newArtifact;
|
BlackboardArtifact newArtifact;
|
||||||
Collection<BlackboardAttribute> attributes = new ArrayList<>();
|
Collection<BlackboardAttribute> attributes = new ArrayList<>();
|
||||||
if (originalKeyword.getArtifactAttributeType() != ATTRIBUTE_TYPE.TSK_CARD_NUMBER) {
|
|
||||||
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD, MODULE_NAME, foundKeyword.getSearchTerm()));
|
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD, MODULE_NAME, foundKeyword.getSearchTerm()));
|
||||||
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP, MODULE_NAME, getQueryString()));
|
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP, MODULE_NAME, getQueryString()));
|
||||||
try {
|
|
||||||
newArtifact = content.newArtifact(ARTIFACT_TYPE.TSK_KEYWORD_HIT);
|
try {
|
||||||
} catch (TskCoreException ex) {
|
newArtifact = content.newArtifact(ARTIFACT_TYPE.TSK_KEYWORD_HIT);
|
||||||
LOGGER.log(Level.SEVERE, "Error adding artifact for keyword hit to blackboard", ex); //NON-NLS
|
} catch (TskCoreException ex) {
|
||||||
return null;
|
LOGGER.log(Level.SEVERE, "Error adding artifact for keyword hit to blackboard", ex); //NON-NLS
|
||||||
}
|
return null;
|
||||||
} else {
|
|
||||||
/*
|
|
||||||
* Parse the credit card account attributes from the snippet for the
|
|
||||||
* hit.
|
|
||||||
*/
|
|
||||||
Map<BlackboardAttribute.Type, BlackboardAttribute> parsedTrackAttributeMap = new HashMap<>();
|
|
||||||
Matcher matcher = TermsComponentQuery.CREDIT_CARD_TRACK1_PATTERN.matcher(hit.getSnippet());
|
|
||||||
if (matcher.find()) {
|
|
||||||
parseTrack1Data(parsedTrackAttributeMap, matcher);
|
|
||||||
}
|
|
||||||
matcher = CREDIT_CARD_TRACK2_PATTERN.matcher(hit.getSnippet());
|
|
||||||
if (matcher.find()) {
|
|
||||||
parseTrack2Data(parsedTrackAttributeMap, matcher);
|
|
||||||
}
|
|
||||||
final BlackboardAttribute ccnAttribute = parsedTrackAttributeMap.get(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_CARD_NUMBER));
|
|
||||||
if (ccnAttribute == null || StringUtils.isBlank(ccnAttribute.getValueString())) {
|
|
||||||
if (hit.isArtifactHit()) {
|
|
||||||
LOGGER.log(Level.SEVERE, String.format("Failed to parse credit card account number for artifact keyword hit: term = %s, snippet = '%s', artifact id = %d", foundKeyword.getSearchTerm(), hit.getSnippet(), hit.getArtifactID().get())); //NON-NLS
|
|
||||||
} else {
|
|
||||||
LOGGER.log(Level.SEVERE, String.format("Failed to parse credit card account number for content keyword hit: term = %s, snippet = '%s', object id = %d", foundKeyword.getSearchTerm(), hit.getSnippet(), hit.getContentID())); //NON-NLS
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
attributes.addAll(parsedTrackAttributeMap.values());
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Look up the bank name, scheme, etc. attributes for the bank
|
|
||||||
* indentification number (BIN).
|
|
||||||
*/
|
|
||||||
final int bin = Integer.parseInt(ccnAttribute.getValueString().substring(0, 8));
|
|
||||||
CreditCards.BankIdentificationNumber binInfo = CreditCards.getBINInfo(bin);
|
|
||||||
if (binInfo != null) {
|
|
||||||
binInfo.getScheme().ifPresent(scheme
|
|
||||||
-> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_CARD_SCHEME, MODULE_NAME, scheme)));
|
|
||||||
binInfo.getCardType().ifPresent(cardType
|
|
||||||
-> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_CARD_TYPE, MODULE_NAME, cardType)));
|
|
||||||
binInfo.getBrand().ifPresent(brand
|
|
||||||
-> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_BRAND_NAME, MODULE_NAME, brand)));
|
|
||||||
binInfo.getBankName().ifPresent(bankName
|
|
||||||
-> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_BANK_NAME, MODULE_NAME, bankName)));
|
|
||||||
binInfo.getBankPhoneNumber().ifPresent(phoneNumber
|
|
||||||
-> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PHONE_NUMBER, MODULE_NAME, phoneNumber)));
|
|
||||||
binInfo.getBankURL().ifPresent(url
|
|
||||||
-> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL, MODULE_NAME, url)));
|
|
||||||
binInfo.getCountry().ifPresent(country
|
|
||||||
-> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COUNTRY, MODULE_NAME, country)));
|
|
||||||
binInfo.getBankCity().ifPresent(city
|
|
||||||
-> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_CITY, MODULE_NAME, city)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If the hit is from unused or unallocated space, record the Solr
|
|
||||||
* document id to support showing just the chunk that contained the
|
|
||||||
* hit.
|
|
||||||
*/
|
|
||||||
if (content instanceof AbstractFile) {
|
|
||||||
AbstractFile file = (AbstractFile) content;
|
|
||||||
if (file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS
|
|
||||||
|| file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS) {
|
|
||||||
attributes.add(new BlackboardAttribute(KEYWORD_SEARCH_DOCUMENT_ID, MODULE_NAME, hit.getSolrDocumentId()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Create an account instance.
|
|
||||||
*/
|
|
||||||
try {
|
|
||||||
AccountInstance ccAccountInstance = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().createAccountInstance(Account.Type.CREDIT_CARD, ccnAttribute.getValueString() , MODULE_NAME, content);
|
|
||||||
newArtifact = Case.getCurrentCase().getSleuthkitCase().getBlackboardArtifact(ccAccountInstance.getArtifactId());
|
|
||||||
} catch (TskCoreException ex) {
|
|
||||||
LOGGER.log(Level.SEVERE, "Error adding artifact for account to blackboard", ex); //NON-NLS
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (StringUtils.isNotBlank(listName)) {
|
if (StringUtils.isNotBlank(listName)) {
|
||||||
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, listName));
|
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, listName));
|
||||||
@ -526,6 +476,107 @@ final class RegexQuery implements KeywordSearchQuery {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void createCCNAccount(Content content, Keyword foundKeyword, KeywordHit hit, String snippet, String listName) {
|
||||||
|
|
||||||
|
final String MODULE_NAME = KeywordSearchModuleFactory.getModuleName();
|
||||||
|
|
||||||
|
if (originalKeyword.getArtifactAttributeType() != ATTRIBUTE_TYPE.TSK_CARD_NUMBER) {
|
||||||
|
LOGGER.log(Level.SEVERE, "Keyword hit is not a credit card number"); //NON-NLS
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Create a credit card account with attributes
|
||||||
|
* parsed from the snippet for the hit and looked up based on the
|
||||||
|
* parsed bank identifcation number.
|
||||||
|
*/
|
||||||
|
Collection<BlackboardAttribute> attributes = new ArrayList<>();
|
||||||
|
|
||||||
|
Map<BlackboardAttribute.Type, BlackboardAttribute> parsedTrackAttributeMap = new HashMap<>();
|
||||||
|
Matcher matcher = TermsComponentQuery.CREDIT_CARD_TRACK1_PATTERN.matcher(hit.getSnippet());
|
||||||
|
if (matcher.find()) {
|
||||||
|
parseTrack1Data(parsedTrackAttributeMap, matcher);
|
||||||
|
}
|
||||||
|
matcher = CREDIT_CARD_TRACK2_PATTERN.matcher(hit.getSnippet());
|
||||||
|
if (matcher.find()) {
|
||||||
|
parseTrack2Data(parsedTrackAttributeMap, matcher);
|
||||||
|
}
|
||||||
|
final BlackboardAttribute ccnAttribute = parsedTrackAttributeMap.get(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_CARD_NUMBER));
|
||||||
|
if (ccnAttribute == null || StringUtils.isBlank(ccnAttribute.getValueString())) {
|
||||||
|
if (hit.isArtifactHit()) {
|
||||||
|
LOGGER.log(Level.SEVERE, String.format("Failed to parse credit card account number for artifact keyword hit: term = %s, snippet = '%s', artifact id = %d", foundKeyword.getSearchTerm(), hit.getSnippet(), hit.getArtifactID().get())); //NON-NLS
|
||||||
|
} else {
|
||||||
|
LOGGER.log(Level.SEVERE, String.format("Failed to parse credit card account number for content keyword hit: term = %s, snippet = '%s', object id = %d", foundKeyword.getSearchTerm(), hit.getSnippet(), hit.getContentID())); //NON-NLS
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
attributes.addAll(parsedTrackAttributeMap.values());
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Look up the bank name, scheme, etc. attributes for the bank
|
||||||
|
* indentification number (BIN).
|
||||||
|
*/
|
||||||
|
final int bin = Integer.parseInt(ccnAttribute.getValueString().substring(0, 8));
|
||||||
|
CreditCards.BankIdentificationNumber binInfo = CreditCards.getBINInfo(bin);
|
||||||
|
if (binInfo != null) {
|
||||||
|
binInfo.getScheme().ifPresent(scheme
|
||||||
|
-> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_CARD_SCHEME, MODULE_NAME, scheme)));
|
||||||
|
binInfo.getCardType().ifPresent(cardType
|
||||||
|
-> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_CARD_TYPE, MODULE_NAME, cardType)));
|
||||||
|
binInfo.getBrand().ifPresent(brand
|
||||||
|
-> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_BRAND_NAME, MODULE_NAME, brand)));
|
||||||
|
binInfo.getBankName().ifPresent(bankName
|
||||||
|
-> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_BANK_NAME, MODULE_NAME, bankName)));
|
||||||
|
binInfo.getBankPhoneNumber().ifPresent(phoneNumber
|
||||||
|
-> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PHONE_NUMBER, MODULE_NAME, phoneNumber)));
|
||||||
|
binInfo.getBankURL().ifPresent(url
|
||||||
|
-> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL, MODULE_NAME, url)));
|
||||||
|
binInfo.getCountry().ifPresent(country
|
||||||
|
-> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COUNTRY, MODULE_NAME, country)));
|
||||||
|
binInfo.getBankCity().ifPresent(city
|
||||||
|
-> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_CITY, MODULE_NAME, city)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the hit is from unused or unallocated space, record the Solr
|
||||||
|
* document id to support showing just the chunk that contained the
|
||||||
|
* hit.
|
||||||
|
*/
|
||||||
|
if (content instanceof AbstractFile) {
|
||||||
|
AbstractFile file = (AbstractFile) content;
|
||||||
|
if (file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS
|
||||||
|
|| file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS) {
|
||||||
|
attributes.add(new BlackboardAttribute(KEYWORD_SEARCH_DOCUMENT_ID, MODULE_NAME, hit.getSolrDocumentId()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.isNotBlank(listName)) {
|
||||||
|
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, listName));
|
||||||
|
}
|
||||||
|
if (snippet != null) {
|
||||||
|
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_PREVIEW, MODULE_NAME, snippet));
|
||||||
|
}
|
||||||
|
|
||||||
|
hit.getArtifactID().ifPresent(artifactID
|
||||||
|
-> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, artifactID))
|
||||||
|
);
|
||||||
|
|
||||||
|
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE, MODULE_NAME, KeywordSearch.QueryType.REGEX.ordinal()));
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create an account instance.
|
||||||
|
*/
|
||||||
|
try {
|
||||||
|
AccountInstance ccAccountInstance = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().createAccountInstance(Account.Type.CREDIT_CARD, ccnAttribute.getValueString() , MODULE_NAME, content);
|
||||||
|
|
||||||
|
ccAccountInstance.addAttributes(attributes);
|
||||||
|
|
||||||
|
} catch (TskCoreException ex) {
|
||||||
|
LOGGER.log(Level.SEVERE, "Error creating CCN account instance", ex); //NON-NLS
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Parses the track 2 data from the snippet for a credit card account number
|
* Parses the track 2 data from the snippet for a credit card account number
|
||||||
* hit and turns them into artifact attributes.
|
* hit and turns them into artifact attributes.
|
||||||
|
@ -492,7 +492,7 @@ public final class SearchRunner {
|
|||||||
subProgresses[keywordsSearched].progress(keywordList.getName() + ": " + queryDisplayStr, unitProgress);
|
subProgresses[keywordsSearched].progress(keywordList.getName() + ": " + queryDisplayStr, unitProgress);
|
||||||
|
|
||||||
// Create blackboard artifacts
|
// Create blackboard artifacts
|
||||||
newResults.writeAllHitsToBlackBoard(null, subProgresses[keywordsSearched], this, keywordList.getIngestMessages());
|
newResults.process(null, subProgresses[keywordsSearched], this, keywordList.getIngestMessages());
|
||||||
|
|
||||||
} //if has results
|
} //if has results
|
||||||
|
|
||||||
|
@ -81,8 +81,8 @@ final class TermsComponentQuery implements KeywordSearchQuery {
|
|||||||
* digit is 2 through 6
|
* digit is 2 through 6
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
static final Pattern CREDIT_CARD_NUM_PATTERN =
|
static final Pattern CREDIT_CARD_NUM_PATTERN
|
||||||
Pattern.compile("(?<ccn>[2-6]([ -]?[0-9]){11,18})");
|
= Pattern.compile("(?<ccn>[2-6]([ -]?[0-9]){11,18})");
|
||||||
static final Pattern CREDIT_CARD_TRACK1_PATTERN = Pattern.compile(
|
static final Pattern CREDIT_CARD_TRACK1_PATTERN = Pattern.compile(
|
||||||
/*
|
/*
|
||||||
* Track 1 is alphanumeric.
|
* Track 1 is alphanumeric.
|
||||||
@ -126,7 +126,6 @@ final class TermsComponentQuery implements KeywordSearchQuery {
|
|||||||
+ "?)?)?)?)?)?"); //close nested optional groups //NON-NLS
|
+ "?)?)?)?)?)?"); //close nested optional groups //NON-NLS
|
||||||
static final BlackboardAttribute.Type KEYWORD_SEARCH_DOCUMENT_ID = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_DOCUMENT_ID);
|
static final BlackboardAttribute.Type KEYWORD_SEARCH_DOCUMENT_ID = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_DOCUMENT_ID);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs an object that implements a regex query that will be performed
|
* Constructs an object that implements a regex query that will be performed
|
||||||
* as a two step operation. In the first step, the Solr terms component is
|
* as a two step operation. In the first step, the Solr terms component is
|
||||||
@ -327,101 +326,51 @@ final class TermsComponentQuery implements KeywordSearchQuery {
|
|||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Posts a keyword hit artifact to the blackboard for a given keyword hit.
|
||||||
|
*
|
||||||
|
* @param content The text source object for the hit.
|
||||||
|
* @param foundKeyword The keyword that was found by the search, this may be
|
||||||
|
* different than the Keyword that was searched if, for
|
||||||
|
* example, it was a RegexQuery.
|
||||||
|
* @param hit The keyword hit.
|
||||||
|
* @param snippet A snippet from the text that contains the hit.
|
||||||
|
* @param listName The name of the keyword list that contained the
|
||||||
|
* keyword for which the hit was found.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @return The newly created artifact or null if there was a problem
|
||||||
|
* creating it.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public BlackboardArtifact writeSingleFileHitsToBlackBoard(Content content, Keyword foundKeyword, KeywordHit hit, String snippet, String listName) {
|
public BlackboardArtifact postKeywordHitToBlackboard(Content content, Keyword foundKeyword, KeywordHit hit, String snippet, String listName) {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create either a "plain vanilla" keyword hit artifact with keyword and
|
* CCN hits are handled specially
|
||||||
* regex attributes, or a credit card account artifact with attributes
|
*/
|
||||||
* parsed from from the snippet for the hit and looked up based on the
|
if (originalKeyword.getArtifactAttributeType() == ATTRIBUTE_TYPE.TSK_CARD_NUMBER) {
|
||||||
* parsed bank identifcation number.
|
createCCNAccount(content, hit, snippet, listName);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create a "plain vanilla" keyword hit artifact with keyword and
|
||||||
|
* regex attributes,
|
||||||
*/
|
*/
|
||||||
BlackboardArtifact newArtifact;
|
BlackboardArtifact newArtifact;
|
||||||
Collection<BlackboardAttribute> attributes = new ArrayList<>();
|
Collection<BlackboardAttribute> attributes = new ArrayList<>();
|
||||||
if (originalKeyword.getArtifactAttributeType() != ATTRIBUTE_TYPE.TSK_CARD_NUMBER) {
|
|
||||||
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD, MODULE_NAME, foundKeyword.getSearchTerm()));
|
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD, MODULE_NAME, foundKeyword.getSearchTerm()));
|
||||||
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP, MODULE_NAME, originalKeyword.getSearchTerm()));
|
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP, MODULE_NAME, originalKeyword.getSearchTerm()));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
newArtifact = content.newArtifact(ARTIFACT_TYPE.TSK_KEYWORD_HIT);
|
newArtifact = content.newArtifact(ARTIFACT_TYPE.TSK_KEYWORD_HIT);
|
||||||
|
|
||||||
} catch (TskCoreException ex) {
|
} catch (TskCoreException ex) {
|
||||||
LOGGER.log(Level.SEVERE, "Error adding artifact for keyword hit to blackboard", ex); //NON-NLS
|
LOGGER.log(Level.SEVERE, "Error adding artifact for keyword hit to blackboard", ex); //NON-NLS
|
||||||
return null;
|
return null;
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/*
|
|
||||||
* Parse the credit card account attributes from the snippet for the
|
|
||||||
* hit.
|
|
||||||
*/
|
|
||||||
Map<BlackboardAttribute.Type, BlackboardAttribute> parsedTrackAttributeMap = new HashMap<>();
|
|
||||||
Matcher matcher = CREDIT_CARD_TRACK1_PATTERN.matcher(hit.getSnippet());
|
|
||||||
if (matcher.find()) {
|
|
||||||
parseTrack1Data(parsedTrackAttributeMap, matcher);
|
|
||||||
}
|
|
||||||
matcher = CREDIT_CARD_TRACK2_PATTERN.matcher(hit.getSnippet());
|
|
||||||
if (matcher.find()) {
|
|
||||||
parseTrack2Data(parsedTrackAttributeMap, matcher);
|
|
||||||
}
|
|
||||||
final BlackboardAttribute ccnAttribute = parsedTrackAttributeMap.get(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_CARD_NUMBER));
|
|
||||||
if (ccnAttribute == null || StringUtils.isBlank(ccnAttribute.getValueString())) {
|
|
||||||
if (hit.isArtifactHit()) {
|
|
||||||
LOGGER.log(Level.SEVERE, String.format("Failed to parse credit card account number for artifact keyword hit: term = %s, snippet = '%s', artifact id = %d", searchTerm, hit.getSnippet(), hit.getArtifactID().get())); //NON-NLS
|
|
||||||
} else {
|
|
||||||
LOGGER.log(Level.SEVERE, String.format("Failed to parse credit card account number for content keyword hit: term = %s, snippet = '%s', object id = %d", searchTerm, hit.getSnippet(), hit.getContentID())); //NON-NLS
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
attributes.addAll(parsedTrackAttributeMap.values());
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Look up the bank name, scheme, etc. attributes for the bank
|
|
||||||
* indentification number (BIN).
|
|
||||||
*/
|
|
||||||
final int bin = Integer.parseInt(ccnAttribute.getValueString().substring(0, 8));
|
|
||||||
CreditCards.BankIdentificationNumber binInfo = CreditCards.getBINInfo(bin);
|
|
||||||
if (binInfo != null) {
|
|
||||||
binInfo.getScheme().ifPresent(scheme
|
|
||||||
-> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_CARD_SCHEME, MODULE_NAME, scheme)));
|
|
||||||
binInfo.getCardType().ifPresent(cardType
|
|
||||||
-> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_CARD_TYPE, MODULE_NAME, cardType)));
|
|
||||||
binInfo.getBrand().ifPresent(brand
|
|
||||||
-> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_BRAND_NAME, MODULE_NAME, brand)));
|
|
||||||
binInfo.getBankName().ifPresent(bankName
|
|
||||||
-> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_BANK_NAME, MODULE_NAME, bankName)));
|
|
||||||
binInfo.getBankPhoneNumber().ifPresent(phoneNumber
|
|
||||||
-> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PHONE_NUMBER, MODULE_NAME, phoneNumber)));
|
|
||||||
binInfo.getBankURL().ifPresent(url
|
|
||||||
-> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL, MODULE_NAME, url)));
|
|
||||||
binInfo.getCountry().ifPresent(country
|
|
||||||
-> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COUNTRY, MODULE_NAME, country)));
|
|
||||||
binInfo.getBankCity().ifPresent(city
|
|
||||||
-> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_CITY, MODULE_NAME, city)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If the hit is from unused or unallocated space, record the Solr
|
|
||||||
* document id to support showing just the chunk that contained the
|
|
||||||
* hit.
|
|
||||||
*/
|
|
||||||
if (content instanceof AbstractFile) {
|
|
||||||
AbstractFile file = (AbstractFile) content;
|
|
||||||
if (file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS
|
|
||||||
|| file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS) {
|
|
||||||
attributes.add(new BlackboardAttribute(KEYWORD_SEARCH_DOCUMENT_ID, MODULE_NAME, hit.getSolrDocumentId()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Create an account artifact.
|
|
||||||
*/
|
|
||||||
try {
|
|
||||||
AccountInstance ccAccountInstance = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().createAccountInstance(Account.Type.CREDIT_CARD, ccnAttribute.getValueString() , MODULE_NAME, content);
|
|
||||||
newArtifact = Case.getCurrentCase().getSleuthkitCase().getBlackboardArtifact(ccAccountInstance.getArtifactId());
|
|
||||||
} catch (TskCoreException ex) {
|
|
||||||
LOGGER.log(Level.SEVERE, "Error adding artifact for account to blackboard", ex); //NON-NLS
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (StringUtils.isNotBlank(listName)) {
|
if (StringUtils.isNotBlank(listName)) {
|
||||||
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, listName));
|
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, listName));
|
||||||
@ -446,6 +395,104 @@ final class TermsComponentQuery implements KeywordSearchQuery {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void createCCNAccount(Content content, KeywordHit hit, String snippet, String listName) {
|
||||||
|
|
||||||
|
if (originalKeyword.getArtifactAttributeType() != ATTRIBUTE_TYPE.TSK_CARD_NUMBER) {
|
||||||
|
LOGGER.log(Level.SEVERE, "Keyword hit is not a credit card number"); //NON-NLS
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create a credit card account with attributes
|
||||||
|
* parsed from from the snippet for the hit and looked up based on the
|
||||||
|
* parsed bank identifcation number.
|
||||||
|
*/
|
||||||
|
Collection<BlackboardAttribute> attributes = new ArrayList<>();
|
||||||
|
|
||||||
|
Map<BlackboardAttribute.Type, BlackboardAttribute> parsedTrackAttributeMap = new HashMap<>();
|
||||||
|
Matcher matcher = CREDIT_CARD_TRACK1_PATTERN.matcher(hit.getSnippet());
|
||||||
|
if (matcher.find()) {
|
||||||
|
parseTrack1Data(parsedTrackAttributeMap, matcher);
|
||||||
|
}
|
||||||
|
matcher = CREDIT_CARD_TRACK2_PATTERN.matcher(hit.getSnippet());
|
||||||
|
if (matcher.find()) {
|
||||||
|
parseTrack2Data(parsedTrackAttributeMap, matcher);
|
||||||
|
}
|
||||||
|
final BlackboardAttribute ccnAttribute = parsedTrackAttributeMap.get(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_CARD_NUMBER));
|
||||||
|
if (ccnAttribute == null || StringUtils.isBlank(ccnAttribute.getValueString())) {
|
||||||
|
if (hit.isArtifactHit()) {
|
||||||
|
LOGGER.log(Level.SEVERE, String.format("Failed to parse credit card account number for artifact keyword hit: term = %s, snippet = '%s', artifact id = %d", searchTerm, hit.getSnippet(), hit.getArtifactID().get())); //NON-NLS
|
||||||
|
} else {
|
||||||
|
LOGGER.log(Level.SEVERE, String.format("Failed to parse credit card account number for content keyword hit: term = %s, snippet = '%s', object id = %d", searchTerm, hit.getSnippet(), hit.getContentID())); //NON-NLS
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
attributes.addAll(parsedTrackAttributeMap.values());
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Look up the bank name, scheme, etc. attributes for the bank
|
||||||
|
* indentification number (BIN).
|
||||||
|
*/
|
||||||
|
final int bin = Integer.parseInt(ccnAttribute.getValueString().substring(0, 8));
|
||||||
|
CreditCards.BankIdentificationNumber binInfo = CreditCards.getBINInfo(bin);
|
||||||
|
if (binInfo != null) {
|
||||||
|
binInfo.getScheme().ifPresent(scheme
|
||||||
|
-> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_CARD_SCHEME, MODULE_NAME, scheme)));
|
||||||
|
binInfo.getCardType().ifPresent(cardType
|
||||||
|
-> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_CARD_TYPE, MODULE_NAME, cardType)));
|
||||||
|
binInfo.getBrand().ifPresent(brand
|
||||||
|
-> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_BRAND_NAME, MODULE_NAME, brand)));
|
||||||
|
binInfo.getBankName().ifPresent(bankName
|
||||||
|
-> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_BANK_NAME, MODULE_NAME, bankName)));
|
||||||
|
binInfo.getBankPhoneNumber().ifPresent(phoneNumber
|
||||||
|
-> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PHONE_NUMBER, MODULE_NAME, phoneNumber)));
|
||||||
|
binInfo.getBankURL().ifPresent(url
|
||||||
|
-> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL, MODULE_NAME, url)));
|
||||||
|
binInfo.getCountry().ifPresent(country
|
||||||
|
-> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COUNTRY, MODULE_NAME, country)));
|
||||||
|
binInfo.getBankCity().ifPresent(city
|
||||||
|
-> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_CITY, MODULE_NAME, city)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the hit is from unused or unallocated space, record the Solr
|
||||||
|
* document id to support showing just the chunk that contained the
|
||||||
|
* hit.
|
||||||
|
*/
|
||||||
|
if (content instanceof AbstractFile) {
|
||||||
|
AbstractFile file = (AbstractFile) content;
|
||||||
|
if (file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS
|
||||||
|
|| file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS) {
|
||||||
|
attributes.add(new BlackboardAttribute(KEYWORD_SEARCH_DOCUMENT_ID, MODULE_NAME, hit.getSolrDocumentId()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.isNotBlank(listName)) {
|
||||||
|
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, listName));
|
||||||
|
}
|
||||||
|
if (snippet != null) {
|
||||||
|
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_PREVIEW, MODULE_NAME, snippet));
|
||||||
|
}
|
||||||
|
|
||||||
|
hit.getArtifactID().ifPresent(
|
||||||
|
artifactID -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, artifactID))
|
||||||
|
);
|
||||||
|
|
||||||
|
// TermsComponentQuery is now being used exclusively for substring searches.
|
||||||
|
attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE, MODULE_NAME, KeywordSearch.QueryType.SUBSTRING.ordinal()));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create an account.
|
||||||
|
*/
|
||||||
|
try {
|
||||||
|
AccountInstance ccAccountInstance = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().createAccountInstance(Account.Type.CREDIT_CARD, ccnAttribute.getValueString() , MODULE_NAME, content);
|
||||||
|
ccAccountInstance.addAttributes(attributes);
|
||||||
|
//newArtifact = Case.getCurrentCase().getSleuthkitCase().getBlackboardArtifact(ccAccountInstance.getArtifactId());
|
||||||
|
} catch (TskCoreException ex) {
|
||||||
|
LOGGER.log(Level.SEVERE, "Error creating CCN account instance", ex); //NON-NLS
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Parses the track 2 data from the snippet for a credit card account number
|
* Parses the track 2 data from the snippet for a credit card account number
|
||||||
* hit and turns them into artifact attributes.
|
* hit and turns them into artifact attributes.
|
||||||
|
@ -36,6 +36,7 @@ import java.util.logging.Logger;
|
|||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
import javax.swing.JDialog;
|
import javax.swing.JDialog;
|
||||||
import javax.swing.text.JTextComponent;
|
import javax.swing.text.JTextComponent;
|
||||||
|
import javax.swing.tree.TreePath;
|
||||||
import org.netbeans.jellytools.MainWindowOperator;
|
import org.netbeans.jellytools.MainWindowOperator;
|
||||||
import org.netbeans.jellytools.NbDialogOperator;
|
import org.netbeans.jellytools.NbDialogOperator;
|
||||||
import org.netbeans.jellytools.WizardOperator;
|
import org.netbeans.jellytools.WizardOperator;
|
||||||
@ -53,6 +54,8 @@ import org.netbeans.jemmy.operators.JTabbedPaneOperator;
|
|||||||
import org.netbeans.jemmy.operators.JTableOperator;
|
import org.netbeans.jemmy.operators.JTableOperator;
|
||||||
import org.netbeans.jemmy.operators.JTextFieldOperator;
|
import org.netbeans.jemmy.operators.JTextFieldOperator;
|
||||||
import org.netbeans.jemmy.operators.JToggleButtonOperator;
|
import org.netbeans.jemmy.operators.JToggleButtonOperator;
|
||||||
|
import org.netbeans.jemmy.operators.JTreeOperator;
|
||||||
|
import org.netbeans.jemmy.operators.JTreeOperator.NoSuchPathException;
|
||||||
import org.sleuthkit.autopsy.core.UserPreferences;
|
import org.sleuthkit.autopsy.core.UserPreferences;
|
||||||
import org.sleuthkit.autopsy.core.UserPreferencesException;
|
import org.sleuthkit.autopsy.core.UserPreferencesException;
|
||||||
import org.sleuthkit.autopsy.events.MessageServiceConnectionInfo;
|
import org.sleuthkit.autopsy.events.MessageServiceConnectionInfo;
|
||||||
@ -290,6 +293,16 @@ public class AutopsyTestCases {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testExpandDataSourcesTree() {
|
||||||
|
logger.info("Data Sources Node");
|
||||||
|
MainWindowOperator mwo = MainWindowOperator.getDefault();
|
||||||
|
JTreeOperator jto = new JTreeOperator(mwo, "Data Sources");
|
||||||
|
String [] nodeNames = {"Data Sources"};
|
||||||
|
TreePath tp = jto.findPath(nodeNames);
|
||||||
|
expandNodes(jto, tp);
|
||||||
|
screenshot("Data Sources Tree");
|
||||||
|
}
|
||||||
|
|
||||||
public void testGenerateReportToolbar() {
|
public void testGenerateReportToolbar() {
|
||||||
logger.info("Generate Report Toolbars");
|
logger.info("Generate Report Toolbars");
|
||||||
MainWindowOperator mwo = MainWindowOperator.getDefault();
|
MainWindowOperator mwo = MainWindowOperator.getDefault();
|
||||||
@ -380,4 +393,15 @@ public class AutopsyTestCases {
|
|||||||
logger.log(Level.SEVERE, "Error saving messaging service connection info", ex); //NON-NLS
|
logger.log(Level.SEVERE, "Error saving messaging service connection info", ex); //NON-NLS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void expandNodes (JTreeOperator jto, TreePath tp) {
|
||||||
|
try {
|
||||||
|
jto.expandPath(tp);
|
||||||
|
for (TreePath t : jto.getChildPaths(tp)) {
|
||||||
|
expandNodes(jto, t);
|
||||||
|
}
|
||||||
|
} catch (NoSuchPathException ne) {
|
||||||
|
logger.log(Level.SEVERE, "Error expanding tree path", ne);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -69,6 +69,7 @@ public class RegressionTest extends TestCase {
|
|||||||
"testConfigureSearch",
|
"testConfigureSearch",
|
||||||
"testAddSourceWizard1",
|
"testAddSourceWizard1",
|
||||||
"testIngest",
|
"testIngest",
|
||||||
|
"testExpandDataSourcesTree", //After do ingest, before generate report, we expand Data Sources node
|
||||||
"testGenerateReportToolbar",
|
"testGenerateReportToolbar",
|
||||||
"testGenerateReportButton");
|
"testGenerateReportButton");
|
||||||
}
|
}
|
||||||
@ -83,6 +84,7 @@ public class RegressionTest extends TestCase {
|
|||||||
"testConfigureSearch",
|
"testConfigureSearch",
|
||||||
"testAddSourceWizard1",
|
"testAddSourceWizard1",
|
||||||
"testIngest",
|
"testIngest",
|
||||||
|
"testExpandDataSourcesTree",
|
||||||
"testGenerateReportToolbar",
|
"testGenerateReportToolbar",
|
||||||
"testGenerateReportButton");
|
"testGenerateReportButton");
|
||||||
}
|
}
|
||||||
@ -147,6 +149,9 @@ public class RegressionTest extends TestCase {
|
|||||||
autopsyTests.testIngest();
|
autopsyTests.testIngest();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testExpandDataSourcesTree() {
|
||||||
|
autopsyTests.testExpandDataSourcesTree();
|
||||||
|
}
|
||||||
public void testGenerateReportToolbar() {
|
public void testGenerateReportToolbar() {
|
||||||
autopsyTests.testGenerateReportToolbar();
|
autopsyTests.testGenerateReportToolbar();
|
||||||
}
|
}
|
||||||
|
@ -6,8 +6,8 @@ app.name=${branding.token}
|
|||||||
### if left unset, version will default to today's date
|
### if left unset, version will default to today's date
|
||||||
app.version=4.5.0
|
app.version=4.5.0
|
||||||
### build.type must be one of: DEVELOPMENT, RELEASE
|
### build.type must be one of: DEVELOPMENT, RELEASE
|
||||||
build.type=RELEASE
|
#build.type=RELEASE
|
||||||
#build.type=DEVELOPMENT
|
build.type=DEVELOPMENT
|
||||||
|
|
||||||
project.org.netbeans.progress=org-netbeans-api-progress
|
project.org.netbeans.progress=org-netbeans-api-progress
|
||||||
project.org.sleuthkit.autopsy.experimental=Experimental
|
project.org.sleuthkit.autopsy.experimental=Experimental
|
||||||
|
Loading…
x
Reference in New Issue
Block a user