mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-12 07:56:16 +00:00
Merge branch 'develop' of github.com:sleuthkit/autopsy into 4530_cr_ui_freeze
This commit is contained in:
commit
3ebee7fa2e
@ -65,8 +65,8 @@
|
||||
<property environment="env"/>
|
||||
<copy file="${env.TSK_HOME}/bindings/java/dist/sleuthkit-postgresql-${TSK_VERSION}.jar"
|
||||
tofile="${ext.dir}/sleuthkit-postgresql-${TSK_VERSION}.jar"/>
|
||||
<copy file="${env.TSK_HOME}/bindings/java/lib/sqlite-jdbc-3.8.11.jar"
|
||||
tofile="${ext.dir}/sqlite-jdbc-3.8.11.jar"/>
|
||||
<copy file="${env.TSK_HOME}/bindings/java/lib/sqlite-jdbc-3.25.2.jar"
|
||||
tofile="${ext.dir}/sqlite-jdbc-3.25.2.jar"/>
|
||||
<copy file="${env.TSK_HOME}/bindings/java/lib/postgresql-9.4.1211.jre7.jar"
|
||||
tofile="${ext.dir}/postgresql-9.4.1211.jre7.jar"/>
|
||||
<copy file="${env.TSK_HOME}/bindings/java/lib/mchange-commons-java-0.2.9.jar"
|
||||
|
@ -432,8 +432,8 @@
|
||||
<binary-origin>release/modules/ext/tika-parsers-1.17.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/sqlite-jdbc-3.8.11.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/sqlite-jdbc-3.8.11.jar</binary-origin>
|
||||
<runtime-relative-path>ext/sqlite-jdbc-3.25.2.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/sqlite-jdbc-3.25.2.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/json-simple-1.1.1.jar</runtime-relative-path>
|
||||
|
@ -67,6 +67,7 @@ import org.sleuthkit.autopsy.appservices.AutopsyService;
|
||||
import org.sleuthkit.autopsy.appservices.AutopsyService.CaseContext;
|
||||
import static org.sleuthkit.autopsy.casemodule.Bundle.*;
|
||||
import org.sleuthkit.autopsy.casemodule.CaseMetadata.CaseMetadataException;
|
||||
import org.sleuthkit.autopsy.casemodule.datasourceSummary.DataSourceSummaryAction;
|
||||
import org.sleuthkit.autopsy.casemodule.events.AddingDataSourceEvent;
|
||||
import org.sleuthkit.autopsy.casemodule.events.AddingDataSourceFailedEvent;
|
||||
import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagAddedEvent;
|
||||
@ -1165,6 +1166,7 @@ public class Case {
|
||||
CallableSystemAction.get(AddImageAction.class).setEnabled(true);
|
||||
CallableSystemAction.get(CaseCloseAction.class).setEnabled(true);
|
||||
CallableSystemAction.get(CasePropertiesAction.class).setEnabled(true);
|
||||
CallableSystemAction.get(DataSourceSummaryAction.class).setEnabled(true);
|
||||
CallableSystemAction.get(CaseDeleteAction.class).setEnabled(true);
|
||||
CallableSystemAction.get(OpenTimelineAction.class).setEnabled(true);
|
||||
CallableSystemAction.get(OpenCommVisualizationToolAction.class).setEnabled(true);
|
||||
@ -1218,6 +1220,7 @@ public class Case {
|
||||
CallableSystemAction.get(AddImageAction.class).setEnabled(false);
|
||||
CallableSystemAction.get(CaseCloseAction.class).setEnabled(false);
|
||||
CallableSystemAction.get(CasePropertiesAction.class).setEnabled(false);
|
||||
CallableSystemAction.get(DataSourceSummaryAction.class).setEnabled(false);
|
||||
CallableSystemAction.get(CaseDeleteAction.class).setEnabled(false);
|
||||
CallableSystemAction.get(OpenTimelineAction.class).setEnabled(false);
|
||||
CallableSystemAction.get(OpenCommVisualizationToolAction.class).setEnabled(false);
|
||||
|
@ -55,7 +55,7 @@ final class CasePropertiesAction extends CallableSystemAction {
|
||||
casePropertiesDialog = new JDialog(mainWindow, title, true);
|
||||
CaseInformationPanel caseInformationPanel = new CaseInformationPanel();
|
||||
caseInformationPanel.addCloseButtonAction((ActionEvent e) -> {
|
||||
casePropertiesDialog.setVisible(false);
|
||||
casePropertiesDialog.dispose();
|
||||
});
|
||||
casePropertiesDialog.add(caseInformationPanel);
|
||||
casePropertiesDialog.setResizable(true);
|
||||
|
@ -61,7 +61,8 @@ public final class IngestJobInfoPanel extends javax.swing.JPanel {
|
||||
private void customizeComponents() {
|
||||
refresh();
|
||||
this.ingestJobTable.getSelectionModel().addListSelectionListener((ListSelectionEvent e) -> {
|
||||
this.ingestModuleTableModel = new IngestModuleTableModel(this.ingestJobs.get(ingestJobTable.getSelectedRow()));
|
||||
IngestJobInfo currJob = (ingestJobTable.getSelectedRow() < 0 ? null : this.ingestJobs.get(ingestJobTable.getSelectedRow()));
|
||||
this.ingestModuleTableModel = new IngestModuleTableModel(currJob);
|
||||
this.ingestModuleTable.setModel(this.ingestModuleTableModel);
|
||||
});
|
||||
|
||||
|
@ -0,0 +1,5 @@
|
||||
DataSourceSummaryPanel.fileCountsLabel.text=Files (based on mimetype)
|
||||
DataSourceSummaryPanel.opperatingSystemLabel.text=OS:
|
||||
DataSourceSummaryPanel.ingestJobsLabel.text=Ingest Jobs
|
||||
DataSourceSummaryPanel.closeButton.text=Close
|
||||
DataSourceSummaryPanel.gotoDataSourceButton.text=Goto Data Source
|
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2019 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.casemodule.datasourceSummary;
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.util.EnumSet;
|
||||
import javax.swing.Action;
|
||||
import org.openide.awt.ActionID;
|
||||
import org.openide.awt.ActionRegistration;
|
||||
import org.openide.util.HelpCtx;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.openide.util.actions.CallableSystemAction;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
|
||||
@ActionID(category = "Case", id = "org.sleuthkit.autopsy.casemodule.datasourceSummary.DataSourceSummaryAction")
|
||||
@ActionRegistration(displayName = "#CTL_DataSourceSummaryAction", lazy = false)
|
||||
@Messages({"CTL_DataSourceSummaryAction=Data Source Summary"})
|
||||
/**
|
||||
* DataSourceSummaryAction action for the Case menu to activate a ViewSummaryInformationAction selecting the first data source.
|
||||
*/
|
||||
public class DataSourceSummaryAction extends CallableSystemAction {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Create a datasource summary action which will be disabled when no case is
|
||||
* open.
|
||||
*/
|
||||
DataSourceSummaryAction() {
|
||||
putValue(Action.NAME, Bundle.CTL_DataSourceSummaryAction());
|
||||
this.setEnabled(false);
|
||||
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), (PropertyChangeEvent evt) -> {
|
||||
setEnabled(null != evt.getNewValue());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performAction() {
|
||||
//perform the action of a ViewSummaryInformationAction with a ActionEvent which will not be used
|
||||
new ViewSummaryInformationAction(null).actionPerformed(new ActionEvent(Boolean.TRUE, 0, ""));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return Bundle.CTL_DataSourceSummaryAction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HelpCtx getHelpCtx() {
|
||||
return HelpCtx.DEFAULT_HELP;
|
||||
}
|
||||
}
|
@ -0,0 +1,182 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.6" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
|
||||
<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">
|
||||
<Component id="jSeparator1" alignment="1" max="32767" attributes="0"/>
|
||||
<Group type="102" attributes="0">
|
||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="dataSourcesScrollPane" alignment="1" max="32767" attributes="0"/>
|
||||
<Group type="102" alignment="1" attributes="0">
|
||||
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
|
||||
<Component id="gotoDataSourceButton" linkSize="1" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="closeButton" linkSize="1" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Group type="102" attributes="0">
|
||||
<Group type="103" groupAlignment="1" attributes="0">
|
||||
<Component id="fileCountsLabel" max="32767" attributes="0"/>
|
||||
<Component id="fileCountsScrollPane" alignment="1" min="-2" pref="140" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace type="unrelated" min="-2" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="ingestJobsLabel" max="32767" attributes="0"/>
|
||||
<Component id="ingestJobsScrollPane" pref="474" max="32767" attributes="0"/>
|
||||
<Group type="102" alignment="1" attributes="0">
|
||||
<Component id="opperatingSystemLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="opperatingSystemValueLabel" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace min="-2" pref="8" max="-2" attributes="0"/>
|
||||
<Component id="dataSourcesScrollPane" pref="120" max="32767" attributes="0"/>
|
||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||
<Component id="jSeparator1" min="-2" pref="5" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="fileCountsLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="ingestJobsLabel" alignment="3" min="-2" pref="14" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="fileCountsScrollPane" min="-2" pref="107" max="-2" attributes="0"/>
|
||||
<Component id="ingestJobsScrollPane" pref="0" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="opperatingSystemLabel" alignment="1" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="opperatingSystemValueLabel" min="-2" pref="14" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace min="-2" pref="10" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="closeButton" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="gotoDataSourceButton" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JSeparator" name="jSeparator1">
|
||||
</Component>
|
||||
<Container class="javax.swing.JScrollPane" name="dataSourcesScrollPane">
|
||||
<AuxValues>
|
||||
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JTable" name="dataSourcesTable">
|
||||
<Properties>
|
||||
<Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
||||
<Connection code="dataSourceTableModel" type="code"/>
|
||||
</Property>
|
||||
<Property name="selectionModel" type="javax.swing.ListSelectionModel" editor="org.netbeans.modules.form.editors2.JTableSelectionModelEditor">
|
||||
<JTableSelectionModel selectionMode="0"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Container class="javax.swing.JScrollPane" name="ingestJobsScrollPane">
|
||||
<AuxValues>
|
||||
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JTable" name="ingestJobsTable">
|
||||
<Properties>
|
||||
<Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
||||
<Connection code="ingestJobTableModel" type="code"/>
|
||||
</Property>
|
||||
<Property name="selectionModel" type="javax.swing.ListSelectionModel" editor="org.netbeans.modules.form.editors2.JTableSelectionModelEditor">
|
||||
<JTableSelectionModel selectionMode="0"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Container class="javax.swing.JScrollPane" name="fileCountsScrollPane">
|
||||
<AuxValues>
|
||||
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JTable" name="fileCountsTable">
|
||||
<Properties>
|
||||
<Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
||||
<Connection code="filesTableModel" type="code"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Component class="javax.swing.JLabel" name="opperatingSystemLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties" key="DataSourceSummaryPanel.opperatingSystemLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="opperatingSystemValueLabel">
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="fileCountsLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties" key="DataSourceSummaryPanel.fileCountsLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="ingestJobsLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties" key="DataSourceSummaryPanel.ingestJobsLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="closeButton">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties" key="DataSourceSummaryPanel.closeButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="gotoDataSourceButton">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties" key="DataSourceSummaryPanel.gotoDataSourceButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="enabled" type="boolean" value="false"/>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="gotoDataSourceButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Form>
|
@ -0,0 +1,698 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2019 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.casemodule.datasourceSummary;
|
||||
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.datamodel.utils.FileTypeUtils;
|
||||
import org.sleuthkit.autopsy.directorytree.ViewContextAction;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.CaseDbAccessManager.CaseDbAccessQueryCallback;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
import org.sleuthkit.datamodel.IngestJobInfo;
|
||||
import org.sleuthkit.datamodel.OSInfo;
|
||||
import org.sleuthkit.datamodel.OSUtility;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TskData;
|
||||
|
||||
final class DataSourceSummaryPanel extends javax.swing.JPanel {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final List<IngestJobInfo> allIngestJobs = new ArrayList<>();
|
||||
private List<IngestJobInfo> ingestJobs = new ArrayList<>();
|
||||
private DataSourceTableModel dataSourceTableModel = new DataSourceTableModel();
|
||||
private IngestJobTableModel ingestJobTableModel = new IngestJobTableModel();
|
||||
private FilesTableModel filesTableModel = new FilesTableModel(null);
|
||||
private final List<DataSource> dataSources = new ArrayList<>();
|
||||
private final DateFormat datetimeFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
|
||||
private static final Logger logger = Logger.getLogger(DataSourceSummaryPanel.class.getName());
|
||||
private List<OSInfo> osInfoList;
|
||||
|
||||
/**
|
||||
* Creates new form DataSourceSummaryPanel for displaying a summary of the
|
||||
* data sources for the fcurrent case and the contents found for each
|
||||
* datasource.
|
||||
*/
|
||||
@Messages({"DataSourceSummaryPanel.getDataSources.error.text=Failed to get the list of datasources for the current case.",
|
||||
"DataSourceSummaryPanel.getDataSources.error.title=Load Failure"})
|
||||
DataSourceSummaryPanel() {
|
||||
initComponents();
|
||||
ingestJobsTable.getTableHeader().setReorderingAllowed(false);
|
||||
fileCountsTable.getTableHeader().setReorderingAllowed(false);
|
||||
dataSourcesTable.getTableHeader().setReorderingAllowed(false);
|
||||
try {
|
||||
SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
|
||||
allIngestJobs.addAll(skCase.getIngestJobs());
|
||||
dataSources.addAll(skCase.getDataSources());
|
||||
osInfoList = OSUtility.getOSInfo(skCase);
|
||||
} catch (TskCoreException | NoCurrentCaseException ex) {
|
||||
logger.log(Level.SEVERE, "Failed to load ingest jobs.", ex);
|
||||
JOptionPane.showMessageDialog(this, Bundle.DataSourceSummaryPanel_getDataSources_error_text(), Bundle.DataSourceSummaryPanel_getDataSources_error_title(), JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
dataSourcesTable.getSelectionModel().addListSelectionListener((ListSelectionEvent e) -> {
|
||||
if (!e.getValueIsAdjusting()) {
|
||||
DataSource selectedDataSource = (dataSourcesTable.getSelectedRow() < 0 ? null : dataSources.get(dataSourcesTable.getSelectedRow()));
|
||||
gotoDataSourceButton.setEnabled(selectedDataSource != null);
|
||||
updateIngestJobs(selectedDataSource);
|
||||
filesTableModel = new FilesTableModel(selectedDataSource);
|
||||
fileCountsTable.setModel(filesTableModel);
|
||||
opperatingSystemValueLabel.setText(getOSName(selectedDataSource));
|
||||
this.repaint();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the operating system if it is available. Otherwise get
|
||||
* and empty string.
|
||||
*
|
||||
* @param selectedDataSource the datasource to get the OS information for
|
||||
*
|
||||
* @return the name of the opperating system on the specified datasource,
|
||||
* empty string if no opperating system info found
|
||||
*/
|
||||
private String getOSName(DataSource selectedDataSource) {
|
||||
String osName = "";
|
||||
if (selectedDataSource != null) {
|
||||
for (OSInfo osInfo : osInfoList) {
|
||||
try {
|
||||
//assumes only one Opperating System per datasource
|
||||
//get the datasource id from the OSInfo's first artifact if it has artifacts
|
||||
if (!osInfo.getArtifacts().isEmpty() && osInfo.getArtifacts().get(0).getDataSource().getId() == selectedDataSource.getId()) {
|
||||
osName = osInfo.getOSName();
|
||||
//if this OSInfo object has a name use it otherwise keep checking OSInfo objects
|
||||
if (!osName.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (TskCoreException ignored) {
|
||||
//unable to get datasource for the OSInfo Object
|
||||
//continue checking for OSInfo objects to try and get get the desired information
|
||||
}
|
||||
}
|
||||
}
|
||||
return osName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the ingestJobs list with the ingest jobs for the
|
||||
* selectedDataSource
|
||||
*
|
||||
* @param selectedDataSource the datasource to find the ingest jobs for
|
||||
*/
|
||||
@Messages({"DataSourceSummaryPanel.loadIngestJob.error.text=Failed to load ingest jobs.",
|
||||
"DataSourceSummaryPanel.loadIngestJob.error.title=Load Failure"})
|
||||
private void updateIngestJobs(DataSource selectedDataSource) {
|
||||
ingestJobs.clear();
|
||||
if (selectedDataSource != null) {
|
||||
for (IngestJobInfo ingestJob : allIngestJobs) {
|
||||
if (ingestJob.getObjectId() == selectedDataSource.getId()) {
|
||||
ingestJobs.add(ingestJob);
|
||||
}
|
||||
}
|
||||
}
|
||||
ingestJobTableModel = new IngestJobTableModel();
|
||||
ingestJobsTable.setModel(ingestJobTableModel);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
|
||||
jSeparator1 = new javax.swing.JSeparator();
|
||||
dataSourcesScrollPane = new javax.swing.JScrollPane();
|
||||
dataSourcesTable = new javax.swing.JTable();
|
||||
ingestJobsScrollPane = new javax.swing.JScrollPane();
|
||||
ingestJobsTable = new javax.swing.JTable();
|
||||
fileCountsScrollPane = new javax.swing.JScrollPane();
|
||||
fileCountsTable = new javax.swing.JTable();
|
||||
opperatingSystemLabel = new javax.swing.JLabel();
|
||||
opperatingSystemValueLabel = new javax.swing.JLabel();
|
||||
fileCountsLabel = new javax.swing.JLabel();
|
||||
ingestJobsLabel = new javax.swing.JLabel();
|
||||
closeButton = new javax.swing.JButton();
|
||||
gotoDataSourceButton = new javax.swing.JButton();
|
||||
|
||||
dataSourcesTable.setModel(dataSourceTableModel);
|
||||
dataSourcesTable.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
|
||||
dataSourcesScrollPane.setViewportView(dataSourcesTable);
|
||||
|
||||
ingestJobsTable.setModel(ingestJobTableModel);
|
||||
ingestJobsTable.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
|
||||
ingestJobsScrollPane.setViewportView(ingestJobsTable);
|
||||
|
||||
fileCountsTable.setModel(filesTableModel);
|
||||
fileCountsScrollPane.setViewportView(fileCountsTable);
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(opperatingSystemLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryPanel.class, "DataSourceSummaryPanel.opperatingSystemLabel.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(fileCountsLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryPanel.class, "DataSourceSummaryPanel.fileCountsLabel.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(ingestJobsLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryPanel.class, "DataSourceSummaryPanel.ingestJobsLabel.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(closeButton, org.openide.util.NbBundle.getMessage(DataSourceSummaryPanel.class, "DataSourceSummaryPanel.closeButton.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(gotoDataSourceButton, org.openide.util.NbBundle.getMessage(DataSourceSummaryPanel.class, "DataSourceSummaryPanel.gotoDataSourceButton.text")); // NOI18N
|
||||
gotoDataSourceButton.setEnabled(false);
|
||||
gotoDataSourceButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
gotoDataSourceButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(jSeparator1, javax.swing.GroupLayout.Alignment.TRAILING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(dataSourcesScrollPane, javax.swing.GroupLayout.Alignment.TRAILING)
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
|
||||
.addGap(0, 0, Short.MAX_VALUE)
|
||||
.addComponent(gotoDataSourceButton)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(closeButton))
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
|
||||
.addComponent(fileCountsLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(fileCountsScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 140, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(ingestJobsLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(ingestJobsScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 474, Short.MAX_VALUE)
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
|
||||
.addComponent(opperatingSystemLabel)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(opperatingSystemValueLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))))
|
||||
.addContainerGap())
|
||||
);
|
||||
|
||||
layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {closeButton, gotoDataSourceButton});
|
||||
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGap(8, 8, 8)
|
||||
.addComponent(dataSourcesScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 120, Short.MAX_VALUE)
|
||||
.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.BASELINE)
|
||||
.addComponent(fileCountsLabel)
|
||||
.addComponent(ingestJobsLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 14, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(fileCountsScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 107, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(ingestJobsScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(opperatingSystemLabel, javax.swing.GroupLayout.Alignment.TRAILING)
|
||||
.addComponent(opperatingSystemValueLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 14, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addGap(10, 10, 10)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(closeButton)
|
||||
.addComponent(gotoDataSourceButton))
|
||||
.addContainerGap())
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
/**
|
||||
* Adds an action listener to the Close button of the panel.
|
||||
*
|
||||
* @param action
|
||||
*/
|
||||
void addCloseButtonAction(ActionListener action) {
|
||||
this.closeButton.addActionListener(action);
|
||||
//the gotoDataSourceButton should also close the dialog
|
||||
this.gotoDataSourceButton.addActionListener(action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Select the data source with the specicied data source id. If no data
|
||||
* source matches the dataSourceID it will select the first datasource.
|
||||
*
|
||||
* @param dataSourceID the ID of the datasource to select, null will cause
|
||||
* the first datasource to be selected
|
||||
*/
|
||||
void selectDataSource(Long dataSourceID) {
|
||||
if (dataSourceID != null) {
|
||||
for (int i = 0; i < dataSources.size(); i++) {
|
||||
if (dataSources.get(i).getId() == dataSourceID) {
|
||||
dataSourcesTable.setRowSelectionInterval(i, i);
|
||||
//scroll down from top of table to where selected datasource is
|
||||
dataSourcesTable.scrollRectToVisible(new Rectangle(dataSourcesTable.getCellRect(i, 0, true)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
//if there are data sources in the list and none were found that matched the specied dataSourceID select the first one
|
||||
if (!dataSources.isEmpty()) {
|
||||
dataSourcesTable.setRowSelectionInterval(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performed when the Goto Data Source button is clicked, will cause the
|
||||
* window to be closed and the data source which was selected to be
|
||||
* navigated to in the tree.
|
||||
*/
|
||||
private void gotoDataSourceButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_gotoDataSourceButtonActionPerformed
|
||||
//the dialog will be closed due to the action listener added in addCloseButtonAction
|
||||
DataSource selectedDataSource = (dataSourcesTable.getSelectedRow() < 0 ? null : dataSources.get(dataSourcesTable.getSelectedRow()));
|
||||
if (selectedDataSource != null) {
|
||||
new ViewContextAction("", selectedDataSource).actionPerformed(evt);
|
||||
}
|
||||
}//GEN-LAST:event_gotoDataSourceButtonActionPerformed
|
||||
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JButton closeButton;
|
||||
private javax.swing.JScrollPane dataSourcesScrollPane;
|
||||
private javax.swing.JTable dataSourcesTable;
|
||||
private javax.swing.JLabel fileCountsLabel;
|
||||
private javax.swing.JScrollPane fileCountsScrollPane;
|
||||
private javax.swing.JTable fileCountsTable;
|
||||
private javax.swing.JButton gotoDataSourceButton;
|
||||
private javax.swing.JLabel ingestJobsLabel;
|
||||
private javax.swing.JScrollPane ingestJobsScrollPane;
|
||||
private javax.swing.JTable ingestJobsTable;
|
||||
private javax.swing.JSeparator jSeparator1;
|
||||
private javax.swing.JLabel opperatingSystemLabel;
|
||||
private javax.swing.JLabel opperatingSystemValueLabel;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
/**
|
||||
* Table model for the Data source table, to display all data sources for
|
||||
* the current case.
|
||||
*/
|
||||
@Messages({"DataSourceSummaryPanel.DataSourceTableModel.dataSourceName.header=Data Source Name",
|
||||
"DataSourceSummaryPanel.DataSourceTableModel.type.header=Type",
|
||||
"DataSourceSummaryPanel.DataSourceTableModel.files.header=Files",
|
||||
"DataSourceSummaryPanel.DataSourceTableModel.results.header=Results",
|
||||
"DataSourceSummaryPanel.DataSourceTableModel.tags.header=Tags"})
|
||||
private class DataSourceTableModel extends AbstractTableModel {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final List<String> columnHeaders = new ArrayList<>();
|
||||
private final Map<Long, Long> fileCountsMap;
|
||||
private final Map<Long, Long> artifactCountsMap;
|
||||
private final Map<Long, Long> tagCountsMap;
|
||||
|
||||
/**
|
||||
* Create a new DataSourceTableModel for the current case.
|
||||
*/
|
||||
DataSourceTableModel() {
|
||||
columnHeaders.add(Bundle.DataSourceSummaryPanel_DataSourceTableModel_dataSourceName_header());
|
||||
columnHeaders.add(Bundle.DataSourceSummaryPanel_DataSourceTableModel_type_header());
|
||||
columnHeaders.add(Bundle.DataSourceSummaryPanel_DataSourceTableModel_files_header());
|
||||
columnHeaders.add(Bundle.DataSourceSummaryPanel_DataSourceTableModel_results_header());
|
||||
columnHeaders.add(Bundle.DataSourceSummaryPanel_DataSourceTableModel_tags_header());
|
||||
fileCountsMap = getCountsOfFiles();
|
||||
artifactCountsMap = getCountsOfArtifacts();
|
||||
tagCountsMap = getCountsOfTags();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRowCount() {
|
||||
return dataSources.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnCount() {
|
||||
return columnHeaders.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValueAt(int rowIndex, int columnIndex) {
|
||||
DataSource currentDataSource = dataSources.get(rowIndex);
|
||||
Long count;
|
||||
switch (columnIndex) {
|
||||
case 0:
|
||||
return currentDataSource.getName();
|
||||
case 1:
|
||||
return "";
|
||||
case 2:
|
||||
//display 0 if no count is found
|
||||
count = fileCountsMap.get(currentDataSource.getId());
|
||||
return count == null ? 0 : count;
|
||||
case 3:
|
||||
//display 0 if no count is found
|
||||
count = artifactCountsMap.get(currentDataSource.getId());
|
||||
return count == null ? 0 : count;
|
||||
case 4:
|
||||
//display 0 if no count is found
|
||||
count = tagCountsMap.get(currentDataSource.getId());
|
||||
return count == null ? 0 : count;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a map containing the number of files in each data source in the
|
||||
* current case.
|
||||
*
|
||||
* @return Collection which maps datasource id to a count for the number
|
||||
* of files in the datasource, will only contain entries for
|
||||
* datasources which have at least 1 file
|
||||
*/
|
||||
private Map<Long, Long> getCountsOfFiles() {
|
||||
try {
|
||||
SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
|
||||
DataSourceCountsCallback callback = new DataSourceCountsCallback();
|
||||
final String countFilesQuery = "data_source_obj_id, COUNT(*) AS count"
|
||||
+ " FROM tsk_files WHERE type<>" + TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType()
|
||||
+ " AND dir_type<>" + TskData.TSK_FS_NAME_TYPE_ENUM.VIRT_DIR.getValue()
|
||||
+ " AND name<>'' GROUP BY data_source_obj_id"; //NON-NLS
|
||||
skCase.getCaseDbAccessManager().select(countFilesQuery, callback);
|
||||
return callback.getMapOfCounts();
|
||||
} catch (TskCoreException | NoCurrentCaseException ex) {
|
||||
logger.log(Level.WARNING, "Unable to get counts of files for all datasources, providing empty results", ex);
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a map containing the number of artifacts in each data source in
|
||||
* the current case.
|
||||
*
|
||||
* @return Collection which maps datasource id to a count for the number
|
||||
* of artifacts in the datasource, will only contain entries for
|
||||
* datasources which have at least 1 artifact
|
||||
*/
|
||||
private Map<Long, Long> getCountsOfArtifacts() {
|
||||
try {
|
||||
SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
|
||||
DataSourceCountsCallback callback = new DataSourceCountsCallback();
|
||||
final String countArtifactsQuery = "data_source_obj_id, COUNT(*) AS count"
|
||||
+ " FROM blackboard_artifacts WHERE review_status_id !=" + BlackboardArtifact.ReviewStatus.REJECTED.getID()
|
||||
+ " GROUP BY data_source_obj_id"; //NON-NLS
|
||||
skCase.getCaseDbAccessManager().select(countArtifactsQuery, callback);
|
||||
return callback.getMapOfCounts();
|
||||
} catch (TskCoreException | NoCurrentCaseException ex) {
|
||||
logger.log(Level.WARNING, "Unable to get counts of artifacts for all datasources, providing empty results", ex);
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a map containing the number of tags which have been applied in
|
||||
* each data source in the current case. Not necessarily the same as the
|
||||
* number of items tagged, as an item can have any number of tags.
|
||||
*
|
||||
* @return Collection which maps datasource id to a count for the number
|
||||
* of tags which have been applied in the datasource, will only
|
||||
* contain entries for datasources which have at least 1 item
|
||||
* tagged.
|
||||
*/
|
||||
private Map<Long, Long> getCountsOfTags() {
|
||||
try {
|
||||
SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
|
||||
DataSourceCountsCallback fileCountcallback = new DataSourceCountsCallback();
|
||||
final String countFileTagsQuery = "data_source_obj_id, COUNT(*) AS count"
|
||||
+ " FROM content_tags as content_tags, tsk_files as tsk_files"
|
||||
+ " WHERE content_tags.obj_id = tsk_files.obj_id"
|
||||
+ " GROUP BY data_source_obj_id"; //NON-NLS
|
||||
skCase.getCaseDbAccessManager().select(countFileTagsQuery, fileCountcallback);
|
||||
Map<Long, Long> tagCountMap = new HashMap<>(fileCountcallback.getMapOfCounts());
|
||||
DataSourceCountsCallback artifactCountcallback = new DataSourceCountsCallback();
|
||||
final String countArtifactTagsQuery = "data_source_obj_id, COUNT(*) AS count"
|
||||
+ " FROM blackboard_artifact_tags as artifact_tags, blackboard_artifacts AS arts"
|
||||
+ " WHERE artifact_tags.artifact_id = arts.artifact_id"
|
||||
+ " GROUP BY data_source_obj_id"; //NON-NLS
|
||||
skCase.getCaseDbAccessManager().select(countArtifactTagsQuery, artifactCountcallback);
|
||||
//combine the results from the count artifact tags query into the copy of the mapped results from the count file tags query
|
||||
artifactCountcallback.getMapOfCounts().forEach((key, value) -> tagCountMap.merge(key, value, (value1, value2) -> value1 + value2));
|
||||
return tagCountMap;
|
||||
} catch (TskCoreException | NoCurrentCaseException ex) {
|
||||
logger.log(Level.WARNING, "Unable to get counts of tags for all datasources, providing empty results", ex);
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getColumnName(int column) {
|
||||
return columnHeaders.get(column);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the map of Data Source ID to counts of items found for a query
|
||||
* which selects data_source_obj_id and count(*) with a group by
|
||||
* data_source_obj_id clause.
|
||||
*/
|
||||
private class DataSourceCountsCallback implements CaseDbAccessQueryCallback {
|
||||
|
||||
Map<Long, Long> dataSourceObjIdCounts = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public void process(ResultSet rs) {
|
||||
try {
|
||||
while (rs.next()) {
|
||||
try {
|
||||
dataSourceObjIdCounts.put(rs.getLong("data_source_obj_id"), rs.getLong("count"));
|
||||
} catch (SQLException ex) {
|
||||
logger.log(Level.WARNING, "Unable to get data_source_obj_id or count from result set", ex);
|
||||
}
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
logger.log(Level.WARNING, "Failed to get next result for counts by datasource", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the processed results
|
||||
*
|
||||
* @return Collection which maps datasource id to a count for the
|
||||
* number of items found with that datasource id, only
|
||||
* contains entries for datasources with at least 1 item
|
||||
* found.
|
||||
*/
|
||||
Map<Long, Long> getMapOfCounts() {
|
||||
return Collections.unmodifiableMap(dataSourceObjIdCounts);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Table model for the Ingest Job table to display ingest jobs for the
|
||||
* selected datasource.
|
||||
*/
|
||||
@Messages({"DataSourceSummaryPanel.IngestJobTableModel.StartTime.header=Start Time",
|
||||
"DataSourceSummaryPanel.IngestJobTableModel.EndTime.header=End Time",
|
||||
"DataSourceSummaryPanel.IngestJobTableModel.IngestStatus.header=Ingest Status"})
|
||||
private class IngestJobTableModel extends AbstractTableModel {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final List<String> columnHeaders = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Create a new IngestJobTableModel
|
||||
*/
|
||||
IngestJobTableModel() {
|
||||
columnHeaders.add(Bundle.DataSourceSummaryPanel_IngestJobTableModel_StartTime_header());
|
||||
columnHeaders.add(Bundle.DataSourceSummaryPanel_IngestJobTableModel_EndTime_header());
|
||||
columnHeaders.add(Bundle.DataSourceSummaryPanel_IngestJobTableModel_IngestStatus_header());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRowCount() {
|
||||
return ingestJobs.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnCount() {
|
||||
return columnHeaders.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValueAt(int rowIndex, int columnIndex) {
|
||||
IngestJobInfo currIngestJob = ingestJobs.get(rowIndex);
|
||||
switch (columnIndex) {
|
||||
case 0:
|
||||
return datetimeFormat.format(currIngestJob.getStartDateTime());
|
||||
case 1:
|
||||
Date endDate = currIngestJob.getEndDateTime();
|
||||
if (endDate.getTime() == 0) {
|
||||
return "N/A";
|
||||
}
|
||||
return datetimeFormat.format(currIngestJob.getEndDateTime());
|
||||
case 2:
|
||||
return currIngestJob.getStatus().getDisplayName();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getColumnName(int column) {
|
||||
return columnHeaders.get(column);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Table model for the files table model to display counts of specific file
|
||||
* types found in the currently selected data source.
|
||||
*/
|
||||
@Messages({"DataSourceSummaryPanel.FilesTableModel.type.header=File Type",
|
||||
"DataSourceSummaryPanel.FilesTableModel.count.header=Count"})
|
||||
private class FilesTableModel extends AbstractTableModel {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final DataSource currentDataSource;
|
||||
private final List<String> columnHeaders = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Create a FilesTableModel for the speicified datasource.
|
||||
*
|
||||
* @param selectedDataSource the datasource which this filesTablemodel
|
||||
* will represent
|
||||
*/
|
||||
FilesTableModel(DataSource selectedDataSource) {
|
||||
columnHeaders.add(Bundle.DataSourceSummaryPanel_FilesTableModel_type_header());
|
||||
columnHeaders.add(Bundle.DataSourceSummaryPanel_FilesTableModel_count_header());
|
||||
currentDataSource = selectedDataSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRowCount() {
|
||||
//should be kept equal to the number of types we are displaying in the tables
|
||||
return 5;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnCount() {
|
||||
return columnHeaders.size();
|
||||
}
|
||||
|
||||
@Messages({
|
||||
"DataSourceSummaryPanel.FilesTableModel.images.row=Images",
|
||||
"DataSourceSummaryPanel.FilesTableModel.videos.row=Videos",
|
||||
"DataSourceSummaryPanel.FilesTableModel.audio.row=Audio",
|
||||
"DataSourceSummaryPanel.FilesTableModel.documents.row=Documents",
|
||||
"DataSourceSummaryPanel.FilesTableModel.executables.row=Executables"
|
||||
})
|
||||
@Override
|
||||
public Object getValueAt(int rowIndex, int columnIndex) {
|
||||
if (columnIndex == 0) {
|
||||
switch (rowIndex) {
|
||||
case 0:
|
||||
return Bundle.DataSourceSummaryPanel_FilesTableModel_images_row();
|
||||
case 1:
|
||||
return Bundle.DataSourceSummaryPanel_FilesTableModel_videos_row();
|
||||
case 2:
|
||||
return Bundle.DataSourceSummaryPanel_FilesTableModel_audio_row();
|
||||
case 3:
|
||||
return Bundle.DataSourceSummaryPanel_FilesTableModel_documents_row();
|
||||
case 4:
|
||||
return Bundle.DataSourceSummaryPanel_FilesTableModel_executables_row();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if (columnIndex == 1) {
|
||||
switch (rowIndex) {
|
||||
case 0:
|
||||
return getCountOfFiles(FileTypeUtils.FileTypeCategory.IMAGE.getMediaTypes());
|
||||
case 1:
|
||||
return getCountOfFiles(FileTypeUtils.FileTypeCategory.VIDEO.getMediaTypes());
|
||||
case 2:
|
||||
return getCountOfFiles(FileTypeUtils.FileTypeCategory.AUDIO.getMediaTypes());
|
||||
case 3:
|
||||
return getCountOfFiles(FileTypeUtils.FileTypeCategory.DOCUMENTS.getMediaTypes());
|
||||
case 4:
|
||||
return getCountOfFiles(FileTypeUtils.FileTypeCategory.EXECUTABLE.getMediaTypes());
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of files in the case database for the current data
|
||||
* source which have the specified mimetypes.
|
||||
*
|
||||
* @param setOfMimeTypes the set of mime types which we are finding the
|
||||
* number of occurences of
|
||||
*
|
||||
* @return a Long value which represents the number of occurrences of
|
||||
* the specified mime types in the current case for the
|
||||
* specified data source, null if no count was retrieved
|
||||
*/
|
||||
private Long getCountOfFiles(Set<String> setOfMimeTypes) {
|
||||
if (currentDataSource != null) {
|
||||
try {
|
||||
String inClause = String.join("', '", setOfMimeTypes);
|
||||
SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
|
||||
return skCase.countFilesWhere("data_source_obj_id=" + currentDataSource.getId()
|
||||
+ " AND type<>" + TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType()
|
||||
+ " AND dir_type<>" + TskData.TSK_FS_NAME_TYPE_ENUM.VIRT_DIR.getValue()
|
||||
+ " AND mime_type IN ('" + inClause + "')"
|
||||
+ " AND name<>''");
|
||||
} catch (TskCoreException | NoCurrentCaseException ex) {
|
||||
logger.log(Level.WARNING, "Unable to get count of files for specified mime types", ex);
|
||||
//unable to get count of files for the specified mimetypes cell will be displayed as empty
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getColumnName(int column) {
|
||||
return columnHeaders.get(column);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2019 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.casemodule.datasourceSummary;
|
||||
|
||||
import java.awt.Frame;
|
||||
import java.awt.event.ActionEvent;
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.SwingUtilities;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.openide.windows.WindowManager;
|
||||
|
||||
/**
|
||||
* ViewSummaryInformationAction action for opening a Data Source Summary Panel
|
||||
* with the specified data source selected if it is present.
|
||||
*/
|
||||
public final class ViewSummaryInformationAction extends AbstractAction {
|
||||
|
||||
private static JDialog dataSourceSummaryDialog;
|
||||
private static Long selectDataSource;
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Create a ViewSummaryInformationAction for the selected datasource.
|
||||
*
|
||||
* @param selectedDataSource - the data source which is currently selected
|
||||
* and will be selected initially when the
|
||||
* DataSourceSummaryPanel opens.
|
||||
*/
|
||||
@Messages({"ViewSummaryInformationAction.name.text=View Summary Information"})
|
||||
public ViewSummaryInformationAction(Long selectedDataSource) {
|
||||
super(Bundle.ViewSummaryInformationAction_name_text());
|
||||
selectDataSource = selectedDataSource;
|
||||
}
|
||||
|
||||
@Messages({"ViewSummaryInformationAction.window.title=Data Source Summary"})
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent actionEvent) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
String title = Bundle.ViewSummaryInformationAction_window_title();
|
||||
Frame mainWindow = WindowManager.getDefault().getMainWindow();
|
||||
dataSourceSummaryDialog = new JDialog(mainWindow, title, true);
|
||||
DataSourceSummaryPanel dataSourceSummaryPanel = new DataSourceSummaryPanel();
|
||||
//allow the buttons in DataSourceSummaryPanel to close this dialog
|
||||
dataSourceSummaryPanel.addCloseButtonAction((ActionEvent event) -> {
|
||||
dataSourceSummaryDialog.dispose();
|
||||
});
|
||||
//select the specifed data source
|
||||
dataSourceSummaryPanel.selectDataSource(selectDataSource);
|
||||
dataSourceSummaryDialog.add(dataSourceSummaryPanel);
|
||||
dataSourceSummaryDialog.setResizable(true);
|
||||
dataSourceSummaryDialog.pack();
|
||||
dataSourceSummaryDialog.setLocationRelativeTo(mainWindow);
|
||||
dataSourceSummaryDialog.setVisible(true);
|
||||
dataSourceSummaryDialog.toFront();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -18,6 +18,7 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.core;
|
||||
|
||||
import java.nio.file.Paths;
|
||||
import org.sleuthkit.autopsy.coreutils.TextConverter;
|
||||
import java.util.prefs.BackingStoreException;
|
||||
import org.sleuthkit.autopsy.events.MessageServiceConnectionInfo;
|
||||
@ -38,7 +39,6 @@ import org.sleuthkit.datamodel.TskData.DbType;
|
||||
*/
|
||||
public final class UserPreferences {
|
||||
|
||||
private static final boolean IS_WINDOWS_OS = PlatformUtil.isWindowsOS();
|
||||
private static final Preferences preferences = NbPreferences.forModule(UserPreferences.class);
|
||||
public static final String KEEP_PREFERRED_VIEWER = "KeepPreferredViewer"; // NON-NLS
|
||||
public static final String HIDE_KNOWN_FILES_IN_DATA_SRCS_TREE = "HideKnownFilesInDataSourcesTree"; //NON-NLS
|
||||
@ -75,6 +75,7 @@ public final class UserPreferences {
|
||||
public static final String SHOW_ONLY_CURRENT_USER_TAGS = "ShowOnlyCurrentUserTags";
|
||||
public static final String HIDE_CENTRAL_REPO_COMMENTS_AND_OCCURRENCES = "HideCentralRepoCommentsAndOccurrences";
|
||||
public static final String DISPLAY_TRANSLATED_NAMES = "DisplayTranslatedNames";
|
||||
public static final String EXTERNAL_HEX_EDITOR_PATH = "ExternalHexEditorPath";
|
||||
|
||||
// Prevent instantiation.
|
||||
private UserPreferences() {
|
||||
@ -471,4 +472,23 @@ public final class UserPreferences {
|
||||
public static void setLogFileCount(int count) {
|
||||
preferences.putInt(MAX_NUM_OF_LOG_FILE, count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the HdX path.
|
||||
*
|
||||
* @param executablePath User-inputted path to HxD executable
|
||||
*/
|
||||
public static void setExternalHexEditorPath(String executablePath) {
|
||||
preferences.put(EXTERNAL_HEX_EDITOR_PATH, executablePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the HdXEditor path set by the User. If not found, the default
|
||||
* will be the default install location of HxD.
|
||||
*
|
||||
* @return Path to HdX
|
||||
*/
|
||||
public static String getExternalHexEditorPath() {
|
||||
return preferences.get(EXTERNAL_HEX_EDITOR_PATH, Paths.get("C:", "Program Files", "HxD", "HxD.exe").toString());
|
||||
}
|
||||
}
|
||||
|
@ -48,6 +48,7 @@
|
||||
<attr name="noIconInMenu" boolvalue="false"/>
|
||||
</file>
|
||||
<file name="org-sleuthkit-autopsy-casemodule-CasePropertiesAction.instance"/>
|
||||
<file name="org-sleuthkit-autopsy-casemodule-datasourceSummary-DataSourceSummaryAction.instance"/>
|
||||
<file name="org-sleuthkit-autopsy-casemodule-CaseDeleteAction.instance"/>
|
||||
<file name="org-sleuthkit-autopsy-casemodule-CaseSaveAction.instance">
|
||||
<attr name="delegate" newvalue="org.sleuthkit.autopsy.casemodule.CaseSaveAction"/>
|
||||
@ -182,6 +183,10 @@
|
||||
<attr name="originalFile" stringvalue="Actions/Case/org-sleuthkit-autopsy-casemodule-CasePropertiesAction.instance"/>
|
||||
<attr name="position" intvalue="301"/>
|
||||
</file>
|
||||
<file name="org-sleuthkit-autopsy-casemodule-datasourceSummary-DataSourceSummaryAction.shadow">
|
||||
<attr name="originalFile" stringvalue="Actions/Case/org-sleuthkit-autopsy-casemodule-datasourceSummary-DataSourceSummaryAction.instance"/>
|
||||
<attr name="position" intvalue="302"/>
|
||||
</file>
|
||||
<file name="org-netbeans-modules-print-action-PageSetupAction.shadow_hidden"/>
|
||||
<file name="org-netbeans-modules-print-action-PrintAction.shadow_hidden"/>
|
||||
<file name="org-openide-actions-SaveAction.shadow_hidden"/>
|
||||
|
@ -193,3 +193,34 @@ ViewPreferencesPanel.keepCurrentViewerRadioButton.text=Stay on the same file vie
|
||||
ViewPreferencesPanel.useBestViewerRadioButton.toolTipText=For example, change from Hex to Media when a JPEG is selected.
|
||||
ViewPreferencesPanel.useBestViewerRadioButton.text=Change to the most specific file viewer
|
||||
ViewPreferencesPanel.fileNameTranslationColumnCheckbox.text=Add column in result viewer for file name translation
|
||||
DataContentViewerHex.launchHxDButton.text=Launch in HxD
|
||||
ExternalViewerGlobalSettingsPanel.jButton2.text=jButton2
|
||||
ExternalViewerGlobalSettingsPanel.newRuleButton1.text=New Rule
|
||||
ExternalViewerGlobalSettingsPanel.newRuleButton.text=New Rule
|
||||
ExternalViewerGlobalSettingsPanel.browseHxDDirectory.text=Browse
|
||||
ExternalViewerGlobalSettingsPanel.jLabel2.text=Set launchable content viewer extensions
|
||||
ExternalViewerGlobalSettingsPanel.jLabel1.text=HxD Editor Path:
|
||||
ExternalViewerGlobalSettingsPanel.editRuleButton.text=Edit Rule
|
||||
ExternalViewerGlobalSettingsPanel.deleteRuleButton.text=Delete Rule
|
||||
ExternalViewerGlobalSettingsPanel.externalViewerTitleLabel.text=Set aplication viewer to use for files with specific mime types/extensions:
|
||||
ExternalViewerGlobalSettingsPanel.jTable1.columnModel.title1=Application
|
||||
ExternalViewerGlobalSettingsPanel.jTable1.columnModel.title0=Mime type/Extension
|
||||
AddExternalViewerRulePanel.nameLabel.text=MIME type or extension
|
||||
AddExternalViewerRulePanel.browseButton.text=Browse
|
||||
AddExternalViewerRulePanel.exePathTextField.text=
|
||||
AddExternalViewerRulePanel.exePathLabel.text=Path of the program to use for files with this type or extension
|
||||
AddExternalViewerRulePanel.extRadioButton.text=Extension
|
||||
AddExternalViewerRulePanel.mimeRadioButton.text=MIME type
|
||||
AddExternalViewerRulePanel.nameTextField.text=
|
||||
ExternalViewerGlobalSettingsPanel.jButton2.text_1=jButton2
|
||||
ExternalViewerGlobalSettingsPanel.newRuleButton1.text_1=New Rule
|
||||
ExternalViewerGlobalSettingsPanel.newRuleButton.text_1=New Rule
|
||||
ExternalViewerGlobalSettingsPanel.browseHxDDirectory.text_1=Browse
|
||||
ExternalViewerGlobalSettingsPanel.jLabel2.text_1=Set launchable content viewer extensions
|
||||
ExternalViewerGlobalSettingsPanel.jLabel1.text_1=HxD Editor Path:
|
||||
ExternalViewerGlobalSettingsPanel.HxDPath.text=C:/Program Files/HxD/HxD.exe
|
||||
ExternalViewerGlobalSettingsPanel.editRuleButton.text_1=Edit Rule
|
||||
ExternalViewerGlobalSettingsPanel.deleteRuleButton.text_1=Delete Rule
|
||||
ExternalViewerGlobalSettingsPanel.externalViewerTitleLabel.text_1=Set aplication viewer to use for files with specific mime types/extensions:
|
||||
ExternalViewerGlobalSettingsPanel.jTable1.columnModel.title1_1=Application
|
||||
ExternalViewerGlobalSettingsPanel.jTable1.columnModel.title0_1=Mime type/Extension
|
||||
|
@ -45,7 +45,7 @@
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="jScrollPane2" pref="100" max="32767" attributes="0"/>
|
||||
<Component id="jScrollPane2" pref="827" max="32767" attributes="0"/>
|
||||
<Component id="jScrollPane3" alignment="0" pref="0" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
@ -54,7 +54,7 @@
|
||||
<Group type="102" alignment="1" attributes="0">
|
||||
<Component id="jScrollPane2" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="jScrollPane3" pref="27" max="32767" attributes="0"/>
|
||||
<Component id="jScrollPane3" pref="239" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
@ -120,7 +120,9 @@
|
||||
<Component id="goToOffsetLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="goToOffsetTextField" min="-2" pref="79" max="-2" attributes="0"/>
|
||||
<EmptySpace pref="32" max="32767" attributes="0"/>
|
||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||
<Component id="launchHxDButton" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace pref="146" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
@ -140,7 +142,10 @@
|
||||
<Component id="goToPageLabel" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="goToPageTextField" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="goToOffsetLabel" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="goToOffsetTextField" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
<Group type="103" alignment="0" groupAlignment="3" attributes="0">
|
||||
<Component id="goToOffsetTextField" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="launchHxDButton" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
@ -289,6 +294,19 @@
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="goToOffsetTextFieldActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="launchHxDButton">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataContentViewerHex.launchHxDButton.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="launchHxDButtonActionPerformed"/>
|
||||
</Events>
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_AddingCodePost" type="java.lang.String" value="launchHxDButton.setEnabled(PlatformUtil.isWindowsOS());"/>
|
||||
</AuxValues>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
|
@ -21,7 +21,11 @@ package org.sleuthkit.autopsy.corecomponents;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.logging.Level;
|
||||
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
|
||||
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
@ -31,7 +35,13 @@ import javax.swing.text.BadLocationException;
|
||||
import javax.swing.text.Utilities;
|
||||
import org.openide.nodes.Node;
|
||||
import org.openide.util.lookup.ServiceProvider;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.core.UserPreferences;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
|
||||
import static org.sleuthkit.autopsy.corecomponents.Bundle.*;
|
||||
import org.sleuthkit.autopsy.coreutils.FileUtil;
|
||||
import org.sleuthkit.autopsy.datamodel.ContentUtils;
|
||||
import org.sleuthkit.autopsy.datamodel.DataConversion;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.TskException;
|
||||
@ -105,6 +115,7 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont
|
||||
goToPageLabel = new javax.swing.JLabel();
|
||||
goToOffsetLabel = new javax.swing.JLabel();
|
||||
goToOffsetTextField = new javax.swing.JTextField();
|
||||
launchHxDButton = new javax.swing.JButton();
|
||||
|
||||
copyMenuItem.setText(org.openide.util.NbBundle.getMessage(DataContentViewerHex.class, "DataContentViewerHex.copyMenuItem.text")); // NOI18N
|
||||
rightClickMenu.add(copyMenuItem);
|
||||
@ -187,6 +198,13 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont
|
||||
}
|
||||
});
|
||||
|
||||
launchHxDButton.setText(org.openide.util.NbBundle.getMessage(DataContentViewerHex.class, "DataContentViewerHex.launchHxDButton.text")); // NOI18N
|
||||
launchHxDButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
launchHxDButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
javax.swing.GroupLayout hexViewerPanelLayout = new javax.swing.GroupLayout(hexViewerPanel);
|
||||
hexViewerPanel.setLayout(hexViewerPanelLayout);
|
||||
hexViewerPanelLayout.setHorizontalGroup(
|
||||
@ -214,7 +232,9 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont
|
||||
.addComponent(goToOffsetLabel)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(goToOffsetTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 79, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addContainerGap(32, Short.MAX_VALUE))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addComponent(launchHxDButton)
|
||||
.addContainerGap(146, Short.MAX_VALUE))
|
||||
);
|
||||
hexViewerPanelLayout.setVerticalGroup(
|
||||
hexViewerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
@ -231,17 +251,21 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont
|
||||
.addComponent(goToPageLabel)
|
||||
.addComponent(goToPageTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(goToOffsetLabel)
|
||||
.addComponent(goToOffsetTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addGroup(hexViewerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(goToOffsetTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(launchHxDButton)))
|
||||
.addGap(0, 0, 0))
|
||||
);
|
||||
|
||||
launchHxDButton.setEnabled(PlatformUtil.isWindowsOS());
|
||||
|
||||
jScrollPane2.setViewportView(hexViewerPanel);
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 100, Short.MAX_VALUE)
|
||||
.addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 827, Short.MAX_VALUE)
|
||||
.addComponent(jScrollPane3, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
@ -249,7 +273,7 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
|
||||
.addComponent(jScrollPane2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(jScrollPane3, javax.swing.GroupLayout.DEFAULT_SIZE, 27, Short.MAX_VALUE))
|
||||
.addComponent(jScrollPane3, javax.swing.GroupLayout.DEFAULT_SIZE, 239, Short.MAX_VALUE))
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
@ -334,6 +358,37 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont
|
||||
}
|
||||
}//GEN-LAST:event_goToOffsetTextFieldActionPerformed
|
||||
|
||||
@NbBundle.Messages({"DataContentViewerHex.launchError=Unable to launch HxD Editor. "
|
||||
+ "Please set-up the HdX install location in Tools -> Options -> External Viewer"})
|
||||
private void launchHxDButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_launchHxDButtonActionPerformed
|
||||
try {
|
||||
File HdXExecutable = new File(UserPreferences.getExternalHexEditorPath());
|
||||
if(!HdXExecutable.exists() || !HdXExecutable.canExecute()) {
|
||||
JOptionPane.showMessageDialog(null, DataContentViewerHex_launchError());
|
||||
return;
|
||||
}
|
||||
|
||||
String tempDirectory = Case.getCurrentCaseThrows().getTempDirectory();
|
||||
File dataSourceInTempDirectory = Paths.get(tempDirectory,
|
||||
FileUtil.escapeFileName(dataSource.getId() + dataSource.getName())).toFile();
|
||||
ContentUtils.writeToFile(dataSource, dataSourceInTempDirectory);
|
||||
|
||||
try {
|
||||
ProcessBuilder launchHdXExecutable = new ProcessBuilder();
|
||||
launchHdXExecutable.command(String.format("\"%s\" \"%s\"",
|
||||
HdXExecutable.getAbsolutePath(),
|
||||
dataSourceInTempDirectory.getAbsolutePath()));
|
||||
launchHdXExecutable.start();
|
||||
} catch (IOException ex) {
|
||||
JOptionPane.showMessageDialog(null, DataContentViewerHex_launchError());
|
||||
dataSourceInTempDirectory.delete();
|
||||
}
|
||||
} catch (NoCurrentCaseException | IOException ex) {
|
||||
logger.log(Level.SEVERE, "Unable to copy file into temp directory", ex);
|
||||
JOptionPane.showMessageDialog(null, DataContentViewerHex_launchError());
|
||||
}
|
||||
}//GEN-LAST:event_launchHxDButtonActionPerformed
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JMenuItem copyMenuItem;
|
||||
private javax.swing.JLabel currentPageLabel;
|
||||
@ -344,6 +399,7 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont
|
||||
private javax.swing.JPanel hexViewerPanel;
|
||||
private javax.swing.JScrollPane jScrollPane2;
|
||||
private javax.swing.JScrollPane jScrollPane3;
|
||||
private javax.swing.JButton launchHxDButton;
|
||||
private javax.swing.JButton nextPageButton;
|
||||
private javax.swing.JLabel ofLabel;
|
||||
private javax.swing.JTextArea outputTextArea;
|
||||
@ -504,6 +560,7 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont
|
||||
goToPageLabel.setVisible(isVisible);
|
||||
goToOffsetTextField.setVisible(isVisible);
|
||||
goToOffsetLabel.setVisible(isVisible);
|
||||
launchHxDButton.setVisible(isVisible);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -528,17 +585,4 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont
|
||||
public Component getComponent() {
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* Show the right click menu only if evt is the correct mouse event
|
||||
*/
|
||||
private void maybeShowPopup(java.awt.event.MouseEvent evt) {
|
||||
if (evt.isPopupTrigger()) {
|
||||
rightClickMenu.setLocation(evt.getLocationOnScreen());
|
||||
rightClickMenu.setVisible(true);
|
||||
copyMenuItem.setEnabled(outputTextArea.getSelectedText() != null);
|
||||
} else {
|
||||
rightClickMenu.setVisible(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ import org.openide.util.NbBundle;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.casemodule.datasourceSummary.ViewSummaryInformationAction;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.directorytree.ExplorerNodeActionVisitor;
|
||||
import org.sleuthkit.autopsy.directorytree.FileSearchAction;
|
||||
@ -108,6 +109,7 @@ public class ImageNode extends AbstractContentNode<Image> {
|
||||
actionsList.addAll(ExplorerNodeActionVisitor.getActions(content));
|
||||
actionsList.add(new FileSearchAction(
|
||||
Bundle.ImageNode_getActions_openFileSearchByAttr_text()));
|
||||
actionsList.add(new ViewSummaryInformationAction(content.getId()));
|
||||
actionsList.add(new RunIngestModulesAction(Collections.<Content>singletonList(content)));
|
||||
actionsList.add(new NewWindowViewAction(
|
||||
NbBundle.getMessage(this.getClass(), "ImageNode.getActions.viewInNewWin.text"), this));
|
||||
|
@ -23,6 +23,7 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import javax.swing.Action;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.casemodule.datasourceSummary.ViewSummaryInformationAction;
|
||||
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
|
||||
import org.sleuthkit.autopsy.directorytree.ExtractAction;
|
||||
import org.sleuthkit.autopsy.directorytree.FileSearchAction;
|
||||
@ -63,6 +64,7 @@ public abstract class SpecialDirectoryNode extends AbstractAbstractFileNode<Spec
|
||||
actions.add(null); // creates a menu separator
|
||||
actions.add(new FileSearchAction(Bundle.ImageNode_getActions_openFileSearchByAttr_text()));
|
||||
if (content.isDataSource()) {
|
||||
actions.add(new ViewSummaryInformationAction(content.getId()));
|
||||
actions.add(new RunIngestModulesAction(Collections.<Content>singletonList(content)));
|
||||
} else {
|
||||
actions.add(new RunIngestModulesAction(content));
|
||||
|
@ -0,0 +1,207 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2018-2019 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.datamodel.utils;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import static java.util.Arrays.asList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import javax.imageio.ImageIO;
|
||||
import static org.apache.commons.collections4.ListUtils.removeAll;
|
||||
import org.openide.util.NbBundle;
|
||||
|
||||
/**
|
||||
* Utilities for dealing with file/mime-types
|
||||
*/
|
||||
public final class FileTypeUtils {
|
||||
|
||||
private static final ImmutableSet<String> IMAGE_MIME_TYPES
|
||||
= new ImmutableSet.Builder<String>()
|
||||
.addAll(removeAll(asList(ImageIO.getReaderMIMETypes()),
|
||||
asList("application/octet-stream"))) //this claims to be supported, but is not really an image.
|
||||
.add("image/bmp", //NON-NLS
|
||||
"image/gif", //NON-NLS
|
||||
"image/jpeg", //NON-NLS
|
||||
"image/png", //NON-NLS
|
||||
"image/tiff", //NON-NLS
|
||||
"image/vnd.adobe.photoshop", //NON-NLS
|
||||
"image/x-raw-nikon", //NON-NLS
|
||||
"image/x-ms-bmp", //NON-NLS
|
||||
"image/x-icon", //NON-NLS
|
||||
"image/webp", //NON-NLS
|
||||
"image/vnd.microsoft.icon", //NON-NLS
|
||||
"image/x-rgb", //NON-NLS
|
||||
"image/x-ms-bmp", //NON-NLS
|
||||
"image/x-xbitmap", //NON-NLS
|
||||
"image/x-portable-graymap", //NON-NLS
|
||||
"image/x-portable-bitmap" //NON-NLS
|
||||
).build();
|
||||
private static final ImmutableSet<String> AUDIO_MIME_TYPES
|
||||
= new ImmutableSet.Builder<String>()
|
||||
.add("audio/midi", //NON-NLS
|
||||
"audio/mpeg", //NON-NLS
|
||||
"audio/webm", //NON-NLS
|
||||
"audio/ogg", //NON-NLS
|
||||
"audio/wav", //NON-NLS
|
||||
"audio/vnd.wave", //NON-NLS
|
||||
"audio/x-ms-wma"//NON-NLS
|
||||
).build();
|
||||
private static final ImmutableSet<String> VIDEO_MIME_TYPES
|
||||
= new ImmutableSet.Builder<String>()
|
||||
.add("video/webm", //NON-NLS
|
||||
"video/3gpp", //NON-NLS
|
||||
"video/3gpp2", //NON-NLS
|
||||
"video/ogg", //NON-NLS
|
||||
"video/mpeg", //NON-NLS
|
||||
"video/mp4", //NON-NLS
|
||||
"video/quicktime", //NON-NLS
|
||||
"video/x-msvideo", //NON-NLS
|
||||
"video/x-flv", //NON-NLS
|
||||
"video/x-m4v", //NON-NLS
|
||||
"video/x-ms-wmv"//NON-NLS
|
||||
).build();
|
||||
private static final ImmutableSet<String> DOCUMENT_MIME_TYPES
|
||||
= new ImmutableSet.Builder<String>()
|
||||
.add("text/plain", //NON-NLS
|
||||
"text/css", //NON-NLS
|
||||
"text/html", //NON-NLS
|
||||
"text/csv", //NON-NLS
|
||||
"text/xml", //NON-NLS
|
||||
"text/x-log", //NON-NLS
|
||||
"application/rtf", //NON-NLS
|
||||
"application/pdf", //NON-NLS
|
||||
"application/json", //NON-NLS
|
||||
"application/javascript", //NON-NLS
|
||||
"application/xml", //NON-NLS
|
||||
"application/x-msoffice", //NON-NLS
|
||||
"application/x-ooxml", //NON-NLS
|
||||
"application/msword", //NON-NLS
|
||||
"application/msword2", //NON-NLS
|
||||
"application/vnd.wordperfect", //NON-NLS
|
||||
"application/vnd.openxmlformats-officedocument.wordprocessingml.document", //NON-NLS
|
||||
"application/vnd.ms-powerpoint", //NON-NLS
|
||||
"application/vnd.openxmlformats-officedocument.presentationml.presentation", //NON-NLS
|
||||
"application/vnd.ms-excel", //NON-NLS
|
||||
"application/vnd.ms-excel.sheet.4", //NON-NLS
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", //NON-NLS
|
||||
"application/vnd.oasis.opendocument.presentation", //NON-NLS
|
||||
"application/vnd.oasis.opendocument.spreadsheet", //NON-NLS
|
||||
"application/vnd.oasis.opendocument.text" //NON-NLS
|
||||
).build();
|
||||
private static final ImmutableSet<String> EXECUTABLE_MIME_TYPES
|
||||
= new ImmutableSet.Builder<String>()
|
||||
.add("application/x-bat",//NON-NLS
|
||||
"application/x-dosexec",//NON-NLS
|
||||
"application/vnd.microsoft.portable-executable",//NON-NLS
|
||||
"application/x-msdownload",//NON-NLS
|
||||
"application/exe",//NON-NLS
|
||||
"application/x-exe",//NON-NLS
|
||||
"application/dos-exe",//NON-NLS
|
||||
"vms/exe",//NON-NLS
|
||||
"application/x-winexe",//NON-NLS
|
||||
"application/msdos-windows",//NON-NLS
|
||||
"application/x-msdos-program"//NON-NLS
|
||||
).build();
|
||||
private static final ImmutableSet<String> MULTI_MEDIA_MIME_TYPES
|
||||
= new ImmutableSet.Builder<String>()
|
||||
.addAll(IMAGE_MIME_TYPES)
|
||||
.addAll(AUDIO_MIME_TYPES)
|
||||
.addAll(VIDEO_MIME_TYPES)
|
||||
.build();
|
||||
private static final ImmutableSet<String> VISUAL_MEDIA_MIME_TYPES
|
||||
= new ImmutableSet.Builder<String>()
|
||||
.addAll(IMAGE_MIME_TYPES)
|
||||
.addAll(VIDEO_MIME_TYPES)
|
||||
.add("application/vnd.ms-asf", //NON-NLS
|
||||
"application/vnd.rn-realmedia", //NON-NLS
|
||||
"application/x-shockwave-flash" //NON-NLS
|
||||
).build();
|
||||
|
||||
private FileTypeUtils() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Enum of categories/groups of file types.
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"FileTypeCategory.Audio.displayName=Audio",
|
||||
"FileTypeCategory.Video.displayName=Video",
|
||||
"FileTypeCategory.Image.displayName=Image",
|
||||
"FileTypeCategory.Media.displayName=Media",
|
||||
"FileTypeCategory.Visual.displayName=Visual",
|
||||
"FileTypeCategory.Documents.displayName=Documents",
|
||||
"FileTypeCategory.Executables.displayName=Executables"})
|
||||
public enum FileTypeCategory {
|
||||
|
||||
IMAGE(Bundle.FileTypeCategory_Image_displayName(),
|
||||
IMAGE_MIME_TYPES,
|
||||
Collections.emptyList()),
|
||||
VIDEO(Bundle.FileTypeCategory_Video_displayName(),
|
||||
VIDEO_MIME_TYPES,
|
||||
Collections.emptyList()),
|
||||
AUDIO(Bundle.FileTypeCategory_Audio_displayName(),
|
||||
AUDIO_MIME_TYPES,
|
||||
Collections.emptyList()),
|
||||
/**
|
||||
* Images, Videos, flash Animations, etc
|
||||
*/
|
||||
VISUAL(Bundle.FileTypeCategory_Media_displayName(),
|
||||
VISUAL_MEDIA_MIME_TYPES,
|
||||
Collections.emptyList()),
|
||||
/**
|
||||
* VISUAL plus AUDIO.
|
||||
*/
|
||||
MEDIA(Bundle.FileTypeCategory_Media_displayName(),
|
||||
MULTI_MEDIA_MIME_TYPES,
|
||||
Collections.emptyList()),
|
||||
EXECUTABLE(Bundle.FileTypeCategory_Executables_displayName(),
|
||||
EXECUTABLE_MIME_TYPES,
|
||||
Collections.emptyList()),
|
||||
/**
|
||||
* (Plain) Text and "Office" documents.
|
||||
*/
|
||||
DOCUMENTS(Bundle.FileTypeCategory_Documents_displayName(),
|
||||
DOCUMENT_MIME_TYPES,
|
||||
Collections.emptyList());
|
||||
|
||||
private final String displayName;
|
||||
private final ImmutableSet<String> mediaTypes;
|
||||
private final ImmutableSet<String> extensions;
|
||||
|
||||
private FileTypeCategory(String displayName, Collection<String> mediaTypes, Collection<String> extensions) {
|
||||
this.displayName = displayName;
|
||||
this.mediaTypes = ImmutableSet.copyOf(mediaTypes);
|
||||
this.extensions = ImmutableSet.copyOf(extensions);
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public ImmutableSet<String> getMediaTypes() {
|
||||
return mediaTypes;
|
||||
|
||||
}
|
||||
|
||||
public ImmutableSet<String> getExtensions() {
|
||||
throw new UnsupportedOperationException("This method is not implemented yet."); //just to be explicit.
|
||||
}
|
||||
}
|
||||
}
|
@ -90,12 +90,6 @@ ExtractUnallocAction.done.errMsg.msg=Error extracting unallocated space\: {0}
|
||||
ExtractAction.done.notifyMsg.extractErr=Error extracting files\: {0}
|
||||
OptionsCategory_Name_ExternalViewer=External Viewer
|
||||
OptionsCategory_Keywords_ExternalViewer=ExternalViewer
|
||||
ExternalViewerGlobalSettingsPanel.newRuleButton.text=New Rule
|
||||
ExternalViewerGlobalSettingsPanel.editRuleButton.text=Edit Rule
|
||||
ExternalViewerGlobalSettingsPanel.deleteRuleButton.text=Delete Rule
|
||||
ExternalViewerGlobalSettingsPanel.externalViewerTitleLabel.text=Add your custom rules for external viewers
|
||||
ExternalViewerGlobalSettingsPanel.ruleListLabel.text=MIME type and extensions
|
||||
ExternalViewerGlobalSettingsPanel.exePathLabel.text=Program associated with this MIME type or extension
|
||||
ExternalViewerGlobalSettingsPanel.exePathLabel.MIME.text=Program associated with this MIME type
|
||||
ExternalViewerGlobalSettingsPanel.exePathLabel.EXT.text=Program associated with this extension
|
||||
ExternalViewerGlobalSettingsPanel.exePathLabel.empty.text=No MIME type or extension selected
|
||||
@ -108,7 +102,6 @@ ExternalViewerGlobalSettingsPanel.JOptionPane.invalidExt.message=The extension i
|
||||
ExternalViewerGlobalSettingsPanel.JOptionPane.invalidExt.title=Invalid extension
|
||||
ExternalViewerGlobalSettingsPanel.JOptionPane.invalidExePath.message=The path to the program executable is invalid
|
||||
ExternalViewerGlobalSettingsPanel.JOptionPane.invalidExePath.title=Invalid Path
|
||||
ExternalViewerGlobalSettingsPanel.exePathNameLabel.text=No MIME type or extension currently selected
|
||||
ExternalViewerGlobalSettingsPanel.JOptionPane.ruleAlreadyExists.message=A rule already exists with this MIME type or extension. Please edit that one.
|
||||
ExternalViewerGlobalSettingsPanel.JOptionPane.ruleAlreadyExists.title=Rule not added
|
||||
AddExternalViewerRulePanel.mimeRadioButton.text=MIME type
|
||||
@ -124,3 +117,16 @@ GroupDataSourcesDialog.yesButton.text=Yes
|
||||
GroupDataSourcesDialog.noButton.text=No
|
||||
GroupDataSourcesDialog.title=Group by Data Source?
|
||||
DirectoryTreeTopComponent.openViewPreferencesButton.text=
|
||||
ExternalViewerGlobalSettingsPanel.jTable1.columnModel.title3=Title 4
|
||||
ExternalViewerGlobalSettingsPanel.jTable1.columnModel.title2=Title 3
|
||||
ExternalViewerGlobalSettingsPanel.jTable1.columnModel.title3_1=Title 4
|
||||
ExternalViewerGlobalSettingsPanel.jTable1.columnModel.title2_1=Title 3
|
||||
ExternalViewerGlobalSettingsPanel.newRuleButton1.text=New Rule
|
||||
ExternalViewerGlobalSettingsPanel.editRuleButton.text=Edit Rule
|
||||
ExternalViewerGlobalSettingsPanel.deleteRuleButton.text=Delete Rule
|
||||
ExternalViewerGlobalSettingsPanel.externalViewerTitleLabel.text=Add your custom rules for external viewers:
|
||||
ExternalViewerGlobalSettingsPanel.newRuleButton.text=New Rule
|
||||
ExternalViewerGlobalSettingsPanel.jButton2.text=jButton2
|
||||
ExternalViewerGlobalSettingsPanel.browseHxDDirectory.text=Browse
|
||||
ExternalViewerGlobalSettingsPanel.HxDLabel.text=HxD Editor Path:
|
||||
ExternalViewerGlobalSettingsPanel.ContentViewerExtensionLabel.text=Add content viewer extensions:
|
||||
|
@ -1,6 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
|
||||
<NonVisualComponents>
|
||||
<Component class="javax.swing.JButton" name="newRuleButton1">
|
||||
<Properties>
|
||||
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||
<Image iconType="3" name="/org/sleuthkit/autopsy/images/add16.png"/>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/directorytree/Bundle.properties" key="ExternalViewerGlobalSettingsPanel.newRuleButton1.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[111, 25]"/>
|
||||
</Property>
|
||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[111, 25]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="jButton2">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/directorytree/Bundle.properties" key="ExternalViewerGlobalSettingsPanel.jButton2.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
</NonVisualComponents>
|
||||
<Properties>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[701, 453]"/>
|
||||
@ -21,12 +46,12 @@
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="jPanel1" alignment="1" max="32767" attributes="0"/>
|
||||
<Component id="jPanel1" alignment="0" pref="1191" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="jPanel1" alignment="1" max="32767" attributes="0"/>
|
||||
<Component id="jPanel1" alignment="0" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
@ -41,241 +66,251 @@
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="externalViewerTitleLabel" pref="681" max="32767" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="jScrollPane1" max="32767" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="102" attributes="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="jPanel2" min="-2" max="-2" attributes="0"/>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||
<Component id="jPanel3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
<EmptySpace min="-2" pref="547" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="externalViewerTitleLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace pref="428" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<EmptySpace min="-2" pref="32" max="-2" attributes="0"/>
|
||||
<Component id="jScrollPane1" max="32767" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||
<Component id="jPanel2" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace type="separate" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="jPanel3" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace pref="83" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JLabel" name="externalViewerTitleLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/directorytree/Bundle.properties" key="ExternalViewerGlobalSettingsPanel.externalViewerTitleLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Container class="javax.swing.JScrollPane" name="jScrollPane1">
|
||||
<Container class="javax.swing.JPanel" name="jPanel2">
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JSplitPane" name="jSplitPane1">
|
||||
<Properties>
|
||||
<Property name="dividerSize" type="int" value="1"/>
|
||||
</Properties>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JPanel" name="exePanel">
|
||||
<Properties>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[311, 224]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
|
||||
<JSplitPaneConstraints position="right"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="jScrollPane4" min="-2" pref="634" max="-2" attributes="0"/>
|
||||
<Group type="102" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="exePathLabel" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="exePathNameLabel" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="externalViewerTitleLabel" min="-2" max="-2" attributes="0"/>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Component id="newRuleButton" min="-2" pref="97" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="editRuleButton" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="deleteRuleButton" min="-2" pref="109" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
<EmptySpace max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="exePathLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="exePathNameLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace pref="355" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<Component id="externalViewerTitleLabel" max="32767" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="12" max="-2" attributes="0"/>
|
||||
<Component id="jScrollPane4" min="-2" pref="219" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="newRuleButton" alignment="3" max="32767" attributes="0"/>
|
||||
<Component id="editRuleButton" alignment="3" max="32767" attributes="0"/>
|
||||
<Component id="deleteRuleButton" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JLabel" name="exePathLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/directorytree/Bundle.properties" key="ExternalViewerGlobalSettingsPanel.exePathLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="exePathNameLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/directorytree/Bundle.properties" key="ExternalViewerGlobalSettingsPanel.exePathNameLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Container class="javax.swing.JPanel" name="rulesPanel">
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
|
||||
<JSplitPaneConstraints position="left"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JButton" name="newRuleButton">
|
||||
<Properties>
|
||||
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||
<Image iconType="3" name="/org/sleuthkit/autopsy/images/add16.png"/>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/directorytree/Bundle.properties" key="ExternalViewerGlobalSettingsPanel.newRuleButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[111, 25]"/>
|
||||
</Property>
|
||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[111, 25]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="newRuleButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Container class="javax.swing.JScrollPane" name="jScrollPane4">
|
||||
<Properties>
|
||||
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
|
||||
<Border info="org.netbeans.modules.form.compat2.border.LineBorderInfo">
|
||||
<LineBorder>
|
||||
<Color PropertyName="color" blue="cc" green="cc" red="cc" type="rgb"/>
|
||||
</LineBorder>
|
||||
</Border>
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="ruleListLabel" max="32767" attributes="0"/>
|
||||
<Group type="102" alignment="1" attributes="0">
|
||||
<EmptySpace min="-2" pref="6" max="-2" attributes="0"/>
|
||||
<Component id="newRuleButton" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="16" max="-2" attributes="0"/>
|
||||
<Component id="editRuleButton" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="16" max="-2" attributes="0"/>
|
||||
<Component id="deleteRuleButton" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<Component id="jScrollPane2" alignment="0" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||
<Component id="ruleListLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="jScrollPane2" max="32767" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="12" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="newRuleButton" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="editRuleButton" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="deleteRuleButton" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JLabel" name="ruleListLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/directorytree/Bundle.properties" key="ExternalViewerGlobalSettingsPanel.ruleListLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="newRuleButton">
|
||||
<Properties>
|
||||
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||
<Image iconType="3" name="/org/sleuthkit/autopsy/images/add16.png"/>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/directorytree/Bundle.properties" key="ExternalViewerGlobalSettingsPanel.newRuleButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[111, 25]"/>
|
||||
</Property>
|
||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[111, 25]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="newRuleButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="editRuleButton">
|
||||
<Properties>
|
||||
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||
<Image iconType="3" name="/org/sleuthkit/autopsy/images/edit16.png"/>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/directorytree/Bundle.properties" key="ExternalViewerGlobalSettingsPanel.editRuleButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[111, 25]"/>
|
||||
</Property>
|
||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[111, 25]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="editRuleButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="deleteRuleButton">
|
||||
<Properties>
|
||||
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||
<Image iconType="3" name="/org/sleuthkit/autopsy/images/delete16.png"/>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/directorytree/Bundle.properties" key="ExternalViewerGlobalSettingsPanel.deleteRuleButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[111, 25]"/>
|
||||
</Property>
|
||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[111, 25]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="deleteRuleButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Container class="javax.swing.JScrollPane" name="jScrollPane2">
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JList" name="rulesList">
|
||||
<Properties>
|
||||
<Property name="model" type="javax.swing.ListModel" editor="org.netbeans.modules.form.editors2.ListModelEditor">
|
||||
<StringArray count="0"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="<ExternalViewerRule>"/>
|
||||
</AuxValues>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JTable" name="ExternalViewerRulesTable">
|
||||
<Properties>
|
||||
<Property name="columnModel" type="javax.swing.table.TableColumnModel" editor="org.netbeans.modules.form.editors2.TableColumnModelEditor">
|
||||
<TableColumnModel selectionModel="0">
|
||||
<Column maxWidth="-1" minWidth="-1" prefWidth="-1" resizable="true">
|
||||
<Title editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/directorytree/Bundle.properties" key="ExternalViewerGlobalSettingsPanel.ExternalViewerRulesTable.columnModel.title0" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Title>
|
||||
<Editor/>
|
||||
<Renderer/>
|
||||
</Column>
|
||||
<Column maxWidth="-1" minWidth="-1" prefWidth="-1" resizable="true">
|
||||
<Title editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/directorytree/Bundle.properties" key="ExternalViewerGlobalSettingsPanel.ExternalViewerRulesTable.columnModel.title1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Title>
|
||||
<Editor/>
|
||||
<Renderer/>
|
||||
</Column>
|
||||
</TableColumnModel>
|
||||
</Property>
|
||||
<Property name="tableHeader" type="javax.swing.table.JTableHeader" editor="org.netbeans.modules.form.editors2.JTableHeaderEditor">
|
||||
<TableHeader reorderingAllowed="true" resizingAllowed="true"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new javax.swing.JTable()"/>
|
||||
</AuxValues>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Component class="javax.swing.JLabel" name="externalViewerTitleLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/directorytree/Bundle.properties" key="ExternalViewerGlobalSettingsPanel.externalViewerTitleLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="deleteRuleButton">
|
||||
<Properties>
|
||||
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||
<Image iconType="3" name="/org/sleuthkit/autopsy/images/delete16.png"/>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/directorytree/Bundle.properties" key="ExternalViewerGlobalSettingsPanel.deleteRuleButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[111, 25]"/>
|
||||
</Property>
|
||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[111, 25]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="deleteRuleButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="editRuleButton">
|
||||
<Properties>
|
||||
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||
<Image iconType="3" name="/org/sleuthkit/autopsy/images/edit16.png"/>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/directorytree/Bundle.properties" key="ExternalViewerGlobalSettingsPanel.editRuleButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[111, 25]"/>
|
||||
</Property>
|
||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[111, 25]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="editRuleButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Container class="javax.swing.JPanel" name="jPanel3">
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<Component id="ContentViewerExtensionLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<Group type="102" attributes="0">
|
||||
<EmptySpace min="-2" pref="21" max="-2" attributes="0"/>
|
||||
<Component id="HxDLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="HxDPath" pref="441" max="32767" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="browseHxDDirectory" min="-2" pref="82" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<Component id="ContentViewerExtensionLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="HxDPath" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="HxDLabel" alignment="3" min="-2" pref="23" max="-2" attributes="0"/>
|
||||
<Component id="browseHxDDirectory" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JTextField" name="HxDPath">
|
||||
<Properties>
|
||||
<Property name="editable" type="boolean" value="false"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="HxDLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/directorytree/Bundle.properties" key="ExternalViewerGlobalSettingsPanel.HxDLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="ContentViewerExtensionLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/directorytree/Bundle.properties" key="ExternalViewerGlobalSettingsPanel.ContentViewerExtensionLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="browseHxDDirectory">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/directorytree/Bundle.properties" key="ExternalViewerGlobalSettingsPanel.browseHxDDirectory.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="browseHxDDirectoryActionPerformed"/>
|
||||
</Events>
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_AddingCodePost" type="java.lang.String" value="browseHxDDirectory.setEnabled(PlatformUtil.isWindowsOS());"/>
|
||||
</AuxValues>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
|
@ -1,15 +1,15 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
*
|
||||
* Copyright 2011-2018 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -18,16 +18,21 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.directorytree;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import javax.swing.DefaultListModel;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
import org.netbeans.spi.options.OptionsPanelController;
|
||||
import org.openide.util.NbBundle;
|
||||
import java.util.List;
|
||||
import javax.swing.JFileChooser;
|
||||
import org.sleuthkit.autopsy.corecomponents.OptionsPanel;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.netbeans.spi.options.OptionsPanelController;
|
||||
import org.sleuthkit.autopsy.casemodule.GeneralFilter;
|
||||
import org.sleuthkit.autopsy.core.UserPreferences;
|
||||
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
|
||||
|
||||
/**
|
||||
* An options panel for the user to create, edit, and delete associations for
|
||||
@ -37,39 +42,67 @@ import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
||||
final class ExternalViewerGlobalSettingsPanel extends javax.swing.JPanel implements OptionsPanel {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(ExternalViewerGlobalSettingsPanel.class.getName());
|
||||
private DefaultListModel<ExternalViewerRule> rulesListModel;
|
||||
private java.util.List<ExternalViewerRule> rules;
|
||||
private ExternalViewerGlobalSettingsTableModel tableModel;
|
||||
|
||||
public ExternalViewerGlobalSettingsPanel() {
|
||||
this(new ExternalViewerGlobalSettingsTableModel(new String[] {
|
||||
"Mime type/Extension", "Application"}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new form ExternalViewerGlobalSettingsPanel
|
||||
*/
|
||||
public ExternalViewerGlobalSettingsPanel() {
|
||||
public ExternalViewerGlobalSettingsPanel(ExternalViewerGlobalSettingsTableModel tableModel) {
|
||||
initComponents();
|
||||
customizeComponents();
|
||||
this.tableModel = tableModel;
|
||||
customizeComponents(tableModel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes field variables. Adds a listener to the list of rules.
|
||||
*/
|
||||
private void customizeComponents() {
|
||||
rulesListModel = new DefaultListModel<>();
|
||||
rules = new ArrayList<>();
|
||||
rulesList.setModel(rulesListModel);
|
||||
rulesList.addListSelectionListener(new ListSelectionListener() {
|
||||
@Override
|
||||
public void valueChanged(ListSelectionEvent e) {
|
||||
if (e.getValueIsAdjusting() == false) {
|
||||
if (rulesList.getSelectedIndex() == -1) {
|
||||
clearExePath();
|
||||
} else {
|
||||
populateExePath();
|
||||
}
|
||||
}
|
||||
}
|
||||
private void customizeComponents(ExternalViewerGlobalSettingsTableModel tableModel) {
|
||||
ExternalViewerRulesTable.setModel(tableModel);
|
||||
ExternalViewerRulesTable.setAutoCreateRowSorter(true);
|
||||
ExternalViewerRulesTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
ListSelectionModel selectionModel = ExternalViewerRulesTable.getSelectionModel();
|
||||
|
||||
selectionModel.addListSelectionListener((ListSelectionEvent selection) -> {
|
||||
enableButtons();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Simulate the delete rule button click action.
|
||||
*
|
||||
* @param selectedIndex Index to delete in JTable
|
||||
*/
|
||||
public void deleteRuleButtonClick(int selectedIndex) {
|
||||
setSelectionInterval(selectedIndex, selectedIndex);
|
||||
deleteRuleButton.getListeners(ActionListener.class)[0].actionPerformed(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulate selecting an entry in the JTable.
|
||||
*
|
||||
* In single selection mode, only the endIndex is used.
|
||||
*
|
||||
* @param rowIndex Index to select
|
||||
*/
|
||||
public void setSelectionInterval(int startingIndex, int endIndex) {
|
||||
ExternalViewerRulesTable.getSelectionModel().setSelectionInterval(startingIndex, endIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether the index is selected.
|
||||
*
|
||||
* @param index
|
||||
* @return
|
||||
*/
|
||||
public boolean isSelected(int index) {
|
||||
return ExternalViewerRulesTable.getSelectionModel().isSelectedIndex(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
@ -79,60 +112,33 @@ final class ExternalViewerGlobalSettingsPanel extends javax.swing.JPanel impleme
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
|
||||
newRuleButton1 = new javax.swing.JButton();
|
||||
jButton2 = new javax.swing.JButton();
|
||||
jPanel1 = new javax.swing.JPanel();
|
||||
externalViewerTitleLabel = new javax.swing.JLabel();
|
||||
jScrollPane1 = new javax.swing.JScrollPane();
|
||||
jSplitPane1 = new javax.swing.JSplitPane();
|
||||
exePanel = new javax.swing.JPanel();
|
||||
exePathLabel = new javax.swing.JLabel();
|
||||
exePathNameLabel = new javax.swing.JLabel();
|
||||
rulesPanel = new javax.swing.JPanel();
|
||||
ruleListLabel = new javax.swing.JLabel();
|
||||
jPanel2 = new javax.swing.JPanel();
|
||||
newRuleButton = new javax.swing.JButton();
|
||||
editRuleButton = new javax.swing.JButton();
|
||||
jScrollPane4 = new javax.swing.JScrollPane();
|
||||
ExternalViewerRulesTable = new javax.swing.JTable();
|
||||
externalViewerTitleLabel = new javax.swing.JLabel();
|
||||
deleteRuleButton = new javax.swing.JButton();
|
||||
jScrollPane2 = new javax.swing.JScrollPane();
|
||||
rulesList = new javax.swing.JList<>();
|
||||
editRuleButton = new javax.swing.JButton();
|
||||
jPanel3 = new javax.swing.JPanel();
|
||||
HxDPath = new javax.swing.JTextField();
|
||||
HxDLabel = new javax.swing.JLabel();
|
||||
ContentViewerExtensionLabel = new javax.swing.JLabel();
|
||||
browseHxDDirectory = new javax.swing.JButton();
|
||||
|
||||
newRuleButton1.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/add16.png"))); // NOI18N
|
||||
org.openide.awt.Mnemonics.setLocalizedText(newRuleButton1, org.openide.util.NbBundle.getMessage(ExternalViewerGlobalSettingsPanel.class, "ExternalViewerGlobalSettingsPanel.newRuleButton1.text")); // NOI18N
|
||||
newRuleButton1.setMaximumSize(new java.awt.Dimension(111, 25));
|
||||
newRuleButton1.setMinimumSize(new java.awt.Dimension(111, 25));
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(jButton2, org.openide.util.NbBundle.getMessage(ExternalViewerGlobalSettingsPanel.class, "ExternalViewerGlobalSettingsPanel.jButton2.text")); // NOI18N
|
||||
|
||||
setPreferredSize(new java.awt.Dimension(701, 453));
|
||||
|
||||
jPanel1.setPreferredSize(new java.awt.Dimension(701, 453));
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(externalViewerTitleLabel, org.openide.util.NbBundle.getMessage(ExternalViewerGlobalSettingsPanel.class, "ExternalViewerGlobalSettingsPanel.externalViewerTitleLabel.text")); // NOI18N
|
||||
|
||||
jSplitPane1.setDividerSize(1);
|
||||
|
||||
exePanel.setPreferredSize(new java.awt.Dimension(311, 224));
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(exePathLabel, org.openide.util.NbBundle.getMessage(ExternalViewerGlobalSettingsPanel.class, "ExternalViewerGlobalSettingsPanel.exePathLabel.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(exePathNameLabel, org.openide.util.NbBundle.getMessage(ExternalViewerGlobalSettingsPanel.class, "ExternalViewerGlobalSettingsPanel.exePathNameLabel.text")); // NOI18N
|
||||
|
||||
javax.swing.GroupLayout exePanelLayout = new javax.swing.GroupLayout(exePanel);
|
||||
exePanel.setLayout(exePanelLayout);
|
||||
exePanelLayout.setHorizontalGroup(
|
||||
exePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(exePanelLayout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(exePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(exePathLabel)
|
||||
.addComponent(exePathNameLabel))
|
||||
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
);
|
||||
exePanelLayout.setVerticalGroup(
|
||||
exePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(exePanelLayout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addComponent(exePathLabel)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(exePathNameLabel)
|
||||
.addContainerGap(355, Short.MAX_VALUE))
|
||||
);
|
||||
|
||||
jSplitPane1.setRightComponent(exePanel);
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(ruleListLabel, org.openide.util.NbBundle.getMessage(ExternalViewerGlobalSettingsPanel.class, "ExternalViewerGlobalSettingsPanel.ruleListLabel.text")); // NOI18N
|
||||
|
||||
newRuleButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/add16.png"))); // NOI18N
|
||||
org.openide.awt.Mnemonics.setLocalizedText(newRuleButton, org.openide.util.NbBundle.getMessage(ExternalViewerGlobalSettingsPanel.class, "ExternalViewerGlobalSettingsPanel.newRuleButton.text")); // NOI18N
|
||||
newRuleButton.setMaximumSize(new java.awt.Dimension(111, 25));
|
||||
@ -143,6 +149,26 @@ final class ExternalViewerGlobalSettingsPanel extends javax.swing.JPanel impleme
|
||||
}
|
||||
});
|
||||
|
||||
jScrollPane4.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(204, 204, 204)));
|
||||
|
||||
jScrollPane4.setViewportView(ExternalViewerRulesTable);
|
||||
if (ExternalViewerRulesTable.getColumnModel().getColumnCount() > 0) {
|
||||
ExternalViewerRulesTable.getColumnModel().getColumn(0).setHeaderValue(org.openide.util.NbBundle.getMessage(ExternalViewerGlobalSettingsPanel.class, "ExternalViewerGlobalSettingsPanel.ExternalViewerRulesTable.columnModel.title0")); // NOI18N
|
||||
ExternalViewerRulesTable.getColumnModel().getColumn(1).setHeaderValue(org.openide.util.NbBundle.getMessage(ExternalViewerGlobalSettingsPanel.class, "ExternalViewerGlobalSettingsPanel.ExternalViewerRulesTable.columnModel.title1")); // NOI18N
|
||||
}
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(externalViewerTitleLabel, org.openide.util.NbBundle.getMessage(ExternalViewerGlobalSettingsPanel.class, "ExternalViewerGlobalSettingsPanel.externalViewerTitleLabel.text")); // NOI18N
|
||||
|
||||
deleteRuleButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/delete16.png"))); // NOI18N
|
||||
org.openide.awt.Mnemonics.setLocalizedText(deleteRuleButton, org.openide.util.NbBundle.getMessage(ExternalViewerGlobalSettingsPanel.class, "ExternalViewerGlobalSettingsPanel.deleteRuleButton.text")); // NOI18N
|
||||
deleteRuleButton.setMaximumSize(new java.awt.Dimension(111, 25));
|
||||
deleteRuleButton.setMinimumSize(new java.awt.Dimension(111, 25));
|
||||
deleteRuleButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
deleteRuleButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
editRuleButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/edit16.png"))); // NOI18N
|
||||
org.openide.awt.Mnemonics.setLocalizedText(editRuleButton, org.openide.util.NbBundle.getMessage(ExternalViewerGlobalSettingsPanel.class, "ExternalViewerGlobalSettingsPanel.editRuleButton.text")); // NOI18N
|
||||
editRuleButton.setMaximumSize(new java.awt.Dimension(111, 25));
|
||||
@ -153,91 +179,112 @@ final class ExternalViewerGlobalSettingsPanel extends javax.swing.JPanel impleme
|
||||
}
|
||||
});
|
||||
|
||||
deleteRuleButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/delete16.png"))); // NOI18N
|
||||
org.openide.awt.Mnemonics.setLocalizedText(deleteRuleButton, org.openide.util.NbBundle.getMessage(ExternalViewerGlobalSettingsPanel.class, "ExternalViewerGlobalSettingsPanel.deleteRuleButton.text")); // NOI18N
|
||||
deleteRuleButton.setMaximumSize(new java.awt.Dimension(111, 25));
|
||||
deleteRuleButton.setMinimumSize(new java.awt.Dimension(111, 25));
|
||||
deleteRuleButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
deleteRuleButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
jScrollPane2.setViewportView(rulesList);
|
||||
|
||||
javax.swing.GroupLayout rulesPanelLayout = new javax.swing.GroupLayout(rulesPanel);
|
||||
rulesPanel.setLayout(rulesPanelLayout);
|
||||
rulesPanelLayout.setHorizontalGroup(
|
||||
rulesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(rulesPanelLayout.createSequentialGroup()
|
||||
javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2);
|
||||
jPanel2.setLayout(jPanel2Layout);
|
||||
jPanel2Layout.setHorizontalGroup(
|
||||
jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(jPanel2Layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(rulesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(ruleListLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, rulesPanelLayout.createSequentialGroup()
|
||||
.addGap(6, 6, 6)
|
||||
.addComponent(newRuleButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(16, 16, 16)
|
||||
.addComponent(editRuleButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(16, 16, 16)
|
||||
.addComponent(deleteRuleButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
.addComponent(jScrollPane2))
|
||||
.addContainerGap())
|
||||
.addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(jScrollPane4, javax.swing.GroupLayout.PREFERRED_SIZE, 634, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGroup(jPanel2Layout.createSequentialGroup()
|
||||
.addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(externalViewerTitleLabel)
|
||||
.addGroup(jPanel2Layout.createSequentialGroup()
|
||||
.addComponent(newRuleButton, javax.swing.GroupLayout.PREFERRED_SIZE, 97, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(editRuleButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(deleteRuleButton, javax.swing.GroupLayout.PREFERRED_SIZE, 109, javax.swing.GroupLayout.PREFERRED_SIZE)))
|
||||
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))))
|
||||
);
|
||||
rulesPanelLayout.setVerticalGroup(
|
||||
rulesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(rulesPanelLayout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addComponent(ruleListLabel)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(jScrollPane2)
|
||||
jPanel2Layout.setVerticalGroup(
|
||||
jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(jPanel2Layout.createSequentialGroup()
|
||||
.addComponent(externalViewerTitleLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addGap(12, 12, 12)
|
||||
.addGroup(rulesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(newRuleButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(editRuleButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(jScrollPane4, javax.swing.GroupLayout.PREFERRED_SIZE, 219, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(newRuleButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(editRuleButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(deleteRuleButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addContainerGap())
|
||||
);
|
||||
|
||||
jSplitPane1.setLeftComponent(rulesPanel);
|
||||
HxDPath.setEditable(false);
|
||||
|
||||
jScrollPane1.setViewportView(jSplitPane1);
|
||||
org.openide.awt.Mnemonics.setLocalizedText(HxDLabel, org.openide.util.NbBundle.getMessage(ExternalViewerGlobalSettingsPanel.class, "ExternalViewerGlobalSettingsPanel.HxDLabel.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(ContentViewerExtensionLabel, org.openide.util.NbBundle.getMessage(ExternalViewerGlobalSettingsPanel.class, "ExternalViewerGlobalSettingsPanel.ContentViewerExtensionLabel.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(browseHxDDirectory, org.openide.util.NbBundle.getMessage(ExternalViewerGlobalSettingsPanel.class, "ExternalViewerGlobalSettingsPanel.browseHxDDirectory.text")); // NOI18N
|
||||
browseHxDDirectory.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
browseHxDDirectoryActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
javax.swing.GroupLayout jPanel3Layout = new javax.swing.GroupLayout(jPanel3);
|
||||
jPanel3.setLayout(jPanel3Layout);
|
||||
jPanel3Layout.setHorizontalGroup(
|
||||
jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(jPanel3Layout.createSequentialGroup()
|
||||
.addComponent(ContentViewerExtensionLabel)
|
||||
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
.addGroup(jPanel3Layout.createSequentialGroup()
|
||||
.addGap(21, 21, 21)
|
||||
.addComponent(HxDLabel)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(HxDPath, javax.swing.GroupLayout.DEFAULT_SIZE, 441, Short.MAX_VALUE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(browseHxDDirectory, javax.swing.GroupLayout.PREFERRED_SIZE, 82, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
);
|
||||
jPanel3Layout.setVerticalGroup(
|
||||
jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(jPanel3Layout.createSequentialGroup()
|
||||
.addComponent(ContentViewerExtensionLabel)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(HxDPath, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(HxDLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(browseHxDDirectory))
|
||||
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
);
|
||||
|
||||
browseHxDDirectory.setEnabled(PlatformUtil.isWindowsOS());
|
||||
|
||||
javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
|
||||
jPanel1.setLayout(jPanel1Layout);
|
||||
jPanel1Layout.setHorizontalGroup(
|
||||
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(jPanel1Layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addComponent(externalViewerTitleLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 681, Short.MAX_VALUE)
|
||||
.addContainerGap())
|
||||
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(jPanel1Layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addComponent(jScrollPane1)
|
||||
.addContainerGap()))
|
||||
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(jPanel2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGroup(jPanel1Layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addComponent(jPanel3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
|
||||
.addGap(547, 547, 547))
|
||||
);
|
||||
jPanel1Layout.setVerticalGroup(
|
||||
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(jPanel1Layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addComponent(externalViewerTitleLabel)
|
||||
.addContainerGap(428, Short.MAX_VALUE))
|
||||
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(jPanel1Layout.createSequentialGroup()
|
||||
.addGap(32, 32, 32)
|
||||
.addComponent(jScrollPane1)
|
||||
.addContainerGap()))
|
||||
.addComponent(jPanel2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(18, 18, 18)
|
||||
.addComponent(jPanel3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addContainerGap(83, Short.MAX_VALUE))
|
||||
);
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(jPanel1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, 1191, Short.MAX_VALUE)
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(jPanel1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
@ -247,16 +294,13 @@ final class ExternalViewerGlobalSettingsPanel extends javax.swing.JPanel impleme
|
||||
if (result == AddExternalViewerRuleDialog.BUTTON_PRESSED.OK) {
|
||||
ExternalViewerRule newRule = dialog.getRule();
|
||||
// Only allow one association for each MIME type or extension.
|
||||
if (rules.contains(newRule)) {
|
||||
if (tableModel.containsRule(newRule)) {
|
||||
JOptionPane.showMessageDialog(this,
|
||||
NbBundle.getMessage(ExternalViewerGlobalSettingsPanel.class, "ExternalViewerGlobalSettingsPanel.JOptionPane.ruleAlreadyExists.message"),
|
||||
NbBundle.getMessage(ExternalViewerGlobalSettingsPanel.class, "ExternalViewerGlobalSettingsPanel.JOptionPane.ruleAlreadyExists.title"),
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
} else {
|
||||
rules.add(newRule);
|
||||
updateRulesListModel();
|
||||
int index = rules.indexOf(newRule);
|
||||
rulesList.setSelectedIndex(index);
|
||||
tableModel.addRule(newRule);
|
||||
enableButtons();
|
||||
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
|
||||
}
|
||||
@ -264,113 +308,107 @@ final class ExternalViewerGlobalSettingsPanel extends javax.swing.JPanel impleme
|
||||
}//GEN-LAST:event_newRuleButtonActionPerformed
|
||||
|
||||
private void editRuleButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_editRuleButtonActionPerformed
|
||||
int selected = rulesList.getSelectedIndex();
|
||||
AddExternalViewerRuleDialog dialog = new AddExternalViewerRuleDialog(rulesListModel.get(rulesList.getSelectedIndex()));
|
||||
int selectedIndex = ExternalViewerRulesTable.convertRowIndexToModel(ExternalViewerRulesTable.getSelectedRow());
|
||||
ExternalViewerRule selectedRule = tableModel.getRuleAt(selectedIndex);
|
||||
AddExternalViewerRuleDialog dialog = new AddExternalViewerRuleDialog(selectedRule);
|
||||
AddExternalViewerRuleDialog.BUTTON_PRESSED result = dialog.getResult();
|
||||
if (result == AddExternalViewerRuleDialog.BUTTON_PRESSED.OK) {
|
||||
rules.remove(selected);
|
||||
ExternalViewerRule newRule = dialog.getRule();
|
||||
// Only allow one association for each MIME type or extension.
|
||||
if (rules.contains(newRule)) {
|
||||
if (tableModel.containsRule(newRule)) {
|
||||
JOptionPane.showMessageDialog(this,
|
||||
NbBundle.getMessage(ExternalViewerGlobalSettingsPanel.class, "ExternalViewerGlobalSettingsPanel.JOptionPane.ruleAlreadyExists.message"),
|
||||
NbBundle.getMessage(ExternalViewerGlobalSettingsPanel.class, "ExternalViewerGlobalSettingsPanel.JOptionPane.ruleAlreadyExists.title"),
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
} else {
|
||||
rules.add(selected, dialog.getRule());
|
||||
updateRulesListModel();
|
||||
tableModel.setRule(selectedIndex, dialog.getRule());
|
||||
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
|
||||
}
|
||||
}
|
||||
rulesList.setSelectedIndex(selected);
|
||||
enableButtons();
|
||||
}//GEN-LAST:event_editRuleButtonActionPerformed
|
||||
|
||||
private void deleteRuleButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteRuleButtonActionPerformed
|
||||
ExternalViewerRule rule = rulesList.getSelectedValue();
|
||||
rules.remove(rule);
|
||||
updateRulesListModel();
|
||||
tableModel.removeRule(ExternalViewerRulesTable.convertRowIndexToModel(ExternalViewerRulesTable.getSelectedRow()));
|
||||
ExternalViewerRulesTable.getSelectionModel().clearSelection();
|
||||
enableButtons();
|
||||
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
|
||||
}//GEN-LAST:event_deleteRuleButtonActionPerformed
|
||||
|
||||
private void browseHxDDirectoryActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_browseHxDDirectoryActionPerformed
|
||||
JFileChooser fileWindow = new JFileChooser();
|
||||
fileWindow.setFileSelectionMode(JFileChooser.FILES_ONLY);
|
||||
GeneralFilter exeFilter = new GeneralFilter(GeneralFilter.EXECUTABLE_EXTS, GeneralFilter.EXECUTABLE_DESC);
|
||||
File HxDPathFile = new File(HxDPath.getText());
|
||||
if(HxDPathFile.exists() && HxDPathFile.canExecute()) {
|
||||
fileWindow.setCurrentDirectory(new File(HxDPath.getText()));
|
||||
}
|
||||
fileWindow.setDragEnabled(false);
|
||||
fileWindow.setFileFilter(exeFilter);
|
||||
fileWindow.setMultiSelectionEnabled(false);
|
||||
int returnVal = fileWindow.showSaveDialog(this);
|
||||
if(returnVal == JFileChooser.APPROVE_OPTION) {
|
||||
File HxDExecutable = fileWindow.getSelectedFile();
|
||||
HxDPath.setForeground(Color.BLACK);
|
||||
HxDPath.setText(HxDExecutable.getAbsolutePath());
|
||||
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
|
||||
}
|
||||
}//GEN-LAST:event_browseHxDDirectoryActionPerformed
|
||||
|
||||
@Override
|
||||
public void store() {
|
||||
//Dump rules from table model into a list to be stored by the rules manager.
|
||||
List<ExternalViewerRule> rules = new ArrayList<>();
|
||||
for(int i = 0; i < tableModel.getRowCount(); i++) {
|
||||
rules.add(tableModel.getRuleAt(i));
|
||||
}
|
||||
ExternalViewerRulesManager.getInstance().setUserRules(rules);
|
||||
UserPreferences.setExternalHexEditorPath(HxDPath.getText());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
rules = ExternalViewerRulesManager.getInstance().getUserRules();
|
||||
updateRulesListModel();
|
||||
List<ExternalViewerRule> rules = ExternalViewerRulesManager.getInstance().getUserRules();
|
||||
for(ExternalViewerRule rule : rules) {
|
||||
if(!tableModel.containsRule(rule)) {
|
||||
tableModel.addRule(rule);
|
||||
}
|
||||
}
|
||||
String editorPath = UserPreferences.getExternalHexEditorPath();
|
||||
File HdXExecutable = new File(editorPath);
|
||||
if(HdXExecutable.exists() || HdXExecutable.canExecute()) {
|
||||
HxDPath.setText(editorPath);
|
||||
} else {
|
||||
HxDPath.setForeground(Color.RED);
|
||||
HxDPath.setText(editorPath);
|
||||
}
|
||||
enableButtons();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable edit and delete buttons if there is a rule selected.
|
||||
*/
|
||||
private void enableButtons() {
|
||||
boolean ruleIsSelected = rulesList.getSelectedIndex() != -1;
|
||||
boolean enableButtons() {
|
||||
boolean ruleIsSelected = ExternalViewerRulesTable.getSelectedRow() >= 0;
|
||||
editRuleButton.setEnabled(ruleIsSelected);
|
||||
deleteRuleButton.setEnabled(ruleIsSelected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the list model for the rules list component, sorted by the MIME
|
||||
* type or extension alphabetically.
|
||||
*/
|
||||
private void updateRulesListModel() {
|
||||
rulesListModel.clear();
|
||||
Collections.sort(rules);
|
||||
for (ExternalViewerRule rule : rules) {
|
||||
rulesListModel.addElement(rule);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills in the .exe file path label if a rule is selected.
|
||||
*/
|
||||
private void populateExePath() {
|
||||
ExternalViewerRule rule = rulesList.getSelectedValue();
|
||||
if (rule != null) {
|
||||
if (rule.getRuleType() == ExternalViewerRule.RuleType.MIME) {
|
||||
exePathLabel.setText(NbBundle.getMessage(ExternalViewerGlobalSettingsPanel.class,
|
||||
"ExternalViewerGlobalSettingsPanel.exePathLabel.MIME.text"));
|
||||
} else {
|
||||
exePathLabel.setText(NbBundle.getMessage(ExternalViewerGlobalSettingsPanel.class,
|
||||
"ExternalViewerGlobalSettingsPanel.exePathLabel.EXT.text"));
|
||||
}
|
||||
exePathNameLabel.setText(rule.getExePath());
|
||||
}
|
||||
enableButtons();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the .exe file path label.
|
||||
*/
|
||||
private void clearExePath() {
|
||||
rulesList.clearSelection();
|
||||
exePathLabel.setText(NbBundle.getMessage(ExternalViewerGlobalSettingsPanel.class,
|
||||
"ExternalViewerGlobalSettingsPanel.exePathLabel.text"));
|
||||
exePathNameLabel.setText(NbBundle.getMessage(ExternalViewerGlobalSettingsPanel.class,
|
||||
"ExternalViewerGlobalSettingsPanel.exePathLabel.empty.text"));
|
||||
enableButtons();
|
||||
return ruleIsSelected;
|
||||
}
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JLabel ContentViewerExtensionLabel;
|
||||
private javax.swing.JTable ExternalViewerRulesTable;
|
||||
private javax.swing.JLabel HxDLabel;
|
||||
private javax.swing.JTextField HxDPath;
|
||||
private javax.swing.JButton browseHxDDirectory;
|
||||
private javax.swing.JButton deleteRuleButton;
|
||||
private javax.swing.JButton editRuleButton;
|
||||
private javax.swing.JPanel exePanel;
|
||||
private javax.swing.JLabel exePathLabel;
|
||||
private javax.swing.JLabel exePathNameLabel;
|
||||
private javax.swing.JLabel externalViewerTitleLabel;
|
||||
private javax.swing.JButton jButton2;
|
||||
private javax.swing.JPanel jPanel1;
|
||||
private javax.swing.JScrollPane jScrollPane1;
|
||||
private javax.swing.JScrollPane jScrollPane2;
|
||||
private javax.swing.JSplitPane jSplitPane1;
|
||||
private javax.swing.JPanel jPanel2;
|
||||
private javax.swing.JPanel jPanel3;
|
||||
private javax.swing.JScrollPane jScrollPane4;
|
||||
private javax.swing.JButton newRuleButton;
|
||||
private javax.swing.JLabel ruleListLabel;
|
||||
private javax.swing.JList<ExternalViewerRule> rulesList;
|
||||
private javax.swing.JPanel rulesPanel;
|
||||
private javax.swing.JScrollPane rulesScrollPane;
|
||||
private javax.swing.JButton newRuleButton1;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
}
|
||||
|
@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2018 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.directorytree;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
|
||||
/**
|
||||
* Model that stores the External Viewer rules for Mime types and extensions.
|
||||
* Located at Tools -> Options -> External Viewer
|
||||
*/
|
||||
class ExternalViewerGlobalSettingsTableModel extends AbstractTableModel {
|
||||
|
||||
private final List<ExternalViewerRule> rules;
|
||||
private final String[] columnNames;
|
||||
|
||||
public ExternalViewerGlobalSettingsTableModel(String... columnNames) {
|
||||
this.columnNames = Arrays.copyOf(columnNames, columnNames.length);
|
||||
this.rules = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores a new external viewer rule.
|
||||
*
|
||||
* @param rule User-inputted rule
|
||||
*/
|
||||
public void addRule(ExternalViewerRule rule) {
|
||||
rules.add(rule);
|
||||
fireTableRowsInserted(getRowCount() - 1, getRowCount() - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of rules stored in this model.
|
||||
*
|
||||
* @return Integer denoting row count in table model
|
||||
*/
|
||||
@Override
|
||||
public int getRowCount() {
|
||||
return rules.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the column name at the given index.
|
||||
*
|
||||
* @param columnIndex
|
||||
*
|
||||
* @return Column name
|
||||
*/
|
||||
@Override
|
||||
public String getColumnName(int columnIndex) {
|
||||
return columnNames[columnIndex];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves column class type. As for now, this is only type string.
|
||||
*
|
||||
* @param columnIndex
|
||||
*
|
||||
* @return String.class
|
||||
*/
|
||||
@Override
|
||||
public Class<String> getColumnClass(int columnIndex) {
|
||||
return String.class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the number of columns in this table model.
|
||||
*
|
||||
* @return Integer denoting column count
|
||||
*/
|
||||
@Override
|
||||
public int getColumnCount() {
|
||||
return columnNames.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves value at a given row and column in the table.
|
||||
*
|
||||
* @param rowIndex Desired row index
|
||||
* @param columnIndex Desired column index
|
||||
*
|
||||
* @return A generic pointer to the underlying data.
|
||||
*/
|
||||
@Override
|
||||
public Object getValueAt(int rowIndex, int columnIndex) {
|
||||
if (columnIndex == 0) {
|
||||
return rules.get(rowIndex).getName();
|
||||
} else {
|
||||
return rules.get(rowIndex).getExePath();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an entire rule instance from a given index.
|
||||
*
|
||||
* @param rowIndex Desired row
|
||||
*
|
||||
* @return User-inputted rule at the desired rowIndex
|
||||
*/
|
||||
public ExternalViewerRule getRuleAt(int rowIndex) {
|
||||
return rules.get(rowIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces an existing rule in the table.
|
||||
*
|
||||
* @param rowIndex Desired row index
|
||||
* @param rule New rule to overwrite the old.
|
||||
*/
|
||||
public void setRule(int rowIndex, ExternalViewerRule rule) {
|
||||
rules.set(rowIndex, rule);
|
||||
fireTableDataChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the rule from the table model.
|
||||
*
|
||||
* @param rowIndex Desired row index to delete
|
||||
*/
|
||||
public void removeRule(int rowIndex) {
|
||||
rules.remove(rowIndex);
|
||||
fireTableDataChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* This table model is not editable.
|
||||
*
|
||||
* @param rowIndex
|
||||
* @param colIndex
|
||||
*
|
||||
* @return False
|
||||
*/
|
||||
@Override
|
||||
public boolean isCellEditable(int rowIndex, int colIndex) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests containment of a given rule in the table model.
|
||||
*
|
||||
* @param rule Rule in question
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean containsRule(ExternalViewerRule rule) {
|
||||
return rules.contains(rule);
|
||||
}
|
||||
}
|
@ -32,6 +32,7 @@ import org.sleuthkit.autopsy.ingest.IngestJobContext;
|
||||
import org.sleuthkit.autopsy.ingest.IngestMessage;
|
||||
import org.sleuthkit.autopsy.ingest.IngestMessage.MessageType;
|
||||
import org.sleuthkit.autopsy.ingest.IngestServices;
|
||||
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.Image;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
@ -300,6 +301,8 @@ public class DataSourceIntegrityIngestModule implements DataSourceIngestModule {
|
||||
BlackboardArtifact verificationFailedArtifact = Case.getCurrentCase().getSleuthkitCase().newBlackboardArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_VERIFICATION_FAILED, img.getId());
|
||||
verificationFailedArtifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT,
|
||||
DataSourceIntegrityModuleFactory.getModuleName(), artifactComment));
|
||||
IngestServices.getInstance().fireModuleDataEvent(new ModuleDataEvent(DataSourceIntegrityModuleFactory.getModuleName(),
|
||||
BlackboardArtifact.ARTIFACT_TYPE.TSK_VERIFICATION_FAILED));
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Error creating verification failed artifact", ex);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2013-2018 Basis Technology Corp.
|
||||
* Copyright 2013-2019 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -258,7 +258,7 @@ class SevenZipExtractor {
|
||||
org.sleuthkit.datamodel.Blackboard tskBlackboard = tskCase.getBlackboard();
|
||||
// Create artifact if it doesn't already exist.
|
||||
if (!tskBlackboard.artifactExists(archiveFile, BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT, attributes)) {
|
||||
BlackboardArtifact artifact = archiveFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT);
|
||||
BlackboardArtifact artifact = rootArchive.getArchiveFile().newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT);
|
||||
artifact.addAttributes(attributes);
|
||||
|
||||
try {
|
||||
|
@ -90,51 +90,51 @@ public class EmbeddedFileTest extends NbTestCase {
|
||||
CaseUtils.closeCurrentCase();
|
||||
}
|
||||
|
||||
// public void testEncryptionAndZipBomb() {
|
||||
// try {
|
||||
// List<AbstractFile> results = openCase.getSleuthkitCase().findAllFilesWhere("name LIKE '%%'");
|
||||
// final String zipBombSetName = "Possible Zip Bomb";
|
||||
// final String protectedName1 = "password_protected.zip";
|
||||
// final String protectedName2 = "level1_protected.zip";
|
||||
// final String protectedName3 = "42.zip";
|
||||
// final String depthZipBomb = "DepthTriggerZipBomb.zip";
|
||||
// final String ratioZipBomb = "RatioTriggerZipBomb.zip";
|
||||
// int zipBombs = 0;
|
||||
// assertEquals("The number of files in the test image has changed", 2221, results.size());
|
||||
// int passwdProtectedZips = 0;
|
||||
// for (AbstractFile file : results) {
|
||||
// //.zip file has artifact TSK_ENCRYPTION_DETECTED
|
||||
// if (file.getName().equalsIgnoreCase(protectedName1) || file.getName().equalsIgnoreCase(protectedName2) || file.getName().equalsIgnoreCase(protectedName3)) {
|
||||
// ArrayList<BlackboardArtifact> artifacts = file.getAllArtifacts();
|
||||
// assertEquals("Password protected zip file " + file.getName() + " has incorrect number of artifacts", 1, artifacts.size());
|
||||
// for (BlackboardArtifact artifact : artifacts) {
|
||||
// assertEquals("Artifact for password protected zip file " + file.getName() + " has incorrect type ID", artifact.getArtifactTypeID(), BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED.getTypeID());
|
||||
// passwdProtectedZips++;
|
||||
// }
|
||||
// } else if (file.getName().equalsIgnoreCase(depthZipBomb) || file.getName().equalsIgnoreCase(ratioZipBomb)) {
|
||||
// ArrayList<BlackboardArtifact> artifacts = file.getAllArtifacts();
|
||||
// assertEquals("Zip bomb " + file.getName() + " has incorrect number of artifacts", 1, artifacts.size());
|
||||
// for (BlackboardArtifact artifact : artifacts) {
|
||||
// assertEquals("Artifact for Zip bomb " + file.getName() + " has incorrect type ID", artifact.getArtifactTypeID(), BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID());
|
||||
// BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME));
|
||||
// assertNotNull("No attribute found for artifact on zip bomb " + file.getName(), attribute);
|
||||
// assertEquals("Interesting artifact on file, " + file.getName() + ", does not reflect it being a zip bomb", zipBombSetName, attribute.getDisplayString());
|
||||
// zipBombs++;
|
||||
// }
|
||||
// } else {//No other files have artifact defined
|
||||
// assertEquals("Unexpected file, " + file.getName() + ", has artifacts", 0, file.getAllArtifacts().size());
|
||||
// }
|
||||
//
|
||||
// }
|
||||
// //Make sure 3 password protected zip files have been tested: password_protected.zip, level1_protected.zip and 42.zip that we download for bomb testing.
|
||||
// assertEquals("Unexpected number of artifacts reflecting password protected zip files found", 3, passwdProtectedZips);
|
||||
// //Make sure 2 zip bomb files have been tested: DepthTriggerZipBomb.zip and RatioTriggerZipBomb.zip.
|
||||
// assertEquals("Unexpected number of artifacts reflecting zip bombs found", 2, zipBombs);
|
||||
// } catch (TskCoreException ex) {
|
||||
// Exceptions.printStackTrace(ex);
|
||||
// Assert.fail(ex.getMessage());
|
||||
// }
|
||||
// }
|
||||
public void testEncryptionAndZipBomb() {
|
||||
try {
|
||||
List<AbstractFile> results = openCase.getSleuthkitCase().findAllFilesWhere("name LIKE '%%'");
|
||||
final String zipBombSetName = "Possible Zip Bomb";
|
||||
final String protectedName1 = "password_protected.zip";
|
||||
final String protectedName2 = "level1_protected.zip";
|
||||
final String protectedName3 = "42.zip";
|
||||
final String depthZipBomb = "DepthTriggerZipBomb.zip";
|
||||
final String ratioZipBomb = "RatioTriggerZipBomb.zip";
|
||||
int zipBombs = 0;
|
||||
assertEquals("The number of files in the test image has changed", 2221, results.size());
|
||||
int passwdProtectedZips = 0;
|
||||
for (AbstractFile file : results) {
|
||||
//.zip file has artifact TSK_ENCRYPTION_DETECTED
|
||||
if (file.getName().equalsIgnoreCase(protectedName1) || file.getName().equalsIgnoreCase(protectedName2) || file.getName().equalsIgnoreCase(protectedName3)) {
|
||||
ArrayList<BlackboardArtifact> artifacts = file.getAllArtifacts();
|
||||
assertEquals("Password protected zip file " + file.getName() + " has incorrect number of artifacts", 1, artifacts.size());
|
||||
for (BlackboardArtifact artifact : artifacts) {
|
||||
assertEquals("Artifact for password protected zip file " + file.getName() + " has incorrect type ID", artifact.getArtifactTypeID(), BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED.getTypeID());
|
||||
passwdProtectedZips++;
|
||||
}
|
||||
} else if (file.getName().equalsIgnoreCase(depthZipBomb) || file.getName().equalsIgnoreCase(ratioZipBomb)) {
|
||||
ArrayList<BlackboardArtifact> artifacts = file.getAllArtifacts();
|
||||
assertEquals("Zip bomb " + file.getName() + " has incorrect number of artifacts", 1, artifacts.size());
|
||||
for (BlackboardArtifact artifact : artifacts) {
|
||||
assertEquals("Artifact for Zip bomb " + file.getName() + " has incorrect type ID", artifact.getArtifactTypeID(), BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID());
|
||||
BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME));
|
||||
assertNotNull("No attribute found for artifact on zip bomb " + file.getName(), attribute);
|
||||
assertEquals("Interesting artifact on file, " + file.getName() + ", does not reflect it being a zip bomb", zipBombSetName, attribute.getDisplayString());
|
||||
zipBombs++;
|
||||
}
|
||||
} else {//No other files have artifact defined
|
||||
assertEquals("Unexpected file, " + file.getName() + ", has artifacts", 0, file.getAllArtifacts().size());
|
||||
}
|
||||
|
||||
}
|
||||
//Make sure 3 password protected zip files have been tested: password_protected.zip, level1_protected.zip and 42.zip that we download for bomb testing.
|
||||
assertEquals("Unexpected number of artifacts reflecting password protected zip files found", 3, passwdProtectedZips);
|
||||
//Make sure 2 zip bomb files have been tested: DepthTriggerZipBomb.zip and RatioTriggerZipBomb.zip.
|
||||
assertEquals("Unexpected number of artifacts reflecting zip bombs found", 2, zipBombs);
|
||||
} catch (TskCoreException ex) {
|
||||
Exceptions.printStackTrace(ex);
|
||||
Assert.fail(ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void testBigFolder() {
|
||||
final int numOfFilesToTest = 1000;
|
||||
|
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2018 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.directorytree;
|
||||
|
||||
import junit.framework.Assert;
|
||||
import org.junit.Test;
|
||||
import org.sleuthkit.autopsy.directorytree.ExternalViewerRule.RuleType;
|
||||
|
||||
/**
|
||||
* 72% code coverage of ExternalViewerGlobalSettingsPanel
|
||||
*/
|
||||
public class ExternalViewerGlobalSettingsPanelTest {
|
||||
|
||||
static final String[] testColumnNames = {"A", "B"};
|
||||
|
||||
/**
|
||||
* Default constructor for JUnit
|
||||
*/
|
||||
public ExternalViewerGlobalSettingsPanelTest(){
|
||||
//Codacy complains if there is no comment here
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEnableButtons() {
|
||||
ExternalViewerGlobalSettingsTableModel testModel = new ExternalViewerGlobalSettingsTableModel(testColumnNames);
|
||||
ExternalViewerGlobalSettingsPanel panel = new ExternalViewerGlobalSettingsPanel(testModel);
|
||||
Assert.assertFalse(panel.enableButtons());
|
||||
|
||||
testModel.addRule(new ExternalViewerRule("image/png", "fake.exe", RuleType.MIME));
|
||||
|
||||
Assert.assertFalse(panel.enableButtons());
|
||||
panel.setSelectionInterval(0, 0);
|
||||
Assert.assertTrue(panel.enableButtons());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDisableButtons() {
|
||||
ExternalViewerGlobalSettingsTableModel testModel = new ExternalViewerGlobalSettingsTableModel(testColumnNames);
|
||||
ExternalViewerGlobalSettingsPanel panel = new ExternalViewerGlobalSettingsPanel(testModel);
|
||||
|
||||
testModel.addRule(new ExternalViewerRule("image/png", "fake.exe", RuleType.MIME));
|
||||
Assert.assertFalse(panel.enableButtons());
|
||||
panel.setSelectionInterval(0, 0);
|
||||
Assert.assertTrue(panel.enableButtons());
|
||||
|
||||
testModel.removeRule(0);
|
||||
Assert.assertFalse(panel.enableButtons());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteRuleButtonClick() {
|
||||
ExternalViewerGlobalSettingsTableModel testModel = new ExternalViewerGlobalSettingsTableModel(testColumnNames);
|
||||
ExternalViewerGlobalSettingsPanel testPanel = new ExternalViewerGlobalSettingsPanel(testModel);
|
||||
Assert.assertFalse(testPanel.enableButtons());
|
||||
|
||||
testModel.addRule(new ExternalViewerRule(".txt", "notepad.exe", RuleType.EXT));
|
||||
testPanel.setSelectionInterval(0, 0);
|
||||
Assert.assertTrue(testPanel.enableButtons());
|
||||
Assert.assertEquals(1, testModel.getRowCount());
|
||||
|
||||
testPanel.deleteRuleButtonClick(0);
|
||||
|
||||
Assert.assertFalse(testPanel.enableButtons());
|
||||
Assert.assertEquals(0, testModel.getRowCount());
|
||||
}
|
||||
|
||||
@Test(expected = IndexOutOfBoundsException.class)
|
||||
public void testDeleteButtonClickFail() {
|
||||
ExternalViewerGlobalSettingsTableModel testModel = new ExternalViewerGlobalSettingsTableModel(testColumnNames);
|
||||
ExternalViewerGlobalSettingsPanel testPanel = new ExternalViewerGlobalSettingsPanel(testModel);
|
||||
|
||||
testPanel.deleteRuleButtonClick(-1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSingleSelection() {
|
||||
ExternalViewerGlobalSettingsTableModel testModel = new ExternalViewerGlobalSettingsTableModel(testColumnNames);
|
||||
ExternalViewerGlobalSettingsPanel testPanel = new ExternalViewerGlobalSettingsPanel(testModel);
|
||||
testModel.addRule(new ExternalViewerRule(".txt", "notepad.exe", RuleType.EXT));
|
||||
testModel.addRule(new ExternalViewerRule(".txt", "notepad.exe", RuleType.EXT));
|
||||
testModel.addRule(new ExternalViewerRule(".txt", "notepad.exe", RuleType.EXT));
|
||||
testModel.addRule(new ExternalViewerRule(".txt", "notepad.exe", RuleType.EXT));
|
||||
testModel.addRule(new ExternalViewerRule(".txt", "notepad.exe", RuleType.EXT));
|
||||
|
||||
testPanel.setSelectionInterval(0, 2);
|
||||
|
||||
Assert.assertFalse(testPanel.isSelected(0));
|
||||
Assert.assertFalse(testPanel.isSelected(1));
|
||||
Assert.assertTrue(testPanel.isSelected(2));
|
||||
}
|
||||
}
|
@ -0,0 +1,217 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2018 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.directorytree;
|
||||
|
||||
import junit.framework.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.sleuthkit.autopsy.directorytree.ExternalViewerRule.RuleType;
|
||||
|
||||
/**
|
||||
* 100% code coverage of ExternalViewerGlobalSettingsTableModel
|
||||
*/
|
||||
public class ExternalViewerGlobalSettingsTableModelTest {
|
||||
|
||||
static final String[] testColumnNames = {"A", "B"};
|
||||
private ExternalViewerRule pngMime;
|
||||
private ExternalViewerRule txtExt;
|
||||
private ExternalViewerRule wavExt;
|
||||
|
||||
/**
|
||||
* Initialize JUnit test
|
||||
*/
|
||||
public ExternalViewerGlobalSettingsTableModelTest() {
|
||||
//Empty constructor
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
pngMime = new ExternalViewerRule("image/png", "test.exe", RuleType.MIME);
|
||||
txtExt = new ExternalViewerRule(".txt", "notepad.exe", RuleType.EXT);
|
||||
wavExt = new ExternalViewerRule(".wav", "video.exe", RuleType.EXT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of addRule method, of class ExternalViewerGlobalSettingsTableModel.
|
||||
*/
|
||||
@Test
|
||||
public void testAddRule() {
|
||||
ExternalViewerGlobalSettingsTableModel testModel = new ExternalViewerGlobalSettingsTableModel(testColumnNames);
|
||||
testModel.addRule(pngMime);
|
||||
|
||||
Assert.assertEquals(1, testModel.getRowCount());
|
||||
|
||||
ExternalViewerRule rule = testModel.getRuleAt(0);
|
||||
Assert.assertEquals("image/png", rule.getName());
|
||||
Assert.assertEquals("test.exe", rule.getExePath());
|
||||
Assert.assertEquals(RuleType.MIME, rule.getRuleType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of getRowCount method, of class ExternalViewerGlobalSettingsTableModel.
|
||||
*/
|
||||
@Test
|
||||
public void testGetRowCount() {
|
||||
ExternalViewerGlobalSettingsTableModel testModel = new ExternalViewerGlobalSettingsTableModel(testColumnNames);
|
||||
Assert.assertEquals(0, testModel.getRowCount());
|
||||
|
||||
testModel.addRule(pngMime);
|
||||
testModel.addRule(txtExt);
|
||||
testModel.addRule(wavExt);
|
||||
|
||||
Assert.assertEquals(3, testModel.getRowCount());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of getColumnName method, of class ExternalViewerGlobalSettingsTableModel.
|
||||
*/
|
||||
@Test
|
||||
public void testGetColumnName() {
|
||||
ExternalViewerGlobalSettingsTableModel testModel = new ExternalViewerGlobalSettingsTableModel(testColumnNames);
|
||||
Assert.assertEquals("A", testModel.getColumnName(0));
|
||||
Assert.assertEquals("B", testModel.getColumnName(1));
|
||||
}
|
||||
|
||||
@Test(expected = ArrayIndexOutOfBoundsException.class)
|
||||
public void testColumnNameOutOfBounds() {
|
||||
ExternalViewerGlobalSettingsTableModel testModel = new ExternalViewerGlobalSettingsTableModel(testColumnNames);
|
||||
testModel.getColumnName(2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of getColumnClass method, of class ExternalViewerGlobalSettingsTableModel.
|
||||
*/
|
||||
@Test
|
||||
public void testGetColumnClass() {
|
||||
ExternalViewerGlobalSettingsTableModel testModel = new ExternalViewerGlobalSettingsTableModel(testColumnNames);
|
||||
Assert.assertEquals(String.class, testModel.getColumnClass(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of getColumnCount method, of class ExternalViewerGlobalSettingsTableModel.
|
||||
*/
|
||||
@Test
|
||||
public void testGetColumnCount() {
|
||||
ExternalViewerGlobalSettingsTableModel testModel = new ExternalViewerGlobalSettingsTableModel(testColumnNames);
|
||||
Assert.assertEquals(2, testModel.getColumnCount());
|
||||
ExternalViewerGlobalSettingsTableModel testModelTwo = new ExternalViewerGlobalSettingsTableModel(new String[] {"A", "B", "C", "D", "E"});
|
||||
Assert.assertEquals(5, testModelTwo.getColumnCount());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of getValueAt method, of class ExternalViewerGlobalSettingsTableModel.
|
||||
*/
|
||||
@Test
|
||||
public void testGetValueAt() {
|
||||
ExternalViewerGlobalSettingsTableModel testModel = new ExternalViewerGlobalSettingsTableModel(testColumnNames);
|
||||
testModel.addRule(pngMime);
|
||||
testModel.addRule(txtExt);
|
||||
testModel.addRule(wavExt);
|
||||
|
||||
Assert.assertEquals(".txt", testModel.getValueAt(1,0));
|
||||
Assert.assertEquals("notepad.exe", testModel.getValueAt(1,1));
|
||||
Assert.assertEquals("image/png", testModel.getValueAt(0,0));
|
||||
Assert.assertEquals("test.exe", testModel.getValueAt(0,1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of getRuleAt method, of class ExternalViewerGlobalSettingsTableModel.
|
||||
*/
|
||||
@Test
|
||||
public void testGetRuleAt() {
|
||||
ExternalViewerGlobalSettingsTableModel testModel = new ExternalViewerGlobalSettingsTableModel(testColumnNames);
|
||||
testModel.addRule(pngMime);
|
||||
testModel.addRule(txtExt);
|
||||
testModel.addRule(wavExt);
|
||||
|
||||
ExternalViewerRule rule = testModel.getRuleAt(1);
|
||||
Assert.assertEquals(".txt", rule.getName());
|
||||
Assert.assertEquals("notepad.exe", rule.getExePath());
|
||||
Assert.assertEquals(RuleType.EXT, rule.getRuleType());
|
||||
|
||||
ExternalViewerRule ruleTwo = testModel.getRuleAt(0);
|
||||
Assert.assertEquals("image/png", ruleTwo.getName());
|
||||
Assert.assertEquals("test.exe", ruleTwo.getExePath());
|
||||
Assert.assertEquals(RuleType.MIME, ruleTwo.getRuleType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of setRule method, of class ExternalViewerGlobalSettingsTableModel.
|
||||
*/
|
||||
@Test
|
||||
public void testSetRule() {
|
||||
ExternalViewerGlobalSettingsTableModel testModel = new ExternalViewerGlobalSettingsTableModel(testColumnNames);
|
||||
testModel.addRule(pngMime);
|
||||
testModel.addRule(txtExt);
|
||||
testModel.addRule(wavExt);
|
||||
|
||||
testModel.setRule(0, txtExt);
|
||||
ExternalViewerRule rule = testModel.getRuleAt(1);
|
||||
Assert.assertEquals(".txt", rule.getName());
|
||||
Assert.assertEquals("notepad.exe", rule.getExePath());
|
||||
Assert.assertEquals(RuleType.EXT, rule.getRuleType());
|
||||
|
||||
testModel.setRule(2, pngMime);
|
||||
ExternalViewerRule ruleTwo = testModel.getRuleAt(2);
|
||||
Assert.assertEquals("image/png", ruleTwo.getName());
|
||||
Assert.assertEquals("test.exe", ruleTwo.getExePath());
|
||||
Assert.assertEquals(RuleType.MIME, ruleTwo.getRuleType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of removeRule method, of class ExternalViewerGlobalSettingsTableModel.
|
||||
*/
|
||||
@Test
|
||||
public void testRemoveRule() {
|
||||
ExternalViewerGlobalSettingsTableModel testModel = new ExternalViewerGlobalSettingsTableModel(testColumnNames);
|
||||
testModel.addRule(pngMime);
|
||||
Assert.assertEquals(1, testModel.getRowCount());
|
||||
|
||||
testModel.removeRule(0);
|
||||
Assert.assertEquals(0, testModel.getRowCount());
|
||||
Assert.assertFalse(testModel.containsRule(pngMime));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of isCellEditable method, of class ExternalViewerGlobalSettingsTableModel.
|
||||
*/
|
||||
@Test
|
||||
public void testIsCellEditable() {
|
||||
ExternalViewerGlobalSettingsTableModel testModel = new ExternalViewerGlobalSettingsTableModel(testColumnNames);
|
||||
Assert.assertFalse(testModel.isCellEditable(0, 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of containsRule method, of class ExternalViewerGlobalSettingsTableModel.
|
||||
*/
|
||||
@Test
|
||||
public void testContainsRule() {
|
||||
ExternalViewerGlobalSettingsTableModel testModel = new ExternalViewerGlobalSettingsTableModel(testColumnNames);
|
||||
testModel.addRule(pngMime);
|
||||
Assert.assertTrue(testModel.containsRule(pngMime));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotContains() {
|
||||
ExternalViewerGlobalSettingsTableModel testModel = new ExternalViewerGlobalSettingsTableModel(testColumnNames);
|
||||
testModel.addRule(pngMime);
|
||||
Assert.assertFalse(testModel.containsRule(new ExternalViewerRule("not", "a rule", RuleType.EXT)));
|
||||
Assert.assertFalse(testModel.containsRule(null));
|
||||
}
|
||||
}
|
@ -15,11 +15,4 @@
|
||||
out-of-date/unneeded stuff in the installer-->
|
||||
<delete dir="${ext.dir}"/>
|
||||
</target>
|
||||
|
||||
<target name="get-thirdparty-jars" description="get third-party jar dependencies">
|
||||
<mkdir dir="${ext.dir}"/>
|
||||
<copy file="${thirdparty.dir}/sqlite-jdbc/sqlite-jdbc-3.7.8-SNAPSHOT.jar" todir="${ext.dir}" />
|
||||
</target>
|
||||
|
||||
<target name="init" depends="get-thirdparty-jars,harness.init" />
|
||||
</project>
|
||||
|
@ -142,8 +142,8 @@
|
||||
</module-dependencies>
|
||||
<public-packages/>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/sqlite-jdbc-3.7.8-SNAPSHOT.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/sqlite-jdbc-3.7.8-SNAPSHOT.jar</binary-origin>
|
||||
<runtime-relative-path>ext/sqlite-jdbc-3.25.2.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/sqlite-jdbc-3.25.2.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
</data>
|
||||
</configuration>
|
||||
|
@ -698,7 +698,8 @@ public final class ImageGalleryController {
|
||||
//grab files with supported mime-types
|
||||
+ MIMETYPE_CLAUSE //NON-NLS
|
||||
//grab files with image or video mime-types even if we don't officially support them
|
||||
+ " OR mime_type LIKE 'video/%' OR mime_type LIKE 'image/%' )"; //NON-NLS
|
||||
+ " OR mime_type LIKE 'video/%' OR mime_type LIKE 'image/%' )" //NON-NLS
|
||||
+ " ORDER BY parent_path ";
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -393,6 +393,7 @@ public class ImageGalleryModule {
|
||||
* database.
|
||||
*/
|
||||
if (controller.isListeningEnabled()) {
|
||||
controller.getGroupManager().resetCurrentPathGroup();
|
||||
DrawableDB drawableDb = controller.getDatabase();
|
||||
if (drawableDb.getDataSourceDbBuildStatus(dataSourceObjId) == DrawableDB.DrawableDbBuildStatusEnum.IN_PROGRESS) {
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2013-2018 Basis Technology Corp.
|
||||
* Copyright 2013-2019 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -36,6 +36,7 @@ import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import static java.util.Objects.isNull;
|
||||
@ -77,6 +78,7 @@ import org.sleuthkit.datamodel.TagName;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TskData.DbType;
|
||||
import org.sleuthkit.datamodel.TskDataException;
|
||||
import org.sleuthkit.datamodel.VersionNumber;
|
||||
import org.sqlite.SQLiteJDBCLoader;
|
||||
|
||||
/**
|
||||
@ -97,6 +99,16 @@ public final class DrawableDB {
|
||||
private static final String GROUPS_TABLENAME = "image_gallery_groups"; //NON-NLS
|
||||
private static final String GROUPS_SEEN_TABLENAME = "image_gallery_groups_seen"; //NON-NLS
|
||||
|
||||
private static final String IG_DB_INFO_TABLE = "image_gallery_db_info";
|
||||
|
||||
private static final String IG_SCHEMA_MAJOR_VERSION_KEY = "IG_SCHEMA_MAJOR_VERSION";
|
||||
private static final String IG_SCHEMA_MINOR_VERSION_KEY = "IG_SCHEMA_MINOR_VERSION";
|
||||
private static final String IG_CREATION_SCHEMA_MAJOR_VERSION_KEY = "IG_CREATION_SCHEMA_MAJOR_VERSION";
|
||||
private static final String IG_CREATION_SCHEMA_MINOR_VERSION_KEY = "IG_CREATION_SCHEMA_MINOR_VERSION";
|
||||
|
||||
private static final VersionNumber IG_STARTING_SCHEMA_VERSION = new VersionNumber(1, 0, 0); // IG Schema Starting version
|
||||
private static final VersionNumber IG_SCHEMA_VERSION = new VersionNumber(1, 1, 0); // IG Schema Current version
|
||||
|
||||
private PreparedStatement insertHashSetStmt;
|
||||
|
||||
private List<PreparedStatement> preparedStatements = new ArrayList<>();
|
||||
@ -216,7 +228,7 @@ public final class DrawableDB {
|
||||
dbWriteLock();
|
||||
try {
|
||||
con = DriverManager.getConnection("jdbc:sqlite:" + dbPath.toString()); //NON-NLS
|
||||
if (!initializeDBSchema() || !prepareStatements() || !initializeStandardGroups() || !initializeImageList()) {
|
||||
if (!initializeDBSchema() || !upgradeDBSchema() || !prepareStatements() || !initializeStandardGroups() || !initializeImageList()) {
|
||||
close();
|
||||
throw new TskCoreException("Failed to initialize drawables database for Image Gallery use"); //NON-NLS
|
||||
}
|
||||
@ -390,6 +402,34 @@ public final class DrawableDB {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the specified table exists in Drawable DB
|
||||
*
|
||||
* @param tableName table to check
|
||||
* @return true if the table exists in the database
|
||||
*
|
||||
* @throws SQLException
|
||||
*/
|
||||
private boolean doesTableExist(String tableName) throws SQLException {
|
||||
ResultSet tableQueryResults = null;
|
||||
boolean tableExists = false;
|
||||
try (Statement stmt = con.createStatement()) {
|
||||
tableQueryResults = stmt.executeQuery("SELECT name FROM sqlite_master WHERE type='table'"); //NON-NLS
|
||||
while (tableQueryResults.next()) {
|
||||
if (tableQueryResults.getString("name").equalsIgnoreCase(tableName)) {
|
||||
tableExists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if (tableQueryResults != null) {
|
||||
tableQueryResults.close();
|
||||
}
|
||||
}
|
||||
return tableExists;
|
||||
}
|
||||
|
||||
private static void deleteDatabaseIfOlderVersion(Path dbPath) throws SQLException, IOException {
|
||||
if (Files.exists(dbPath)) {
|
||||
boolean hasDrawableFilesTable = false;
|
||||
@ -474,6 +514,8 @@ public final class DrawableDB {
|
||||
private boolean initializeDBSchema() {
|
||||
dbWriteLock();
|
||||
try {
|
||||
boolean existingDB = true;
|
||||
|
||||
if (isClosed()) {
|
||||
logger.log(Level.SEVERE, "The drawables database is closed"); //NON-NLS
|
||||
return false;
|
||||
@ -490,6 +532,31 @@ public final class DrawableDB {
|
||||
* Create tables in the drawables database.
|
||||
*/
|
||||
try (Statement stmt = con.createStatement()) {
|
||||
|
||||
// Check if the database is a new or existing database
|
||||
existingDB = doesTableExist("datasources");
|
||||
if (false == doesTableExist(IG_DB_INFO_TABLE)) {
|
||||
try {
|
||||
VersionNumber ig_creation_schema_version = existingDB
|
||||
? IG_STARTING_SCHEMA_VERSION
|
||||
: IG_SCHEMA_VERSION;
|
||||
|
||||
stmt.execute("CREATE TABLE IF NOT EXISTS " + IG_DB_INFO_TABLE + " (name TEXT PRIMARY KEY, value TEXT NOT NULL)");
|
||||
|
||||
// backfill creation schema ver
|
||||
stmt.execute(String.format("INSERT INTO %s (name, value) VALUES ('%s', '%s')", IG_DB_INFO_TABLE, IG_CREATION_SCHEMA_MAJOR_VERSION_KEY, ig_creation_schema_version.getMajor() ));
|
||||
stmt.execute(String.format("INSERT INTO %s (name, value) VALUES ('%s', '%s')", IG_DB_INFO_TABLE, IG_CREATION_SCHEMA_MINOR_VERSION_KEY, ig_creation_schema_version.getMinor() ));
|
||||
|
||||
// set current schema ver: at DB initialization - current version is same as starting version
|
||||
stmt.execute(String.format("INSERT INTO %s (name, value) VALUES ('%s', '%s')", IG_DB_INFO_TABLE, IG_SCHEMA_MAJOR_VERSION_KEY, ig_creation_schema_version.getMajor() ));
|
||||
stmt.execute(String.format("INSERT INTO %s (name, value) VALUES ('%s', '%s')", IG_DB_INFO_TABLE, IG_SCHEMA_MINOR_VERSION_KEY, ig_creation_schema_version.getMinor() ));
|
||||
|
||||
} catch (SQLException ex) {
|
||||
logger.log(Level.SEVERE, "Failed to create ig_db_info table", ex); //NON-NLS
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
String sql = "CREATE TABLE IF NOT EXISTS datasources " //NON-NLS
|
||||
+ "( id INTEGER PRIMARY KEY, " //NON-NLS
|
||||
@ -582,12 +649,51 @@ public final class DrawableDB {
|
||||
* Create tables in the case database.
|
||||
*/
|
||||
String autogenKeyType = (DbType.POSTGRESQL == tskCase.getDatabaseType()) ? "BIGSERIAL" : "INTEGER";
|
||||
|
||||
try {
|
||||
VersionNumber ig_creation_schema_version = existingDB
|
||||
? IG_STARTING_SCHEMA_VERSION
|
||||
: IG_SCHEMA_VERSION;
|
||||
|
||||
String tableSchema = "( id " + autogenKeyType + " PRIMARY KEY, "
|
||||
+ " name TEXT UNIQUE NOT NULL,"
|
||||
+ " value TEXT NOT NULL )";
|
||||
tskCase.getCaseDbAccessManager().createTable(IG_DB_INFO_TABLE, tableSchema);
|
||||
|
||||
// backfill creation version
|
||||
String creationMajorVerSQL = String.format(" (name, value) VALUES ('%s', '%s')", IG_CREATION_SCHEMA_MAJOR_VERSION_KEY, ig_creation_schema_version.getMajor());
|
||||
String creationMinorVerSQL = String.format(" (name, value) VALUES ('%s', '%s')", IG_CREATION_SCHEMA_MINOR_VERSION_KEY, ig_creation_schema_version.getMinor());
|
||||
|
||||
// set current version - at the onset, current version is same as creation version
|
||||
String currentMajorVerSQL = String.format(" (name, value) VALUES ('%s', '%s')", IG_SCHEMA_MAJOR_VERSION_KEY, ig_creation_schema_version.getMajor());
|
||||
String currentMinorVerSQL = String.format(" (name, value) VALUES ('%s', '%s')", IG_SCHEMA_MINOR_VERSION_KEY, ig_creation_schema_version.getMinor());
|
||||
|
||||
if (DbType.POSTGRESQL == tskCase.getDatabaseType()) {
|
||||
creationMajorVerSQL += " ON CONFLICT DO NOTHING ";
|
||||
creationMinorVerSQL += " ON CONFLICT DO NOTHING ";
|
||||
|
||||
currentMajorVerSQL += " ON CONFLICT DO NOTHING ";
|
||||
currentMinorVerSQL += " ON CONFLICT DO NOTHING ";
|
||||
}
|
||||
|
||||
tskCase.getCaseDbAccessManager().insert(IG_DB_INFO_TABLE, creationMajorVerSQL);
|
||||
tskCase.getCaseDbAccessManager().insert(IG_DB_INFO_TABLE, creationMinorVerSQL);
|
||||
|
||||
tskCase.getCaseDbAccessManager().insert(IG_DB_INFO_TABLE, currentMajorVerSQL);
|
||||
tskCase.getCaseDbAccessManager().insert(IG_DB_INFO_TABLE, currentMinorVerSQL);
|
||||
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Failed to create ig_db_info table in Case database", ex); //NON-NLS
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
String tableSchema
|
||||
= "( group_id " + autogenKeyType + " PRIMARY KEY, " //NON-NLS
|
||||
+ " data_source_obj_id integer DEFAULT 0, "
|
||||
+ " value VARCHAR(255) not null, " //NON-NLS
|
||||
+ " attribute VARCHAR(255) not null, " //NON-NLS
|
||||
+ " is_analyzed integer DEFAULT 0, "
|
||||
+ " UNIQUE(data_source_obj_id, value, attribute) )"; //NON-NLS
|
||||
|
||||
tskCase.getCaseDbAccessManager().createTable(GROUPS_TABLENAME, tableSchema);
|
||||
@ -620,6 +726,250 @@ public final class DrawableDB {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Schema version from DrawableDB
|
||||
*
|
||||
* @return image gallery schema version in DrawableDB
|
||||
* @throws SQLException
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
private VersionNumber getDrawableDbIgSchemaVersion() throws SQLException, TskCoreException {
|
||||
|
||||
Statement statement = con.createStatement();
|
||||
ResultSet resultSet = null;
|
||||
|
||||
try {
|
||||
int majorVersion = -1;
|
||||
String majorVersionStr = null;
|
||||
resultSet = statement.executeQuery(String.format("SELECT value FROM %s WHERE name='%s'", IG_DB_INFO_TABLE, IG_SCHEMA_MAJOR_VERSION_KEY));
|
||||
if (resultSet.next()) {
|
||||
majorVersionStr = resultSet.getString("value");
|
||||
try {
|
||||
majorVersion = Integer.parseInt(majorVersionStr);
|
||||
} catch (NumberFormatException ex) {
|
||||
throw new TskCoreException("Bad value for schema major version = " + majorVersionStr, ex);
|
||||
}
|
||||
} else {
|
||||
throw new TskCoreException("Failed to read schema major version from ig_db_info table");
|
||||
}
|
||||
|
||||
int minorVersion = -1;
|
||||
String minorVersionStr = null;
|
||||
resultSet = statement.executeQuery(String.format("SELECT value FROM %s WHERE name='%s'", IG_DB_INFO_TABLE, IG_SCHEMA_MINOR_VERSION_KEY));
|
||||
if (resultSet.next()) {
|
||||
minorVersionStr = resultSet.getString("value");
|
||||
try {
|
||||
minorVersion = Integer.parseInt(minorVersionStr);
|
||||
} catch (NumberFormatException ex) {
|
||||
throw new TskCoreException("Bad value for schema minor version = " + minorVersionStr, ex);
|
||||
}
|
||||
} else {
|
||||
throw new TskCoreException("Failed to read schema minor version from ig_db_info table");
|
||||
}
|
||||
|
||||
return new VersionNumber(majorVersion, minorVersion, 0 );
|
||||
}
|
||||
finally {
|
||||
if (resultSet != null) {
|
||||
resultSet.close();
|
||||
}
|
||||
if (statement != null) {
|
||||
statement.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the ImageGallery schema version from CaseDB
|
||||
*
|
||||
* @return image gallery schema version in CaseDB
|
||||
* @throws SQLException
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
private VersionNumber getCaseDbIgSchemaVersion() throws TskCoreException {
|
||||
|
||||
// Callback to process result of get version query
|
||||
class GetSchemaVersionQueryResultProcessor implements CaseDbAccessQueryCallback {
|
||||
|
||||
private int version = -1;
|
||||
|
||||
int getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(ResultSet resultSet) {
|
||||
try {
|
||||
if (resultSet.next()) {
|
||||
String versionStr = resultSet.getString("value");
|
||||
try {
|
||||
version = Integer.parseInt(versionStr);
|
||||
} catch (NumberFormatException ex) {
|
||||
logger.log(Level.SEVERE, "Bad value for version = " + versionStr, ex);
|
||||
}
|
||||
} else {
|
||||
logger.log(Level.SEVERE, "Failed to get version");
|
||||
}
|
||||
}
|
||||
catch (SQLException ex) {
|
||||
logger.log(Level.SEVERE, "Failed to get version", ex); //NON-NLS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GetSchemaVersionQueryResultProcessor majorVersionResultProcessor = new GetSchemaVersionQueryResultProcessor();
|
||||
GetSchemaVersionQueryResultProcessor minorVersionResultProcessor = new GetSchemaVersionQueryResultProcessor();
|
||||
|
||||
String versionQueryTemplate = "value FROM %s WHERE name = \'%s\' ";
|
||||
tskCase.getCaseDbAccessManager().select(String.format(versionQueryTemplate, IG_DB_INFO_TABLE, IG_SCHEMA_MAJOR_VERSION_KEY), majorVersionResultProcessor);
|
||||
tskCase.getCaseDbAccessManager().select(String.format(versionQueryTemplate, IG_DB_INFO_TABLE, IG_SCHEMA_MINOR_VERSION_KEY), minorVersionResultProcessor);
|
||||
|
||||
return new VersionNumber(majorVersionResultProcessor.getVersion(), minorVersionResultProcessor.getVersion(), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the IG schema version in the Drawable DB
|
||||
*
|
||||
* @param version new version number
|
||||
* @param transaction transaction under which the update happens
|
||||
*
|
||||
* @throws SQLException
|
||||
*/
|
||||
private void updateDrawableDbIgSchemaVersion(VersionNumber version, DrawableTransaction transaction) throws SQLException, TskCoreException {
|
||||
|
||||
if (transaction == null) {
|
||||
throw new TskCoreException("Schema version update must be done in a transaction");
|
||||
}
|
||||
|
||||
dbWriteLock();
|
||||
try {
|
||||
Statement statement = con.createStatement();
|
||||
|
||||
// update schema version
|
||||
statement.execute(String.format("UPDATE %s SET value = '%s' WHERE name = '%s'", IG_DB_INFO_TABLE, version.getMajor(), IG_SCHEMA_MAJOR_VERSION_KEY ));
|
||||
statement.execute(String.format("UPDATE %s SET value = '%s' WHERE name = '%s'", IG_DB_INFO_TABLE, version.getMinor(), IG_SCHEMA_MINOR_VERSION_KEY ));
|
||||
|
||||
statement.close();
|
||||
}
|
||||
finally {
|
||||
dbWriteUnlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the IG schema version in CaseDB
|
||||
*
|
||||
* @param version new version number
|
||||
* @param caseDbTransaction transaction to use to update the CaseDB
|
||||
*
|
||||
* @throws SQLException
|
||||
*/
|
||||
private void updateCaseDbIgSchemaVersion(VersionNumber version, CaseDbTransaction caseDbTransaction) throws TskCoreException {
|
||||
|
||||
String updateSQLTemplate = " SET value = %s WHERE name = '%s' ";
|
||||
tskCase.getCaseDbAccessManager().update(IG_DB_INFO_TABLE, String.format(updateSQLTemplate, version.getMajor(), IG_SCHEMA_MAJOR_VERSION_KEY), caseDbTransaction);
|
||||
tskCase.getCaseDbAccessManager().update(IG_DB_INFO_TABLE, String.format(updateSQLTemplate, version.getMinor(), IG_SCHEMA_MINOR_VERSION_KEY), caseDbTransaction);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Upgrades the DB schema.
|
||||
*
|
||||
* @return true if the upgrade is successful
|
||||
*
|
||||
* @throws SQLException
|
||||
*
|
||||
*/
|
||||
private boolean upgradeDBSchema() throws TskCoreException, SQLException {
|
||||
|
||||
// Read current version from the DBs
|
||||
VersionNumber drawableDbIgSchemaVersion = getDrawableDbIgSchemaVersion();
|
||||
VersionNumber caseDbIgSchemaVersion = getCaseDbIgSchemaVersion();
|
||||
|
||||
// Upgrade Schema in both DrawableDB and CaseDB
|
||||
CaseDbTransaction caseDbTransaction = tskCase.beginTransaction();
|
||||
DrawableTransaction transaction = beginTransaction();
|
||||
|
||||
try {
|
||||
caseDbIgSchemaVersion = upgradeCaseDbIgSchema1dot0TO1dot1(caseDbIgSchemaVersion, caseDbTransaction);
|
||||
drawableDbIgSchemaVersion = upgradeDrawableDbIgSchema1dot0TO1dot1(drawableDbIgSchemaVersion, transaction);
|
||||
|
||||
// update the versions in the tables
|
||||
updateCaseDbIgSchemaVersion(caseDbIgSchemaVersion, caseDbTransaction );
|
||||
updateDrawableDbIgSchemaVersion(drawableDbIgSchemaVersion, transaction);
|
||||
|
||||
caseDbTransaction.commit();
|
||||
caseDbTransaction = null;
|
||||
commitTransaction(transaction, false);
|
||||
transaction = null;
|
||||
}
|
||||
catch (TskCoreException | SQLException ex) {
|
||||
if (null != caseDbTransaction) {
|
||||
try {
|
||||
caseDbTransaction.rollback();
|
||||
} catch (TskCoreException ex2) {
|
||||
logger.log(Level.SEVERE, String.format("Failed to roll back case db transaction after error: %s", ex.getMessage()), ex2); //NON-NLS
|
||||
}
|
||||
}
|
||||
if (null != transaction) {
|
||||
try {
|
||||
rollbackTransaction(transaction);
|
||||
} catch (SQLException ex2) {
|
||||
logger.log(Level.SEVERE, String.format("Failed to roll back drawables db transaction after error: %s", ex.getMessage()), ex2); //NON-NLS
|
||||
}
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Upgrades IG tables in CaseDB from 1.0 to 1.1
|
||||
* Does nothing if the incoming version is not 1.0
|
||||
*
|
||||
* @param currVersion version to upgrade from
|
||||
* @param caseDbTransaction transaction to use for all updates
|
||||
*
|
||||
* @return new version number
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
private VersionNumber upgradeCaseDbIgSchema1dot0TO1dot1(VersionNumber currVersion, CaseDbTransaction caseDbTransaction ) throws TskCoreException {
|
||||
|
||||
if (currVersion.getMajor() != 1 ||
|
||||
currVersion.getMinor() != 0) {
|
||||
return currVersion;
|
||||
}
|
||||
|
||||
// 1.0 -> 1.1 upgrade
|
||||
// Add a 'isAnalyzed' column to groups table in CaseDB
|
||||
String alterSQL = " ADD COLUMN is_analyzed integer DEFAULT 1 "; //NON-NLS
|
||||
if (false == tskCase.getCaseDbAccessManager().columnExists(GROUPS_TABLENAME, "is_analyzed", caseDbTransaction )) {
|
||||
tskCase.getCaseDbAccessManager().alterTable(GROUPS_TABLENAME, alterSQL, caseDbTransaction);
|
||||
}
|
||||
return new VersionNumber(1,1,0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Upgrades IG tables in DrawableDB from 1.0 to 1.1
|
||||
* Does nothing if the incoming version is not 1.0
|
||||
*
|
||||
* @param currVersion version to upgrade from
|
||||
* @param transaction transaction to use for all updates
|
||||
*
|
||||
* @return new version number
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
private VersionNumber upgradeDrawableDbIgSchema1dot0TO1dot1(VersionNumber currVersion, DrawableTransaction transaction ) throws TskCoreException {
|
||||
|
||||
if (currVersion.getMajor() != 1 ||
|
||||
currVersion.getMinor() != 0) {
|
||||
return currVersion;
|
||||
}
|
||||
|
||||
// There are no changes in DrawableDB schema in 1.0 -> 1.1
|
||||
return new VersionNumber(1,1,0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
/*
|
||||
@ -829,6 +1179,26 @@ public final class DrawableDB {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the isAnalysed flag in the groups table for the given group to true.
|
||||
*
|
||||
* @param groupKey group key.
|
||||
*
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
public void markGroupAnalyzed(GroupKey<?> groupKey) throws TskCoreException {
|
||||
|
||||
|
||||
String updateSQL = String.format(" SET is_analyzed = %d "
|
||||
+ " WHERE attribute = \'%s\' AND value = \'%s\' and data_source_obj_id = %d ",
|
||||
1,
|
||||
SleuthkitCase.escapeSingleQuotes(groupKey.getAttribute().attrName.toString()),
|
||||
SleuthkitCase.escapeSingleQuotes(groupKey.getValueDisplayName()),
|
||||
groupKey.getAttribute() == DrawableAttribute.PATH ? groupKey.getDataSourceObjId() : 0);
|
||||
|
||||
tskCase.getCaseDbAccessManager().update(GROUPS_TABLENAME, updateSQL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a file from the drawables databse.
|
||||
*
|
||||
@ -857,6 +1227,14 @@ public final class DrawableDB {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the image file.
|
||||
*
|
||||
* @param f file to update.
|
||||
*
|
||||
* @throws TskCoreException
|
||||
* @throws SQLException
|
||||
*/
|
||||
public void updateFile(DrawableFile f) throws TskCoreException, SQLException {
|
||||
DrawableTransaction trans = null;
|
||||
CaseDbTransaction caseDbTransaction = null;
|
||||
@ -885,25 +1263,14 @@ public final class DrawableDB {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert basic file data (no groups) into the DB during pre-population
|
||||
* phase
|
||||
*
|
||||
* @param f
|
||||
* @param tr
|
||||
* @param caseDbTransaction
|
||||
*/
|
||||
public void insertBasicFileData(DrawableFile f, DrawableTransaction tr, CaseDbTransaction caseDbTransaction) {
|
||||
insertOrUpdateFile(f, tr, caseDbTransaction, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update an existing entry (or make a new one) into the DB that includes
|
||||
* group information. Called when a file has been analyzed or during a bulk
|
||||
* rebuild
|
||||
*
|
||||
* @param f
|
||||
* @param tr
|
||||
* @param f file to update
|
||||
* @param tr
|
||||
* @param caseDbTransaction
|
||||
*/
|
||||
public void updateFile(DrawableFile f, DrawableTransaction tr, CaseDbTransaction caseDbTransaction) {
|
||||
@ -1285,29 +1652,53 @@ public final class DrawableDB {
|
||||
}
|
||||
}
|
||||
|
||||
public Boolean isGroupAnalyzed(GroupKey<?> gk) throws SQLException, TskCoreException {
|
||||
dbWriteLock();
|
||||
try {
|
||||
if (isClosed()) {
|
||||
throw new SQLException("The drawables database is closed");
|
||||
/**
|
||||
* Returns whether or not the given group is analyzed and ready to be viewed.
|
||||
*
|
||||
* @param groupKey group key.
|
||||
* @return true if the group is analyzed.
|
||||
* @throws SQLException
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
public Boolean isGroupAnalyzed(GroupKey<?> groupKey) throws SQLException, TskCoreException {
|
||||
|
||||
// Callback to process result of isAnalyzed query
|
||||
class IsGroupAnalyzedQueryResultProcessor implements CaseDbAccessQueryCallback {
|
||||
|
||||
private boolean isAnalyzed = false;
|
||||
|
||||
boolean getIsAnalyzed() {
|
||||
return isAnalyzed;
|
||||
}
|
||||
try (Statement stmt = con.createStatement()) {
|
||||
// In testing, this method appears to be a lot faster than doing one large select statement
|
||||
Set<Long> fileIDsInGroup = getFileIDsInGroup(gk);
|
||||
for (Long fileID : fileIDsInGroup) {
|
||||
ResultSet analyzedQuery = stmt.executeQuery("SELECT analyzed FROM drawable_files WHERE obj_id = " + fileID); //NON-NLS
|
||||
while (analyzedQuery.next()) {
|
||||
if (analyzedQuery.getInt(ANALYZED) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(ResultSet resultSet) {
|
||||
try {
|
||||
if (resultSet.next()) {
|
||||
isAnalyzed = resultSet.getInt("is_analyzed") == 1 ? true: false;
|
||||
}
|
||||
return true; // THIS APPEARS TO BE A BUG (see JIRA-1130), THE FOR LOOP EXECUTES AT MOST ONCE
|
||||
} catch (SQLException ex) {
|
||||
logger.log(Level.SEVERE, "Failed to get group is_analyzed", ex); //NON-NLS
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} finally {
|
||||
dbWriteUnlock();
|
||||
}
|
||||
|
||||
IsGroupAnalyzedQueryResultProcessor queryResultProcessor = new IsGroupAnalyzedQueryResultProcessor();
|
||||
try {
|
||||
String groupAnalyzedQueryStmt = String.format("is_analyzed FROM " + GROUPS_TABLENAME
|
||||
+ " WHERE attribute = \'%s\' AND value = \'%s\' and data_source_obj_id = %d ",
|
||||
SleuthkitCase.escapeSingleQuotes(groupKey.getAttribute().attrName.toString()),
|
||||
SleuthkitCase.escapeSingleQuotes(groupKey.getValueDisplayName()),
|
||||
groupKey.getAttribute() == DrawableAttribute.PATH ? groupKey.getDataSourceObjId() : 0);
|
||||
|
||||
tskCase.getCaseDbAccessManager().select(groupAnalyzedQueryStmt, queryResultProcessor);
|
||||
return queryResultProcessor.getIsAnalyzed();
|
||||
} catch ( TskCoreException ex) {
|
||||
String msg = String.format("Failed to get group is_analyzed for group key %s", groupKey.getValueDisplayName()); //NON-NLS
|
||||
logger.log(Level.SEVERE, msg, ex);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1497,8 +1888,9 @@ public final class DrawableDB {
|
||||
return;
|
||||
}
|
||||
|
||||
String insertSQL = String.format(" (data_source_obj_id, value, attribute) VALUES (%d, \'%s\', \'%s\')",
|
||||
ds_obj_id, SleuthkitCase.escapeSingleQuotes(value), SleuthkitCase.escapeSingleQuotes(groupBy.attrName.toString()));
|
||||
int isAnalyzed = (groupBy == DrawableAttribute.PATH) ? 0 : 1;
|
||||
String insertSQL = String.format(" (data_source_obj_id, value, attribute, is_analyzed) VALUES (%d, \'%s\', \'%s\', %d)",
|
||||
ds_obj_id, SleuthkitCase.escapeSingleQuotes(value), SleuthkitCase.escapeSingleQuotes(groupBy.attrName.toString()), isAnalyzed);
|
||||
if (DbType.POSTGRESQL == tskCase.getDatabaseType()) {
|
||||
insertSQL += " ON CONFLICT DO NOTHING";
|
||||
}
|
||||
@ -1775,8 +2167,13 @@ public final class DrawableDB {
|
||||
*/
|
||||
public class DrawableTransaction {
|
||||
|
||||
private final Set<Long> updatedFiles = new HashSet<>();
|
||||
private final Set<Long> removedFiles = new HashSet<>();
|
||||
// The files are processed ORDERED BY parent path
|
||||
// We want to preserve that order here, so that we can detect a
|
||||
// change in path, and thus mark the path group as analyzed
|
||||
// Hence we use a LinkedHashSet here.
|
||||
private final Set<Long> updatedFiles = new LinkedHashSet<>();
|
||||
private final Set<Long> removedFiles = new LinkedHashSet<>();
|
||||
|
||||
private boolean completed;
|
||||
|
||||
private DrawableTransaction() throws TskCoreException, SQLException {
|
||||
|
@ -107,6 +107,12 @@ public class GroupManager {
|
||||
|
||||
private final ImageGalleryController controller;
|
||||
|
||||
/**
|
||||
* Keeps track of the current path group
|
||||
* - a change in path indicates the current path group is analyzed
|
||||
*/
|
||||
@GuardedBy("this") //NOPMD
|
||||
private GroupKey<?> currentPathGroup = null;
|
||||
/**
|
||||
* list of all analyzed groups
|
||||
*/
|
||||
@ -238,7 +244,7 @@ public class GroupManager {
|
||||
setGroupBy(DrawableAttribute.PATH);
|
||||
setSortOrder(SortOrder.ASCENDING);
|
||||
setDataSource(null);
|
||||
|
||||
|
||||
unSeenGroups.forEach(controller.getCategoryManager()::unregisterListener);
|
||||
unSeenGroups.clear();
|
||||
analyzedGroups.forEach(controller.getCategoryManager()::unregisterListener);
|
||||
@ -618,6 +624,8 @@ public class GroupManager {
|
||||
for (GroupKey<?> gk : groupsForFile) {
|
||||
// see if a group has been created yet for the key
|
||||
DrawableGroup g = getGroupForKey(gk);
|
||||
|
||||
updateCurrentPathGroup(gk);
|
||||
addFileToGroup(g, gk, fileId);
|
||||
}
|
||||
}
|
||||
@ -625,7 +633,58 @@ public class GroupManager {
|
||||
//we fire this event for all files so that the category counts get updated during initial db population
|
||||
controller.getCategoryManager().fireChange(updatedFileIDs, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given path is different from the current path group.
|
||||
* If so, updates the current path group as analyzed, and sets current path
|
||||
* group to the given path.
|
||||
*
|
||||
* The idea is that when the path of the files being processed changes,
|
||||
* we have moved from one folder to the next, and the group for the
|
||||
* previous PATH can be considered as analyzed and can be displayed.
|
||||
*
|
||||
* NOTE: this a close approximation for when all files in a folder have been processed,
|
||||
* but there's some room for error - files may go down the ingest pipleline
|
||||
* out of order or the events may not always arrive in the same order
|
||||
*
|
||||
* @param groupKey
|
||||
*/
|
||||
synchronized private void updateCurrentPathGroup(GroupKey<?> groupKey) {
|
||||
try {
|
||||
if (groupKey.getAttribute() == DrawableAttribute.PATH) {
|
||||
|
||||
if (this.currentPathGroup == null) {
|
||||
currentPathGroup = groupKey;
|
||||
}
|
||||
else if (groupKey.getValue().toString().equalsIgnoreCase(this.currentPathGroup.getValue().toString()) == false) {
|
||||
// mark the last path group as analyzed
|
||||
getDrawableDB().markGroupAnalyzed(currentPathGroup);
|
||||
popuplateIfAnalyzed(currentPathGroup, null);
|
||||
|
||||
currentPathGroup = groupKey;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Error setting is_analyzed status for group: %s", groupKey.getValue().toString()), ex); //NON-NLS
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets current path group, after marking the current path group as analyzed.
|
||||
*/
|
||||
synchronized public void resetCurrentPathGroup() {
|
||||
try {
|
||||
if (currentPathGroup != null) {
|
||||
getDrawableDB().markGroupAnalyzed(currentPathGroup);
|
||||
popuplateIfAnalyzed(currentPathGroup, null);
|
||||
currentPathGroup = null;
|
||||
}
|
||||
}
|
||||
catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Error resetting last path group: %s", currentPathGroup.getValue().toString()), ex); //NON-NLS
|
||||
}
|
||||
}
|
||||
/**
|
||||
* If the group is analyzed (or other criteria based on grouping) and should
|
||||
* be shown to the user, then add it to the appropriate data structures so
|
||||
|
@ -206,8 +206,8 @@
|
||||
<binary-origin>release/modules/ext/quartz-2.2.0.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/sqlite-jdbc-3.19.3.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/sqlite-jdbc-3.19.3.jar</binary-origin>
|
||||
<runtime-relative-path>ext/sqlite-jdbc-3.25.2.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/sqlite-jdbc-3.25.2.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/guava-17.0.jar</runtime-relative-path>
|
||||
|
@ -2,7 +2,7 @@
|
||||
*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2012-2018 Basis Technology Corp.
|
||||
* Copyright 2012-2019 Basis Technology Corp.
|
||||
*
|
||||
* Copyright 2012 42six Solutions.
|
||||
*
|
||||
@ -79,7 +79,6 @@ class Chrome extends Extract {
|
||||
this.getHistory();
|
||||
this.getBookmark();
|
||||
this.getCookie();
|
||||
this.getLogin();
|
||||
this.getDownload();
|
||||
}
|
||||
|
||||
@ -518,108 +517,6 @@ class Chrome extends Extract {
|
||||
BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, bbartifacts));
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries for login files and adds artifacts
|
||||
*/
|
||||
private void getLogin() {
|
||||
FileManager fileManager = currentCase.getServices().getFileManager();
|
||||
List<AbstractFile> signonFiles;
|
||||
try {
|
||||
signonFiles = fileManager.findFiles(dataSource, "signons.sqlite", "Chrome"); //NON-NLS
|
||||
} catch (TskCoreException ex) {
|
||||
String msg = NbBundle.getMessage(this.getClass(), "Chrome.getLogin.errMsg.errGettingFiles");
|
||||
logger.log(Level.SEVERE, msg, ex);
|
||||
this.addErrorMessage(this.getName() + ": " + msg);
|
||||
return;
|
||||
}
|
||||
|
||||
if (signonFiles.isEmpty()) {
|
||||
logger.log(Level.INFO, "Didn't find any Chrome signon files."); //NON-NLS
|
||||
return;
|
||||
}
|
||||
|
||||
dataFound = true;
|
||||
Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
|
||||
int j = 0;
|
||||
while (j < signonFiles.size()) {
|
||||
AbstractFile signonFile = signonFiles.get(j++);
|
||||
if (signonFile.getSize() == 0) {
|
||||
continue;
|
||||
}
|
||||
String temps = RAImageIngestModule.getRATempPath(currentCase, "chrome") + File.separator + signonFile.getName() + j + ".db"; //NON-NLS
|
||||
try {
|
||||
ContentUtils.writeToFile(signonFile, new File(temps), context::dataSourceIngestIsCancelled);
|
||||
} catch (ReadContentInputStreamException ex) {
|
||||
logger.log(Level.WARNING, String.format("Error reading Chrome login artifacts file '%s' (id=%d).",
|
||||
signonFile.getName(), signonFile.getId()), ex); //NON-NLS
|
||||
this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Chrome.getLogin.errMsg.errAnalyzingFiles",
|
||||
this.getName(), signonFile.getName()));
|
||||
continue;
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Error writing temp sqlite db file '%s' for Chrome login artifacts file '%s' (id=%d).",
|
||||
temps, signonFile.getName(), signonFile.getId()), ex); //NON-NLS
|
||||
this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Chrome.getLogin.errMsg.errAnalyzingFiles",
|
||||
this.getName(), signonFile.getName()));
|
||||
continue;
|
||||
}
|
||||
File dbFile = new File(temps);
|
||||
if (context.dataSourceIngestIsCancelled()) {
|
||||
dbFile.delete();
|
||||
break;
|
||||
}
|
||||
List<HashMap<String, Object>> tempList = this.dbConnect(temps, LOGIN_QUERY);
|
||||
logger.log(Level.INFO, "{0}- Now getting login information from {1} with {2}artifacts identified.", new Object[]{moduleName, temps, tempList.size()}); //NON-NLS
|
||||
for (HashMap<String, Object> result : tempList) {
|
||||
Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
|
||||
NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"),
|
||||
((result.get("origin_url").toString() != null) ? result.get("origin_url").toString() : ""))); //NON-NLS
|
||||
//bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL_DECODED.getTypeID(), "Recent Activity", ((result.get("origin_url").toString() != null) ? EscapeUtil.decodeURL(result.get("origin_url").toString()) : "")));
|
||||
//TODO Revisit usage of deprecated constructor as per TSK-583
|
||||
//bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED.getTypeID(), "Recent Activity", "Last Visited", ((Long.valueOf(result.get("last_visit_time").toString())) / 1000000)));
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
|
||||
NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"),
|
||||
(Long.valueOf(result.get("last_visit_time").toString()) / 1000000) - Long.valueOf("11644473600"))); //NON-NLS
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_REFERRER,
|
||||
NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"),
|
||||
((result.get("from_visit").toString() != null) ? result.get("from_visit").toString() : ""))); //NON-NLS
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME,
|
||||
NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"),
|
||||
((result.get("title").toString() != null) ? result.get("title").toString() : ""))); //NON-NLS
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
|
||||
NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"),
|
||||
NbBundle.getMessage(this.getClass(), "Chrome.moduleName")));
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL_DECODED,
|
||||
NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"),
|
||||
(NetworkUtils.extractDomain((result.get("origin_url").toString() != null) ? result.get("url").toString() : "")))); //NON-NLS
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_NAME,
|
||||
NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"),
|
||||
((result.get("username_value").toString() != null) ? result.get("username_value").toString().replaceAll("'", "''") : ""))); //NON-NLS
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
|
||||
NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"),
|
||||
result.get("signon_realm").toString())); //NON-NLS
|
||||
|
||||
BlackboardArtifact bbart = this.addArtifact(ARTIFACT_TYPE.TSK_WEB_HISTORY, signonFile, bbattributes);
|
||||
if (bbart != null) {
|
||||
bbartifacts.add(bbart);
|
||||
}
|
||||
|
||||
// Don't add TSK_OS_ACCOUNT artifacts to the ModuleDataEvent
|
||||
Collection<BlackboardAttribute> osAcctAttributes = new ArrayList<>();
|
||||
osAcctAttributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_NAME,
|
||||
NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"),
|
||||
((result.get("username_value").toString() != null) ? result.get("username_value").toString().replaceAll("'", "''") : ""))); //NON-NLS
|
||||
this.addArtifact(ARTIFACT_TYPE.TSK_OS_ACCOUNT, signonFile, osAcctAttributes);
|
||||
}
|
||||
|
||||
dbFile.delete();
|
||||
}
|
||||
|
||||
IngestServices.getInstance().fireModuleDataEvent(new ModuleDataEvent(
|
||||
NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"),
|
||||
BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY, bbartifacts));
|
||||
}
|
||||
|
||||
private boolean isChromePreVersion30(String temps) {
|
||||
String query = "PRAGMA table_info(downloads)"; //NON-NLS
|
||||
List<HashMap<String, Object>> columns = this.dbConnect(temps, query);
|
||||
|
@ -2,7 +2,7 @@
|
||||
*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011-2018 Basis Technology Corp.
|
||||
* Copyright 2011-2019 Basis Technology Corp.
|
||||
*
|
||||
* Copyright 2012 42six Solutions.
|
||||
* Contact: aebadirad <at> 42six <dot> com
|
||||
@ -23,7 +23,6 @@
|
||||
package org.sleuthkit.autopsy.recentactivity;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.coreutils.ExecUtil;
|
||||
import org.sleuthkit.autopsy.coreutils.NetworkUtils;
|
||||
@ -36,8 +35,6 @@ import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.HashSet;
|
||||
import java.util.logging.Level;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import java.util.Collection;
|
||||
@ -460,10 +457,6 @@ class ExtractIE extends Extract {
|
||||
logger.log(Level.WARNING, "Unable to find the Pasco file at " + file.getPath(), ex); //NON-NLS
|
||||
return bbartifacts;
|
||||
}
|
||||
|
||||
// Keep a list of reported user accounts to avoid repeats
|
||||
Set<String> reportedUserAccounts = new HashSet<>();
|
||||
|
||||
while (fileScanner.hasNext()) {
|
||||
String line = fileScanner.nextLine();
|
||||
if (!line.startsWith("URL")) { //NON-NLS
|
||||
@ -570,18 +563,6 @@ class ExtractIE extends Extract {
|
||||
// index the artifact for keyword search
|
||||
this.indexArtifact(bbart);
|
||||
bbartifacts.add(bbart);
|
||||
|
||||
if ((!user.isEmpty()) && (!reportedUserAccounts.contains(user))) {
|
||||
BlackboardArtifact osAttr = origFile.newArtifact(ARTIFACT_TYPE.TSK_OS_ACCOUNT);
|
||||
osAttr.addAttribute(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_NAME,
|
||||
NbBundle.getMessage(this.getClass(), "ExtractIE.parentModuleName.noSpace"), user));
|
||||
|
||||
// index the artifact for keyword search
|
||||
this.indexArtifact(osAttr);
|
||||
bbartifacts.add(osAttr);
|
||||
|
||||
reportedUserAccounts.add(user);
|
||||
}
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Error writing Internet Explorer web history artifact to the blackboard.", ex); //NON-NLS
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2012-2018 Basis Technology Corp.
|
||||
* Copyright 2012-2019 Basis Technology Corp.
|
||||
*
|
||||
* Copyright 2012 42six Solutions.
|
||||
* Contact: aebadirad <at> 42six <dot> com
|
||||
@ -688,18 +688,48 @@ class ExtractRegistry extends Extract {
|
||||
|
||||
case "ProfileList": //NON-NLS
|
||||
try {
|
||||
|
||||
String homeDir = value;
|
||||
String sid = artnode.getAttribute("sid"); //NON-NLS
|
||||
String username = artnode.getAttribute("username"); //NON-NLS
|
||||
BlackboardArtifact bbart = regFile.newArtifact(ARTIFACT_TYPE.TSK_OS_ACCOUNT);
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_NAME,
|
||||
parentModuleName, username));
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_ID,
|
||||
parentModuleName, sid));
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH,
|
||||
parentModuleName, homeDir));
|
||||
|
||||
BlackboardArtifact bbart = null;
|
||||
try {
|
||||
//check if any of the existing artifacts match this username
|
||||
ArrayList<BlackboardArtifact> existingArtifacts = currentCase.getSleuthkitCase().getBlackboardArtifacts(ARTIFACT_TYPE.TSK_OS_ACCOUNT);
|
||||
for (BlackboardArtifact artifact : existingArtifacts) {
|
||||
if (artifact.getDataSource().getId() == regFile.getDataSourceObjectId()) {
|
||||
BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_USER_ID));
|
||||
if (attribute != null && attribute.getValueString().equals(sid)) {
|
||||
bbart = artifact;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.WARNING, "Error getting existing os account artifact", ex);
|
||||
}
|
||||
if (bbart == null) {
|
||||
//create new artifact
|
||||
bbart = regFile.newArtifact(ARTIFACT_TYPE.TSK_OS_ACCOUNT);
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_NAME,
|
||||
parentModuleName, username));
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_ID,
|
||||
parentModuleName, sid));
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH,
|
||||
parentModuleName, homeDir));
|
||||
} else {
|
||||
//add attributes to existing artifact
|
||||
BlackboardAttribute bbattr = bbart.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_USER_NAME));
|
||||
|
||||
if (bbattr == null) {
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_NAME,
|
||||
parentModuleName, username));
|
||||
}
|
||||
bbattr = bbart.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_PATH));
|
||||
if (bbattr == null) {
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH,
|
||||
parentModuleName, homeDir));
|
||||
}
|
||||
}
|
||||
bbart.addAttributes(bbattributes);
|
||||
// index the artifact for keyword search
|
||||
this.indexArtifact(bbart);
|
||||
@ -759,7 +789,7 @@ class ExtractRegistry extends Extract {
|
||||
if (!usbBBartifacts.isEmpty()) {
|
||||
IngestServices.getInstance().fireModuleDataEvent(new ModuleDataEvent(moduleName, BlackboardArtifact.ARTIFACT_TYPE.TSK_DEVICE_ATTACHED, usbBBartifacts));
|
||||
}
|
||||
if (!wifiBBartifacts.isEmpty()){
|
||||
if (!wifiBBartifacts.isEmpty()) {
|
||||
IngestServices.getInstance().fireModuleDataEvent(new ModuleDataEvent(moduleName, BlackboardArtifact.ARTIFACT_TYPE.TSK_WIFI_NETWORK, wifiBBartifacts));
|
||||
}
|
||||
return true;
|
||||
|
@ -1,28 +1,36 @@
|
||||
/*! \page quick_start_guide Quick Start Guide
|
||||
|
||||
\section s1 Adding a Data Source (image, local disk, logical files)
|
||||
\section s1 Cases and Data Sources
|
||||
|
||||
Data sources are added to a <strong>case</strong>. A case can have a single data source or it can have multiple data sources. Currently, a single report is generated for an entire case, so if you need to report on individual data sources, then you should use one data source per case. If there are many drives/phones/other data sources for one investigation, then your case should have multiple data sources.
|
||||
Autopsy organizes data by <strong>case</strong>. Each case can have one or more <strong>data sources</strong>, which can be a disk image, a set of logical files, a USB-connected device, etc.
|
||||
|
||||
\subsection s2 Creating a Case
|
||||
Cases can either be single-user or multi-user. Multi-user cases allow several examiners to review the data at the same time and collaborate, but require some additional open source servers to be configured.
|
||||
|
||||
When you have several data sources and are deciding about creating creating a case, consider:
|
||||
- You can have only one case open at a time
|
||||
- Reports are generated at a case-level
|
||||
- The application can slow down when there are many large data sources in the same case
|
||||
|
||||
\subsection s1a Creating a Case
|
||||
To create a case, use either the "Create New Case" option on the Welcome screen or from the "Case" menu. This will start the <strong>New Case Wizard</strong>. You will need to supply it with the name of the case and a directory to store the case results into. You can optionally provide case numbers and reviewer names.
|
||||
|
||||
\subsection s3 Adding a Data Source
|
||||
\subsection s1b Adding a Data Source
|
||||
The next step is to add an input data source to the case. The <strong>Add Data Source Wizard</strong> will start automatically after the case is created or you can manually start it from the "Case" menu or toolbar. You will need to choose the type of input data source to add (image, local disk, or logical files and folders). Next, supply it with the location of the source to add.
|
||||
|
||||
|
||||
- For a disk image, browse to the first file in the set (Autopsy will find the rest of the files). Autopsy currently supports E01 and raw (dd) files.
|
||||
- For local disk, select one of the detected disks. Autopsy will add the current view of the disk to the case (i.e. snapshot of the meta-data). However, the individual file content (not meta-data) does get updated with the changes made to the disk. Note, you may need run Autopsy as an Administrator to detect all disks.
|
||||
- For local disk, select one of the detected disks. Autopsy will add the current view of the disk to the case (i.e. snapshot of the meta-data). However, the individual file content (not meta-data) does get updated with the changes made to the disk. You can optionally create a copy of all data read from the local disk to a VHD file, which can be useful for triage situations. Note, you may need run Autopsy as an Administrator to detect all disks.
|
||||
- For logical files (a single file or folder of files), use the "Add" button to add one or more files or folders on your system to the case. Folders will be recursively added to the case.
|
||||
|
||||
There are a couple of options in the wizard that will allow you to make the ingest process faster. These typically deal with deleted files. It will take longer if unallocated space is analyzed and the entire drive is searched for deleted files. In some scenarios, these recovery steps must be performed and in other scenarios these steps are not needed and instead fast results on the allocated files are needed. Use these options to control how long the analysis will take.
|
||||
|
||||
Autopsy will start to analyze these data sources and add them to the case and the internal database. While it is doing that, it will prompt you to configure the Ingest Modules.
|
||||
After supplying the needed data, Autopsy will quickly review the data sources and add minimal metadata to the case databases so that it can schedule the files for analysis. While it is doing that, it will prompt you to configure the Ingest Modules.
|
||||
|
||||
|
||||
\subsection s4 Ingest Modules
|
||||
\subsection s1c Ingest Modules
|
||||
|
||||
You will next be prompted to configure the Ingest Modules. Ingest modules will run in the background and perform specific tasks. The Ingest Modules analyze files in a prioritized order so that files in a user's directory are analyzed before files in other folders. Ingest modules can be developed by third-parties. The standard ingest modules included with Autopsy are:
|
||||
Ingest modules are responsible for analyzing the data source contents and will run in the background. The Ingest Modules analyze files in a prioritized order so that files in a user's directory are analyzed before files in other folders. Ingest modules can be developed by third-parties.
|
||||
|
||||
The standard ingest modules included with Autopsy are:
|
||||
|
||||
- <strong>\subpage recent_activity_page</strong> extracts user activity as saved by web browsers and the OS. Also runs Regripper on the registry hive.
|
||||
- <strong>\subpage hash_db_page</strong> uses hash sets to ignore known files from the NIST NSRL and flag known bad files. Use the "Advanced" button to add and configure the hash sets to use during this process. You will get updates on known bad file hits as the ingest occurs. You can later add hash sets via the Tools -> Options menu in the main UI. You can download an index of the NIST NSRL from http://sourceforge.net/projects/autopsy/files/NSRL/
|
||||
@ -36,7 +44,7 @@ You will next be prompted to configure the Ingest Modules. Ingest modules will r
|
||||
- <strong>\subpage android_analyzer_page</strong> allows you to parse common items from Android devices. Places artifacts into the BlackBoard.
|
||||
- <strong>\subpage interesting_files_identifier_page</strong> searches for files and directories based on user-specified rules in Tools, Options, Interesting Files. It works as a "File Alerting Module". It generates messages in the inbox when specified files are found.
|
||||
- <strong>\subpage photorec_carver_page</strong> carves files from unallocated space and sends them through the file processing chain.
|
||||
- <strong>\subpage cr_ingest_module</strong> adds file hashes and other extracted properties to a central repository.
|
||||
- <strong>\subpage cr_ingest_module</strong> adds file hashes and other extracted properties to a central repository for future correlation and to flag previously notable files.
|
||||
- <strong>\subpage encryption_page</strong> looks for encrypted files.
|
||||
- <strong>\subpage vm_extractor_page</strong> extracts data from virtual machine files
|
||||
|
||||
@ -44,7 +52,9 @@ When you select a module, you will have the option to change its settings. For
|
||||
|
||||
While ingest modules are running in the background, you will see a progress bar in the lower right. You can use the GUI to review incoming results and perform other tasks while ingesting at the same time.
|
||||
|
||||
\section s1a Analysis Basics
|
||||
\section s2 Analysis Basics
|
||||
|
||||
After the ingest modules start to analyze the data source, you'll see the main analysis interface. You can choose to search for specific items, browse to specific folders, or review ingest module results.
|
||||
|
||||
\image html screenshot.PNG
|
||||
|
||||
@ -53,7 +63,7 @@ You will start all of your analysis techniques from the tree on the left.
|
||||
- The Data Sources root node shows all data in the case.
|
||||
- The individual image nodes show the file system structure of the disk images or local disks in the case.
|
||||
- The LogicalFileSet nodes show the logical files in the case.
|
||||
- The Views node shows the same data from a file type or timeline perspective.
|
||||
- The Views node shows the same data from a different perspective, such as organized by file type.
|
||||
- The Results node shows the output from the ingest modules.
|
||||
|
||||
When you select a node from the tree on the left, a list of files will be shown in the upper right. You can use the Thumbnail view in the upper right to view the pictures. When you select a file from the upper right, its contents will be shown in the lower right. You can use the tabs in the lower right to view the text of the file, an image, or the hex data.
|
||||
@ -66,23 +76,23 @@ The tree on the left as well as the table on the right have a \ref ui_quick_sear
|
||||
|
||||
You can tag (bookmark) arbitrary files so that you can more quickly find them later or so that you can include them specifically in a report.
|
||||
|
||||
\subsection s2a Ingest Inbox
|
||||
|
||||
As you are going through the results in the tree, the ingest modules are running in the background.
|
||||
The results are shown in the tree as soon as the ingest modules find them and report them.
|
||||
\section s3 Other Analysis Interfaces
|
||||
|
||||
The Ingest Inbox receives messages from the ingest modules as they find results.
|
||||
You can open the inbox to see what has been recently found.
|
||||
It keeps track of what messages you have read.
|
||||
In addition to the 3-panel UI with the tree on the left, there are other interfaces that are more specialized.
|
||||
|
||||
The intended use of this inbox is that you can focus on some data for a while and then check back on the inbox at a time that is convenient for them.
|
||||
You can then see what else was found while you were focused on the previous task.
|
||||
You may learn that a known bad file was found or that a file was found with a relevant keyword and then decide to focus on that for a while.
|
||||
\subsection s3a Timeline
|
||||
|
||||
When you select a message, you can then jump to the Results tree where more details can be found or jump to the file's location in the filesystem.
|
||||
The timeline feature can be opened from the "Tools" menu or the toolbar. This will show you file system and other events organized by time using various display techniques. See the \subpage timeline_page section for more details.
|
||||
|
||||
\subsection s2b Timeline
|
||||
There is a basic timeline view that you can access via the "Tools", "Make Timeline" feature. This will take a few minutes to create the timeline for analysis. Its features are still in development.
|
||||
|
||||
\subsection s3b Image Gallery
|
||||
|
||||
The Image Gallery focuses on showing the pictures and videos from the data source organized by folder. It will show you files as soon as they have been hashed and EXIF data extracted. You can open it from the "Tools" menu. See the \subpage image_gallery_page section for more details.
|
||||
|
||||
\subsection s3c Communications
|
||||
|
||||
The Communications interface focuses on showing which accounts were communicated with the most and what messages were sent. It allows you to focus on certain relationships or communications within a certain date rage. You can open it from the "Tools" menu. See the \subpage communications_page section for more details.
|
||||
|
||||
|
||||
\section s5 Example Use Cases
|
||||
@ -110,16 +120,14 @@ If you want to see all images and video on the disk image, then go to the "
|
||||
Select either "Images" or "Videos".
|
||||
You can use the thumbnail option in the upper right to view thumbnails of all images.
|
||||
|
||||
<strong>Note</strong>: We are working on making this more efficient when there are lots of images. We are also working on the feature to display video thumbnails.
|
||||
|
||||
You can select an image or video from the upper right and view the video or image in the lower right. Video will be played with sound.
|
||||
|
||||
\section s6 Reporting
|
||||
|
||||
A final report can be generated that will include all analysis results.
|
||||
Use the "Generate Report" button to create this.
|
||||
It will create an HTML or XLS report in the Reports folder of the case folder.
|
||||
If you forgot the location of your case folder, you can determine it using the "Case Properties" option in the "Case" menu.
|
||||
There is also an option to export report files to a separate folder outside of the case folder.
|
||||
A final report can be generated that will include all analysis results using the "Generate Report" toolbar button. Reports can be generated in HTML, XLS, KML, and other formats.
|
||||
|
||||
You can later find your generated reports by going to the tree and opening the Reports node at the bottom.
|
||||
|
||||
|
||||
*/
|
Loading…
x
Reference in New Issue
Block a user