Merge branch 'develop' of github.com:sleuthkit/autopsy into java11-upgrade

This commit is contained in:
esaunders 2020-09-29 10:29:11 -04:00
commit f5c8b36a48
308 changed files with 19862 additions and 7977 deletions

View File

@ -49,6 +49,11 @@
<fileset dir="${thirdparty.dir}/gstreamer"/>
</copy>
<!--Copy iLeapp to release-->
<copy todir="${basedir}/release/iLeapp" >
<fileset dir="${thirdparty.dir}/iLeapp"/>
</copy>
<!--Copy 7-Zip to release-->
<copy todir="${basedir}/release/7-Zip" >
<fileset dir="${thirdparty.dir}/7-Zip"/>

View File

@ -46,6 +46,9 @@
<!-- map support for geolocation -->
<dependency conf="core->default" org="org.jxmapviewer" name="jxmapviewer2" rev="2.4"/>
<!-- For Discovery testing -->
<dependency conf="core->default" org="org.mockito" name="mockito-core" rev="3.5.7"/>
<!-- https://mvnrepository.com/artifact/javax.ws.rs/javax.ws.rs-api -->
<dependency conf="core->default" org="javax.ws.rs" name="javax.ws.rs-api" rev="2.0"/>
<override org="jakarta.ws.rs" module="jakarta.ws.rs-api" rev="2.1.5"/>

View File

@ -2,7 +2,7 @@ Manifest-Version: 1.0
OpenIDE-Module: org.sleuthkit.autopsy.core/10
OpenIDE-Module-Localizing-Bundle: org/sleuthkit/autopsy/core/Bundle.properties
OpenIDE-Module-Layer: org/sleuthkit/autopsy/core/layer.xml
OpenIDE-Module-Implementation-Version: 33
OpenIDE-Module-Implementation-Version: 34
OpenIDE-Module-Requires: org.openide.windows.WindowManager
AutoUpdate-Show-In-Client: true
AutoUpdate-Essential-Module: true

View File

@ -11,6 +11,8 @@ file.reference.bcpkix-jdk15on-1.54.jar=release\\modules\\ext\\bcpkix-jdk15on-1.5
file.reference.bcprov-ext-jdk15on-1.54.jar=release\\modules\\ext\\bcprov-ext-jdk15on-1.54.jar
file.reference.bcprov-jdk15on-1.52.jar=release\\modules\\ext\\bcprov-jdk15on-1.52.jar
file.reference.bcprov-jdk15on-1.54.jar=release\\modules\\ext\\bcprov-jdk15on-1.54.jar
file.reference.byte-buddy-1.10.13.jar=release\\modules\\ext\\byte-buddy-1.10.13.jar
file.reference.byte-buddy-agent-1.10.13.jar=release\\modules\\ext\\byte-buddy-agent-1.10.13.jar
file.reference.c3p0-0.9.5.jar=release\\modules\\ext\\c3p0-0.9.5.jar
file.reference.checker-compat-qual-2.5.3.jar=release\\modules\\ext\\checker-compat-qual-2.5.3.jar
file.reference.commons-beanutils-1.9.2.jar=release\\modules\\ext\\commons-beanutils-1.9.2.jar
@ -70,9 +72,11 @@ file.reference.jai_core-1.1.3.jar=release\\modules\\ext\\jai_core-1.1.3.jar
file.reference.jai_imageio-1.1.jar=release\\modules\\ext\\jai_imageio-1.1.jar
file.reference.javax.annotation-api-1.3.2.jar=release\\modules\\ext\\javax.annotation-api-1.3.2.jar
file.reference.javax.ws.rs-api-2.0.jar=release\\modules\\ext\\javax.ws.rs-api-2.0.jar
file.reference.jcommon-1.0.23.jar=release/modules/ext/jcommon-1.0.23.jar
file.reference.jdom-2.0.5-contrib.jar=release\\modules\\ext\\jdom-2.0.5-contrib.jar
file.reference.jdom-2.0.5.jar=release\\modules\\ext\\jdom-2.0.5.jar
file.reference.jericho-html-3.3.jar=release\\modules\\ext\\jericho-html-3.3.jar
file.reference.jfreechart-1.0.19.jar=release/modules/ext/jfreechart-1.0.19.jar
file.reference.jgraphx-4.1.0.jar=release\\modules\\ext\\jgraphx-4.1.0.jar
file.reference.jline-0.9.94.jar=release\\modules\\ext\\jline-0.9.94.jar
file.reference.jsoup-1.10.3.jar=release\\modules\\ext\\jsoup-1.10.3.jar
@ -86,7 +90,9 @@ file.reference.listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar=re
file.reference.log4j-1.2.16.jar=release\\modules\\ext\\log4j-1.2.16.jar
file.reference.mchange-commons-java-0.2.9.jar=release\\modules\\ext\\mchange-commons-java-0.2.9.jar
file.reference.metadata-extractor-2.11.0.jar=release\\modules\\ext\\metadata-extractor-2.11.0.jar
file.reference.mockito-core-3.5.7.jar=release\\modules\\ext\\mockito-core-3.5.7.jar
file.reference.netty-3.7.0.Final.jar=release\\modules\\ext\\netty-3.7.0.Final.jar
file.reference.objenesis-3.1.jar=release\\modules\\ext\\objenesis-3.1.jar
file.reference.okhttp-2.7.5.jar=release\\modules\\ext\\okhttp-2.7.5.jar
file.reference.okio-1.6.0.jar=release\\modules\\ext\\okio-1.6.0.jar
file.reference.opencensus-api-0.19.2.jar=release\\modules\\ext\\opencensus-api-0.19.2.jar
@ -102,8 +108,8 @@ file.reference.protobuf-java-util-3.7.0.jar=release\\modules\\ext\\protobuf-java
file.reference.Rejistry-1.1-SNAPSHOT.jar=release\\modules\\ext\\Rejistry-1.1-SNAPSHOT.jar
file.reference.sevenzipjbinding-AllPlatforms.jar=release\\modules\\ext\\sevenzipjbinding-AllPlatforms.jar
file.reference.sevenzipjbinding.jar=release\\modules\\ext\\sevenzipjbinding.jar
file.reference.sleuthkit-4.10.0.jar=release\\modules\\ext\\sleuthkit-4.10.0.jar
file.reference.sleuthkit-caseuco-4.10.0.jar=release\\modules\\ext\\sleuthkit-caseuco-4.10.0.jar
file.reference.sleuthkit-4.10.1.jar=release/modules/ext/sleuthkit-4.10.1.jar
file.reference.sleuthkit-caseuco-4.10.1.jar=release/modules/ext/sleuthkit-caseuco-4.10.1.jar
file.reference.slf4j-api-1.7.6.jar=release\\modules\\ext\\slf4j-api-1.7.6.jar
file.reference.slf4j-log4j12-1.7.6.jar=release\\modules\\ext\\slf4j-log4j12-1.7.6.jar
file.reference.SparseBitSet-1.1.jar=release\\modules\\ext\\SparseBitSet-1.1.jar
@ -120,5 +126,4 @@ nbm.homepage=http://www.sleuthkit.org/
nbm.module.author=Brian Carrier
nbm.needs.restart=true
source.reference.curator-recipes-2.8.0.jar=release/modules/ext/curator-recipes-2.8.0-sources.jar
spec.version.base=10.21
spec.version.base=10.22

View File

@ -399,6 +399,10 @@
<runtime-relative-path>ext/proto-google-cloud-translate-v3beta1-0.53.0.jar</runtime-relative-path>
<binary-origin>release\modules\ext\proto-google-cloud-translate-v3beta1-0.53.0.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/byte-buddy-1.10.13.jar</runtime-relative-path>
<binary-origin>release\modules\ext\byte-buddy-1.10.13.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/error_prone_annotations-2.3.2.jar</runtime-relative-path>
<binary-origin>release\modules\ext\error_prone_annotations-2.3.2.jar</binary-origin>
@ -439,14 +443,6 @@
<runtime-relative-path>ext/jxmapviewer2-2.4.jar</runtime-relative-path>
<binary-origin>release\modules\ext\jxmapviewer2-2.4.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/jfreechart-1.0.19.jar</runtime-relative-path>
<binary-origin>release/modules/ext/jfreechart-1.0.19.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/jcommon-1.0.23.jar</runtime-relative-path>
<binary-origin>release/modules/ext/jcommon-1.0.23.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/jdom-2.0.5-contrib.jar</runtime-relative-path>
<binary-origin>release\modules\ext\jdom-2.0.5-contrib.jar</binary-origin>
@ -552,24 +548,16 @@
<binary-origin>release\modules\ext\checker-compat-qual-2.5.3.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/sleuthkit-4.10.0.jar</runtime-relative-path>
<binary-origin>release\modules\ext\sleuthkit-4.10.0.jar</binary-origin>
<runtime-relative-path>ext/sleuthkit-4.10.1.jar</runtime-relative-path>
<binary-origin>release/modules/ext/sleuthkit-4.10.1.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/animal-sniffer-annotations-1.17.jar</runtime-relative-path>
<binary-origin>release\modules\ext\animal-sniffer-annotations-1.17.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/sleuthkit-caseuco-4.10.0.jar</runtime-relative-path>
<binary-origin>release\modules\ext\sleuthkit-caseuco-4.10.0.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/sleuthkit-4.10.0.jar</runtime-relative-path>
<binary-origin>release/modules/ext/sleuthkit-4.10.0.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/sleuthkit-caseuco-4.10.0.jar</runtime-relative-path>
<binary-origin>release/modules/ext/sleuthkit-caseuco-4.10.0.jar</binary-origin>
<runtime-relative-path>ext/sleuthkit-caseuco-4.10.1.jar</runtime-relative-path>
<binary-origin>release/modules/ext/sleuthkit-caseuco-4.10.1.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/gax-1.44.0.jar</runtime-relative-path>
@ -611,6 +599,10 @@
<runtime-relative-path>ext/decodetect-core-0.3.jar</runtime-relative-path>
<binary-origin>release\modules\ext\decodetect-core-0.3.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/mockito-core-3.5.7.jar</runtime-relative-path>
<binary-origin>release\modules\ext\mockito-core-3.5.7.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/httpclient-4.5.5.jar</runtime-relative-path>
<binary-origin>release\modules\ext\httpclient-4.5.5.jar</binary-origin>
@ -623,6 +615,10 @@
<runtime-relative-path>ext/jackson-annotations-2.9.0.jar</runtime-relative-path>
<binary-origin>release\modules\ext\jackson-annotations-2.9.0.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/objenesis-3.1.jar</runtime-relative-path>
<binary-origin>release\modules\ext\objenesis-3.1.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/jackson-core-2.9.7.jar</runtime-relative-path>
<binary-origin>release\modules\ext\jackson-core-2.9.7.jar</binary-origin>
@ -715,6 +711,10 @@
<runtime-relative-path>ext/netty-3.7.0.Final.jar</runtime-relative-path>
<binary-origin>release\modules\ext\netty-3.7.0.Final.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/jfreechart-1.0.19.jar</runtime-relative-path>
<binary-origin>release/modules/ext/jfreechart-1.0.19.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/opencensus-contrib-grpc-metrics-0.19.2.jar</runtime-relative-path>
<binary-origin>release\modules\ext\opencensus-contrib-grpc-metrics-0.19.2.jar</binary-origin>
@ -743,6 +743,10 @@
<runtime-relative-path>ext/javax.ws.rs-api-2.0.jar</runtime-relative-path>
<binary-origin>release\modules\ext\javax.ws.rs-api-2.0.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/jcommon-1.0.23.jar</runtime-relative-path>
<binary-origin>release/modules/ext/jcommon-1.0.23.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/icepdf-core-6.2.2.jar</runtime-relative-path>
<binary-origin>release\modules\ext\icepdf-core-6.2.2.jar</binary-origin>
@ -795,6 +799,10 @@
<runtime-relative-path>ext/jutf7-1.0.0.jar</runtime-relative-path>
<binary-origin>release\modules\ext\jutf7-1.0.0.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/byte-buddy-agent-1.10.13.jar</runtime-relative-path>
<binary-origin>release\modules\ext\byte-buddy-agent-1.10.13.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/batik-awt-util-1.6.jar</runtime-relative-path>
<binary-origin>release\modules\ext\batik-awt-util-1.6.jar</binary-origin>

View File

@ -14,6 +14,7 @@ AddContentTagAction.taggingErr=Tagging Error
AddContentTagAction.unableToTag.msg=Unable to tag {0}, not a regular file.
# {0} - fileName
AddContentTagAction.unableToTag.msg2=Unable to tag {0}.
CTL_DumpThreadAction=Thread Dump
CTL_ShowIngestProgressSnapshotAction=Ingest Status Details
DeleteBlackboardArtifactTagAction.deleteTag=Remove Selected Tag(s)
DeleteBlackboardArtifactTagAction.tagDelErr=Tag Deletion Error

View File

@ -44,7 +44,7 @@ import org.sleuthkit.autopsy.coreutils.Logger;
* This action should only be invoked in the event dispatch thread (EDT).
*/
@ActionRegistration(displayName = "#CTL_OpenLogFolder", iconInMenu = true)
@ActionReference(path = "Menu/Help", position = 1750)
@ActionReference(path = "Menu/Help", position = 2000)
@ActionID(id = "org.sleuthkit.autopsy.actions.OpenLogFolderAction", category = "Help")
public final class OpenLogFolderAction implements ActionListener {

View File

@ -0,0 +1,156 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 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.actions;
import java.awt.Desktop;
import java.awt.event.ActionListener;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.stream.Collectors;
import javax.swing.SwingWorker;
import org.openide.awt.ActionID;
import org.openide.awt.ActionReference;
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;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
/**
* Action class for the Thread Dump help menu item. If there is no case open the
* dump file will be created in PlatformUtil.getLogDirectory() otherwise the
* file will be created in Case.getCurrentCase().getLogDirectoryPath()
*/
@ActionID(category = "Help", id = "org.sleuthkit.autopsy.actions.ThreadDumpAction")
@ActionRegistration(displayName = "#CTL_DumpThreadAction", lazy = false)
@ActionReference(path = "Menu/Help", position = 1750)
@Messages({
"CTL_DumpThreadAction=Thread Dump"
})
public final class ThreadDumpAction extends CallableSystemAction implements ActionListener {
private static final long serialVersionUID = 1L;
private static final Logger logger = Logger.getLogger(ThreadDumpAction.class.getName());
private static final DateFormat DATE_FORMAT = new SimpleDateFormat("MM-dd-yyyy-HH-mm-ss-SSSS");
@Override
public void performAction() {
(new ThreadDumper()).run();
}
@Override
public String getName() {
return Bundle.CTL_DumpThreadAction();
}
@Override
public HelpCtx getHelpCtx() {
return HelpCtx.DEFAULT_HELP;
}
/**
* SwingWorker to that will create the thread dump file. Once the file is
* created it will be opened in an external viewer.
*/
private final class ThreadDumper extends SwingWorker<File, Void> {
@Override
protected File doInBackground() throws Exception {
return createThreadDump();
}
@Override
protected void done() {
File dumpFile = null;
try {
dumpFile = get();
Desktop.getDesktop().open(dumpFile);
} catch (ExecutionException | InterruptedException ex) {
logger.log(Level.SEVERE, "Failure occurred while creating thread dump file", ex);
} catch (IOException ex) {
if (dumpFile != null) {
logger.log(Level.WARNING, "Failed to open thread dump file in external viewer: " + dumpFile.getAbsolutePath(), ex);
} else {
logger.log(Level.SEVERE, "Failed to create thread dump file.", ex);
}
}
}
/**
* Create the thread dump file.
*
* @throws IOException
*/
private File createThreadDump() throws IOException {
File dumpFile = createFilePath().toFile();
try (BufferedWriter writer = new BufferedWriter(new FileWriter(dumpFile, true))) {
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
ThreadInfo[] threadInfos = threadMXBean.getThreadInfo(threadMXBean.getAllThreadIds(), 100);
for (ThreadInfo threadInfo : threadInfos) {
writer.write(threadInfo.toString());
writer.write("\n");
}
long[] deadlockThreadIds = threadMXBean.findDeadlockedThreads();
if (deadlockThreadIds != null) {
writer.write("-------------------List of Deadlocked Thread IDs ---------------------");
String idsList = (Arrays
.stream(deadlockThreadIds)
.boxed()
.collect(Collectors.toList()))
.stream().map(n -> String.valueOf(n))
.collect(Collectors.joining("-", "{", "}"));
writer.write(idsList);
}
}
return dumpFile;
}
/**
* Create the dump file path.
*
* @return Path for dump file.
*/
private Path createFilePath() {
String fileName = "ThreadDump_" + DATE_FORMAT.format(new Date()) + ".txt";
if (Case.isCaseOpen()) {
return Paths.get(Case.getCurrentCase().getLogDirectoryPath(), fileName);
}
return Paths.get(PlatformUtil.getLogDirectory(), fileName);
}
}
}

View File

@ -107,7 +107,7 @@ import org.sleuthkit.autopsy.coreutils.Version;
import org.sleuthkit.autopsy.events.AutopsyEvent;
import org.sleuthkit.autopsy.events.AutopsyEventException;
import org.sleuthkit.autopsy.events.AutopsyEventPublisher;
import org.sleuthkit.autopsy.discovery.OpenDiscoveryAction;
import org.sleuthkit.autopsy.discovery.ui.OpenDiscoveryAction;
import org.sleuthkit.autopsy.ingest.IngestJob;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.IngestServices;
@ -1468,9 +1468,16 @@ public class Case {
*/
public boolean hasData() {
boolean hasDataSources = false;
try {
hasDataSources = (getDataSources().size() > 0);
} catch (TskCoreException ex) {
String query = "SELECT count(*) AS count FROM tsk_objects WHERE par_obj_id IS NULL";
try (SleuthkitCase.CaseDbQuery dbQuery = caseDb.executeQuery(query)) {
ResultSet resultSet = dbQuery.getResultSet();
if (resultSet.next()) {
long numDataSources = resultSet.getLong("count");
if (numDataSources > 0) {
hasDataSources = true;
}
}
} catch (TskCoreException | SQLException ex) {
logger.log(Level.SEVERE, "Error accessing case database", ex); //NON-NLS
}
return hasDataSources;

View File

@ -1,6 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<Properties>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[32767, 32767]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
@ -13,104 +18,154 @@
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace min="-2" pref="15" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="jLabel2" min="-2" max="-2" attributes="0"/>
<Component id="jScrollPane1" pref="485" max="32767" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="8" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="jScrollPane2" min="-2" pref="254" max="-2" attributes="0"/>
<Component id="jLabel1" min="-2" max="-2" attributes="0"/>
</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"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="jLabel1" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="jLabel2" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="jScrollPane1" pref="162" max="32767" attributes="0"/>
<Component id="jScrollPane2" pref="0" max="32767" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="10" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
<SubComponents>
<Container class="javax.swing.JScrollPane" name="jScrollPane1">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="null"/>
</Property>
</Properties>
<Container class="javax.swing.JScrollPane" name="mainScrollPane">
<AuxValues>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
<BorderConstraints direction="Center"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JTable" name="ingestJobTable">
<Container class="javax.swing.JPanel" name="contentPanel">
<Properties>
<Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="ingestJobTableModel" type="code"/>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[32767, 32767]"/>
</Property>
<Property name="columnModel" type="javax.swing.table.TableColumnModel" editor="org.netbeans.modules.form.editors2.TableColumnModelEditor">
<TableColumnModel selectionModel="1"/>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[625, 150]"/>
</Property>
<Property name="tableHeader" type="javax.swing.table.JTableHeader" editor="org.netbeans.modules.form.editors2.JTableHeaderEditor">
<TableHeader reorderingAllowed="false" resizingAllowed="true"/>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[625, 150]"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Container>
<Component class="javax.swing.JLabel" name="jLabel1">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="IngestJobInfoPanel.jLabel1.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="jLabel2">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="IngestJobInfoPanel.jLabel2.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Container class="javax.swing.JScrollPane" name="jScrollPane2">
<AuxValues>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JTable" name="ingestModuleTable">
<Properties>
<Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="ingestModuleTableModel" type="code"/>
</Property>
<Property name="columnModel" type="javax.swing.table.TableColumnModel" editor="org.netbeans.modules.form.editors2.TableColumnModelEditor">
<TableColumnModel selectionModel="0"/>
</Property>
<Property name="tableHeader" type="javax.swing.table.JTableHeader" editor="org.netbeans.modules.form.editors2.JTableHeaderEditor">
<TableHeader reorderingAllowed="true" resizingAllowed="true"/>
</Property>
</Properties>
</Component>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
<SubComponents>
<Container class="javax.swing.JScrollPane" name="ingestJobsScrollPane">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="null"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[16, 16]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="null"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="1" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="2" insetsLeft="10" insetsBottom="10" insetsRight="0" anchor="18" weightX="1.0" weightY="1.0"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JTable" name="ingestJobTable">
<Properties>
<Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="ingestJobTableModel" type="code"/>
</Property>
<Property name="columnModel" type="javax.swing.table.TableColumnModel" editor="org.netbeans.modules.form.editors2.TableColumnModelEditor">
<TableColumnModel selectionModel="1"/>
</Property>
<Property name="tableHeader" type="javax.swing.table.JTableHeader" editor="org.netbeans.modules.form.editors2.JTableHeaderEditor">
<TableHeader reorderingAllowed="false" resizingAllowed="true"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Container>
<Component class="javax.swing.JLabel" name="jLabel1">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="IngestJobInfoPanel.jLabel1.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="1" gridY="0" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="10" insetsLeft="10" insetsBottom="0" insetsRight="0" anchor="18" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JLabel" name="jLabel2">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="IngestJobInfoPanel.jLabel2.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="10" insetsLeft="10" insetsBottom="0" insetsRight="0" anchor="18" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Container class="javax.swing.JScrollPane" name="ingestModulesScrollPane">
<Properties>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[254, 32767]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[254, 16]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[254, 16]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="1" gridY="1" gridWidth="1" gridHeight="1" fill="3" ipadX="0" ipadY="0" insetsTop="2" insetsLeft="8" insetsBottom="10" insetsRight="10" anchor="18" weightX="0.0" weightY="1.0"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JTable" name="ingestModuleTable">
<Properties>
<Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="ingestModuleTableModel" type="code"/>
</Property>
<Property name="columnModel" type="javax.swing.table.TableColumnModel" editor="org.netbeans.modules.form.editors2.TableColumnModelEditor">
<TableColumnModel selectionModel="0"/>
</Property>
<Property name="tableHeader" type="javax.swing.table.JTableHeader" editor="org.netbeans.modules.form.editors2.JTableHeaderEditor">
<TableHeader reorderingAllowed="true" resizingAllowed="true"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Container>
</SubComponents>
</Container>
</SubComponents>

View File

@ -241,65 +241,84 @@ public final class IngestJobInfoPanel extends javax.swing.JPanel {
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
java.awt.GridBagConstraints gridBagConstraints;
jScrollPane1 = new javax.swing.JScrollPane();
javax.swing.JScrollPane mainScrollPane = new javax.swing.JScrollPane();
javax.swing.JPanel contentPanel = new javax.swing.JPanel();
javax.swing.JScrollPane ingestJobsScrollPane = new javax.swing.JScrollPane();
ingestJobTable = new javax.swing.JTable();
jLabel1 = new javax.swing.JLabel();
jLabel2 = new javax.swing.JLabel();
jScrollPane2 = new javax.swing.JScrollPane();
javax.swing.JLabel jLabel1 = new javax.swing.JLabel();
javax.swing.JLabel jLabel2 = new javax.swing.JLabel();
javax.swing.JScrollPane ingestModulesScrollPane = new javax.swing.JScrollPane();
ingestModuleTable = new javax.swing.JTable();
jScrollPane1.setBorder(null);
setMaximumSize(new java.awt.Dimension(32767, 32767));
setLayout(new java.awt.BorderLayout());
contentPanel.setMaximumSize(new java.awt.Dimension(32767, 32767));
contentPanel.setMinimumSize(new java.awt.Dimension(625, 150));
contentPanel.setPreferredSize(new java.awt.Dimension(625, 150));
contentPanel.setLayout(new java.awt.GridBagLayout());
ingestJobsScrollPane.setBorder(null);
ingestJobsScrollPane.setMinimumSize(new java.awt.Dimension(16, 16));
ingestJobsScrollPane.setPreferredSize(null);
ingestJobTable.setModel(ingestJobTableModel);
ingestJobTable.getTableHeader().setReorderingAllowed(false);
jScrollPane1.setViewportView(ingestJobTable);
ingestJobsScrollPane.setViewportView(ingestJobTable);
ingestJobTable.getColumnModel().getSelectionModel().setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 1;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.weighty = 1.0;
gridBagConstraints.insets = new java.awt.Insets(2, 10, 10, 0);
contentPanel.add(ingestJobsScrollPane, gridBagConstraints);
org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(IngestJobInfoPanel.class, "IngestJobInfoPanel.jLabel1.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 0;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new java.awt.Insets(10, 10, 0, 0);
contentPanel.add(jLabel1, gridBagConstraints);
org.openide.awt.Mnemonics.setLocalizedText(jLabel2, org.openide.util.NbBundle.getMessage(IngestJobInfoPanel.class, "IngestJobInfoPanel.jLabel2.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new java.awt.Insets(10, 10, 0, 0);
contentPanel.add(jLabel2, gridBagConstraints);
ingestModulesScrollPane.setMaximumSize(new java.awt.Dimension(254, 32767));
ingestModulesScrollPane.setMinimumSize(new java.awt.Dimension(254, 16));
ingestModulesScrollPane.setPreferredSize(new java.awt.Dimension(254, 16));
ingestModuleTable.setModel(ingestModuleTableModel);
jScrollPane2.setViewportView(ingestModuleTable);
ingestModulesScrollPane.setViewportView(ingestModuleTable);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(15, 15, 15)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jLabel2)
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 485, Short.MAX_VALUE))
.addGap(8, 8, 8)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jScrollPane2, javax.swing.GroupLayout.PREFERRED_SIZE, 254, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(jLabel1))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(8, 8, 8)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jLabel1)
.addComponent(jLabel2))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 162, Short.MAX_VALUE)
.addComponent(jScrollPane2, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE))
.addGap(10, 10, 10))
);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 1;
gridBagConstraints.fill = java.awt.GridBagConstraints.VERTICAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weighty = 1.0;
gridBagConstraints.insets = new java.awt.Insets(2, 8, 10, 10);
contentPanel.add(ingestModulesScrollPane, gridBagConstraints);
mainScrollPane.setViewportView(contentPanel);
add(mainScrollPane, java.awt.BorderLayout.CENTER);
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JTable ingestJobTable;
private javax.swing.JTable ingestModuleTable;
private javax.swing.JLabel jLabel1;
private javax.swing.JLabel jLabel2;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JScrollPane jScrollPane2;
// End of variables declaration//GEN-END:variables
}

View File

@ -193,7 +193,7 @@ public class LocalFilesDSProcessor implements DataSourceProcessor, AutoIngestDat
List<String> command = new ArrayList<>();
for (final String l01Path : logicalEvidenceFilePaths) {
command.clear();
command.add(ewfexportPath.toAbsolutePath().toString());
command.add(String.format("\"%s\"", ewfexportPath.toAbsolutePath().toString()));
command.add("-f");
command.add("files");
command.add("-t");
@ -203,8 +203,8 @@ public class LocalFilesDSProcessor implements DataSourceProcessor, AutoIngestDat
}
Path dirPath = Paths.get(FilenameUtils.getBaseName(l01Path) + UNIQUENESS_CONSTRAINT_SEPERATOR + System.currentTimeMillis());
command.add(dirPath.toString());
command.add(l01Path);
command.add(String.format("\"%s\"", dirPath.toString()));
command.add(String.format("\"%s\"", l01Path));
ProcessBuilder processBuilder = new ProcessBuilder(command);
processBuilder.directory(l01Dir);
try {

View File

@ -173,10 +173,10 @@ class UnpackagePortableCaseProgressDialog extends javax.swing.JDialog implements
throw new TskCoreException("Error finding 7-Zip executable"); // NON-NLS
}
String outputFolderSwitch = "-o" + outputFolder; // NON-NLS
String outputFolderSwitch = String.format("\"-o%s\"",outputFolder); // NON-NLS
ProcessBuilder procBuilder = new ProcessBuilder();
procBuilder.command(
sevenZipExe.getAbsolutePath(),
String.format("\"%s\"",sevenZipExe.getAbsolutePath()),
"x", // Extract
packagedCase,
outputFolderSwitch

View File

@ -19,6 +19,7 @@
package org.sleuthkit.autopsy.centralrepository.datamodel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@ -46,6 +47,7 @@ import org.sleuthkit.datamodel.TskData;
public class CorrelationAttributeUtil {
private static final Logger logger = Logger.getLogger(CorrelationAttributeUtil.class.getName());
private static final List<String> domainsToSkip = Arrays.asList("localhost", "127.0.0.1");
/**
* Gets a string that is expected to be the same string that is stored in
@ -148,8 +150,11 @@ public class CorrelationAttributeUtil {
|| artifactTypeID == ARTIFACT_TYPE.TSK_WEB_COOKIE.getTypeID()
|| artifactTypeID == ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID()
|| artifactTypeID == ARTIFACT_TYPE.TSK_WEB_HISTORY.getTypeID()) {
makeCorrAttrFromArtifactAttr(correlationAttrs, sourceArtifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN, CorrelationAttributeInstance.DOMAIN_TYPE_ID);
BlackboardAttribute domainAttr = sourceArtifact.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DOMAIN));
if ((domainAttr != null)
&& ! domainsToSkip.contains(domainAttr.getValueString())) {
makeCorrAttrFromArtifactAttr(correlationAttrs, sourceArtifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN, CorrelationAttributeInstance.DOMAIN_TYPE_ID);
}
} else if (artifactTypeID == ARTIFACT_TYPE.TSK_DEVICE_ATTACHED.getTypeID()) {
makeCorrAttrFromArtifactAttr(correlationAttrs, sourceArtifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DEVICE_ID, CorrelationAttributeInstance.USBID_TYPE_ID);
makeCorrAttrFromArtifactAttr(correlationAttrs, sourceArtifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MAC_ADDRESS, CorrelationAttributeInstance.MAC_TYPE_ID);

View File

@ -130,14 +130,6 @@ public final class PersonasTopComponent extends TopComponent {
}
});
searchNameRadio.addActionListener((ActionEvent e) -> {
searchField.setText("");
});
searchAccountRadio.addActionListener((ActionEvent e) -> {
searchField.setText("");
});
createAccountBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {

View File

@ -49,8 +49,7 @@ public class CommandLineOptionProcessor extends OptionProcessor {
private final Option dataSourceObjectIdOption = Option.requiredArgument('i', "dataSourceObjectId");
private final Option addDataSourceCommandOption = Option.withoutArgument('a', "addDataSource");
private final Option caseDirOption = Option.requiredArgument('d', "caseDir");
private final Option runIngestCommandOption = Option.withoutArgument('r', "runIngest");
private final Option ingestProfileOption = Option.requiredArgument('p', "ingestProfile");
private final Option runIngestCommandOption = Option.optionalArgument('r', "runIngest");
private final Option listAllDataSourcesCommandOption = Option.withoutArgument('l', "listAllDataSources");
private final Option generateReportsOption = Option.optionalArgument('g', "generateReports");
private final Option defaultArgument = Option.defaultArguments();
@ -76,7 +75,6 @@ public class CommandLineOptionProcessor extends OptionProcessor {
set.add(dataSourceObjectIdOption);
set.add(caseDirOption);
set.add(runIngestCommandOption);
set.add(ingestProfileOption);
set.add(listAllDataSourcesCommandOption);
set.add(generateReportsOption);
set.add(defaultArgument);
@ -205,21 +203,6 @@ public class CommandLineOptionProcessor extends OptionProcessor {
}
}
String ingestProfile = "";
if (values.containsKey(ingestProfileOption)) {
argDirs = values.get(ingestProfileOption);
if (argDirs.length < 1) {
handleError("Argument missing from 'ingestProfile' option");
}
ingestProfile = argDirs[0];
// verify inputs
if (ingestProfile == null || ingestProfile.isEmpty()) {
handleError("Missing argument 'ingestProfile'");
}
}
// Create commands in order in which they should be executed:
// First create the "CREATE_CASE" command, if present
if (values.containsKey(createCaseCommandOption)) {
@ -263,9 +246,15 @@ public class CommandLineOptionProcessor extends OptionProcessor {
runFromCommandLine = true;
}
String ingestProfile = "";
// Add RUN_INGEST command, if present
if (values.containsKey(runIngestCommandOption)) {
argDirs = values.get(runIngestCommandOption);
if(argDirs != null && argDirs.length > 0) {
ingestProfile = argDirs[0];
}
// 'caseDir' must only be specified if the case is not being created during the current run
if (!values.containsKey(createCaseCommandOption) && caseDir.isEmpty()) {
// new case is not being created during this run, so 'caseDir' should have been specified

View File

@ -134,7 +134,7 @@ class MediaFileViewer extends javax.swing.JPanel implements FileTypeViewer {
mediaPlayerPanel.loadFile(file);
this.showVideoPanel();
} else if (imagePanelInited && imagePanel.isSupported(file)) {
imagePanel.showImageFx(file);
imagePanel.loadFile(file);
this.showImagePanel();
}
} catch (Exception e) {

View File

@ -65,6 +65,7 @@ MessageAccountPanel.account.justification=Account found in Message artifact
MessageAccountPanel_button_create_label=Create
MessageAccountPanel_button_view_label=View
MessageAccountPanel_contact_label=Contact:
MessageAccountPanel_copy_label=Copy
MessageAccountPanel_no_matches=No matches found.
MessageAccountPanel_persona_label=Persona:
MessageAccountPanel_unknown_label=Unknown

View File

@ -34,6 +34,7 @@ import javax.swing.JMenuItem;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JTextPane;
import javax.swing.SwingUtilities;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
@ -60,10 +61,10 @@ final class CommunicationArtifactViewerHelper {
/**
* Adds a new heading to the panel.
*
* @param panel Panel to update.
* @param panel Panel to update.
* @param gridbagLayout Layout to use.
* @param constraints Constrains to use.
* @param headerString Heading string to display.
* @param constraints Constrains to use.
* @param headerString Heading string to display.
*
* @return JLabel Heading label added.
*/
@ -113,10 +114,10 @@ final class CommunicationArtifactViewerHelper {
*
* Caller must know what it's doing and set up all the constraints properly.
*
* @param panel Panel to update.
* @param panel Panel to update.
* @param gridbagLayout Layout to use.
* @param constraints Constrains to use.
* @param component Component to add.
* @param constraints Constrains to use.
* @param component Component to add.
*/
static void addComponent(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints, JComponent component) {
@ -129,9 +130,9 @@ final class CommunicationArtifactViewerHelper {
* Adds a filler/glue at the end of the line to keep the other columns
* aligned, in case the panel is resized.
*
* @param panel Panel to update.
* @param panel Panel to update.
* @param gridbagLayout Layout to use.
* @param constraints Constrains to use.
* @param constraints Constrains to use.
*/
static void addLineEndGlue(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints) {
// Place the filler just past the last column.
@ -156,9 +157,9 @@ final class CommunicationArtifactViewerHelper {
* Adds a filler/glue at the bottom of the panel to keep the data rows
* aligned, in case the panel is resized.
*
* @param panel Panel to update.
* @param panel Panel to update.
* @param gridbagLayout Layout to use.
* @param constraints Constrains to use.
* @param constraints Constrains to use.
*/
static void addPageEndGlue(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints) {
@ -182,9 +183,9 @@ final class CommunicationArtifactViewerHelper {
/**
* Adds a blank line to the panel.
*
* @param panel Panel to update.
* @param panel Panel to update.
* @param gridbagLayout Layout to use.
* @param constraints Constrains to use.
* @param constraints Constrains to use.
*/
static void addBlankLine(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints) {
constraints.gridy++;
@ -200,10 +201,10 @@ final class CommunicationArtifactViewerHelper {
/**
* Adds a label/key to the panel at col 0.
*
* @param panel Panel to update.
* @param panel Panel to update.
* @param gridbagLayout Layout to use.
* @param constraints Constrains to use.
* @param keyString Key name to display.
* @param constraints Constrains to use.
* @param keyString Key name to display.
*
* @return Label added.
*/
@ -214,11 +215,11 @@ final class CommunicationArtifactViewerHelper {
/**
* Adds a label/key to the panel at specified column.
*
* @param panel Panel to update.
* @param panel Panel to update.
* @param gridbagLayout Layout to use.
* @param constraints Constrains to use.
* @param keyString Key name to display.
* @param gridx column index, must be less than MAX_COLS - 1.
* @param constraints Constrains to use.
* @param keyString Key name to display.
* @param gridx column index, must be less than MAX_COLS - 1.
*
* @return Label added.
*/
@ -243,38 +244,41 @@ final class CommunicationArtifactViewerHelper {
/**
* Adds a value string to the panel at col 1.
*
* @param panel Panel to update.
* @param panel Panel to update.
* @param gridbagLayout Layout to use.
* @param constraints Constrains to use.
* @param keyString Value string to display.
* @param constraints Constrains to use.
* @param keyString Value string to display.
*
* @return Label added.
*/
static JLabel addValue(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints, String valueString) {
static JTextPane addValue(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints, String valueString) {
return addValueAtCol(panel, gridbagLayout, constraints, valueString, 1);
}
/**
* Adds a value string to the panel at specified column.
*
* @param panel Panel to update.
* @param panel Panel to update.
* @param gridbagLayout Layout to use.
* @param constraints Constrains to use.
* @param keyString Value string to display.
* @param gridx Column index, must be less than MAX_COLS;
* @param constraints Constrains to use.
* @param keyString Value string to display.
* @param gridx Column index, must be less than MAX_COLS;
*
* @return Label added.
*/
static JLabel addValueAtCol(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints, String valueString, int gridx) {
static JTextPane addValueAtCol(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints, String valueString, int gridx) {
// create label,
javax.swing.JLabel valueField = new javax.swing.JLabel();
JTextPane valueField = new JTextPane();
valueField.setEditable(false);
valueField.setOpaque(false);
constraints.gridx = gridx < MAX_COLS ? gridx : MAX_COLS - 1;
int savedGridwidth = constraints.gridwidth;
GridBagConstraints cloneConstraints = (GridBagConstraints) constraints.clone();
// let the value span 2 cols
constraints.gridwidth = 2;
cloneConstraints.gridwidth = 2;
cloneConstraints.fill = GridBagConstraints.BOTH;
// set text
valueField.setText(valueString);
@ -288,12 +292,9 @@ final class CommunicationArtifactViewerHelper {
});
// add label to panel
gridbagLayout.setConstraints(valueField, constraints);
gridbagLayout.setConstraints(valueField, cloneConstraints);
panel.add(valueField);
// restore constraints
constraints.gridwidth = savedGridwidth;
// end the line
addLineEndGlue(panel, gridbagLayout, constraints);
@ -304,9 +305,9 @@ final class CommunicationArtifactViewerHelper {
* Displays a message string, starting at column 0, and spanning the entire
* row.
*
* @param panel Panel to show.
* @param panel Panel to show.
* @param gridbagLayout Layout to use.
* @param constraints Constraints to use.
* @param constraints Constraints to use.
*
* @param messageString Message to display.
*
@ -320,9 +321,9 @@ final class CommunicationArtifactViewerHelper {
* Displays a message string, starting at specified column, and spanning the
* entire row.
*
* @param panel Panel to show.
* @param panel Panel to show.
* @param gridbagLayout Layout to use.
* @param constraints Constraints to use.
* @param constraints Constraints to use.
*
* @param messageString Message to display.
*
@ -364,12 +365,12 @@ final class CommunicationArtifactViewerHelper {
*
* If CentralRepostory is disabled, just displays 'Unknown' persona name.
*
* @param panel Panel to update.
* @param gridbagLayout Layout to use.
* @param constraints Constrains to use.
* @param panel Panel to update.
* @param gridbagLayout Layout to use.
* @param constraints Constrains to use.
* @param accountIdentifier Account identifier to search the persona.
*
* @return List of AccountPersonaSearcherData objects.
* @return List of AccountPersonaSearcherData objects.
*/
@NbBundle.Messages({
"CommunicationArtifactViewerHelper_persona_label=Persona: ",
@ -444,7 +445,7 @@ final class CommunicationArtifactViewerHelper {
"CommunicationArtifactViewerHelper_contact_label=Contact: {0}",
"CommunicationArtifactViewerHelper_contact_label_unknown=Unknown"
})
static JLabel addContactRow(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints, String contactId) {
static JComponent addContactRow(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints, String contactId) {
// Increase the y value because we are not calling the addKey
constraints.gridy++;
//Don't change the origian constraints, just make a copy to modify
@ -462,13 +463,13 @@ final class CommunicationArtifactViewerHelper {
* Event handler for mouse click event. Attaches a 'Copy' menu item to right
* click.
*
* @param evt Event to check.
* @param evt Event to check.
* @param valueLabel Label to attach the menu item to.
*/
@NbBundle.Messages({
"CommunicationArtifactViewerHelper_menuitem_copy=Copy"
})
private static void valueLabelMouseClicked(java.awt.event.MouseEvent evt, JLabel valueLabel) {
private static void valueLabelMouseClicked(java.awt.event.MouseEvent evt, JTextPane valueLabel) {
if (SwingUtilities.isRightMouseButton(evt)) {
JPopupMenu popup = new JPopupMenu();
@ -477,7 +478,6 @@ final class CommunicationArtifactViewerHelper {
@Override
public void actionPerformed(ActionEvent e) {
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(valueLabel.getText()), null);
}
});

View File

@ -18,8 +18,13 @@
*/
package org.sleuthkit.autopsy.contentviewers.artifactviewers;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.datatransfer.StringSelection;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@ -32,7 +37,10 @@ import javax.swing.GroupLayout.ParallelGroup;
import javax.swing.GroupLayout.SequentialGroup;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JTextPane;
import javax.swing.LayoutStyle.ComponentPlacement;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
@ -120,7 +128,7 @@ final class MessageAccountPanel extends JPanel {
return new ArrayList<>();
}
if ( !((DataSource)(artifact.getDataSource())).getDeviceId().equals(account.getTypeSpecificID())) {
if (!((DataSource) (artifact.getDataSource())).getDeviceId().equals(account.getTypeSpecificID())) {
List<BlackboardArtifact> contactList = ContactCache.getContacts(account);
BlackboardArtifact contact = null;
@ -276,13 +284,20 @@ final class MessageAccountPanel extends JPanel {
private Persona persona = null;
private final String contactName;
private JLabel accountLabel;
private JTextPane accountLabel;
private JLabel personaHeader;
private JLabel personaDisplayName;
private JTextPane personaDisplayName;
private JLabel contactHeader;
private JLabel contactDisplayName;
private JTextPane contactDisplayName;
private JButton button;
private JMenuItem contactCopyMenuItem;
private JMenuItem personaCopyMenuItem;
private JMenuItem accountCopyMenuItem;
JPopupMenu contactPopupMenu = new JPopupMenu();
JPopupMenu personaPopupMenu = new JPopupMenu();
JPopupMenu accountPopupMenu = new JPopupMenu();
/**
* Construct a new AccountContainer
*
@ -304,24 +319,103 @@ final class MessageAccountPanel extends JPanel {
"MessageAccountPanel_unknown_label=Unknown",
"MessageAccountPanel_button_view_label=View",
"MessageAccountPanel_button_create_label=Create",
"MessageAccountPanel_contact_label=Contact:"
"MessageAccountPanel_contact_label=Contact:",
"MessageAccountPanel_copy_label=Copy"
})
/**
* Swing components will not be initialized until this method is called.
*/
private void initalizeSwingControls() {
accountLabel = new JLabel();
accountLabel = new JTextPane();
accountLabel.setEditable(false);
accountLabel.setOpaque(false);
personaHeader = new JLabel(Bundle.MessageAccountPanel_persona_label());
contactHeader = new JLabel(Bundle.MessageAccountPanel_contact_label());
personaDisplayName = new JLabel();
contactDisplayName = new JLabel();
personaDisplayName = new JTextPane();
personaDisplayName.setOpaque(false);
personaDisplayName.setEditable(false);
personaDisplayName.setPreferredSize(new Dimension(100, 26));
personaDisplayName.setMaximumSize(new Dimension(100, 26));
contactDisplayName = new JTextPane();
contactDisplayName.setOpaque(false);
contactDisplayName.setEditable(false);
contactDisplayName.setPreferredSize(new Dimension(100, 26));
button = new JButton();
button.addActionListener(new PersonaButtonListener(this));
accountLabel.setText(account.getTypeSpecificID());
contactDisplayName.setText(contactName);
personaDisplayName.setText(persona != null ? persona.getName() : Bundle.MessageAccountPanel_unknown_label());
//This is a bit of a hack to size the JTextPane correctly, but it gets the job done.
personaDisplayName.setMaximumSize((new JLabel(personaDisplayName.getText()).getMaximumSize()));
contactDisplayName.setMaximumSize((new JLabel(contactDisplayName.getText()).getMaximumSize()));
accountLabel.setMaximumSize((new JLabel(accountLabel.getText()).getMaximumSize()));
button.setText(persona != null ? Bundle.MessageAccountPanel_button_view_label() : Bundle.MessageAccountPanel_button_create_label());
initalizePopupMenus();
}
/**
* Initialize the copy popup menus for the persona and the contact label.
*/
private void initalizePopupMenus() {
contactCopyMenuItem = new JMenuItem(Bundle.MessageAccountPanel_copy_label());
personaCopyMenuItem = new JMenuItem(Bundle.MessageAccountPanel_copy_label());
accountCopyMenuItem = new JMenuItem(Bundle.MessageAccountPanel_copy_label());
personaPopupMenu.add(personaCopyMenuItem);
contactPopupMenu.add(contactCopyMenuItem);
accountPopupMenu.add(accountCopyMenuItem);
personaDisplayName.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent evt) {
if (SwingUtilities.isRightMouseButton(evt)) {
personaPopupMenu.show(personaDisplayName, evt.getX(), evt.getY());
}
}
});
personaCopyMenuItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(personaDisplayName.getText()), null);
}
});
contactDisplayName.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent evt) {
if (SwingUtilities.isRightMouseButton(evt)) {
contactPopupMenu.show(contactDisplayName, evt.getX(), evt.getY());
}
}
});
contactCopyMenuItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(contactDisplayName.getText()), null);
}
});
accountLabel.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent evt) {
if (SwingUtilities.isRightMouseButton(evt)) {
accountPopupMenu.show(accountLabel, evt.getX(), evt.getY());
}
}
});
accountCopyMenuItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(accountLabel.getText()), null);
}
});
}
private String getNameFromContactArtifact(BlackboardArtifact contactArtifact) throws TskCoreException {
@ -349,6 +443,9 @@ final class MessageAccountPanel extends JPanel {
public void run() {
personaDisplayName.setText(persona != null ? persona.getName() : Bundle.MessageAccountPanel_unknown_label());
button.setText(persona != null ? Bundle.MessageAccountPanel_button_view_label() : Bundle.MessageAccountPanel_button_create_label());
//This is a bit of a hack to size the JTextPane correctly, but it gets the job done.
personaDisplayName.setMaximumSize((new JLabel(personaDisplayName.getText()).getMaximumSize()));
revalidate();
repaint();
}
@ -378,7 +475,7 @@ final class MessageAccountPanel extends JPanel {
*
* @return JLabel object
*/
private JLabel getAccountLabel() {
private JTextPane getAccountLabel() {
return accountLabel;
}
@ -409,7 +506,7 @@ final class MessageAccountPanel extends JPanel {
return group;
}
private SequentialGroup getContactSequentialGroup(GroupLayout layout) {
private SequentialGroup getContactSequentialGroup(GroupLayout layout) {
SequentialGroup group = layout.createSequentialGroup();
group
@ -428,14 +525,21 @@ final class MessageAccountPanel extends JPanel {
* @return A group for the personal controls.
*/
private ParallelGroup getPersonLineVerticalGroup(GroupLayout layout) {
return layout.createParallelGroup(Alignment.BASELINE)
return layout.createParallelGroup(Alignment.CENTER)
.addComponent(personaHeader)
.addComponent(personaDisplayName)
.addComponent(button);
}
/**
* Generates the vertical layout code for the contact line.
*
* @param layout Instance of GroupLayout to update.
*
* @return A group for the personal controls.
*/
private ParallelGroup getContactLineVerticalGroup(GroupLayout layout) {
return layout.createParallelGroup(Alignment.BASELINE)
return layout.createParallelGroup(Alignment.CENTER)
.addComponent(contactHeader)
.addComponent(contactDisplayName);
}

View File

@ -33,7 +33,7 @@ import org.openide.util.NbBundle;
*/
@ActionID(id = "org.sleuthkit.autopsy.corecomponents.AboutWindowAction", category = "Help")
@ActionRegistration(displayName = "#CTL_CustomAboutAction", iconInMenu = true, lazy = false)
@ActionReference(path = "Menu/Help", position = 3000)
@ActionReference(path = "Menu/Help", position = 3000, separatorBefore = 2999)
public class AboutWindowAction extends AboutAction {
@Override

View File

@ -135,25 +135,6 @@ OptionsCategory_Name_View=View
OptionsCategory_Keywords_View=View
ViewPreferencesPanel.currentSessionSettingsPanel.border.title=Current Session Settings
ViewPreferencesPanel.hideRejectedResultsCheckbox.text=Hide rejected results
ViewPreferencesPanel.selectFileLabel.text=When selecting a file:
ViewPreferencesPanel.globalSettingsPanel.border.title=Global Settings
ViewPreferencesPanel.translateTextLabel.text=Translate text:
ViewPreferencesPanel.hideOtherUsersTagsLabel.text=Hide other users' tags in the:
ViewPreferencesPanel.hideOtherUsersTagsCheckbox.text=Tags area in the tree
ViewPreferencesPanel.useAnotherTimeRadioButton.text=Use another time zone
ViewPreferencesPanel.useLocalTimeRadioButton.text=Use local time zone
ViewPreferencesPanel.displayTimeLabel.text=When displaying times:
ViewPreferencesPanel.viewsHideSlackCheckbox.text=Views area
ViewPreferencesPanel.dataSourcesHideSlackCheckbox.text=Data Sources area (the directory hierarchy)
ViewPreferencesPanel.hideSlackFilesLabel.text=Hide slack files in the:
ViewPreferencesPanel.viewsHideKnownCheckbox.text=Views area
ViewPreferencesPanel.dataSourcesHideKnownCheckbox.text=Data Sources area (the directory hierarchy)
ViewPreferencesPanel.hideKnownFilesLabel.text=Hide known files (i.e. those in the NIST NSRL) in the:
ViewPreferencesPanel.keepCurrentViewerRadioButton.toolTipText=For example, stay in Hex view when a JPEG is selected.
ViewPreferencesPanel.keepCurrentViewerRadioButton.text=Stay on the same file viewer
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
@ -193,12 +174,7 @@ DataResultViewerTable.pagePrevButton.text=
DataResultViewerTable.pagesLabel.text=Pages:
DataResultViewerTable.pageNumLabel.text=
DataResultViewerTable.pageLabel.text=Page:
ViewPreferencesPanel.maxResultsLabel.text=Maximum number of Results to show in table:
ViewPreferencesPanel.maxResultsLabel.toolTipText=<html>\nSetting this value to 0 will display all results in the results table.\n<br>Note that setting this value to 0 may result in poor UI responsiveness when there are large numbers of results.\n</html>
DataResultViewerTable.exportCSVButton.text=Save Table as CSV
ViewPreferencesPanel.scoColumnsCheckbox.text=S(core), C(omments), and O(ccurences)
ViewPreferencesPanel.scoColumnsWrapAroundText.text=to reduce loading times
ViewPreferencesPanel.scoColumnsLabel.text=Do not add columns for:
AutopsyOptionsPanel.logoPanel.border.title=Logo
AutopsyOptionsPanel.agencyLogoPathFieldValidationLabel.text=
AutopsyOptionsPanel.specifyLogoRB.text=Specify a logo
@ -223,3 +199,27 @@ AutopsyOptionsPanel.totalMemoryLabel.text=Total System Memory:
AutopsyOptionsPanel.maxMemoryUnitsLabel.text=GB
AutopsyOptionsPanel.maxMemoryLabel.text=Maximum JVM Memory:
AutopsyOptionsPanel.runtimePanel.border.title=Runtime
ViewPreferencesPanel.maxResultsLabel.toolTipText=<html>\nSetting this value to 0 will display all results in the results table.\n<br>Note that setting this value to 0 may result in poor UI responsiveness when there are large numbers of results.\n</html>
ViewPreferencesPanel.maxResultsLabel.text=Maximum number of Results to show in table:
ViewPreferencesPanel.fileNameTranslationColumnCheckbox.text=Add column in result viewer for file name translation
ViewPreferencesPanel.scoColumnsWrapAroundText.text=to reduce loading times
ViewPreferencesPanel.translateTextLabel.text=Translate text:
ViewPreferencesPanel.scoColumnsCheckbox.text=S(core), C(omments), and O(ccurences)
ViewPreferencesPanel.scoColumnsLabel.text=Do not add columns for:
ViewPreferencesPanel.hideOtherUsersTagsLabel.text=Hide other users' tags in the:
ViewPreferencesPanel.hideOtherUsersTagsCheckbox.text=Tags area in the tree
ViewPreferencesPanel.useAnotherTimeRadioButton.text=Use another time zone
ViewPreferencesPanel.useLocalTimeRadioButton.text=Use local time zone
ViewPreferencesPanel.displayTimeLabel.text=When displaying times:
ViewPreferencesPanel.viewsHideSlackCheckbox.text=Views area
ViewPreferencesPanel.dataSourcesHideSlackCheckbox.text=Data Sources area (the directory hierarchy)
ViewPreferencesPanel.hideSlackFilesLabel.text=Hide slack files in the:
ViewPreferencesPanel.viewsHideKnownCheckbox.text=Views area
ViewPreferencesPanel.dataSourcesHideKnownCheckbox.text=Data Sources area (the directory hierarchy)
ViewPreferencesPanel.hideKnownFilesLabel.text=Hide known files (i.e. those in the NIST NSRL) in the:
ViewPreferencesPanel.keepCurrentViewerRadioButton.toolTipText=For example, stay in Hex view when a JPEG is selected.
ViewPreferencesPanel.keepCurrentViewerRadioButton.text=Stay on the same file viewer
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.selectFileLabel.text=When selecting a file:
ViewPreferencesPanel.globalSettingsPanel.border.title=Global Settings

View File

@ -192,25 +192,6 @@ OptionsCategory_Name_View=View
OptionsCategory_Keywords_View=View
ViewPreferencesPanel.currentSessionSettingsPanel.border.title=Current Session Settings
ViewPreferencesPanel.hideRejectedResultsCheckbox.text=Hide rejected results
ViewPreferencesPanel.selectFileLabel.text=When selecting a file:
ViewPreferencesPanel.globalSettingsPanel.border.title=Global Settings
ViewPreferencesPanel.translateTextLabel.text=Translate text:
ViewPreferencesPanel.hideOtherUsersTagsLabel.text=Hide other users' tags in the:
ViewPreferencesPanel.hideOtherUsersTagsCheckbox.text=Tags area in the tree
ViewPreferencesPanel.useAnotherTimeRadioButton.text=Use another time zone
ViewPreferencesPanel.useLocalTimeRadioButton.text=Use local time zone
ViewPreferencesPanel.displayTimeLabel.text=When displaying times:
ViewPreferencesPanel.viewsHideSlackCheckbox.text=Views area
ViewPreferencesPanel.dataSourcesHideSlackCheckbox.text=Data Sources area (the directory hierarchy)
ViewPreferencesPanel.hideSlackFilesLabel.text=Hide slack files in the:
ViewPreferencesPanel.viewsHideKnownCheckbox.text=Views area
ViewPreferencesPanel.dataSourcesHideKnownCheckbox.text=Data Sources area (the directory hierarchy)
ViewPreferencesPanel.hideKnownFilesLabel.text=Hide known files (i.e. those in the NIST NSRL) in the:
ViewPreferencesPanel.keepCurrentViewerRadioButton.toolTipText=For example, stay in Hex view when a JPEG is selected.
ViewPreferencesPanel.keepCurrentViewerRadioButton.text=Stay on the same file viewer
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
@ -250,12 +231,7 @@ DataResultViewerTable.pagePrevButton.text=
DataResultViewerTable.pagesLabel.text=Pages:
DataResultViewerTable.pageNumLabel.text=
DataResultViewerTable.pageLabel.text=Page:
ViewPreferencesPanel.maxResultsLabel.text=Maximum number of Results to show in table:
ViewPreferencesPanel.maxResultsLabel.toolTipText=<html>\nSetting this value to 0 will display all results in the results table.\n<br>Note that setting this value to 0 may result in poor UI responsiveness when there are large numbers of results.\n</html>
DataResultViewerTable.exportCSVButton.text=Save Table as CSV
ViewPreferencesPanel.scoColumnsCheckbox.text=S(core), C(omments), and O(ccurences)
ViewPreferencesPanel.scoColumnsWrapAroundText.text=to reduce loading times
ViewPreferencesPanel.scoColumnsLabel.text=Do not add columns for:
AutopsyOptionsPanel.logoPanel.border.title=Logo
AutopsyOptionsPanel.agencyLogoPathFieldValidationLabel.text=
AutopsyOptionsPanel.specifyLogoRB.text=Specify a logo
@ -280,3 +256,27 @@ AutopsyOptionsPanel.totalMemoryLabel.text=Total System Memory:
AutopsyOptionsPanel.maxMemoryUnitsLabel.text=GB
AutopsyOptionsPanel.maxMemoryLabel.text=Maximum JVM Memory:
AutopsyOptionsPanel.runtimePanel.border.title=Runtime
ViewPreferencesPanel.maxResultsLabel.toolTipText=<html>\nSetting this value to 0 will display all results in the results table.\n<br>Note that setting this value to 0 may result in poor UI responsiveness when there are large numbers of results.\n</html>
ViewPreferencesPanel.maxResultsLabel.text=Maximum number of Results to show in table:
ViewPreferencesPanel.fileNameTranslationColumnCheckbox.text=Add column in result viewer for file name translation
ViewPreferencesPanel.scoColumnsWrapAroundText.text=to reduce loading times
ViewPreferencesPanel.translateTextLabel.text=Translate text:
ViewPreferencesPanel.scoColumnsCheckbox.text=S(core), C(omments), and O(ccurences)
ViewPreferencesPanel.scoColumnsLabel.text=Do not add columns for:
ViewPreferencesPanel.hideOtherUsersTagsLabel.text=Hide other users' tags in the:
ViewPreferencesPanel.hideOtherUsersTagsCheckbox.text=Tags area in the tree
ViewPreferencesPanel.useAnotherTimeRadioButton.text=Use another time zone
ViewPreferencesPanel.useLocalTimeRadioButton.text=Use local time zone
ViewPreferencesPanel.displayTimeLabel.text=When displaying times:
ViewPreferencesPanel.viewsHideSlackCheckbox.text=Views area
ViewPreferencesPanel.dataSourcesHideSlackCheckbox.text=Data Sources area (the directory hierarchy)
ViewPreferencesPanel.hideSlackFilesLabel.text=Hide slack files in the:
ViewPreferencesPanel.viewsHideKnownCheckbox.text=Views area
ViewPreferencesPanel.dataSourcesHideKnownCheckbox.text=Data Sources area (the directory hierarchy)
ViewPreferencesPanel.hideKnownFilesLabel.text=Hide known files (i.e. those in the NIST NSRL) in the:
ViewPreferencesPanel.keepCurrentViewerRadioButton.toolTipText=For example, stay in Hex view when a JPEG is selected.
ViewPreferencesPanel.keepCurrentViewerRadioButton.text=Stay on the same file viewer
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.selectFileLabel.text=When selecting a file:
ViewPreferencesPanel.globalSettingsPanel.border.title=Global Settings

View File

@ -233,29 +233,29 @@ ViewOptionsController.moduleErr=\u5024\u306e\u5909\u66f4\u3092\u51e6\u7406\u4e2d
ViewOptionsController.moduleErr.msg=\u5024\u306e\u5909\u66f4\u306e\u51e6\u7406\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002
ViewPreferencesPanel.currentCaseSettingsPanel.border.title=\u73fe\u5728\u306e\u30b1\u30fc\u30b9\u8a2d\u5b9a
ViewPreferencesPanel.currentSessionSettingsPanel.border.title=\u73fe\u5728\u306e\u30bb\u30c3\u30b7\u30e7\u30f3\u8a2d\u5b9a
ViewPreferencesPanel.dataSourcesHideKnownCheckbox.text=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u9818\u57df(\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u968e\u5c64)
ViewPreferencesPanel.dataSourcesHideSlackCheckbox.text=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u9818\u57df(\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u968e\u5c64)
ViewPreferencesPanel.displayTimeLabel.text=\u6642\u523b\u8868\u793a\u6642\:
ViewPreferencesPanel.fileNameTranslationColumnCheckbox.text=\u7d50\u679c\u30d3\u30e5\u30fc\u30ef\u30fc\u306b\u30d5\u30a1\u30a4\u30eb\u540d\u7ffb\u8a33\u7528\u5217\u3092\u8ffd\u52a0
ViewPreferencesPanel.globalSettingsPanel.border.title=\u30b0\u30ed\u30fc\u30d0\u30eb\u8a2d\u5b9a
ViewPreferencesPanel.groupByDataSourceCheckbox.text=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u5225\u306b\u30b0\u30eb\u30fc\u30d7\u5316
ViewPreferencesPanel.hideKnownFilesLabel.text=\u6b21\u306e\u65e2\u77e5\u306e\u30d5\u30a1\u30a4\u30eb(NIST NSRL\u5185\u306e\u30d5\u30a1\u30a4\u30eb)\u3092\u975e\u8868\u793a\u306b\u3059\u308b\:
ViewPreferencesPanel.hideOtherUsersTagsCheckbox.text=\u30c4\u30ea\u30fc\u5185\u306e\u30bf\u30b0\u9818\u57df
ViewPreferencesPanel.hideOtherUsersTagsLabel.text=\u6b21\u306e\u305d\u306e\u4ed6\u306e\u30e6\u30fc\u30b6\u30fc\u306e\u30bf\u30b0\u3092\u975e\u8868\u793a\u306b\u3059\u308b\:
ViewPreferencesPanel.hideRejectedResultsCheckbox.text=\u62d2\u5426\u3055\u308c\u305f\u7d50\u679c\u3092\u975e\u8868\u793a\u306b\u3059\u308b
ViewPreferencesPanel.hideSlackFilesLabel.text=\u6b21\u306e\u30b9\u30e9\u30c3\u30af\u30d5\u30a1\u30a4\u30eb\u3092\u975e\u8868\u793a\u306b\u3059\u308b\:
ViewPreferencesPanel.keepCurrentViewerRadioButton.text=\u540c\u3058\u30d5\u30a1\u30a4\u30eb\u30d3\u30e5\u30fc\u306e\u307e\u307e\u306b\u3059\u308b
ViewPreferencesPanel.keepCurrentViewerRadioButton.toolTipText=\u305f\u3068\u3048\u3070\u3001JPEG\u9078\u629e\u6642\u306f16\u9032\u30d3\u30e5\u30fc\u306e\u307e\u307e\u306b\u3057\u307e\u3059\u3002
ViewPreferencesPanel.maxResultsLabel.text=\u30c6\u30fc\u30d6\u30eb\u3067\u8868\u793a\u3059\u308b\u6700\u5927\u7d50\u679c\u6570\:
ViewPreferencesPanel.maxResultsLabel.toolTipText=<html>\n\u3053\u306e\u5024\u30920 \u306b\u8a2d\u5b9a\u3059\u308b\u3068\u3001\u3059\u3079\u3066\u306e\u7d50\u679c\u304c\u7d50\u679c\u30c6\u30fc\u30d6\u30eb\u306b\u8868\u793a\u3055\u308c\u307e\u3059\u3002\n<br>\u3053\u306e\u5024\u30920 \u306b\u8a2d\u5b9a\u3059\u308b\u3068\u3001\u7d50\u679c\u6570\u304c\u591a\u3044\u5834\u5408UI\u306e\u5fdc\u7b54\u6027\u304c\u60aa\u304f\u306a\u308b\u3053\u3068\u304c\u3042\u308a\u307e\u3059 \u3002\n</html>
ViewPreferencesPanel.scoColumnsCheckbox.text=S(\u30b9\u30b3\u30a2)\u3001C(\u30b3\u30e1\u30f3\u30c8)\u3001O(\u767a\u751f)
ViewPreferencesPanel.scoColumnsLabel.text=\u6b21\u306e\u305f\u3081\u306e\u5217\u3092\u8ffd\u52a0\u3057\u306a\u3044\:
ViewPreferencesPanel.maxResultsLabel.text=\u30c6\u30fc\u30d6\u30eb\u3067\u8868\u793a\u3059\u308b\u6700\u5927\u7d50\u679c\u6570:
ViewPreferencesPanel.fileNameTranslationColumnCheckbox.text=\u7d50\u679c\u30d3\u30e5\u30fc\u30ef\u30fc\u306b\u30d5\u30a1\u30a4\u30eb\u540d\u7ffb\u8a33\u7528\u5217\u3092\u8ffd\u52a0
ViewPreferencesPanel.scoColumnsWrapAroundText.text=\u975e\u8868\u793a\u306b\u3059\u308b\u3068\u8aad\u8fbc\u307f\u304c\u65e9\u304f\u306a\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002
ViewPreferencesPanel.selectFileLabel.text=\u30d5\u30a1\u30a4\u30eb\u9078\u629e\u6642\:
ViewPreferencesPanel.translateTextLabel.text=\u30c6\u30ad\u30b9\u30c8\u3092\u7ffb\u8a33\:
ViewPreferencesPanel.translateTextLabel.text=\u30c6\u30ad\u30b9\u30c8\u3092\u7ffb\u8a33:
ViewPreferencesPanel.scoColumnsCheckbox.text=S(\u30b9\u30b3\u30a2)\u3001C(\u30b3\u30e1\u30f3\u30c8)\u3001O(\u767a\u751f)
ViewPreferencesPanel.scoColumnsLabel.text=\u6b21\u306e\u305f\u3081\u306e\u5217\u3092\u8ffd\u52a0\u3057\u306a\u3044:
ViewPreferencesPanel.hideOtherUsersTagsLabel.text=\u6b21\u306e\u305d\u306e\u4ed6\u306e\u30e6\u30fc\u30b6\u30fc\u306e\u30bf\u30b0\u3092\u975e\u8868\u793a\u306b\u3059\u308b:
ViewPreferencesPanel.hideOtherUsersTagsCheckbox.text=\u30c4\u30ea\u30fc\u5185\u306e\u30bf\u30b0\u9818\u57df
ViewPreferencesPanel.useAnotherTimeRadioButton.text=\u5225\u306e\u30bf\u30a4\u30e0\u30be\u30fc\u30f3\u3092\u4f7f\u7528
ViewPreferencesPanel.useBestViewerRadioButton.text=\u6700\u3082\u56fa\u6709\u306e\u30d5\u30a1\u30a4\u30eb\u30d3\u30e5\u30fc\u306b\u5207\u308a\u66ff\u3048\u308b
ViewPreferencesPanel.useBestViewerRadioButton.toolTipText=\u305f\u3068\u3048\u3070\u3001JPEG\u9078\u629e\u6642\u306f16\u9032\u304b\u3089\u30e1\u30c7\u30a3\u30a2\u306b\u5207\u308a\u66ff\u3048\u307e\u3059\u3002
ViewPreferencesPanel.useLocalTimeRadioButton.text=\u30ed\u30fc\u30ab\u30eb\u30bf\u30a4\u30e0\u30be\u30fc\u30f3\u3092\u4f7f\u7528
ViewPreferencesPanel.viewsHideKnownCheckbox.text=\u30d3\u30e5\u30fc\u9818\u57df
ViewPreferencesPanel.displayTimeLabel.text=\u6642\u523b\u8868\u793a\u6642:
ViewPreferencesPanel.viewsHideSlackCheckbox.text=\u30d3\u30e5\u30fc\u9818\u57df
ViewPreferencesPanel.dataSourcesHideSlackCheckbox.text=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u9818\u57df(\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u968e\u5c64)
ViewPreferencesPanel.hideSlackFilesLabel.text=\u6b21\u306e\u30b9\u30e9\u30c3\u30af\u30d5\u30a1\u30a4\u30eb\u3092\u975e\u8868\u793a\u306b\u3059\u308b:
ViewPreferencesPanel.viewsHideKnownCheckbox.text=\u30d3\u30e5\u30fc\u9818\u57df
ViewPreferencesPanel.dataSourcesHideKnownCheckbox.text=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u9818\u57df(\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u968e\u5c64)
ViewPreferencesPanel.hideKnownFilesLabel.text=\u6b21\u306e\u65e2\u77e5\u306e\u30d5\u30a1\u30a4\u30eb(NIST NSRL\u5185\u306e\u30d5\u30a1\u30a4\u30eb)\u3092\u975e\u8868\u793a\u306b\u3059\u308b:
ViewPreferencesPanel.keepCurrentViewerRadioButton.toolTipText=\u305f\u3068\u3048\u3070\u3001JPEG\u9078\u629e\u6642\u306f16\u9032\u30d3\u30e5\u30fc\u306e\u307e\u307e\u306b\u3057\u307e\u3059\u3002
ViewPreferencesPanel.keepCurrentViewerRadioButton.text=\u540c\u3058\u30d5\u30a1\u30a4\u30eb\u30d3\u30e5\u30fc\u306e\u307e\u307e\u306b\u3059\u308b
ViewPreferencesPanel.useBestViewerRadioButton.toolTipText=\u305f\u3068\u3048\u3070\u3001JPEG\u9078\u629e\u6642\u306f16\u9032\u304b\u3089\u30e1\u30c7\u30a3\u30a2\u306b\u5207\u308a\u66ff\u3048\u307e\u3059\u3002
ViewPreferencesPanel.useBestViewerRadioButton.text=\u6700\u3082\u56fa\u6709\u306e\u30d5\u30a1\u30a4\u30eb\u30d3\u30e5\u30fc\u306b\u5207\u308a\u66ff\u3048\u308b
ViewPreferencesPanel.selectFileLabel.text=\u30d5\u30a1\u30a4\u30eb\u9078\u629e\u6642:
ViewPreferencesPanel.globalSettingsPanel.border.title=\u30b0\u30ed\u30fc\u30d0\u30eb\u8a2d\u5b9a

View File

@ -96,100 +96,49 @@
<EmptySpace min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Component id="maxResultsLabel" max="32767" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/>
<Component id="maxResultsSpinner" min="-2" pref="74" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="325" max="-2" attributes="0"/>
<Component id="maxResultsLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="13" max="-2" attributes="0"/>
<Component id="maxResultsSpinner" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<Group type="103" groupAlignment="1" attributes="0">
<Group type="102" alignment="1" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="hideKnownFilesLabel" max="32767" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Component id="hideOtherUsersTagsLabel" max="32767" attributes="0"/>
<EmptySpace min="-2" pref="77" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="1" attributes="0">
<Group type="103" groupAlignment="1" attributes="0">
<Component id="scoColumnsLabel" alignment="1" max="32767" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="10" max="-2" attributes="0"/>
<Component id="hideOtherUsersTagsCheckbox" max="32767" attributes="0"/>
</Group>
</Group>
<EmptySpace min="-2" pref="99" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="32" max="-2" attributes="0"/>
<Component id="scoColumnsWrapAroundText" max="32767" attributes="0"/>
<EmptySpace min="-2" pref="93" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="10" max="-2" attributes="0"/>
<Component id="scoColumnsCheckbox" max="32767" attributes="0"/>
<EmptySpace min="-2" pref="14" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace min="-2" pref="10" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<Component id="hideSlackFilesLabel" max="32767" attributes="0"/>
<EmptySpace min="-2" pref="51" max="-2" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<EmptySpace min="-2" pref="10" max="-2" attributes="0"/>
<Group type="103" groupAlignment="1" attributes="0">
<Component id="dataSourcesHideSlackCheckbox" alignment="0" max="32767" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Component id="viewsHideKnownCheckbox" max="32767" attributes="0"/>
<EmptySpace min="-2" pref="103" max="-2" attributes="0"/>
</Group>
<Component id="dataSourcesHideKnownCheckbox" alignment="0" max="32767" attributes="0"/>
<Group type="102" attributes="0">
<Component id="viewsHideSlackCheckbox" max="32767" attributes="0"/>
<EmptySpace min="-2" pref="116" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Component id="displayTimeLabel" max="32767" attributes="0"/>
<EmptySpace min="-2" pref="91" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<Component id="translateTextLabel" max="32767" attributes="0"/>
<EmptySpace min="-2" pref="46" max="-2" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<Component id="selectFileLabel" max="32767" attributes="0"/>
<EmptySpace min="-2" pref="90" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="10" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" max="-2" attributes="0">
<Component id="viewsHideKnownCheckbox" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="dataSourcesHideKnownCheckbox" alignment="0" max="32767" attributes="0"/>
<Component id="hideOtherUsersTagsCheckbox" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="viewsHideSlackCheckbox" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="hideOtherUsersTagsLabel" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="scoColumnsLabel" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="scoColumnsCheckbox" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="dataSourcesHideSlackCheckbox" alignment="0" max="32767" attributes="0"/>
<Component id="scoColumnsWrapAroundText" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<Component id="hideSlackFilesLabel" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="hideKnownFilesLabel" min="-2" pref="289" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="10" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace min="24" pref="24" max="-2" attributes="0"/>
<Component id="jScrollPane1" max="32767" attributes="0"/>
</Group>
<Component id="fileNameTranslationColumnCheckbox" max="32767" attributes="0"/>
<Component id="useAnotherTimeRadioButton" alignment="0" max="32767" attributes="0"/>
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="useLocalTimeRadioButton" alignment="0" max="32767" attributes="0"/>
<Component id="keepCurrentViewerRadioButton" alignment="0" max="32767" attributes="0"/>
<Component id="useBestViewerRadioButton" alignment="0" max="32767" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="44" max="-2" attributes="0"/>
</Group>
<Component id="useLocalTimeRadioButton" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="keepCurrentViewerRadioButton" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="fileNameTranslationColumnCheckbox" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="useAnotherTimeRadioButton" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="useBestViewerRadioButton" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="jScrollPane1" alignment="0" min="-2" pref="246" max="-2" attributes="0"/>
</Group>
</Group>
<Component id="displayTimeLabel" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="selectFileLabel" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="translateTextLabel" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</Group>
<EmptySpace min="-2" max="-2" attributes="0"/>
<EmptySpace pref="107" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
@ -401,6 +350,14 @@
</Events>
</Component>
<Container class="javax.swing.JScrollPane" name="jScrollPane1">
<Properties>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[150, 130]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[150, 130]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues>
@ -483,9 +440,9 @@
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" max="-2" attributes="0"/>
<Component id="groupByDataSourceCheckbox" max="32767" attributes="0"/>
<EmptySpace min="-2" pref="474" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="groupByDataSourceCheckbox" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
@ -526,9 +483,9 @@
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" max="-2" attributes="0"/>
<Component id="hideRejectedResultsCheckbox" max="32767" attributes="0"/>
<EmptySpace min="-2" pref="418" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="hideRejectedResultsCheckbox" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>

View File

@ -272,6 +272,9 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
}
});
jScrollPane1.setMaximumSize(new java.awt.Dimension(150, 130));
jScrollPane1.setPreferredSize(new java.awt.Dimension(150, 130));
timeZoneList.addListSelectionListener(new javax.swing.event.ListSelectionListener() {
public void valueChanged(javax.swing.event.ListSelectionEvent evt) {
timeZoneListValueChanged(evt);
@ -308,74 +311,40 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
.addContainerGap()
.addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(globalSettingsPanelLayout.createSequentialGroup()
.addComponent(maxResultsLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(maxResultsSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, 74, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(325, 325, 325))
.addComponent(maxResultsLabel)
.addGap(13, 13, 13)
.addComponent(maxResultsSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGroup(globalSettingsPanelLayout.createSequentialGroup()
.addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addGroup(globalSettingsPanelLayout.createSequentialGroup()
.addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(hideKnownFilesLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(globalSettingsPanelLayout.createSequentialGroup()
.addComponent(hideOtherUsersTagsLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGap(77, 77, 77))
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, globalSettingsPanelLayout.createSequentialGroup()
.addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(scoColumnsLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(javax.swing.GroupLayout.Alignment.LEADING, globalSettingsPanelLayout.createSequentialGroup()
.addGap(10, 10, 10)
.addComponent(hideOtherUsersTagsCheckbox, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))
.addGap(99, 99, 99))
.addGroup(globalSettingsPanelLayout.createSequentialGroup()
.addGap(32, 32, 32)
.addComponent(scoColumnsWrapAroundText, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGap(93, 93, 93))
.addGroup(globalSettingsPanelLayout.createSequentialGroup()
.addGap(10, 10, 10)
.addComponent(scoColumnsCheckbox, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGap(14, 14, 14)))
.addGap(10, 10, 10))
.addGroup(javax.swing.GroupLayout.Alignment.LEADING, globalSettingsPanelLayout.createSequentialGroup()
.addComponent(hideSlackFilesLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGap(51, 51, 51))
.addGroup(globalSettingsPanelLayout.createSequentialGroup()
.addGap(10, 10, 10)
.addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(dataSourcesHideSlackCheckbox, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(javax.swing.GroupLayout.Alignment.LEADING, globalSettingsPanelLayout.createSequentialGroup()
.addComponent(viewsHideKnownCheckbox, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGap(103, 103, 103))
.addComponent(dataSourcesHideKnownCheckbox, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(globalSettingsPanelLayout.createSequentialGroup()
.addComponent(viewsHideSlackCheckbox, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGap(116, 116, 116)))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)))
.addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(globalSettingsPanelLayout.createSequentialGroup()
.addComponent(displayTimeLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGap(91, 91, 91))
.addGroup(globalSettingsPanelLayout.createSequentialGroup()
.addComponent(translateTextLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGap(46, 46, 46))
.addGroup(globalSettingsPanelLayout.createSequentialGroup()
.addComponent(selectFileLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGap(90, 90, 90))
.addGap(10, 10, 10)
.addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
.addComponent(viewsHideKnownCheckbox)
.addComponent(dataSourcesHideKnownCheckbox, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(hideOtherUsersTagsCheckbox)
.addComponent(viewsHideSlackCheckbox)
.addComponent(hideOtherUsersTagsLabel)
.addComponent(scoColumnsLabel)
.addComponent(scoColumnsCheckbox)
.addComponent(dataSourcesHideSlackCheckbox, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(scoColumnsWrapAroundText)))
.addComponent(hideSlackFilesLabel)
.addComponent(hideKnownFilesLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 289, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(globalSettingsPanelLayout.createSequentialGroup()
.addGap(10, 10, 10)
.addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(globalSettingsPanelLayout.createSequentialGroup()
.addGap(24, 24, 24)
.addComponent(jScrollPane1))
.addComponent(fileNameTranslationColumnCheckbox, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(useAnotherTimeRadioButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(globalSettingsPanelLayout.createSequentialGroup()
.addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(useLocalTimeRadioButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(keepCurrentViewerRadioButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(useBestViewerRadioButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addGap(44, 44, 44)))))))
.addContainerGap())
.addComponent(useLocalTimeRadioButton)
.addComponent(keepCurrentViewerRadioButton)
.addComponent(fileNameTranslationColumnCheckbox)
.addComponent(useAnotherTimeRadioButton)
.addComponent(useBestViewerRadioButton)
.addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 246, javax.swing.GroupLayout.PREFERRED_SIZE)))
.addComponent(displayTimeLabel)
.addComponent(selectFileLabel)
.addComponent(translateTextLabel))))
.addContainerGap(107, Short.MAX_VALUE))
);
globalSettingsPanelLayout.setVerticalGroup(
globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
@ -444,8 +413,8 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
currentCaseSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(currentCaseSettingsPanelLayout.createSequentialGroup()
.addContainerGap()
.addComponent(groupByDataSourceCheckbox, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGap(474, 474, 474))
.addComponent(groupByDataSourceCheckbox)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
currentCaseSettingsPanelLayout.setVerticalGroup(
currentCaseSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
@ -469,8 +438,8 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
currentSessionSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(currentSessionSettingsPanelLayout.createSequentialGroup()
.addContainerGap()
.addComponent(hideRejectedResultsCheckbox, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGap(418, 418, 418))
.addComponent(hideRejectedResultsCheckbox)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
currentSessionSettingsPanelLayout.setVerticalGroup(
currentSessionSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
@ -532,6 +501,22 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
}
}//GEN-LAST:event_hideRejectedResultsCheckboxActionPerformed
private void maxResultsSpinnerStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_maxResultsSpinnerStateChanged
if (immediateUpdates) {
UserPreferences.setResultsTablePageSize((int)maxResultsSpinner.getValue());
} else {
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}
}//GEN-LAST:event_maxResultsSpinnerStateChanged
private void fileNameTranslationColumnCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_fileNameTranslationColumnCheckboxActionPerformed
if (immediateUpdates) {
UserPreferences.setDisplayTranslatedFileNames(fileNameTranslationColumnCheckbox.isSelected());
} else {
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}
}//GEN-LAST:event_fileNameTranslationColumnCheckboxActionPerformed
private void timeZoneListValueChanged(javax.swing.event.ListSelectionEvent evt) {//GEN-FIRST:event_timeZoneListValueChanged
if (immediateUpdates && useAnotherTimeRadioButton.isSelected()) {
UserPreferences.setTimeZoneForDisplays(timeZoneList.getSelectedValue().substring(11).trim());
@ -630,22 +615,6 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
}
}//GEN-LAST:event_useBestViewerRadioButtonActionPerformed
private void fileNameTranslationColumnCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_fileNameTranslationColumnCheckboxActionPerformed
if (immediateUpdates) {
UserPreferences.setDisplayTranslatedFileNames(fileNameTranslationColumnCheckbox.isSelected());
} else {
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}
}//GEN-LAST:event_fileNameTranslationColumnCheckboxActionPerformed
private void maxResultsSpinnerStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_maxResultsSpinnerStateChanged
if (immediateUpdates) {
UserPreferences.setResultsTablePageSize((int)maxResultsSpinner.getValue());
} else {
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}
}//GEN-LAST:event_maxResultsSpinnerStateChanged
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JPanel currentCaseSettingsPanel;

View File

@ -38,6 +38,7 @@ import java.util.Collections;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import static java.util.Objects.nonNull;
import java.util.SortedSet;
import java.util.TreeSet;
@ -58,7 +59,6 @@ import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.event.IIOReadProgressListener;
import javax.imageio.stream.ImageInputStream;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.openide.util.NbBundle;
@ -962,7 +962,7 @@ public class ImageUtils {
LOGGER.log(Level.WARNING, IMAGEIO_COULD_NOT_READ_UNSUPPORTED_OR_CORRUPT, ImageUtils.getContentPathSafe(file));
} else if (fxImage.isError()) {
//if there was somekind of error, log it
LOGGER.log(Level.WARNING, IMAGEIO_COULD_NOT_READ_UNSUPPORTED_OR_CORRUPT + ": " + ObjectUtils.toString(fxImage.getException()), ImageUtils.getContentPathSafe(file));
LOGGER.log(Level.WARNING, IMAGEIO_COULD_NOT_READ_UNSUPPORTED_OR_CORRUPT + ": " + Objects.toString(fxImage.getException()), ImageUtils.getContentPathSafe(file));
}
} catch (InterruptedException | ExecutionException ex) {
failed();
@ -972,7 +972,7 @@ public class ImageUtils {
@Override
protected void failed() {
super.failed();
LOGGER.log(Level.WARNING, IMAGEIO_COULD_NOT_READ_UNSUPPORTED_OR_CORRUPT + ": " + ObjectUtils.toString(getException()), ImageUtils.getContentPathSafe(file));
LOGGER.log(Level.WARNING, IMAGEIO_COULD_NOT_READ_UNSUPPORTED_OR_CORRUPT + ": " + Objects.toString(getException()), ImageUtils.getContentPathSafe(file));
}
@Override

View File

@ -89,6 +89,20 @@ public final class IconsUtil {
imageFile = "gps_trackpoint.png"; //NON-NLS
} else if (typeID == ARTIFACT_TYPE.TSK_REMOTE_DRIVE.getTypeID()) {
imageFile = "drive_network.png"; //NON-NLS
} else if (typeID == ARTIFACT_TYPE.TSK_OS_ACCOUNT.getTypeID()) {
imageFile = "os-account.png"; //NON-NLS
} else if (typeID == ARTIFACT_TYPE.TSK_OBJECT_DETECTED.getTypeID()) {
imageFile = "objects.png"; //NON-NLS
} else if (typeID == ARTIFACT_TYPE.TSK_WEB_FORM_AUTOFILL.getTypeID()) {
imageFile = "web-form.png"; //NON-NLS
} else if (typeID == ARTIFACT_TYPE.TSK_WEB_CACHE.getTypeID()) {
imageFile = "cache.png"; //NON-NLS
} else if (typeID == ARTIFACT_TYPE.TSK_USER_CONTENT_SUSPECTED.getTypeID()) {
imageFile = "user-content.png"; //NON-NLS
} else if (typeID == ARTIFACT_TYPE.TSK_METADATA.getTypeID()) {
imageFile = "metadata.png"; //NON-NLS
} else if (typeID == ARTIFACT_TYPE.TSK_CLIPBOARD_CONTENT.getTypeID()) {
imageFile = "clipboard.png"; //NON-NLS
} else if (typeID == ARTIFACT_TYPE.TSK_FACE_DETECTED.getTypeID()) {
imageFile = "face.png"; //NON-NLS
} else if (typeID == ARTIFACT_TYPE.TSK_WIFI_NETWORK.getTypeID()) {
@ -103,6 +117,8 @@ public final class IconsUtil {
imageFile = "devices.png"; //NON-NLS
} else if (typeID == ARTIFACT_TYPE.TSK_VERIFICATION_FAILED.getTypeID()) {
imageFile = "validationFailed.png"; //NON-NLS
} else if (typeID == ARTIFACT_TYPE.TSK_WEB_ACCOUNT_TYPE.getTypeID()) {
imageFile = "web-account-type.png"; //NON-NLS
} else {
imageFile = "artifact-icon.png"; //NON-NLS
}

View File

@ -18,8 +18,10 @@
*/
package org.sleuthkit.autopsy.datasourcesummary.datamodel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultArtifactUpdateGovernor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@ -40,7 +42,7 @@ import org.sleuthkit.datamodel.TskCoreException;
/**
* Providing data for the data source analysis tab.
*/
public class DataSourceAnalysisSummary {
public class AnalysisSummary implements DefaultArtifactUpdateGovernor {
private static final BlackboardAttribute.Type TYPE_SET_NAME = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_SET_NAME);
@ -52,12 +54,19 @@ public class DataSourceAnalysisSummary {
"CREDIT CARD NUMBERS"
));
private static final Set<Integer> ARTIFACT_UPDATE_TYPE_IDS = new HashSet<>(Arrays.asList(
ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID(),
ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID(),
ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID(),
ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()
));
private final SleuthkitCaseProvider provider;
/**
* Main constructor.
*/
public DataSourceAnalysisSummary() {
public AnalysisSummary() {
this(SleuthkitCaseProvider.DEFAULT);
}
@ -66,10 +75,15 @@ public class DataSourceAnalysisSummary {
*
* @param provider The means of obtaining a sleuthkit case.
*/
public DataSourceAnalysisSummary(SleuthkitCaseProvider provider) {
public AnalysisSummary(SleuthkitCaseProvider provider) {
this.provider = provider;
}
@Override
public Set<Integer> getArtifactTypeIdsForRefresh() {
return ARTIFACT_UPDATE_TYPE_IDS;
}
/**
* Gets counts for hashset hits.
*
@ -133,6 +147,11 @@ public class DataSourceAnalysisSummary {
*/
private List<Pair<String, Long>> getCountsData(DataSource dataSource, BlackboardAttribute.Type keyType, ARTIFACT_TYPE... artifactTypes)
throws SleuthkitCaseProviderException, TskCoreException {
if (dataSource == null) {
return Collections.emptyList();
}
List<BlackboardArtifact> artifacts = new ArrayList<>();
SleuthkitCase skCase = provider.get();

View File

@ -1,2 +1,3 @@
DataSourceUserActivitySummary_getRecentAccounts_calllogMessage=Call Log
DataSourceUserActivitySummary_getRecentAccounts_emailMessage=Email Message
IngestModuleCheckUtil_recentActivityModuleName=Recent Activity

View File

@ -18,21 +18,62 @@
*/
package org.sleuthkit.autopsy.datasourcesummary.datamodel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultArtifactUpdateGovernor;
import java.sql.SQLException;
import java.util.logging.Level;
import org.sleuthkit.autopsy.coreutils.Logger;
import static org.sleuthkit.autopsy.datasourcesummary.datamodel.DataSourceInfoUtilities.getBaseQueryResult;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
import org.sleuthkit.autopsy.ingest.ModuleContentEvent;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
/**
* Provides methods to query for data source overview details.
*/
public class DataSourceDetailsSummary {
public class ContainerSummary implements DefaultArtifactUpdateGovernor {
private static final Logger logger = Logger.getLogger(DataSourceDetailsSummary.class.getName());
private static final Set<Integer> ARTIFACT_UPDATE_TYPE_IDS = new HashSet<>(Arrays.asList(
BlackboardArtifact.ARTIFACT_TYPE.TSK_OS_INFO.getTypeID(),
BlackboardArtifact.ARTIFACT_TYPE.TSK_DATA_SOURCE_USAGE.getTypeID()
));
private final SleuthkitCaseProvider provider;
/**
* Main constructor.
*/
public ContainerSummary() {
this(SleuthkitCaseProvider.DEFAULT);
}
/**
* Main constructor.
*
* @param provider The means of obtaining a sleuthkit case.
*/
public ContainerSummary(SleuthkitCaseProvider provider) {
this.provider = provider;
}
@Override
public boolean isRefreshRequired(ModuleContentEvent evt) {
return true;
}
@Override
public boolean isRefreshRequired(AbstractFile file) {
return true;
}
@Override
public Set<Integer> getArtifactTypeIdsForRefresh() {
return ARTIFACT_UPDATE_TYPE_IDS;
}
/**
* Gets the size of unallocated files in a particular datasource.
@ -40,8 +81,13 @@ public class DataSourceDetailsSummary {
* @param currentDataSource The data source.
*
* @return The size or null if the query could not be executed.
*
* @throws SleuthkitCaseProviderException
* @throws TskCoreException
* @throws SQLException
*/
public static Long getSizeOfUnallocatedFiles(DataSource currentDataSource) {
public Long getSizeOfUnallocatedFiles(DataSource currentDataSource)
throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException {
if (currentDataSource == null) {
return null;
}
@ -66,9 +112,8 @@ public class DataSourceDetailsSummary {
return null;
}
};
String errorMessage = "Unable to get size of unallocated files; returning null.";
return DataSourceInfoUtilities.getBaseQueryResult(query, handler, errorMessage);
return DataSourceInfoUtilities.getBaseQueryResult(provider.get(), query, handler);
}
/**
@ -79,8 +124,14 @@ public class DataSourceDetailsSummary {
*
* @return The concatenated value or null if the query could not be
* executed.
*
* @throws SleuthkitCaseProviderException
* @throws TskCoreException
* @throws SQLException
*/
public static String getOperatingSystems(DataSource dataSource) {
public String getOperatingSystems(DataSource dataSource)
throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException {
if (dataSource == null) {
return null;
}
@ -98,8 +149,14 @@ public class DataSourceDetailsSummary {
*
* @return The concatenated value or null if the query could not be
* executed.
*
* @throws SleuthkitCaseProviderException
* @throws TskCoreException
* @throws SQLException
*/
public static String getDataSourceType(DataSource dataSource) {
public String getDataSourceType(DataSource dataSource)
throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException {
if (dataSource == null) {
return null;
}
@ -116,35 +173,33 @@ public class DataSourceDetailsSummary {
* @param query The query.
* @param valueParam The parameter for the value in the result set.
* @param separator The string separator used in concatenation.
* @param errorMessage The error message if the result set could not
* be received.
* @param singleErrorMessage The error message if a single result could not
* be obtained.
*
* @return The concatenated string or null if the query could not be
* executed.
*
* @throws SleuthkitCaseProviderException
* @throws TskCoreException
* @throws SQLException
*/
private static String getConcattedStringsResult(String query, String valueParam, String separator, String errorMessage, String singleErrorMessage) {
private String getConcattedStringsResult(String query, String valueParam, String separator)
throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException {
DataSourceInfoUtilities.ResultSetHandler<String> handler = (resultSet) -> {
String toRet = "";
boolean first = true;
while (resultSet.next()) {
try {
if (first) {
first = false;
} else {
toRet += separator;
}
toRet += resultSet.getString(valueParam);
} catch (SQLException ex) {
logger.log(Level.WARNING, singleErrorMessage, ex);
if (first) {
first = false;
} else {
toRet += separator;
}
toRet += resultSet.getString(valueParam);
}
return toRet;
};
return getBaseQueryResult(query, handler, errorMessage);
return DataSourceInfoUtilities.getBaseQueryResult(provider.get(), query, handler);
}
/**
@ -157,8 +212,14 @@ public class DataSourceDetailsSummary {
*
* @return The concatenated value or null if the query could not be
* executed.
*
* @throws SleuthkitCaseProviderException
* @throws TskCoreException
* @throws SQLException
*/
private static String getConcattedAttrValue(long dataSourceId, int artifactTypeId, int attributeTypeId) {
private String getConcattedAttrValue(long dataSourceId, int artifactTypeId, int attributeTypeId)
throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException {
final String valueParam = "concatted_attribute_value";
String query = "SELECT attr.value_text AS " + valueParam
+ " FROM blackboard_artifacts bba "
@ -167,12 +228,7 @@ public class DataSourceDetailsSummary {
+ " AND bba.artifact_type_id = " + artifactTypeId
+ " AND attr.attribute_type_id = " + attributeTypeId;
String errorMessage = "Unable to execute query to retrieve concatted attribute values.";
String singleErrorMessage = "There was an error retrieving one of the results. That result will be omitted from concatted value.";
String separator = ", ";
return getConcattedStringsResult(query, valueParam, separator, errorMessage, singleErrorMessage);
}
private DataSourceDetailsSummary() {
return getConcattedStringsResult(query, valueParam, separator);
}
}

View File

@ -1,160 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019 - 2020 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.datasourcesummary.datamodel;
import java.sql.SQLException;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.logging.Level;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataSourceInfoUtilities.ResultSetHandler;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.TskData;
/**
* Provides information for the DataSourceSummaryCountsPanel.
*/
public class DataSourceCountsSummary {
private static final Logger logger = Logger.getLogger(DataSourceCountsSummary.class.getName());
/**
* Get count of regular files (not directories) in a data source.
*
* @param currentDataSource The data source.
*
* @return The count.
*/
public static Long getCountOfFiles(DataSource currentDataSource) {
return DataSourceInfoUtilities.getCountOfRegularFiles(currentDataSource, null,
"Unable to get count of files, providing empty results");
}
/**
* Get count of allocated files in a data source.
*
* @param currentDataSource The data source.
*
* @return The count.
*/
public static Long getCountOfAllocatedFiles(DataSource currentDataSource) {
return DataSourceInfoUtilities.getCountOfRegularFiles(currentDataSource,
DataSourceInfoUtilities.getMetaFlagsContainsStatement(TskData.TSK_FS_META_FLAG_ENUM.ALLOC),
"Unable to get counts of unallocated files for datasource, providing empty results");
}
/**
* Get count of unallocated files in a data source.
*
* @param currentDataSource The data source.
*
* @return The count.
*/
public static Long getCountOfUnallocatedFiles(DataSource currentDataSource) {
return DataSourceInfoUtilities.getCountOfRegularFiles(currentDataSource,
DataSourceInfoUtilities.getMetaFlagsContainsStatement(TskData.TSK_FS_META_FLAG_ENUM.UNALLOC)
+ " AND type<>" + TskData.TSK_DB_FILES_TYPE_ENUM.SLACK.getFileType(),
"Unable to get counts of unallocated files for datasource, providing empty results");
}
/**
* Get count of directories in a data source.
*
* @param currentDataSource The data source.
*
* @return The count.
*/
public static Long getCountOfDirectories(DataSource currentDataSource) {
return DataSourceInfoUtilities.getCountOfTskFiles(currentDataSource,
"meta_type=" + TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_DIR.getValue()
+ " AND type<>" + TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType(),
"Unable to get count of directories for datasource, providing empty results");
}
/**
* Get count of slack files in a data source.
*
* @param currentDataSource The data source.
*
* @return The count.
*/
public static Long getCountOfSlackFiles(DataSource currentDataSource) {
return DataSourceInfoUtilities.getCountOfRegularFiles(currentDataSource,
DataSourceInfoUtilities.getMetaFlagsContainsStatement(TskData.TSK_FS_META_FLAG_ENUM.UNALLOC)
+ " AND type=" + TskData.TSK_DB_FILES_TYPE_ENUM.SLACK.getFileType(),
"Unable to get count of slack files for datasources, providing empty results");
}
/**
* Retrieves counts for each artifact type in a data source.
*
* @param selectedDataSource The data source.
*
* @return A mapping of artifact type name to the counts or null if there
* was an error executing the query.
*/
public static Map<String, Long> getCountsOfArtifactsByType(DataSource selectedDataSource) {
if (selectedDataSource == null) {
return Collections.emptyMap();
}
final String nameParam = "name";
final String valueParam = "value";
String query
= "SELECT bbt.display_name AS " + nameParam + ", COUNT(*) AS " + valueParam
+ " FROM blackboard_artifacts bba "
+ " INNER JOIN blackboard_artifact_types bbt ON bba.artifact_type_id = bbt.artifact_type_id"
+ " WHERE bba.data_source_obj_id =" + selectedDataSource.getId()
+ " GROUP BY bbt.display_name";
String errorMessage = "Unable to get artifact type counts; returning null.";
return DataSourceInfoUtilities.getBaseQueryResult(query,
getStringLongResultSetHandler(nameParam, valueParam),
errorMessage);
}
/**
* Generates a result set handler that will return a map of string to long.
*
* @param keyParam The named parameter in the result set representing the
* key.
* @param valueParam The named parameter in the result set representing the
* value.
*
* @return The result set handler to generate the map of string to long.
*/
private static ResultSetHandler<LinkedHashMap<String, Long>> getStringLongResultSetHandler(String keyParam, String valueParam) {
return (resultSet) -> {
LinkedHashMap<String, Long> toRet = new LinkedHashMap<>();
while (resultSet.next()) {
try {
toRet.put(resultSet.getString(keyParam), resultSet.getLong(valueParam));
} catch (SQLException ex) {
logger.log(Level.WARNING, "Failed to get a result pair from the result set.", ex);
}
}
return toRet;
};
}
private DataSourceCountsSummary() {
}
}

View File

@ -26,19 +26,14 @@ import java.util.Date;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.logging.Level;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
import org.apache.commons.lang.StringUtils;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
import org.sleuthkit.datamodel.BlackboardAttribute.Type;
import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.TskData.TSK_DB_FILES_TYPE_ENUM;
import org.sleuthkit.datamodel.TskData.TSK_FS_META_FLAG_ENUM;
import org.sleuthkit.datamodel.TskData.TSK_FS_META_TYPE_ENUM;
@ -48,54 +43,73 @@ import org.sleuthkit.datamodel.TskData.TSK_FS_META_TYPE_ENUM;
*/
final class DataSourceInfoUtilities {
private static final Logger logger = Logger.getLogger(DataSourceInfoUtilities.class.getName());
/**
* Gets a count of tsk_files for a particular datasource where dir_type is
* not a virtual directory and has a name.
* Gets a count of tsk_files for a particular datasource.
*
* @param skCase The current SleuthkitCase.
* @param currentDataSource The datasource.
* @param additionalWhere Additional sql where clauses.
* @param onError The message to log on error.
*
* @return The count of files or null on error.
*
* @throws TskCoreException
* @throws SQLException
*/
static Long getCountOfTskFiles(DataSource currentDataSource, String additionalWhere, String onError) {
static Long getCountOfTskFiles(SleuthkitCase skCase, DataSource currentDataSource, String additionalWhere)
throws TskCoreException, SQLException {
if (currentDataSource != null) {
try {
SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
return skCase.countFilesWhere(
"data_source_obj_id=" + currentDataSource.getId()
+ " AND dir_type<>" + TskData.TSK_FS_NAME_TYPE_ENUM.VIRT_DIR.getValue()
+ " AND name<>''"
+ (StringUtils.isBlank(additionalWhere) ? "" : (" AND " + additionalWhere)));
} catch (TskCoreException | NoCurrentCaseException ex) {
logger.log(Level.WARNING, onError, ex);
//unable to get count of files for the specified types cell will be displayed as empty
}
return skCase.countFilesWhere(
"data_source_obj_id=" + currentDataSource.getId()
+ (StringUtils.isBlank(additionalWhere) ? "" : (" AND " + additionalWhere)));
}
return null;
}
/**
* Gets a count of regular files for a particular datasource where the
* dir_type and type are not a virtual directory and has a name.
* Gets a count of regular files for a particular datasource.
*
* @param skCase The current SleuthkitCase.
* @param currentDataSource The datasource.
* @param additionalWhere Additional sql where clauses.
* @param onError The message to log on error.
*
* @return The count of files or null on error.
*
* @throws TskCoreException
* @throws SQLException
*/
static Long getCountOfRegularFiles(DataSource currentDataSource, String additionalWhere, String onError) {
String whereClause = "meta_type=" + TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_REG.getValue()
+ " AND type<>" + TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType();
static Long getCountOfRegularFiles(SleuthkitCase skCase, DataSource currentDataSource, String additionalWhere)
throws TskCoreException, SQLException {
String whereClause = "meta_type=" + TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_REG.getValue();
if (StringUtils.isNotBlank(additionalWhere)) {
whereClause += " AND " + additionalWhere;
}
return getCountOfTskFiles(currentDataSource, whereClause, onError);
return getCountOfTskFiles(skCase, currentDataSource, whereClause);
}
/**
* Gets a count of regular non-slack files for a particular datasource.
*
* @param skCase The current SleuthkitCase.
* @param currentDataSource The datasource.
* @param additionalWhere Additional sql where clauses.
*
* @return The count of files or null on error.
*
* @throws TskCoreException
* @throws SQLException
*/
static Long getCountOfRegNonSlackFiles(SleuthkitCase skCase, DataSource currentDataSource, String additionalWhere)
throws TskCoreException, SQLException {
String whereClause = "meta_type=" + TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_REG.getValue()
+ " AND type<>" + TSK_DB_FILES_TYPE_ENUM.SLACK.getFileType();
if (StringUtils.isNotBlank(additionalWhere)) {
whereClause += " AND " + additionalWhere;
}
return getCountOfTskFiles(skCase, currentDataSource, whereClause);
}
/**
@ -109,42 +123,22 @@ final class DataSourceInfoUtilities {
/**
* Retrieves a result based on the provided query.
*
* @param query The query.
* @param processor The result set handler.
* @param errorMessage The error message to display if there is an error
* retrieving the resultset.
* @param skCase The current SleuthkitCase.
* @param query The query.
* @param processor The result set handler.
*
* @return The ResultSetHandler value or null if no ResultSet could be
* obtained.
*/
static <T> T getBaseQueryResult(String query, ResultSetHandler<T> processor, String errorMessage) {
return getBaseQueryResult(SleuthkitCaseProvider.DEFAULT, query, processor, errorMessage);
}
/**
* Retrieves a result based on the provided query.
*
* @param provider The means of obtaining a SleuthkitCase.
* @param query The query.
* @param processor The result set handler.
* @param errorMessage The error message to display if there is an error
* retrieving the resultset.
*
* @return The ResultSetHandler value or null if no ResultSet could be
* obtained.
* @throws TskCoreException
* @throws SQLException
*/
static <T> T getBaseQueryResult(SleuthkitCaseProvider provider, String query, ResultSetHandler<T> processor, String errorMessage) {
try (SleuthkitCase.CaseDbQuery dbQuery = provider.get().executeQuery(query)) {
static <T> T getBaseQueryResult(SleuthkitCase skCase, String query, ResultSetHandler<T> processor)
throws TskCoreException, SQLException {
try (SleuthkitCase.CaseDbQuery dbQuery = skCase.executeQuery(query)) {
ResultSet resultSet = dbQuery.getResultSet();
try {
return processor.process(resultSet);
} catch (SQLException ex) {
logger.log(Level.WARNING, errorMessage, ex);
}
} catch (TskCoreException | SleuthkitCaseProviderException ex) {
logger.log(Level.WARNING, errorMessage, ex);
return processor.process(resultSet);
}
return null;
}
/**
@ -317,7 +311,7 @@ final class DataSourceInfoUtilities {
@Override
public int compare(BlackboardAttribute attribute1, BlackboardAttribute attribute2) {
if (attribute1.getAttributeType() != attribute2.getAttributeType()) {
if (!attribute1.getAttributeType().equals(attribute2.getAttributeType())) {
throw new IllegalArgumentException("Unable to compare attributes of different types");
}
@ -344,7 +338,7 @@ final class DataSourceInfoUtilities {
private int compare(BlackboardAttribute.Type type, BlackboardAttribute attribute1, BlackboardAttribute attribute2) {
switch (type.getValueType()) {
case STRING:
return attribute1.getValueString().compareTo(attribute2.getValueString());
return attribute1.getValueString().compareToIgnoreCase(attribute2.getValueString());
case INTEGER:
return Integer.compare(attribute1.getValueInt(), attribute2.getValueInt());
case LONG:

View File

@ -1,107 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 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.datasourcesummary.datamodel;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.sleuthkit.datamodel.DataSource;
/**
* Provides methods to query for datasource files by mime type.
*/
public class DataSourceMimeTypeSummary {
/**
* Get the number of files in the case database for the current data source
* which have the specified mimetypes.
*
* @param currentDataSource the data source which we are finding a file
* count
*
* @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
*/
public static Long getCountOfFilesForMimeTypes(DataSource currentDataSource, Set<String> setOfMimeTypes) {
return DataSourceInfoUtilities.getCountOfRegularFiles(currentDataSource,
"mime_type IN " + getSqlSet(setOfMimeTypes),
"Unable to get count of files for specified mime types");
}
/**
* Get the number of files in the case database for the current data source
* which do not have the specified mimetypes.
*
* @param currentDataSource the data source which we are finding a file
* count
*
* @param setOfMimeTypes the set of mime types that should be excluded.
*
* @return a Long value which represents the number of files that do not
* have the specific mime type, but do have a mime type.
*/
public static Long getCountOfFilesNotInMimeTypes(DataSource currentDataSource, Set<String> setOfMimeTypes) {
return DataSourceInfoUtilities.getCountOfRegularFiles(currentDataSource,
"mime_type NOT IN " + getSqlSet(setOfMimeTypes)
+ " AND mime_type IS NOT NULL AND mime_type <> '' ",
"Unable to get count of files without specified mime types");
}
/**
* Gets the number of files in the data source with no assigned mime type.
*
* @param currentDataSource The data source.
*
* @return The number of files with no mime type or null if there is an
* issue searching the data source.
*
*/
public static Long getCountOfFilesWithNoMimeType(DataSource currentDataSource) {
return DataSourceInfoUtilities.getCountOfRegularFiles(currentDataSource,
"(mime_type IS NULL OR mime_type = '') ",
"Unable to get count of files without a mime type");
}
/**
* Derives a sql set string (i.e. "('val1', 'val2', 'val3')"). A naive
* attempt is made to sanitize the strings by removing single quotes from
* values.
*
* @param setValues The values that should be present in the set. Single
* quotes are removed.
*
* @return The sql set string.
*/
private static String getSqlSet(Set<String> setValues) {
List<String> quotedValues = setValues
.stream()
.map(str -> String.format("'%s'", str.replace("'", "")))
.collect(Collectors.toList());
String commaSeparatedQuoted = String.join(", ", quotedValues);
return String.format("(%s) ", commaSeparatedQuoted);
}
private DataSourceMimeTypeSummary() {
}
}

View File

@ -0,0 +1,145 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 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.datasourcesummary.datamodel;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.IngestJobInfo;
import org.sleuthkit.datamodel.IngestModuleInfo;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Utilities for checking if an ingest module has been run on a datasource.
*/
@Messages({
"IngestModuleCheckUtil_recentActivityModuleName=Recent Activity",
})
public class IngestModuleCheckUtil {
public static final String RECENT_ACTIVITY_FACTORY = "org.sleuthkit.autopsy.recentactivity.RecentActivityExtracterModuleFactory";
public static final String RECENT_ACTIVITY_MODULE_NAME = Bundle.IngestModuleCheckUtil_recentActivityModuleName();
// IngestModuleInfo separator for unique_name
private static final String UNIQUE_NAME_SEPARATOR = "-";
private final SleuthkitCaseProvider caseProvider;
/**
* Main constructor.
*/
public IngestModuleCheckUtil() {
this(SleuthkitCaseProvider.DEFAULT);
}
/**
* Main constructor with external dependencies specified. This constructor
* is designed with unit testing in mind since mocked dependencies can be
* utilized.
*
* @param provider The object providing the current SleuthkitCase.
*/
public IngestModuleCheckUtil(SleuthkitCaseProvider provider) {
this.caseProvider = provider;
}
/**
* Gets the fully qualified factory from the IngestModuleInfo.
* @param info The IngestJobInfo.
* @return The fully qualified factory.
*/
private static String getFullyQualifiedFactory(IngestModuleInfo info) {
if (info == null) {
return null;
}
String qualifiedName = info.getUniqueName();
if (StringUtils.isBlank(qualifiedName)) {
return null;
}
return qualifiedName.split(UNIQUE_NAME_SEPARATOR)[0];
}
/**
* Whether or not the ingest job info contains the ingest modulename.
* @param info The IngestJobInfo.
* @param fullyQualifiedFactory The fully qualified classname of the relevant factory.
* @return True if the ingest module name is contained in the data.
*/
private static boolean hasIngestModule(IngestJobInfo info, String fullyQualifiedFactory) {
if (info == null || info.getIngestModuleInfo() == null || StringUtils.isBlank(fullyQualifiedFactory)) {
return false;
}
return info.getIngestModuleInfo().stream()
.anyMatch((moduleInfo) -> {
String thisQualifiedFactory = getFullyQualifiedFactory(moduleInfo);
return fullyQualifiedFactory.equalsIgnoreCase(thisQualifiedFactory);
});
}
/**
* Whether or not a data source has been ingested with a particular ingest module.
* @param dataSource The datasource.
* @param fullyQualifiedFactory The fully qualified classname of the relevant factory.
* @return Whether or not a data source has been ingested with a particular ingest module.
* @throws TskCoreException
* @throws SleuthkitCaseProviderException
*/
public boolean isModuleIngested(DataSource dataSource, String fullyQualifiedFactory)
throws TskCoreException, SleuthkitCaseProviderException {
if (dataSource == null) {
return false;
}
long dataSourceId = dataSource.getId();
return caseProvider.get().getIngestJobs().stream()
.anyMatch((ingestJob) -> {
return ingestJob != null
&& ingestJob.getObjectId() == dataSourceId
&& hasIngestModule(ingestJob, fullyQualifiedFactory);
});
}
/**
* Get a mapping of fully qualified factory name to display name.
* @param skCase The SleuthkitCase.
* @return The mapping of fully qualified factory name to display name.
* @throws TskCoreException
*/
public static Map<String, String> getFactoryDisplayNames(SleuthkitCase skCase) throws TskCoreException {
return skCase.getIngestJobs().stream()
.flatMap(ingestJob -> ingestJob.getIngestModuleInfo().stream())
.collect(Collectors.toMap(
(moduleInfo) -> getFullyQualifiedFactory(moduleInfo),
(moduleInfo) -> moduleInfo.getDisplayName(),
(a,b) -> a));
}
}

View File

@ -0,0 +1,194 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 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.datasourcesummary.datamodel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultUpdateGovernor;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.ModuleContentEvent;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Provides methods to query for datasource files by mime type.
*/
public class MimeTypeSummary implements DefaultUpdateGovernor {
private final SleuthkitCaseProvider provider;
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS = new HashSet<>(
Arrays.asList(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED));
/**
* Main constructor.
*/
public MimeTypeSummary() {
this(SleuthkitCaseProvider.DEFAULT);
}
/**
* Main constructor.
*
* @param provider The means of obtaining a sleuthkit case.
*/
public MimeTypeSummary(SleuthkitCaseProvider provider) {
this.provider = provider;
}
@Override
public boolean isRefreshRequired(ModuleContentEvent evt) {
return true;
}
@Override
public boolean isRefreshRequired(AbstractFile file) {
return true;
}
@Override
public boolean isRefreshRequired(IngestManager.IngestJobEvent evt) {
return (evt != null && INGEST_JOB_EVENTS.contains(evt));
}
@Override
public Set<IngestManager.IngestJobEvent> getIngestJobEventUpdates() {
return INGEST_JOB_EVENTS;
}
/**
* Get the number of files in the case database for the current data source
* which have the specified mimetypes.
*
* @param currentDataSource the data source which we are finding a file
* count
*
* @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
*
* @throws SleuthkitCaseProviderException
* @throws TskCoreException
* @throws SQLException
*/
public Long getCountOfFilesForMimeTypes(DataSource currentDataSource, Set<String> setOfMimeTypes)
throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException {
return DataSourceInfoUtilities.getCountOfRegNonSlackFiles(
provider.get(),
currentDataSource,
"mime_type IN " + getSqlSet(setOfMimeTypes)
);
}
/**
* Get the number of files in the case database for the current data source
* which do not have the specified mimetypes.
*
* @param currentDataSource the data source which we are finding a file
* count
*
* @param setOfMimeTypes the set of mime types that should be excluded.
*
* @return a Long value which represents the number of files that do not
* have the specific mime type, but do have a mime type.
*
* @throws SleuthkitCaseProviderException
* @throws TskCoreException
* @throws SQLException
*/
public Long getCountOfFilesNotInMimeTypes(DataSource currentDataSource, Set<String> setOfMimeTypes)
throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException {
return DataSourceInfoUtilities.getCountOfRegNonSlackFiles(
provider.get(),
currentDataSource,
"mime_type NOT IN " + getSqlSet(setOfMimeTypes)
+ " AND mime_type IS NOT NULL AND mime_type <> '' "
);
}
/**
* Get a count of all regular files in a datasource.
*
* @param dataSource The datasource.
*
* @return The count of regular files.
*
* @throws SleuthkitCaseProviderException
* @throws TskCoreException
* @throws SQLException
*/
public Long getCountOfAllRegularFiles(DataSource dataSource)
throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException {
return DataSourceInfoUtilities.getCountOfRegNonSlackFiles(provider.get(), dataSource, null);
}
/**
* Gets the number of files in the data source with no assigned mime type.
*
* @param currentDataSource The data source.
*
* @return The number of files with no mime type or null if there is an
* issue searching the data source.
*
* @throws SleuthkitCaseProviderException
* @throws TskCoreException
* @throws SQLException
*/
public Long getCountOfFilesWithNoMimeType(DataSource currentDataSource)
throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException {
return DataSourceInfoUtilities.getCountOfRegNonSlackFiles(
provider.get(),
currentDataSource,
"(mime_type IS NULL OR mime_type = '') "
);
}
/**
* Derives a sql set string (i.e. "('val1', 'val2', 'val3')"). A naive
* attempt is made to sanitize the strings by removing single quotes from
* values.
*
* @param setValues The values that should be present in the set. Single
* quotes are removed.
*
* @return The sql set string.
*/
private String getSqlSet(Set<String> setValues) {
List<String> quotedValues = setValues
.stream()
.map(str -> String.format("'%s'", str.replace("'", "")))
.collect(Collectors.toList());
String commaSeparatedQuoted = String.join(", ", quotedValues);
return String.format("(%s) ", commaSeparatedQuoted);
}
}

View File

@ -0,0 +1,332 @@
/*
* 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.datasourcesummary.datamodel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.lang3.tuple.Pair;
import org.sleuthkit.autopsy.centralrepository.ingestmodule.CentralRepoIngestModuleFactory;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultArtifactUpdateGovernor;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Provides information about how a datasource relates to a previous case. NOTE:
* This code is fragile and has certain expectations about how the central
* repository handles creating artifacts. So, if the central repository changes
* ingest process, this code could break. This code expects that the central
* repository ingest module:
*
* a) Creates a TSK_INTERESTING_FILE_HIT artifact for a file whose hash is in
* the central repository as a notable file.
*
* b) Creates a TSK_INTERESTING_ARTIFACT_HIT artifact for a matching id in the
* central repository.
*
* c) The created artifact will have a TSK_COMMENT attribute attached where one
* of the sources for the attribute matches
* CentralRepoIngestModuleFactory.getModuleName(). The module display name at
* time of ingest will match CentralRepoIngestModuleFactory.getModuleName() as
* well.
*
* d) The content of that TSK_COMMENT attribute will be of the form "Previous
* Case: case1,case2...caseN"
*/
public class PastCasesSummary implements DefaultArtifactUpdateGovernor {
/**
* Return type for results items in the past cases tab.
*/
public static class PastCasesResult {
private final List<Pair<String, Long>> sameIdsResults;
private final List<Pair<String, Long>> taggedNotable;
/**
* Main constructor.
*
* @param sameIdsResults Data for the cases with same id table.
* @param taggedNotable Data for the tagged notable table.
*/
public PastCasesResult(List<Pair<String, Long>> sameIdsResults, List<Pair<String, Long>> taggedNotable) {
this.sameIdsResults = sameIdsResults;
this.taggedNotable = taggedNotable;
}
/**
* @return Data for the cases with same id table.
*/
public List<Pair<String, Long>> getSameIdsResults() {
return sameIdsResults;
}
/**
* @return Data for the tagged notable table.
*/
public List<Pair<String, Long>> getTaggedNotable() {
return taggedNotable;
}
}
private static final Set<Integer> ARTIFACT_UPDATE_TYPE_IDS = new HashSet<>(Arrays.asList(
ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID(),
ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID()
));
private static final String CENTRAL_REPO_INGEST_NAME = CentralRepoIngestModuleFactory.getModuleName().toUpperCase().trim();
private static final BlackboardAttribute.Type TYPE_COMMENT = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_COMMENT);
private static final BlackboardAttribute.Type TYPE_ASSOCIATED_ARTIFACT = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT);
private static final Set<Integer> CR_DEVICE_TYPE_IDS = new HashSet<>(Arrays.asList(
ARTIFACT_TYPE.TSK_DEVICE_ATTACHED.getTypeID(),
ARTIFACT_TYPE.TSK_DEVICE_INFO.getTypeID(),
ARTIFACT_TYPE.TSK_SIM_ATTACHED.getTypeID(),
ARTIFACT_TYPE.TSK_WIFI_NETWORK_ADAPTER.getTypeID()
));
private static final String CASE_SEPARATOR = ",";
private static final String PREFIX_END = ":";
private final SleuthkitCaseProvider caseProvider;
private final java.util.logging.Logger logger;
/**
* Main constructor.
*/
public PastCasesSummary() {
this(
SleuthkitCaseProvider.DEFAULT,
org.sleuthkit.autopsy.coreutils.Logger.getLogger(PastCasesSummary.class.getName())
);
}
/**
* Main constructor with external dependencies specified. This constructor
* is designed with unit testing in mind since mocked dependencies can be
* utilized.
*
* @param provider The object providing the current SleuthkitCase.
* @param logger The logger to use.
*/
public PastCasesSummary(
SleuthkitCaseProvider provider,
java.util.logging.Logger logger) {
this.caseProvider = provider;
this.logger = logger;
}
@Override
public Set<Integer> getArtifactTypeIdsForRefresh() {
return ARTIFACT_UPDATE_TYPE_IDS;
}
/**
* Given the provided sources for an attribute, aims to determine if one of
* those sources is the Central Repository Ingest Module.
*
* @param sources The list of sources found on an attribute.
*
* @return Whether or not this attribute (and subsequently the parent
* artifact) is created by the Central Repository Ingest Module.
*/
private static boolean isCentralRepoGenerated(List<String> sources) {
if (sources == null) {
return false;
}
return sources.stream().anyMatch((str) -> {
return str != null && CENTRAL_REPO_INGEST_NAME.equalsIgnoreCase(str.trim());
});
}
/**
* Gets a list of cases from the TSK_COMMENT of an artifact. The cases
* string is expected to be of a form of "Previous Case:
* case1,case2...caseN".
*
* @param artifact The artifact.
*
* @return The list of cases if found or empty list if not.
*/
private static List<String> getCasesFromArtifact(BlackboardArtifact artifact) {
if (artifact == null) {
return Collections.emptyList();
}
BlackboardAttribute commentAttr = null;
try {
commentAttr = artifact.getAttribute(TYPE_COMMENT);
} catch (TskCoreException ignored) {
// ignore if no attribute can be found
}
if (commentAttr == null) {
return Collections.emptyList();
}
if (!isCentralRepoGenerated(commentAttr.getSources())) {
return Collections.emptyList();
}
String commentStr = commentAttr.getValueString();
int prefixCharIdx = commentStr.indexOf(PREFIX_END);
if (prefixCharIdx < 0 || prefixCharIdx >= commentStr.length() - 1) {
return Collections.emptyList();
}
String justCasesStr = commentStr.substring(prefixCharIdx + 1).trim();
return Stream.of(justCasesStr.split(CASE_SEPARATOR))
.map(String::trim)
.collect(Collectors.toList());
}
/**
* Given a stream of case ids, groups the strings in a case-insensitive
* manner, and then provides a list of cases and the occurrence count sorted
* from max to min.
*
* @param cases A stream of cases.
*
* @return The list of unique cases and their occurrences sorted from max to
* min.
*/
private List<Pair<String, Long>> getCaseCounts(Stream<String> cases) {
Collection<List<String>> groupedCases = cases
// group by case insensitive compare of cases
.collect(Collectors.groupingBy((caseStr) -> caseStr.toUpperCase().trim()))
.values();
return groupedCases
.stream()
// get any cases where an actual case is found
.filter((lst) -> lst != null && lst.size() > 0)
// get non-normalized (i.e. not all caps) case name and number of items found
.map((lst) -> Pair.of(lst.get(0), (long) lst.size()))
// sorted descending
.sorted((a, b) -> -Long.compare(a.getValue(), b.getValue()))
.collect(Collectors.toList());
}
/**
* Given an artifact with a TYPE_ASSOCIATED_ARTIFACT attribute, retrieves
* the related artifact.
*
* @param artifact The artifact with the TYPE_ASSOCIATED_ARTIFACT attribute.
*
* @return The artifact if found or null if not.
*
* @throws SleuthkitCaseProviderException
*/
private BlackboardArtifact getParentArtifact(BlackboardArtifact artifact) throws SleuthkitCaseProviderException {
Long parentId = DataSourceInfoUtilities.getLongOrNull(artifact, TYPE_ASSOCIATED_ARTIFACT);
if (parentId == null) {
return null;
}
SleuthkitCase skCase = caseProvider.get();
try {
return skCase.getArtifactByArtifactId(parentId);
} catch (TskCoreException ex) {
logger.log(Level.WARNING,
String.format("There was an error fetching the parent artifact of a TSK_INTERESTING_ARTIFACT_HIT (parent id: %d)", parentId),
ex);
return null;
}
}
/**
* Returns true if the artifact has an associated artifact of a device type.
*
* @param artifact The artifact.
*
* @return True if there is a device associated artifact.
*
* @throws SleuthkitCaseProviderException
*/
private boolean hasDeviceAssociatedArtifact(BlackboardArtifact artifact) throws SleuthkitCaseProviderException {
BlackboardArtifact parent = getParentArtifact(artifact);
if (parent == null) {
return false;
}
return CR_DEVICE_TYPE_IDS.contains(parent.getArtifactTypeID());
}
/**
* Returns the past cases data to be shown in the past cases tab.
*
* @param dataSource The data source.
*
* @return The retrieved data or null if null dataSource.
*
* @throws SleuthkitCaseProviderException
* @throws TskCoreException
*/
public PastCasesResult getPastCasesData(DataSource dataSource)
throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException {
if (dataSource == null) {
return null;
}
SleuthkitCase skCase = caseProvider.get();
List<String> deviceArtifactCases = new ArrayList<>();
List<String> nonDeviceArtifactCases = new ArrayList<>();
for (BlackboardArtifact artifact : skCase.getBlackboard().getArtifacts(ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID(), dataSource.getId())) {
List<String> cases = getCasesFromArtifact(artifact);
if (cases == null || cases.isEmpty()) {
continue;
}
if (hasDeviceAssociatedArtifact(artifact)) {
deviceArtifactCases.addAll(cases);
} else {
nonDeviceArtifactCases.addAll(cases);
}
}
Stream<String> filesCases = skCase.getBlackboard().getArtifacts(ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID(), dataSource.getId()).stream()
.flatMap((art) -> getCasesFromArtifact(art).stream());
return new PastCasesResult(
getCaseCounts(deviceArtifactCases.stream()),
getCaseCounts(Stream.concat(filesCases, nonDeviceArtifactCases.stream()))
);
}
}

View File

@ -18,32 +18,34 @@
*/
package org.sleuthkit.autopsy.datasourcesummary.datamodel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultArtifactUpdateGovernor;
import java.nio.file.Paths;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_RECENT_OBJECT;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
/**
* Helper class for getting data for the Recent Files Data Summary tab.
*/
public class RecentFilesSummary {
public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
private final static BlackboardAttribute.Type DATETIME_ACCESSED_ATT = new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED);
private final static BlackboardAttribute.Type DOMAIN_ATT = new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN);
@ -52,10 +54,18 @@ public class RecentFilesSummary {
private final static BlackboardAttribute.Type ASSOCATED_ATT = new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT);
private final static BlackboardAttribute.Type EMAIL_FROM_ATT = new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL_FROM);
private final static BlackboardAttribute.Type MSG_DATEIME_SENT_ATT = new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_SENT);
private final static BlackboardArtifact.Type ASSOCATED_OBJ_ART = new BlackboardArtifact.Type(BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT);
private final static BlackboardArtifact.Type ASSOCATED_OBJ_ART = new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT);
private static final DateFormat DATETIME_FORMAT = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.getDefault());
private static final Set<Integer> ARTIFACT_UPDATE_TYPE_IDS = new HashSet<>(Arrays.asList(
ARTIFACT_TYPE.TSK_RECENT_OBJECT.getTypeID(),
ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID(),
ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT.getTypeID(),
ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID(),
ARTIFACT_TYPE.TSK_MESSAGE.getTypeID()
));
private final SleuthkitCaseProvider provider;
/**
@ -78,6 +88,11 @@ public class RecentFilesSummary {
this.provider = provider;
}
@Override
public Set<Integer> getArtifactTypeIdsForRefresh() {
return ARTIFACT_UPDATE_TYPE_IDS;
}
/**
* Return a list of the most recently opened documents based on the
* TSK_RECENT_OBJECT artifact.
@ -94,12 +109,12 @@ public class RecentFilesSummary {
*/
public List<RecentFileDetails> getRecentlyOpenedDocuments(DataSource dataSource, int maxCount) throws SleuthkitCaseProviderException, TskCoreException {
if (dataSource == null) {
throw new IllegalArgumentException("Failed to get recently opened documents given data source was null");
return Collections.emptyList();
}
List<BlackboardArtifact> artifactList
= DataSourceInfoUtilities.getArtifacts(provider.get(),
new BlackboardArtifact.Type(TSK_RECENT_OBJECT),
new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_RECENT_OBJECT),
dataSource,
DATETIME_ATT,
DataSourceInfoUtilities.SortOrder.DESCENDING,
@ -145,9 +160,13 @@ public class RecentFilesSummary {
* @throws SleuthkitCaseProviderException
*/
public List<RecentDownloadDetails> getRecentDownloads(DataSource dataSource, int maxCount) throws TskCoreException, SleuthkitCaseProviderException {
if (dataSource == null) {
return Collections.emptyList();
}
List<BlackboardArtifact> artifactList
= DataSourceInfoUtilities.getArtifacts(provider.get(),
new BlackboardArtifact.Type(TSK_WEB_DOWNLOAD),
new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD),
dataSource,
DATETIME_ACCESSED_ATT,
DataSourceInfoUtilities.SortOrder.DESCENDING,
@ -192,6 +211,10 @@ public class RecentFilesSummary {
* @throws TskCoreException
*/
public List<RecentAttachmentDetails> getRecentAttachments(DataSource dataSource, int maxCount) throws SleuthkitCaseProviderException, TskCoreException {
if (dataSource == null) {
return Collections.emptyList();
}
return createListFromMap(buildAttachmentMap(dataSource), maxCount);
}
@ -247,7 +270,7 @@ public class RecentFilesSummary {
sortedMap.put(date, list);
}
RecentAttachmentDetails details = new RecentAttachmentDetails(path, date, sender);
if(!list.contains(details)) {
if (!list.contains(details)) {
list.add(details);
}
}
@ -302,8 +325,8 @@ public class RecentFilesSummary {
*/
private boolean isMessageArtifact(BlackboardArtifact nodeArtifact) {
final int artifactTypeID = nodeArtifact.getArtifactTypeID();
return artifactTypeID == TSK_EMAIL_MSG.getTypeID()
|| artifactTypeID == TSK_MESSAGE.getTypeID();
return artifactTypeID == ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID()
|| artifactTypeID == ARTIFACT_TYPE.TSK_MESSAGE.getTypeID();
}
/**
@ -418,14 +441,14 @@ public class RecentFilesSummary {
@Override
public boolean equals(Object obj) {
if(!(obj instanceof RecentAttachmentDetails)) {
if (!(obj instanceof RecentAttachmentDetails)) {
return false;
}
RecentAttachmentDetails compareObj = (RecentAttachmentDetails)obj;
RecentAttachmentDetails compareObj = (RecentAttachmentDetails) obj;
return compareObj.getSender().equals(this.sender) &&
compareObj.getPath().equals(this.getPath()) &&
compareObj.getDateAsLong().equals(this.getDateAsLong());
return compareObj.getSender().equals(this.sender)
&& compareObj.getPath().equals(this.getPath())
&& compareObj.getDateAsLong().equals(this.getDateAsLong());
}
@Override

View File

@ -1,76 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 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.datasourcesummary.datamodel;
import java.util.Date;
/**
* Describes a result of a program run on a datasource.
*/
public class TopDomainsResult {
private final String domain;
private final String url;
private final Long visitTimes;
private final Date lastVisit;
/**
* Describes a top domain result.
*
* @param domain The domain.
* @param url The url.
* @param visitTimes The number of times it was visited.
* @param lastVisit The date of the last visit.
*/
public TopDomainsResult(String domain, String url, Long visitTimes, Date lastVisit) {
this.domain = domain;
this.url = url;
this.visitTimes = visitTimes;
this.lastVisit = lastVisit;
}
/**
* @return The domain for the result.
*/
public String getDomain() {
return domain;
}
/**
* @return The url for the result.
*/
public String getUrl() {
return url;
}
/**
* @return The number of times this site is visited.
*/
public Long getVisitTimes() {
return visitTimes;
}
/**
* @return The date of the last visit.
*/
public Date getLastVisit() {
return lastVisit;
}
}

View File

@ -1,74 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 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.datasourcesummary.datamodel;
import java.util.Date;
/**
* Describes a result of a program run on a datasource.
*/
public class TopProgramsResult {
private final String programName;
private final String programPath;
private final Long runTimes;
private final Date lastRun;
/**
* Main constructor.
*
* @param programName The name of the program.
* @param programPath The path of the program.
* @param runTimes The number of runs.
*/
TopProgramsResult(String programName, String programPath, Long runTimes, Date lastRun) {
this.programName = programName;
this.programPath = programPath;
this.runTimes = runTimes;
this.lastRun = lastRun;
}
/**
* @return The name of the program
*/
public String getProgramName() {
return programName;
}
/**
* @return The path of the program.
*/
public String getProgramPath() {
return programPath;
}
/**
* @return The number of run times or null if not present.
*/
public Long getRunTimes() {
return runTimes;
}
/**
* @return The last time the program was run or null if not present.
*/
public Date getLastRun() {
return lastRun;
}
}

View File

@ -18,29 +18,36 @@
*/
package org.sleuthkit.autopsy.datasourcesummary.datamodel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultArtifactUpdateGovernor;
import java.io.File;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import java.util.logging.Level;
import java.util.stream.Collectors;
import org.apache.commons.lang.StringUtils;
import org.sleuthkit.autopsy.coreutils.Logger;
import static org.sleuthkit.autopsy.datasourcesummary.datamodel.DataSourceInfoUtilities.getBaseQueryResult;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Provides information to populate Top Programs Summary queries.
*/
public class DataSourceTopProgramsSummary {
public class TopProgramsSummary implements DefaultArtifactUpdateGovernor {
private static final Logger logger = Logger.getLogger(DataSourceTopProgramsSummary.class.getName());
private static final Set<Integer> ARTIFACT_UPDATE_TYPE_IDS = new HashSet<>(Arrays.asList(
ARTIFACT_TYPE.TSK_PROG_RUN.getTypeID()
));
/**
* A SQL join type.
@ -169,17 +176,21 @@ public class DataSourceTopProgramsSummary {
return column + (isLike ? "" : " NOT") + " LIKE '" + likeString + "'";
}
private final SleuthkitCaseProvider provider;
public DataSourceTopProgramsSummary() {
public TopProgramsSummary() {
this(SleuthkitCaseProvider.DEFAULT);
}
public DataSourceTopProgramsSummary(SleuthkitCaseProvider provider) {
public TopProgramsSummary(SleuthkitCaseProvider provider) {
this.provider = provider;
}
@Override
public Set<Integer> getArtifactTypeIdsForRefresh() {
return ARTIFACT_UPDATE_TYPE_IDS;
}
/**
* Retrieves a list of the top programs used on the data source. Currently
* determines this based off of which prefetch results return the highest
@ -188,9 +199,14 @@ public class DataSourceTopProgramsSummary {
* @param dataSource The data source.
* @param count The number of programs to return.
*
* @return
* @return The top results objects found.
*
* @throws SleuthkitCaseProviderException
* @throws TskCoreException
* @throws SQLException
*/
public List<TopProgramsResult> getTopPrograms(DataSource dataSource, int count) {
public List<TopProgramsResult> getTopPrograms(DataSource dataSource, int count)
throws SleuthkitCaseProviderException, TskCoreException, SQLException {
if (dataSource == null || count <= 0) {
return Collections.emptyList();
}
@ -231,49 +247,46 @@ public class DataSourceTopProgramsSummary {
+ " MAX(" + getFullKey(lastRunParam) + ") DESC,\n"
+ " " + getFullKey(nameParam) + " ASC";
final String errorMessage = "Unable to get top program results; returning null.";
DataSourceInfoUtilities.ResultSetHandler<List<TopProgramsResult>> handler = (resultSet) -> {
List<TopProgramsResult> progResults = new ArrayList<>();
boolean quitAtCount = false;
while (resultSet.next() && (!quitAtCount || progResults.size() < count)) {
try {
long lastRunEpoch = resultSet.getLong(lastRunParam);
Date lastRun = (resultSet.wasNull()) ? null : new Date(lastRunEpoch * 1000);
long lastRunEpoch = resultSet.getLong(lastRunParam);
Date lastRun = (resultSet.wasNull()) ? null : new Date(lastRunEpoch * 1000);
Long runCount = resultSet.getLong(runCountParam);
if (resultSet.wasNull()) {
runCount = null;
}
if (lastRun != null || runCount != null) {
quitAtCount = true;
}
progResults.add(new TopProgramsResult(
resultSet.getString(nameParam),
resultSet.getString(pathParam),
runCount,
lastRun));
} catch (SQLException ex) {
logger.log(Level.WARNING, "Failed to get a top program result from the result set.", ex);
Long runCount = resultSet.getLong(runCountParam);
if (resultSet.wasNull()) {
runCount = null;
}
if (lastRun != null || runCount != null) {
quitAtCount = true;
}
progResults.add(new TopProgramsResult(
resultSet.getString(nameParam),
resultSet.getString(pathParam),
runCount,
lastRun));
}
return progResults;
};
return getBaseQueryResult(provider, query, handler, errorMessage);
}
try (SleuthkitCase.CaseDbQuery dbQuery = provider.get().executeQuery(query);
ResultSet resultSet = dbQuery.getResultSet()) {
return handler.process(resultSet);
}
}
/**
* Determines a short folder name if any. Otherwise, returns empty string.
*
* @param strPath The string path.
* @param strPath The string path.
* @param applicationName The application name.
*
* @return The short folder name or empty string if not found.
*/
@ -301,4 +314,57 @@ public class DataSourceTopProgramsSummary {
return "";
}
/**
* Describes a result of a program run on a datasource.
*/
public static class TopProgramsResult {
private final String programName;
private final String programPath;
private final Long runTimes;
private final Date lastRun;
/**
* Main constructor.
*
* @param programName The name of the program.
* @param programPath The path of the program.
* @param runTimes The number of runs.
*/
TopProgramsResult(String programName, String programPath, Long runTimes, Date lastRun) {
this.programName = programName;
this.programPath = programPath;
this.runTimes = runTimes;
this.lastRun = lastRun;
}
/**
* @return The name of the program
*/
public String getProgramName() {
return programName;
}
/**
* @return The path of the program.
*/
public String getProgramPath() {
return programPath;
}
/**
* @return The number of run times or null if not present.
*/
public Long getRunTimes() {
return runTimes;
}
/**
* @return The last time the program was run or null if not present.
*/
public Date getLastRun() {
return lastRun;
}
}
}

View File

@ -0,0 +1,172 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019 - 2020 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.datasourcesummary.datamodel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultUpdateGovernor;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.ModuleContentEvent;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
/**
* Provides information for the DataSourceSummaryCountsPanel.
*/
public class TypesSummary implements DefaultUpdateGovernor {
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS = new HashSet<>(
Arrays.asList(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED));
private final SleuthkitCaseProvider provider;
/**
* Main constructor.
*/
public TypesSummary() {
this(SleuthkitCaseProvider.DEFAULT);
}
/**
* Main constructor.
*
* @param provider The means of obtaining a sleuthkit case.
*/
public TypesSummary(SleuthkitCaseProvider provider) {
this.provider = provider;
}
@Override
public boolean isRefreshRequired(ModuleContentEvent evt) {
return true;
}
@Override
public boolean isRefreshRequired(AbstractFile file) {
return true;
}
@Override
public boolean isRefreshRequired(IngestManager.IngestJobEvent evt) {
return (evt != null && INGEST_JOB_EVENTS.contains(evt));
}
@Override
public Set<IngestManager.IngestJobEvent> getIngestJobEventUpdates() {
return INGEST_JOB_EVENTS;
}
/**
* Get count of regular files (not directories) in a data source.
*
* @param currentDataSource The data source.
*
* @return The count.
*
* @throws SleuthkitCaseProviderException
* @throws TskCoreException
* @throws SQLException
*/
public Long getCountOfFiles(DataSource currentDataSource)
throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException {
return DataSourceInfoUtilities.getCountOfRegularFiles(
provider.get(),
currentDataSource,
null
);
}
/**
* Get count of allocated files in a data source.
*
* @param currentDataSource The data source.
*
* @return The count.
*
* @throws SleuthkitCaseProviderException
* @throws TskCoreException
* @throws SQLException
*/
public Long getCountOfAllocatedFiles(DataSource currentDataSource)
throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException {
return DataSourceInfoUtilities.getCountOfRegNonSlackFiles(provider.get(), currentDataSource,
DataSourceInfoUtilities.getMetaFlagsContainsStatement(TskData.TSK_FS_META_FLAG_ENUM.ALLOC));
}
/**
* Get count of unallocated files in a data source.
*
* @param currentDataSource The data source.
*
* @return The count.
*
* @throws SleuthkitCaseProviderException
* @throws TskCoreException
* @throws SQLException
*/
public Long getCountOfUnallocatedFiles(DataSource currentDataSource)
throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException {
return DataSourceInfoUtilities.getCountOfRegNonSlackFiles(provider.get(), currentDataSource,
DataSourceInfoUtilities.getMetaFlagsContainsStatement(TskData.TSK_FS_META_FLAG_ENUM.UNALLOC));
}
/**
* Get count of directories in a data source.
*
* @param currentDataSource The data source.
*
* @return The count.
*
* @throws SleuthkitCaseProviderException
* @throws TskCoreException
* @throws SQLException
*/
public Long getCountOfDirectories(DataSource currentDataSource)
throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException {
return DataSourceInfoUtilities.getCountOfTskFiles(provider.get(), currentDataSource,
"meta_type=" + TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_DIR.getValue()
+ " AND type<>" + TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType());
}
/**
* Get count of slack files in a data source.
*
* @param currentDataSource The data source.
*
* @return The count.
*
* @throws SleuthkitCaseProviderException
* @throws TskCoreException
* @throws SQLException
*/
public Long getCountOfSlackFiles(DataSource currentDataSource)
throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException {
return DataSourceInfoUtilities.getCountOfRegularFiles(provider.get(), currentDataSource,
"type=" + TskData.TSK_DB_FILES_TYPE_ENUM.SLACK.getFileType());
}
}

View File

@ -18,15 +18,23 @@
*/
package org.sleuthkit.autopsy.datasourcesummary.datamodel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultArtifactUpdateGovernor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
import org.sleuthkit.datamodel.BlackboardArtifact;
@ -44,9 +52,10 @@ import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
* time, the data being provided for domains is fictitious and is done as a
* placeholder.
*/
public class DataSourceUserActivitySummary {
public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
private static final BlackboardArtifact.Type TYPE_DEVICE_ATTACHED = new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_DEVICE_ATTACHED);
private static final BlackboardArtifact.Type TYPE_WEB_HISTORY = new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_WEB_HISTORY);
private static final BlackboardAttribute.Type TYPE_DATETIME = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DATETIME);
private static final BlackboardAttribute.Type TYPE_DATETIME_ACCESSED = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED);
@ -55,24 +64,76 @@ public class DataSourceUserActivitySummary {
private static final BlackboardAttribute.Type TYPE_DEVICE_MODEL = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DEVICE_MODEL);
private static final BlackboardAttribute.Type TYPE_MESSAGE_TYPE = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_MESSAGE_TYPE);
private static final BlackboardAttribute.Type TYPE_TEXT = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_TEXT);
private static final BlackboardAttribute.Type TYPE_DATETIME_RCVD = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DATETIME_RCVD);
private static final BlackboardAttribute.Type TYPE_DATETIME_SENT = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DATETIME_SENT);
private static final BlackboardAttribute.Type TYPE_DATETIME_START = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DATETIME_START);
private static final BlackboardAttribute.Type TYPE_DATETIME_END = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DATETIME_END);
private static final BlackboardAttribute.Type TYPE_DOMAIN = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DOMAIN);
private static final Comparator<TopAccountResult> TOP_ACCOUNT_RESULT_DATE_COMPARE = (a, b) -> a.getLastAccess().compareTo(b.getLastAccess());
private static final Comparator<TopWebSearchResult> TOP_WEBSEARCH_RESULT_DATE_COMPARE = (a, b) -> a.getDateAccessed().compareTo(b.getDateAccessed());
private static final String ROOT_HUB_IDENTIFIER = "ROOT_HUB";
private static final long SLEEP_TIME = 5000;
private static final Set<Integer> ARTIFACT_UPDATE_TYPE_IDS = new HashSet<>(Arrays.asList(
ARTIFACT_TYPE.TSK_WEB_SEARCH_QUERY.getTypeID(),
ARTIFACT_TYPE.TSK_MESSAGE.getTypeID(),
ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID(),
ARTIFACT_TYPE.TSK_CALLLOG.getTypeID(),
ARTIFACT_TYPE.TSK_DEVICE_ATTACHED.getTypeID(),
ARTIFACT_TYPE.TSK_WEB_HISTORY.getTypeID()
));
private static final Set<String> DEVICE_EXCLUDE_LIST = new HashSet<>(Arrays.asList("ROOT_HUB", "ROOT_HUB20"));
private static final Set<String> DOMAIN_EXCLUDE_LIST = new HashSet<>(Arrays.asList("127.0.0.1", "LOCALHOST"));
private static final long MS_PER_DAY = 1000 * 60 * 60 * 24;
private static final long DOMAIN_WINDOW_DAYS = 30;
private static final long DOMAIN_WINDOW_MS = DOMAIN_WINDOW_DAYS * MS_PER_DAY;
private final SleuthkitCaseProvider caseProvider;
private final TextTranslationService translationService;
private final java.util.logging.Logger logger;
/**
* A function to calculate a result from 2 parameters.
* Main constructor.
*/
interface Function2<A1, A2, O> {
public UserActivitySummary() {
this(SleuthkitCaseProvider.DEFAULT, TextTranslationService.getInstance(),
org.sleuthkit.autopsy.coreutils.Logger.getLogger(UserActivitySummary.class.getName()));
}
O apply(A1 a1, A2 a2);
/**
* Main constructor with external dependencies specified. This constructor
* is designed with unit testing in mind since mocked dependencies can be
* utilized.
*
* @param provider The object providing the current SleuthkitCase.
* @param translationService The translation service.
* @param logger The logger to use.
*/
public UserActivitySummary(
SleuthkitCaseProvider provider,
TextTranslationService translationService,
java.util.logging.Logger logger) {
this.caseProvider = provider;
this.translationService = translationService;
this.logger = logger;
}
@Override
public Set<Integer> getArtifactTypeIdsForRefresh() {
return ARTIFACT_UPDATE_TYPE_IDS;
}
/**
* Throws an IllegalArgumentException if count <= 0.
*
* @param count The count being checked.
*/
private void assertValidCount(int count) {
if (count <= 0) {
throw new IllegalArgumentException("Count must be greater than 0");
}
}
/**
@ -85,59 +146,134 @@ public class DataSourceUserActivitySummary {
*
* @throws InterruptedException
*/
public List<TopDomainsResult> getRecentDomains(DataSource dataSource, int count) throws InterruptedException {
Thread.sleep(SLEEP_TIME);
final String dId = Long.toString(dataSource.getId());
final Function2<String, Integer, String> getId = (s, idx) -> String.format("d:%s, f:%s, i:%d", dId, s, idx);
return IntStream.range(0, count)
.mapToObj(num -> new TopDomainsResult(
getId.apply("domain", num),
getId.apply("url", num),
(long) num,
new Date(((long) num) * 1000 * 60 * 60 * 24)
))
public List<TopDomainsResult> getRecentDomains(DataSource dataSource, int count) throws TskCoreException, SleuthkitCaseProviderException {
assertValidCount(count);
if (dataSource == null) {
return Collections.emptyList();
}
Pair<Long, Map<String, List<Long>>> mostRecentAndGroups = getDomainGroupsAndMostRecent(dataSource);
// if no recent domains, return accordingly
if (mostRecentAndGroups.getKey() == null || mostRecentAndGroups.getValue().size() == 0) {
return Collections.emptyList();
}
final long mostRecentMs = mostRecentAndGroups.getLeft();
Map<String, List<Long>> groups = mostRecentAndGroups.getRight();
return groups.entrySet().stream()
.map(entry -> getDomainsResult(entry.getKey(), entry.getValue(), mostRecentMs))
.filter(result -> result != null)
// sort by number of visit times in those 30 days (max to min)
.sorted((a, b) -> -Long.compare(a.getVisitTimes(), b.getVisitTimes()))
// limit the result number to the parameter provided
.limit(count)
.collect(Collectors.toList());
}
private final SleuthkitCaseProvider caseProvider;
private final TextTranslationService translationService;
private final java.util.logging.Logger logger;
/**
* Main constructor.
* Creates a TopDomainsResult from data or null if no visit date exists
* within DOMAIN_WINDOW_MS of mostRecentMs.
*
* @param domain The domain.
* @param visits The number of visits.
* @param mostRecentMs The most recent visit of any domain.
*
* @return The TopDomainsResult or null if no visits to this domain within
* 30 days of mostRecentMs.
*/
public DataSourceUserActivitySummary() {
this(SleuthkitCaseProvider.DEFAULT, TextTranslationService.getInstance(),
org.sleuthkit.autopsy.coreutils.Logger.getLogger(DataSourceUserActivitySummary.class.getName()));
private TopDomainsResult getDomainsResult(String domain, List<Long> visits, long mostRecentMs) {
long visitCount = 0;
Long thisMostRecentMs = null;
for (Long visitMs : visits) {
// make sure that visit is within window of mostRecentMS; otherwise skip it.
if (visitMs + DOMAIN_WINDOW_MS < mostRecentMs) {
continue;
}
// if visit is within window, increment the count and get most recent
visitCount++;
thisMostRecentMs = getMax(thisMostRecentMs, visitMs);
}
// if there are no visits within the window, return null
if (visitCount <= 0 || thisMostRecentMs == null) {
return null;
} else {
// create a top domain result with the domain, count, and most recent visit date
return new TopDomainsResult(domain, visitCount, new Date(thisMostRecentMs));
}
}
/**
* Main constructor with external dependencies specified. This constructor
* is designed with unit testing in mind since mocked dependencies can be
* utilized.
* Queries TSK_WEB_HISTORY artifacts and returning the latest web history
* date accessed and a mapping of domains to all of their visits.
*
* @param provider The object providing the current SleuthkitCase.
* @param translationService The translation service.
* @param logger The logger to use.
* @param dataSource The datasource.
*
* @return A tuple where the first value is the latest web history accessed
* date in milliseconds and the second value maps normalized
* (lowercase; trimmed) domain names to when those domains were
* visited.
*
* @throws TskCoreException
* @throws SleuthkitCaseProviderException
*/
public DataSourceUserActivitySummary(
SleuthkitCaseProvider provider,
TextTranslationService translationService,
java.util.logging.Logger logger) {
private Pair<Long, Map<String, List<Long>>> getDomainGroupsAndMostRecent(DataSource dataSource) throws TskCoreException, SleuthkitCaseProviderException {
List<BlackboardArtifact> artifacts = DataSourceInfoUtilities.getArtifacts(caseProvider.get(), TYPE_WEB_HISTORY,
dataSource, TYPE_DATETIME_ACCESSED, DataSourceInfoUtilities.SortOrder.DESCENDING, 0);
this.caseProvider = provider;
this.translationService = translationService;
this.logger = logger;
Long mostRecentMs = null;
Map<String, List<Long>> domainVisits = new HashMap<>();
for (BlackboardArtifact art : artifacts) {
Long artifactDateSecs = DataSourceInfoUtilities.getLongOrNull(art, TYPE_DATETIME_ACCESSED);
String domain = DataSourceInfoUtilities.getStringOrNull(art, TYPE_DOMAIN);
// if there isn't a last access date or domain for this artifact, it can be ignored.
// Also, ignore the loopback address.
if (artifactDateSecs == null || StringUtils.isBlank(domain) || DOMAIN_EXCLUDE_LIST.contains(domain.toUpperCase().trim())) {
continue;
}
Long artifactDateMs = artifactDateSecs * 1000;
// update the most recent visit date overall
mostRecentMs = getMax(mostRecentMs, artifactDateMs);
//Normalize the domain to lower case.
domain = domain.toLowerCase().trim();
// add this visit date to the list of dates for the domain
List<Long> domainVisitList = domainVisits.get(domain);
if (domainVisitList == null) {
domainVisitList = new ArrayList<>();
domainVisits.put(domain, domainVisitList);
}
domainVisitList.add(artifactDateMs);
}
return Pair.of(mostRecentMs, domainVisits);
}
/**
* Throws an IllegalArgumentException if count <= 0.
* Returns the maximum value given two longs handling possible null values.
*
* @param count The count being checked.
* @param num1 The first number.
* @param num2 The second number.
*
* @return The maximum non-null number or null if both numbers are null.
*/
private void assertValidCount(int count) {
if (count <= 0) {
throw new IllegalArgumentException("Count must be greater than 0");
private static Long getMax(Long num1, Long num2) {
if (num1 == null) {
return num2;
} else if (num2 == null) {
return num1;
} else {
return num2 > num1 ? num2 : num1;
}
}
@ -175,6 +311,10 @@ public class DataSourceUserActivitySummary {
public List<TopWebSearchResult> getMostRecentWebSearches(DataSource dataSource, int count) throws SleuthkitCaseProviderException, TskCoreException {
assertValidCount(count);
if (dataSource == null) {
return Collections.emptyList();
}
// get the artifacts
List<BlackboardArtifact> webSearchArtifacts = caseProvider.get().getBlackboard()
.getArtifacts(ARTIFACT_TYPE.TSK_WEB_SEARCH_QUERY.getTypeID(), dataSource.getId());
@ -183,7 +323,7 @@ public class DataSourceUserActivitySummary {
Collection<List<TopWebSearchResult>> resultGroups = webSearchArtifacts
.stream()
// get items where search string and date is not null
.map(DataSourceUserActivitySummary::getWebSearchResult)
.map(UserActivitySummary::getWebSearchResult)
// remove null records
.filter(result -> result != null)
// get these messages grouped by search to string
@ -259,6 +399,10 @@ public class DataSourceUserActivitySummary {
public List<TopDeviceAttachedResult> getRecentDevices(DataSource dataSource, int count) throws SleuthkitCaseProviderException, TskCoreException {
assertValidCount(count);
if (dataSource == null) {
return Collections.emptyList();
}
return DataSourceInfoUtilities.getArtifacts(caseProvider.get(), TYPE_DEVICE_ATTACHED,
dataSource, TYPE_DATETIME, DataSourceInfoUtilities.SortOrder.DESCENDING, 0)
.stream()
@ -273,7 +417,7 @@ public class DataSourceUserActivitySummary {
// remove Root Hub identifier
.filter(result -> {
return result.getDeviceModel() == null
|| !result.getDeviceModel().trim().toUpperCase().equals(ROOT_HUB_IDENTIFIER);
|| !DEVICE_EXCLUDE_LIST.contains(result.getDeviceModel().trim().toUpperCase());
})
.limit(count)
.collect(Collectors.toList());
@ -344,6 +488,10 @@ public class DataSourceUserActivitySummary {
public List<TopAccountResult> getRecentAccounts(DataSource dataSource, int count) throws SleuthkitCaseProviderException, TskCoreException {
assertValidCount(count);
if (dataSource == null) {
return Collections.emptyList();
}
Stream<TopAccountResult> messageResults = caseProvider.get().getBlackboard().getArtifacts(ARTIFACT_TYPE.TSK_MESSAGE.getTypeID(), dataSource.getId())
.stream()
.map((art) -> getMessageAccountResult(art));
@ -530,4 +678,48 @@ public class DataSourceUserActivitySummary {
return lastAccess;
}
}
/**
* Describes a result of a program run on a datasource.
*/
public static class TopDomainsResult {
private final String domain;
private final Long visitTimes;
private final Date lastVisit;
/**
* Describes a top domain result.
*
* @param domain The domain.
* @param visitTimes The number of times it was visited.
* @param lastVisit The date of the last visit.
*/
public TopDomainsResult(String domain, Long visitTimes, Date lastVisit) {
this.domain = domain;
this.visitTimes = visitTimes;
this.lastVisit = lastVisit;
}
/**
* @return The domain for the result.
*/
public String getDomain() {
return domain;
}
/**
* @return The number of times this site is visited.
*/
public Long getVisitTimes() {
return visitTimes;
}
/**
* @return The date of the last visit.
*/
public Date getLastVisit() {
return lastVisit;
}
}
}

View File

@ -57,6 +57,27 @@
<Property name="axis" type="int" value="3"/>
</Layout>
<SubComponents>
<Container class="javax.swing.JPanel" name="ingestRunningPanel">
<Properties>
<Property name="alignmentX" type="float" value="0.0"/>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[32767, 25]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[10, 25]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[10, 25]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="ingestRunningLabel"/>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
</Container>
<Component class="javax.swing.JLabel" name="hashsetHitsLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">

View File

@ -20,16 +20,17 @@ package org.sleuthkit.autopsy.datasourcesummary.ui;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.function.Function;
import org.apache.commons.lang3.tuple.Pair;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataSourceAnalysisSummary;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.AnalysisSummary;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.DefaultCellModel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel.ColumnModel;
import org.sleuthkit.autopsy.modules.hashdatabase.HashLookupModuleFactory;
import org.sleuthkit.autopsy.modules.interestingitems.InterestingItemsIngestModuleFactory;
import org.sleuthkit.datamodel.DataSource;
/**
@ -38,12 +39,22 @@ import org.sleuthkit.datamodel.DataSource;
*/
@Messages({
"AnalysisPanel_keyColumn_title=Name",
"AnalysisPanel_countColumn_title=Count"
"AnalysisPanel_countColumn_title=Count",
"AnalysisPanel_keywordSearchModuleName=Keyword Search"
})
public class AnalysisPanel extends BaseDataSourceSummaryPanel {
private static final long serialVersionUID = 1L;
private static final String KEYWORD_SEARCH_MODULE_NAME = Bundle.AnalysisPanel_keywordSearchModuleName();
private static final String KEYWORD_SEARCH_FACTORY = "org.sleuthkit.autopsy.keywordsearch.KeywordSearchModuleFactory";
private static final String INTERESTING_ITEM_MODULE_NAME = new InterestingItemsIngestModuleFactory().getModuleDisplayName();
private static final String INTERESTING_ITEM_FACTORY = InterestingItemsIngestModuleFactory.class.getCanonicalName();
private static final String HASHSET_MODULE_NAME = HashLookupModuleFactory.getModuleName();
private static final String HASHSET_FACTORY = HashLookupModuleFactory.class.getCanonicalName();
/**
* Default Column definitions for each table
*/
@ -60,11 +71,19 @@ public class AnalysisPanel extends BaseDataSourceSummaryPanel {
)
);
private final JTablePanel<Pair<String, Long>> hashsetHitsTable = JTablePanel.getJTablePanel(DEFAULT_COLUMNS);
private static final Function<Pair<String, Long>, String> DEFAULT_KEY_PROVIDER = (pair) -> pair.getKey();
private final JTablePanel<Pair<String, Long>> keywordHitsTable = JTablePanel.getJTablePanel(DEFAULT_COLUMNS);
private final JTablePanel<Pair<String, Long>> hashsetHitsTable
= JTablePanel.getJTablePanel(DEFAULT_COLUMNS)
.setKeyFunction(DEFAULT_KEY_PROVIDER);
private final JTablePanel<Pair<String, Long>> interestingItemsTable = JTablePanel.getJTablePanel(DEFAULT_COLUMNS);
private final JTablePanel<Pair<String, Long>> keywordHitsTable
= JTablePanel.getJTablePanel(DEFAULT_COLUMNS)
.setKeyFunction(DEFAULT_KEY_PROVIDER);
private final JTablePanel<Pair<String, Long>> interestingItemsTable
= JTablePanel.getJTablePanel(DEFAULT_COLUMNS)
.setKeyFunction(DEFAULT_KEY_PROVIDER);
private final List<JTablePanel<?>> tables = Arrays.asList(
hashsetHitsTable,
@ -72,6 +91,9 @@ public class AnalysisPanel extends BaseDataSourceSummaryPanel {
interestingItemsTable
);
private final IngestRunningLabel ingestRunningLabel = new IngestRunningLabel();
/**
* All of the components necessary for data fetch swing workers to load data
* for each table.
@ -82,50 +104,47 @@ public class AnalysisPanel extends BaseDataSourceSummaryPanel {
* Creates a new DataSourceUserActivityPanel.
*/
public AnalysisPanel() {
this(new DataSourceAnalysisSummary());
this(new AnalysisSummary());
}
public AnalysisPanel(DataSourceAnalysisSummary analysisData) {
public AnalysisPanel(AnalysisSummary analysisData) {
super(analysisData);
// set up data acquisition methods
dataFetchComponents = Arrays.asList(
// hashset hits loading components
new DataFetchWorker.DataFetchComponents<>(
(dataSource) -> analysisData.getHashsetCounts(dataSource),
(result) -> hashsetHitsTable.showDataFetchResult(result)),
(result) -> showResultWithModuleCheck(hashsetHitsTable, result, HASHSET_FACTORY, HASHSET_MODULE_NAME)),
// keyword hits loading components
new DataFetchWorker.DataFetchComponents<>(
(dataSource) -> analysisData.getKeywordCounts(dataSource),
(result) -> keywordHitsTable.showDataFetchResult(result)),
(result) -> showResultWithModuleCheck(keywordHitsTable, result, KEYWORD_SEARCH_FACTORY, KEYWORD_SEARCH_MODULE_NAME)),
// interesting item hits loading components
new DataFetchWorker.DataFetchComponents<>(
(dataSource) -> analysisData.getInterestingItemCounts(dataSource),
(result) -> interestingItemsTable.showDataFetchResult(result))
(result) -> showResultWithModuleCheck(interestingItemsTable, result, INTERESTING_ITEM_FACTORY, INTERESTING_ITEM_MODULE_NAME))
);
initComponents();
}
@Override
public void close() {
ingestRunningLabel.unregister();
super.close();
}
@Override
protected void fetchInformation(DataSource dataSource) {
fetchInformation(dataFetchComponents, dataSource);
}
@Override
protected void onNewDataSource(DataSource dataSource) {
// if no data source is present or the case is not open,
// set results for tables to null.
if (dataSource == null || !Case.isCaseOpen()) {
this.dataFetchComponents.forEach((item) -> item.getResultHandler()
.accept(DataFetchResult.getSuccessResult(null)));
} else {
// set tables to display loading screen
this.tables.forEach((table) -> table.showDefaultLoadingMessage());
// create swing workers to run for each table
List<DataFetchWorker<?, ?>> workers = dataFetchComponents
.stream()
.map((components) -> new DataFetchWorker<>(components, dataSource))
.collect(Collectors.toList());
// submit swing workers to run
submit(workers);
}
onNewDataSource(dataFetchComponents, tables, dataSource);
}
/**
@ -139,6 +158,7 @@ public class AnalysisPanel extends BaseDataSourceSummaryPanel {
javax.swing.JScrollPane mainScrollPane = new javax.swing.JScrollPane();
javax.swing.JPanel mainContentPanel = new javax.swing.JPanel();
javax.swing.JPanel ingestRunningPanel = ingestRunningLabel;
javax.swing.JLabel hashsetHitsLabel = new javax.swing.JLabel();
javax.swing.Box.Filler filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2), new java.awt.Dimension(32767, 2));
javax.swing.JPanel hashSetHitsPanel = hashsetHitsTable;
@ -157,6 +177,12 @@ public class AnalysisPanel extends BaseDataSourceSummaryPanel {
mainContentPanel.setMinimumSize(new java.awt.Dimension(200, 452));
mainContentPanel.setLayout(new javax.swing.BoxLayout(mainContentPanel, javax.swing.BoxLayout.PAGE_AXIS));
ingestRunningPanel.setAlignmentX(0.0F);
ingestRunningPanel.setMaximumSize(new java.awt.Dimension(32767, 25));
ingestRunningPanel.setMinimumSize(new java.awt.Dimension(10, 25));
ingestRunningPanel.setPreferredSize(new java.awt.Dimension(10, 25));
mainContentPanel.add(ingestRunningPanel);
org.openide.awt.Mnemonics.setLocalizedText(hashsetHitsLabel, org.openide.util.NbBundle.getMessage(AnalysisPanel.class, "AnalysisPanel.hashsetHitsLabel.text")); // NOI18N
mainContentPanel.add(hashsetHitsLabel);
mainContentPanel.add(filler1);
@ -204,6 +230,7 @@ public class AnalysisPanel extends BaseDataSourceSummaryPanel {
);
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
// End of variables declaration//GEN-END:variables
}

View File

@ -18,11 +18,37 @@
*/
package org.sleuthkit.autopsy.datasourcesummary.ui;
import java.beans.PropertyChangeEvent;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
import java.util.logging.Level;
import java.util.stream.Collectors;
import javax.swing.JPanel;
import javax.swing.SwingWorker;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.IngestModuleCheckUtil;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult.ResultType;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.EventUpdateHandler;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableComponent;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.SwingWorkerSequentialExecutor;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.UpdateGovernor;
import org.sleuthkit.autopsy.ingest.IngestManager.IngestJobEvent;
import org.sleuthkit.autopsy.ingest.ModuleContentEvent;
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Base class from which other tabs in data source summary derive.
@ -31,9 +57,184 @@ abstract class BaseDataSourceSummaryPanel extends JPanel {
private static final long serialVersionUID = 1L;
private static final Logger logger = Logger.getLogger(BaseDataSourceSummaryPanel.class.getName());
private final SwingWorkerSequentialExecutor executor = new SwingWorkerSequentialExecutor();
private final IngestModuleCheckUtil ingestModuleCheck = new IngestModuleCheckUtil();
private final EventUpdateHandler updateHandler;
private final List<UpdateGovernor> governors;
private DataSource dataSource;
/**
* In charge of determining when an update is necessary. In instances where
* a datasource is concerned, this checks to see if the datasource is the
* current datasource. Otherwise, it delegates to the underlying governors
* for the object.
*/
private final UpdateGovernor updateGovernor = new UpdateGovernor() {
/**
* Checks to see if artifact is from a datasource.
*
* @param art The artifact.
* @param ds The datasource.
*
* @return True if in datasource; false if not or exception.
*/
private boolean isInDataSource(BlackboardArtifact art, DataSource ds) {
try {
return (art.getDataSource() != null && art.getDataSource().getId() == ds.getId());
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "There was an error fetching datasource for artifact.", ex);
return false;
}
}
@Override
public boolean isRefreshRequired(ModuleDataEvent evt) {
DataSource ds = getDataSource();
// make sure there is an event.
if (ds == null || evt == null) {
return false;
}
//if there are no artifacts with matching datasource, return
// if no artifacts are present, pass it on just in case there was something wrong with ModuleDataEvent
if (evt.getArtifacts() != null
&& !evt.getArtifacts().isEmpty()
&& !evt.getArtifacts().stream().anyMatch((art) -> isInDataSource(art, ds))) {
return false;
}
// otherwise, see if there is something that wants updates
for (UpdateGovernor governor : governors) {
if (governor.isRefreshRequired(evt)) {
return true;
}
}
return false;
}
@Override
public boolean isRefreshRequired(ModuleContentEvent evt) {
DataSource ds = getDataSource();
// make sure there is an event.
if (ds == null || evt == null) {
return false;
}
try {
// if the underlying content has a datasource and that datasource != the
// current datasource, return false
if (evt.getSource() instanceof Content
&& ((Content) evt.getSource()).getDataSource() != null
&& ((Content) evt.getSource()).getDataSource().getId() != ds.getId()) {
return false;
}
} catch (TskCoreException ex) {
// on an exception, keep going for tolerance sake
logger.log(Level.WARNING, "There was an error fetching datasource for content.", ex);
}
for (UpdateGovernor governor : governors) {
if (governor.isRefreshRequired(evt)) {
return true;
}
}
return false;
}
@Override
public boolean isRefreshRequired(AbstractFile file) {
DataSource currentDataSource = getDataSource();
if (currentDataSource == null || file == null) {
return false;
}
// make sure the file is for the current data source
Long fileDsId = null;
try {
Content fileDataSource = file.getDataSource();
fileDsId = fileDataSource.getId();
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Unable to get the datasource for newly added file", ex);
}
if (fileDsId != null && currentDataSource.getId() == fileDsId) {
for (UpdateGovernor governor : governors) {
if (governor.isRefreshRequired(file)) {
return true;
}
}
}
return false;
}
@Override
public boolean isRefreshRequired(IngestJobEvent evt) {
for (UpdateGovernor governor : governors) {
if (governor.isRefreshRequired(evt)) {
return true;
}
}
return false;
}
@Override
public boolean isRefreshRequiredForCaseEvent(PropertyChangeEvent evt) {
for (UpdateGovernor governor : governors) {
if (governor.isRefreshRequiredForCaseEvent(evt)) {
return true;
}
}
return false;
}
@Override
public Set<Case.Events> getCaseEventUpdates() {
// return the union of all case events sets from delegates.
return governors.stream()
.filter(governor -> governor.getCaseEventUpdates() != null)
.flatMap(governor -> governor.getCaseEventUpdates().stream())
.collect(Collectors.toSet());
}
@Override
public Set<IngestJobEvent> getIngestJobEventUpdates() {
// return the union of all case events sets from delegates.
return governors.stream()
.filter(governor -> governor.getIngestJobEventUpdates() != null)
.flatMap(governor -> governor.getIngestJobEventUpdates().stream())
.collect(Collectors.toSet());
}
};
/**
* Main constructor.
*
* @param governors The items governing when this panel should receive
* updates.
*/
protected BaseDataSourceSummaryPanel(UpdateGovernor... governors) {
this.governors = (governors == null) ? Collections.emptyList() : Arrays.asList(governors);
this.updateHandler = new EventUpdateHandler(this::onRefresh, updateGovernor);
this.updateHandler.register();
}
/**
* Closes listeners and resources.
*/
public void close() {
executor.cancelRunning();
updateHandler.unregister();
}
/**
* Sets datasource to visualize in the panel.
*
@ -45,6 +246,13 @@ abstract class BaseDataSourceSummaryPanel extends JPanel {
onNewDataSource(this.dataSource);
}
/**
* @return The current data source.
*/
protected synchronized DataSource getDataSource() {
return this.dataSource;
}
/**
* Submits the following swing workers for execution in sequential order. If
* there are any previous workers, those workers are cancelled.
@ -55,10 +263,155 @@ abstract class BaseDataSourceSummaryPanel extends JPanel {
executor.submit(workers);
}
/**
* When a data source is updated this function is triggered.
*
* @param dataSource The data source.
*/
synchronized void onRefresh() {
// trigger on new data source with the current data source
fetchInformation(this.dataSource);
}
/**
* Action that is called when information needs to be retrieved (on refresh
* or on new data source).
*
* @param dataSource The datasource to fetch information about.
*/
protected abstract void fetchInformation(DataSource dataSource);
/**
* Utility method to be called when solely updating information (not showing
* a loading screen) that creates swing workers from the data source
* argument and data fetch components and then submits them to run.
*
* @param dataFetchComponents The components to be run.
* @param dataSource The data source argument.
*/
protected void fetchInformation(List<DataFetchComponents<DataSource, ?>> dataFetchComponents, DataSource dataSource) {
if (dataSource == null || !Case.isCaseOpen()) {
dataFetchComponents.forEach((item) -> item.getResultHandler()
.accept(DataFetchResult.getSuccessResult(null)));
} else {
// create swing workers to run for each loadable item
List<DataFetchWorker<?, ?>> workers = dataFetchComponents
.stream()
.map((components) -> new DataFetchWorker<>(components, dataSource))
.collect(Collectors.toList());
// submit swing workers to run
if (!workers.isEmpty()) {
submit(workers);
}
}
}
/**
* When a new dataSource is added, this method is called.
*
* @param dataSource The new dataSource.
*/
protected abstract void onNewDataSource(DataSource dataSource);
/**
* Utility method that shows a loading screen with loadable components,
* create swing workers from the datafetch components and data source
* argument and submits them to be executed.
*
* @param dataFetchComponents The components to register.
* @param loadableComponents The components to set to a loading screen.
* @param dataSource The data source argument.
*/
protected void onNewDataSource(
List<DataFetchComponents<DataSource, ?>> dataFetchComponents,
List<? extends LoadableComponent<?>> loadableComponents,
DataSource dataSource) {
// if no data source is present or the case is not open,
// set results for tables to null.
if (dataSource == null || !Case.isCaseOpen()) {
dataFetchComponents.forEach((item) -> item.getResultHandler()
.accept(DataFetchResult.getSuccessResult(null)));
} else {
// set tables to display loading screen
loadableComponents.forEach((table) -> table.showDefaultLoadingMessage());
fetchInformation(dataSource);
}
}
/**
* Get default message when there is a NotIngestedWithModuleException.
*
* @param moduleName The moduleName.
*
* @return Message specifying that the ingest module was not run.
*/
@Messages({
"# {0} - module name",
"BaseDataSourceSummaryPanel_defaultNotIngestMessage=The {0} ingest module has not been run on this data source."
})
protected String getDefaultNoIngestMessage(String moduleName) {
return Bundle.BaseDataSourceSummaryPanel_defaultNotIngestMessage(moduleName);
}
/**
* Utility method to return the IngestModuleCheckUtil.
*
* @return The IngestModuleCheckUtil.
*/
protected IngestModuleCheckUtil getIngestModuleCheckUtil() {
return this.ingestModuleCheck;
}
/**
* Utility method that in the event of a) there are no results and b) a
* relevant ingest module has not been run on this datasource, then a
* message indicating the unrun ingest module will be shown. Otherwise, the
* default LoadableComponent.showDataFetchResult behavior will be used.
*
* @param component The component.
* @param result The data result.
* @param factoryClass The fully qualified class name of the relevant
* factory.
* @param moduleName The name of the ingest module (i.e. 'Keyword
* Search').
*/
protected <T> void showResultWithModuleCheck(LoadableComponent<List<T>> component, DataFetchResult<List<T>> result, String factoryClass, String moduleName) {
Predicate<List<T>> hasResults = (lst) -> lst != null && !lst.isEmpty();
showResultWithModuleCheck(component, result, hasResults, factoryClass, moduleName);
}
/**
* Utility method that in the event of a) there are no results and b) a
* relevant ingest module has not been run on this datasource, then a
* message indicating the unrun ingest module will be shown. Otherwise, the
* default LoadableComponent.showDataFetchResult behavior will be used.
*
* @param component The component.
* @param result The data result.
* @param hasResults Given the data type, will provide whether or not the
* data contains any actual results.
* @param factoryClass The fully qualified class name of the relevant
* factory.
* @param moduleName The name of the ingest module (i.e. 'Keyword
* Search').
*/
protected <T> void showResultWithModuleCheck(LoadableComponent<T> component, DataFetchResult<T> result,
Predicate<T> hasResults, String factoryClass, String moduleName) {
if (result != null && result.getResultType() == ResultType.SUCCESS && !hasResults.test(result.getData())) {
try {
if (!ingestModuleCheck.isModuleIngested(getDataSource(), factoryClass)) {
component.showMessage(getDefaultNoIngestMessage(moduleName));
return;
}
} catch (TskCoreException | SleuthkitCaseProviderException ex) {
logger.log(Level.WARNING, "There was an error while checking for ingest modules for datasource.", ex);
}
}
component.showDataFetchResult(result);
}
}

View File

@ -1,43 +1,44 @@
DataSourceSummaryDialog.closeButton.text=Close
DataSourceSummaryDetailsPanel.displayNameLabel.text=Display Name:
DataSourceSummaryDetailsPanel.originalNameLabel.text=Name:
DataSourceSummaryDetailsPanel.deviceIdLabel.text=Device ID:
DataSourceSummaryDetailsPanel.timeZoneLabel.text=Time Zone:
DataSourceSummaryDetailsPanel.imageTypeLabel.text=Image Type:
DataSourceSummaryDetailsPanel.sizeLabel.text=Size:
DataSourceSummaryDetailsPanel.sectorSizeLabel.text=Sector Size:
DataSourceSummaryDetailsPanel.md5HashLabel.text=MD5:
DataSourceSummaryDetailsPanel.sha1HashLabel.text=SHA1:
DataSourceSummaryDetailsPanel.sha256HashLabel.text=SHA256:
DataSourceSummaryDetailsPanel.filePathsLabel.text=File Paths:
DataSourceSummaryDetailsPanel.displayNameValue.text=
DataSourceSummaryDetailsPanel.originalNameValue.text=
DataSourceSummaryDetailsPanel.deviceIdValue.toolTipText=
DataSourceSummaryDetailsPanel.deviceIdValue.text=
DataSourceSummaryDetailsPanel.timeZoneValue.text=
DataSourceSummaryDetailsPanel.imageTypeValue.toolTipText=
DataSourceSummaryDetailsPanel.imageTypeValue.text=
DataSourceSummaryDetailsPanel.sizeValue.text=
DataSourceSummaryDetailsPanel.sectorSizeValue.text=
DataSourceSummaryDetailsPanel.md5HashValue.toolTipText=
DataSourceSummaryDetailsPanel.md5HashValue.text=
DataSourceSummaryDetailsPanel.sha1HashValue.text=
DataSourceSummaryDetailsPanel.sha256HashValue.text=
DataSourceSummaryDetailsPanel.filePathsTable.columnModel.title0=
DataSourceSummaryDetailsPanel.acquisitionDetailsTextArea.text=
DataSourceSummaryDetailsPanel.acquisitionDetailsLabel.text=Acquisition Details:
DataSourceSummaryDetailsPanel.unallocatedSizeLabel.text=Unallocated Space:
DataSourceSummaryDetailsPanel.unallocatedSizeValue.text=
DataSourceSummaryCountsPanel.byCategoryLabel.text=Files by Category
DataSourceSummaryCountsPanel.resultsByTypeLabel.text=Results by Type
DataSourceSummaryUserActivityPanel.programsRunLabel.text=Recent Programs
DataSourceSummaryUserActivityPanel.recentAccountsLabel.text=Recent Accounts
DataSourceSummaryUserActivityPanel.topWebSearchLabel.text=Recent Web Searches
DataSourceSummaryUserActivityPanel.topDevicesAttachedLabel.text=Recent Devices Attached
DataSourceSummaryUserActivityPanel.recentDomainsLabel.text=Recent Domains
ContainerPanel.displayNameLabel.text=Display Name:
ContainerPanel.originalNameLabel.text=Name:
ContainerPanel.deviceIdLabel.text=Device ID:
ContainerPanel.timeZoneLabel.text=Time Zone:
ContainerPanel.imageTypeLabel.text=Image Type:
ContainerPanel.sizeLabel.text=Size:
ContainerPanel.sectorSizeLabel.text=Sector Size:
ContainerPanel.md5HashLabel.text=MD5:
ContainerPanel.sha1HashLabel.text=SHA1:
ContainerPanel.sha256HashLabel.text=SHA256:
ContainerPanel.filePathsLabel.text=File Paths:
ContainerPanel.displayNameValue.text=
ContainerPanel.originalNameValue.text=
ContainerPanel.deviceIdValue.toolTipText=
ContainerPanel.deviceIdValue.text=
ContainerPanel.timeZoneValue.text=
ContainerPanel.imageTypeValue.toolTipText=
ContainerPanel.imageTypeValue.text=
ContainerPanel.sizeValue.text=
ContainerPanel.sectorSizeValue.text=
ContainerPanel.md5HashValue.toolTipText=
ContainerPanel.md5HashValue.text=
ContainerPanel.sha1HashValue.text=
ContainerPanel.sha256HashValue.text=
ContainerPanel.filePathsTable.columnModel.title0=
ContainerPanel.acquisitionDetailsTextArea.text=
ContainerPanel.acquisitionDetailsLabel.text=Acquisition Details:
ContainerPanel.unallocatedSizeLabel.text=Unallocated Space:
ContainerPanel.unallocatedSizeValue.text=
UserActivityPanel.programsRunLabel.text=Recent Programs
UserActivityPanel.recentAccountsLabel.text=Recent Account Types Used
UserActivityPanel.topWebSearchLabel.text=Recent Web Searches
UserActivityPanel.topDevicesAttachedLabel.text=Recent Devices Attached
UserActivityPanel.recentDomainsLabel.text=Recent Domains
AnalysisPanel.hashsetHitsLabel.text=Hashset Hits
AnalysisPanel.keywordHitsLabel.text=Keyword Hits
AnalysisPanel.interestingItemLabel.text=Interesting Item Hits
RecentFilesPanel.openDocsLabel.text=Recently Opened Documents
RecentFilesPanel.downloadLabel.text=Recent Downloads
RecentFilesPanel.attachmentLabel.text=Recent Attachements
RecentFilesPanel.attachmentLabel.text=Recent Attachments
PastCasesPanel.notableFileLabel.text=Cases with Common Items That Were Tagged as Notable
PastCasesPanel.sameIdLabel.text=Past Cases with the Same Device IDs
DataSourceSummaryTabbedPane.noDataSourceLabel.text=No data source has been selected.

View File

@ -1,63 +1,39 @@
AnalysisPanel_countColumn_title=Count
AnalysisPanel_keyColumn_title=Name
AnalysisPanel_keywordSearchModuleName=Keyword Search
# {0} - module name
BaseDataSourceSummaryPanel_defaultNotIngestMessage=The {0} ingest module has not been run on this data source.
CTL_DataSourceSummaryAction=Data Source Summary
DataSourceSummaryCountsPanel.ArtifactCountsTableModel.count.header=Count
DataSourceSummaryCountsPanel.ArtifactCountsTableModel.type.header=Result Type
DataSourceSummaryCountsPanel.byMimeTypeLabel.text=Files by MIME Type
DataSourceSummaryCountsPanel.FilesByCategoryTableModel.all.row=All
DataSourceSummaryCountsPanel.FilesByCategoryTableModel.allocated.row=Allocated
DataSourceSummaryCountsPanel.FilesByCategoryTableModel.count.header=Count
DataSourceSummaryCountsPanel.FilesByCategoryTableModel.directory.row=Directory
DataSourceSummaryCountsPanel.FilesByCategoryTableModel.slack.row=Slack
DataSourceSummaryCountsPanel.FilesByCategoryTableModel.type.header=File Type
DataSourceSummaryCountsPanel.FilesByCategoryTableModel.unallocated.row=Unallocated
DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.audio.row=Audio
DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.documents.row=Documents
DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.executables.row=Executables
DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.images.row=Images
DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.videos.row=Videos
DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_notAnalyzed_label=Not Analyzed
DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_other_label=Other
DataSourceSummaryDetailsPanel.getDataSources.error.text=Failed to get the list of datasources for the current case.
DataSourceSummaryDetailsPanel.getDataSources.error.title=Load Failure
DataSourceSummaryDetailsPanel.units.bytes=\ bytes
DataSourceSummaryDetailsPanel.units.gigabytes=\ GB
DataSourceSummaryDetailsPanel.units.kilobytes=\ kB
DataSourceSummaryDetailsPanel.units.megabytes=\ MB
DataSourceSummaryDetailsPanel.units.petabytes=\ PB
DataSourceSummaryDetailsPanel.units.terabytes=\ TB
DataSourceSummaryDialog.closeButton.text=Close
DataSourceSummaryDetailsPanel.displayNameLabel.text=Display Name:
DataSourceSummaryDetailsPanel.originalNameLabel.text=Name:
DataSourceSummaryDetailsPanel.deviceIdLabel.text=Device ID:
DataSourceSummaryDetailsPanel.timeZoneLabel.text=Time Zone:
DataSourceSummaryDetailsPanel.imageTypeLabel.text=Image Type:
DataSourceSummaryDetailsPanel.sizeLabel.text=Size:
DataSourceSummaryDetailsPanel.sectorSizeLabel.text=Sector Size:
DataSourceSummaryDetailsPanel.md5HashLabel.text=MD5:
DataSourceSummaryDetailsPanel.sha1HashLabel.text=SHA1:
DataSourceSummaryDetailsPanel.sha256HashLabel.text=SHA256:
DataSourceSummaryDetailsPanel.filePathsLabel.text=File Paths:
DataSourceSummaryDetailsPanel.displayNameValue.text=
DataSourceSummaryDetailsPanel.originalNameValue.text=
DataSourceSummaryDetailsPanel.deviceIdValue.toolTipText=
DataSourceSummaryDetailsPanel.deviceIdValue.text=
DataSourceSummaryDetailsPanel.timeZoneValue.text=
DataSourceSummaryDetailsPanel.imageTypeValue.toolTipText=
DataSourceSummaryDetailsPanel.imageTypeValue.text=
DataSourceSummaryDetailsPanel.sizeValue.text=
DataSourceSummaryDetailsPanel.sectorSizeValue.text=
DataSourceSummaryDetailsPanel.md5HashValue.toolTipText=
DataSourceSummaryDetailsPanel.md5HashValue.text=
DataSourceSummaryDetailsPanel.sha1HashValue.text=
DataSourceSummaryDetailsPanel.sha256HashValue.text=
DataSourceSummaryDetailsPanel.filePathsTable.columnModel.title0=
DataSourceSummaryDetailsPanel.acquisitionDetailsTextArea.text=
DataSourceSummaryDetailsPanel.acquisitionDetailsLabel.text=Acquisition Details:
DataSourceSummaryDetailsPanel.unallocatedSizeLabel.text=Unallocated Space:
DataSourceSummaryDetailsPanel.unallocatedSizeValue.text=
DataSourceSummaryCountsPanel.byCategoryLabel.text=Files by Category
DataSourceSummaryCountsPanel.resultsByTypeLabel.text=Results by Type
ContainerPanel.displayNameLabel.text=Display Name:
ContainerPanel.originalNameLabel.text=Name:
ContainerPanel.deviceIdLabel.text=Device ID:
ContainerPanel.timeZoneLabel.text=Time Zone:
ContainerPanel.imageTypeLabel.text=Image Type:
ContainerPanel.sizeLabel.text=Size:
ContainerPanel.sectorSizeLabel.text=Sector Size:
ContainerPanel.md5HashLabel.text=MD5:
ContainerPanel.sha1HashLabel.text=SHA1:
ContainerPanel.sha256HashLabel.text=SHA256:
ContainerPanel.filePathsLabel.text=File Paths:
ContainerPanel.displayNameValue.text=
ContainerPanel.originalNameValue.text=
ContainerPanel.deviceIdValue.toolTipText=
ContainerPanel.deviceIdValue.text=
ContainerPanel.timeZoneValue.text=
ContainerPanel.imageTypeValue.toolTipText=
ContainerPanel.imageTypeValue.text=
ContainerPanel.sizeValue.text=
ContainerPanel.sectorSizeValue.text=
ContainerPanel.md5HashValue.toolTipText=
ContainerPanel.md5HashValue.text=
ContainerPanel.sha1HashValue.text=
ContainerPanel.sha256HashValue.text=
ContainerPanel.filePathsTable.columnModel.title0=
ContainerPanel.acquisitionDetailsTextArea.text=
ContainerPanel.acquisitionDetailsLabel.text=Acquisition Details:
ContainerPanel.unallocatedSizeLabel.text=Unallocated Space:
ContainerPanel.unallocatedSizeValue.text=
DataSourceSummaryDialog.window.title=Data Sources Summary
DataSourceSummaryNode.column.dataSourceName.header=Data Source Name
DataSourceSummaryNode.column.files.header=Files
@ -67,42 +43,73 @@ DataSourceSummaryNode.column.tags.header=Tags
DataSourceSummaryNode.column.type.header=Type
DataSourceSummaryNode.viewDataSourceAction.text=Go to Data Source
DataSourceSummaryTabbedPane_analysisTab_title=Analysis
DataSourceSummaryTabbedPane_countsTab_title=Counts
DataSourceSummaryTabbedPane_detailsTab_title=Container
DataSourceSummaryTabbedPane_ingestHistoryTab_title=Ingest History
DataSourceSummaryTabbedPane_pastCasesTab_title=Past Cases
DataSourceSummaryTabbedPane_recentFileTab_title=Recent Files
DataSourceSummaryTabbedPane_typesTab_title=Types
DataSourceSummaryTabbedPane_userActivityTab_title=User Activity
DataSourceSummaryUserActivityPanel.programsRunLabel.text=Recent Programs
DataSourceSummaryUserActivityPanel.recentAccountsLabel.text=Recent Accounts
DataSourceSummaryUserActivityPanel.topWebSearchLabel.text=Recent Web Searches
DataSourceSummaryUserActivityPanel.topDevicesAttachedLabel.text=Recent Devices Attached
DataSourceSummaryUserActivityPanel.recentDomainsLabel.text=Recent Domains
AnalysisPanel.hashsetHitsLabel.text=Hashset Hits
AnalysisPanel.keywordHitsLabel.text=Keyword Hits
AnalysisPanel.interestingItemLabel.text=Interesting Item Hits
DataSourceSummaryUserActivityPanel_noDataExists=No communication data exists
DataSourceSummaryUserActivityPanel_tab_title=User Activity
DataSourceSummaryUserActivityPanel_TopAccountTableModel_accountType_header=Account Type
DataSourceSummaryUserActivityPanel_TopAccountTableModel_lastAccess_header=Last Accessed
DataSourceSummaryUserActivityPanel_TopDeviceAttachedTableModel_dateAccessed_header=Last Accessed
DataSourceSummaryUserActivityPanel_TopDeviceAttachedTableModel_deviceId_header=Device Id
DataSourceSummaryUserActivityPanel_TopDeviceAttachedTableModel_makeModel_header=Make and Model
DataSourceSummaryUserActivityPanel_TopDomainsTableModel_domain_header=Domain
DataSourceSummaryUserActivityPanel_TopDomainsTableModel_lastAccess_header=Last Access
DataSourceSummaryUserActivityPanel_TopDomainsTableModel_url_header=URL
DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header=Run Times
DataSourceSummaryUserActivityPanel_TopProgramsTableModel_folder_header=Folder
DataSourceSummaryUserActivityPanel_TopProgramsTableModel_lastrun_header=Last Run
DataSourceSummaryUserActivityPanel_TopProgramsTableModel_name_header=Program
DataSourceSummaryUserActivityPanel_TopWebSearchTableModel_dateAccessed_header=Date Accessed
DataSourceSummaryUserActivityPanel_TopWebSearchTableModel_searchString_header=Search String
DataSourceSummaryUserActivityPanel_TopWebSearchTableModel_translatedResult_header=Translated
PastCasesPanel_caseColumn_title=Case
PastCasesPanel_countColumn_title=Count
PastCasesPanel_onNoCrIngest_message=No results will be shown because the Central Repository module was not run.
RecentFilePanel_col_header_domain=Domain
RecentFilePanel_col_header_path=Path
RecentFilePanel_col_header_sender=Sender
RecentFilePanel_emailParserModuleName=Email Parser
RecentFilePanel_no_open_documents=No recently open documents found.
RecentFilesPanel_col_head_date=Date
SizeRepresentationUtil_units_bytes=\ bytes
SizeRepresentationUtil_units_gigabytes=\ GB
SizeRepresentationUtil_units_kilobytes=\ kB
SizeRepresentationUtil_units_megabytes=\ MB
SizeRepresentationUtil_units_petabytes=\ PB
SizeRepresentationUtil_units_terabytes=\ TB
TypesPanel_artifactsTypesPieChart_title=Artifact Types
TypesPanel_fileMimeTypesChart_audio_title=Audio
TypesPanel_fileMimeTypesChart_documents_title=Documents
TypesPanel_fileMimeTypesChart_executables_title=Executables
TypesPanel_fileMimeTypesChart_images_title=Images
TypesPanel_fileMimeTypesChart_notAnalyzed_title=Not Analyzed
TypesPanel_fileMimeTypesChart_other_title=Other
TypesPanel_fileMimeTypesChart_title=File Types
TypesPanel_fileMimeTypesChart_unknown_title=Unknown
TypesPanel_fileMimeTypesChart_videos_title=Videos
TypesPanel_filesByCategoryTable_allocatedRow_title=Allocated Files
TypesPanel_filesByCategoryTable_directoryRow_title=Directories
TypesPanel_filesByCategoryTable_slackRow_title=Slack Files
TypesPanel_filesByCategoryTable_unallocatedRow_title=Unallocated Files
TypesPanel_osLabel_title=OS
TypesPanel_sizeLabel_title=Size
TypesPanel_usageLabel_title=Usage
UserActivityPanel.programsRunLabel.text=Recent Programs
UserActivityPanel.recentAccountsLabel.text=Recent Account Types Used
UserActivityPanel.topWebSearchLabel.text=Recent Web Searches
UserActivityPanel.topDevicesAttachedLabel.text=Recent Devices Attached
UserActivityPanel.recentDomainsLabel.text=Recent Domains
AnalysisPanel.hashsetHitsLabel.text=Hashset Hits
AnalysisPanel.keywordHitsLabel.text=Keyword Hits
AnalysisPanel.interestingItemLabel.text=Interesting Item Hits
RecentFilesPanel.openDocsLabel.text=Recently Opened Documents
RecentFilesPanel.downloadLabel.text=Recent Downloads
RecentFilesPanel.attachmentLabel.text=Recent Attachements
RecentFilesPanel_col_head_date=Date
RecentFilesPanel.attachmentLabel.text=Recent Attachments
PastCasesPanel.notableFileLabel.text=Cases with Common Items That Were Tagged as Notable
PastCasesPanel.sameIdLabel.text=Past Cases with the Same Device IDs
DataSourceSummaryTabbedPane.noDataSourceLabel.text=No data source has been selected.
UserActivityPanel_noDataExists=No communication data exists
UserActivityPanel_tab_title=User Activity
UserActivityPanel_TopAccountTableModel_accountType_header=Account Type
UserActivityPanel_TopAccountTableModel_lastAccess_header=Last Accessed
UserActivityPanel_TopDeviceAttachedTableModel_dateAccessed_header=Last Accessed
UserActivityPanel_TopDeviceAttachedTableModel_deviceId_header=Device Id
UserActivityPanel_TopDeviceAttachedTableModel_makeModel_header=Make and Model
UserActivityPanel_TopDomainsTableModel_count_header=Visits
UserActivityPanel_TopDomainsTableModel_domain_header=Domain
UserActivityPanel_TopDomainsTableModel_lastAccess_header=Last Accessed
UserActivityPanel_TopProgramsTableModel_count_header=Run Times
UserActivityPanel_TopProgramsTableModel_folder_header=Folder
UserActivityPanel_TopProgramsTableModel_lastrun_header=Last Run
UserActivityPanel_TopProgramsTableModel_name_header=Program
UserActivityPanel_TopWebSearchTableModel_dateAccessed_header=Date Accessed
UserActivityPanel_TopWebSearchTableModel_searchString_header=Search String
UserActivityPanel_TopWebSearchTableModel_translatedResult_header=Translated
ViewSummaryInformationAction.name.text=View Summary Information

View File

@ -13,44 +13,44 @@ DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.executables.row=\u5b9f\u8
DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.images.row=\u30a4\u30e1\u30fc\u30b8
DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.type.header=\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7
DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.videos.row=\u52d5\u753b
DataSourceSummaryDetailsPanel.getDataSources.error.text=\u73fe\u5728\u306e\u30b1\u30fc\u30b9\u306e\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u30ea\u30b9\u30c8\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002
DataSourceSummaryDetailsPanel.getDataSources.error.title=\u8aad\u307f\u8fbc\u307f\u5931\u6557
DataSourceSummaryDetailsPanel.units.bytes=\ \u30d0\u30a4\u30c8
DataSourceSummaryDetailsPanel.units.gigabytes=\ GB
DataSourceSummaryDetailsPanel.units.kilobytes=\ kB
DataSourceSummaryDetailsPanel.units.megabytes=\ MB
DataSourceSummaryDetailsPanel.units.petabytes=\ PB
DataSourceSummaryDetailsPanel.units.terabytes=\ TB
ContainerPanel.getDataSources.error.text=\u73fe\u5728\u306e\u30b1\u30fc\u30b9\u306e\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u30ea\u30b9\u30c8\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002
ContainerPanel.getDataSources.error.title=\u8aad\u307f\u8fbc\u307f\u5931\u6557
ContainerPanel.units.bytes=\ \u30d0\u30a4\u30c8
ContainerPanel.units.gigabytes=\ GB
ContainerPanel.units.kilobytes=\ kB
ContainerPanel.units.megabytes=\ MB
ContainerPanel.units.petabytes=\ PB
ContainerPanel.units.terabytes=\ TB
DataSourceSummaryDialog.closeButton.text=\u9589\u3058\u308b
DataSourceSummaryDetailsPanel.displayNameLabel.text=\u8868\u793a\u540d:
DataSourceSummaryDetailsPanel.originalNameLabel.text=\u540d\u524d:
DataSourceSummaryDetailsPanel.deviceIdLabel.text=\u30c7\u30d0\u30a4\u30b9ID:
DataSourceSummaryDetailsPanel.timeZoneLabel.text=\u30bf\u30a4\u30e0\u30be\u30fc\u30f3:
DataSourceSummaryDetailsPanel.imageTypeLabel.text=\u30a4\u30e1\u30fc\u30b8\u30bf\u30a4\u30d7:
DataSourceSummaryDetailsPanel.sizeLabel.text=\u30b5\u30a4\u30ba:
DataSourceSummaryDetailsPanel.sectorSizeLabel.text=\u30bb\u30af\u30bf\u30fc\u30b5\u30a4\u30ba:
DataSourceSummaryDetailsPanel.md5HashLabel.text=MD5:
DataSourceSummaryDetailsPanel.sha1HashLabel.text=SHA1:
DataSourceSummaryDetailsPanel.sha256HashLabel.text=SHA256:
DataSourceSummaryDetailsPanel.filePathsLabel.text=\u30d5\u30a1\u30a4\u30eb\u30d1\u30b9:
DataSourceSummaryDetailsPanel.displayNameValue.text=
DataSourceSummaryDetailsPanel.originalNameValue.text=
DataSourceSummaryDetailsPanel.deviceIdValue.toolTipText=
DataSourceSummaryDetailsPanel.deviceIdValue.text=
DataSourceSummaryDetailsPanel.timeZoneValue.text=
DataSourceSummaryDetailsPanel.imageTypeValue.toolTipText=
DataSourceSummaryDetailsPanel.imageTypeValue.text=
DataSourceSummaryDetailsPanel.sizeValue.text=
DataSourceSummaryDetailsPanel.sectorSizeValue.text=
DataSourceSummaryDetailsPanel.md5HashValue.toolTipText=
DataSourceSummaryDetailsPanel.md5HashValue.text=
DataSourceSummaryDetailsPanel.sha1HashValue.text=
DataSourceSummaryDetailsPanel.sha256HashValue.text=
DataSourceSummaryDetailsPanel.filePathsTable.columnModel.title0=
DataSourceSummaryDetailsPanel.acquisitionDetailsTextArea.text=
DataSourceSummaryDetailsPanel.acquisitionDetailsLabel.text=\u53d6\u5f97\u306e\u8a73\u7d30:
DataSourceSummaryDetailsPanel.unallocatedSizeLabel.text=\u672a\u4f7f\u7528\u9818\u57df:
DataSourceSummaryDetailsPanel.unallocatedSizeValue.text=
ContainerPanel.displayNameLabel.text=\u8868\u793a\u540d:
ContainerPanel.originalNameLabel.text=\u540d\u524d:
ContainerPanel.deviceIdLabel.text=\u30c7\u30d0\u30a4\u30b9ID:
ContainerPanel.timeZoneLabel.text=\u30bf\u30a4\u30e0\u30be\u30fc\u30f3:
ContainerPanel.imageTypeLabel.text=\u30a4\u30e1\u30fc\u30b8\u30bf\u30a4\u30d7:
ContainerPanel.sizeLabel.text=\u30b5\u30a4\u30ba:
ContainerPanel.sectorSizeLabel.text=\u30bb\u30af\u30bf\u30fc\u30b5\u30a4\u30ba:
ContainerPanel.md5HashLabel.text=MD5:
ContainerPanel.sha1HashLabel.text=SHA1:
ContainerPanel.sha256HashLabel.text=SHA256:
ContainerPanel.filePathsLabel.text=\u30d5\u30a1\u30a4\u30eb\u30d1\u30b9:
ContainerPanel.displayNameValue.text=
ContainerPanel.originalNameValue.text=
ContainerPanel.deviceIdValue.toolTipText=
ContainerPanel.deviceIdValue.text=
ContainerPanel.timeZoneValue.text=
ContainerPanel.imageTypeValue.toolTipText=
ContainerPanel.imageTypeValue.text=
ContainerPanel.sizeValue.text=
ContainerPanel.sectorSizeValue.text=
ContainerPanel.md5HashValue.toolTipText=
ContainerPanel.md5HashValue.text=
ContainerPanel.sha1HashValue.text=
ContainerPanel.sha256HashValue.text=
ContainerPanel.filePathsTable.columnModel.title0=
ContainerPanel.acquisitionDetailsTextArea.text=
ContainerPanel.acquisitionDetailsLabel.text=\u53d6\u5f97\u306e\u8a73\u7d30:
ContainerPanel.unallocatedSizeLabel.text=\u672a\u4f7f\u7528\u9818\u57df:
ContainerPanel.unallocatedSizeValue.text=
DataSourceSummaryCountsPanel.byMimeTypeLabel.text=MIME\u30bf\u30a4\u30d7\u5225\u30d5\u30a1\u30a4\u30eb
DataSourceSummaryCountsPanel.byCategoryLabel.text=\u30ab\u30c6\u30b4\u30ea\u30fc\u5225\u30d5\u30a1\u30a4\u30eb
DataSourceSummaryCountsPanel.jLabel1.text=\u30bf\u30a4\u30d7\u5225\u7d50\u679c

View File

@ -40,7 +40,7 @@
<Component class="javax.swing.JLabel" name="displayNameLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="DataSourceSummaryDetailsPanel.displayNameLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="ContainerPanel.displayNameLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
@ -52,7 +52,7 @@
<Component class="javax.swing.JLabel" name="originalNameLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="DataSourceSummaryDetailsPanel.originalNameLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="ContainerPanel.originalNameLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
@ -64,7 +64,7 @@
<Component class="javax.swing.JLabel" name="sha1HashValue">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="DataSourceSummaryDetailsPanel.sha1HashValue.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="ContainerPanel.sha1HashValue.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
@ -76,7 +76,7 @@
<Component class="javax.swing.JLabel" name="displayNameValue">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="DataSourceSummaryDetailsPanel.displayNameValue.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="ContainerPanel.displayNameValue.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
@ -88,7 +88,7 @@
<Component class="javax.swing.JLabel" name="sha256HashValue">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="DataSourceSummaryDetailsPanel.sha256HashValue.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="ContainerPanel.sha256HashValue.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
@ -100,7 +100,7 @@
<Component class="javax.swing.JLabel" name="originalNameValue">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="DataSourceSummaryDetailsPanel.originalNameValue.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="ContainerPanel.originalNameValue.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
@ -112,10 +112,10 @@
<Component class="javax.swing.JLabel" name="deviceIdValue">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="DataSourceSummaryDetailsPanel.deviceIdValue.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="ContainerPanel.deviceIdValue.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="DataSourceSummaryDetailsPanel.deviceIdValue.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="ContainerPanel.deviceIdValue.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
@ -152,7 +152,7 @@
<TableColumnModel selectionModel="0">
<Column maxWidth="-1" minWidth="-1" prefWidth="-1" resizable="true">
<Title editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="DataSourceSummaryDetailsPanel.filePathsTable.columnModel.title0" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="ContainerPanel.filePathsTable.columnModel.title0" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Title>
<Editor/>
<Renderer/>
@ -169,7 +169,7 @@
<Component class="javax.swing.JLabel" name="timeZoneValue">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="DataSourceSummaryDetailsPanel.timeZoneValue.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="ContainerPanel.timeZoneValue.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
@ -181,10 +181,10 @@
<Component class="javax.swing.JLabel" name="imageTypeValue">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="DataSourceSummaryDetailsPanel.imageTypeValue.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="ContainerPanel.imageTypeValue.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="DataSourceSummaryDetailsPanel.imageTypeValue.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="ContainerPanel.imageTypeValue.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
@ -196,10 +196,10 @@
<Component class="javax.swing.JLabel" name="md5HashValue">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="DataSourceSummaryDetailsPanel.md5HashValue.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="ContainerPanel.md5HashValue.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="DataSourceSummaryDetailsPanel.md5HashValue.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="ContainerPanel.md5HashValue.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
@ -211,7 +211,7 @@
<Component class="javax.swing.JLabel" name="sectorSizeValue">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="DataSourceSummaryDetailsPanel.sectorSizeValue.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="ContainerPanel.sectorSizeValue.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
@ -223,7 +223,7 @@
<Component class="javax.swing.JLabel" name="sizeValue">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="DataSourceSummaryDetailsPanel.sizeValue.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="ContainerPanel.sizeValue.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
@ -235,7 +235,7 @@
<Component class="javax.swing.JLabel" name="filePathsLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="DataSourceSummaryDetailsPanel.filePathsLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="ContainerPanel.filePathsLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
@ -247,7 +247,7 @@
<Component class="javax.swing.JLabel" name="sha256HashLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="DataSourceSummaryDetailsPanel.sha256HashLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="ContainerPanel.sha256HashLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
@ -259,7 +259,7 @@
<Component class="javax.swing.JLabel" name="sha1HashLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="DataSourceSummaryDetailsPanel.sha1HashLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="ContainerPanel.sha1HashLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
@ -271,7 +271,7 @@
<Component class="javax.swing.JLabel" name="md5HashLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="DataSourceSummaryDetailsPanel.md5HashLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="ContainerPanel.md5HashLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
@ -283,7 +283,7 @@
<Component class="javax.swing.JLabel" name="sectorSizeLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="DataSourceSummaryDetailsPanel.sectorSizeLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="ContainerPanel.sectorSizeLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
@ -295,7 +295,7 @@
<Component class="javax.swing.JLabel" name="sizeLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="DataSourceSummaryDetailsPanel.sizeLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="ContainerPanel.sizeLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
@ -307,7 +307,7 @@
<Component class="javax.swing.JLabel" name="imageTypeLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="DataSourceSummaryDetailsPanel.imageTypeLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="ContainerPanel.imageTypeLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
@ -319,7 +319,7 @@
<Component class="javax.swing.JLabel" name="acquisitionDetailsLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="DataSourceSummaryDetailsPanel.acquisitionDetailsLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="ContainerPanel.acquisitionDetailsLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
@ -331,7 +331,7 @@
<Component class="javax.swing.JLabel" name="timeZoneLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="DataSourceSummaryDetailsPanel.timeZoneLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="ContainerPanel.timeZoneLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
@ -343,7 +343,7 @@
<Component class="javax.swing.JLabel" name="deviceIdLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="DataSourceSummaryDetailsPanel.deviceIdLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="ContainerPanel.deviceIdLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
@ -373,7 +373,7 @@
<Property name="columns" type="int" value="20"/>
<Property name="rows" type="int" value="4"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="DataSourceSummaryDetailsPanel.acquisitionDetailsTextArea.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="ContainerPanel.acquisitionDetailsTextArea.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="null"/>
@ -415,7 +415,7 @@
<Component class="javax.swing.JLabel" name="unallocatedSizeLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="DataSourceSummaryDetailsPanel.unallocatedSizeLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="ContainerPanel.unallocatedSizeLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
@ -427,7 +427,7 @@
<Component class="javax.swing.JLabel" name="unallocatedSizeValue">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="DataSourceSummaryDetailsPanel.unallocatedSizeValue.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="ContainerPanel.unallocatedSizeValue.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>

View File

@ -18,13 +18,20 @@
*/
package org.sleuthkit.autopsy.datasourcesummary.ui;
import java.text.DecimalFormat;
import java.beans.PropertyChangeEvent;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import org.sleuthkit.autopsy.coreutils.Logger;
import javax.swing.table.DefaultTableModel;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataSourceDetailsSummary;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.ContainerSummary;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult.ResultType;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultUpdateGovernor;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.UpdateGovernor;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.Image;
import org.sleuthkit.datamodel.TskCoreException;
@ -32,34 +39,122 @@ import org.sleuthkit.datamodel.TskCoreException;
/**
* Panel to display additional details associated with a specific DataSource
*/
class DataSourceSummaryDetailsPanel extends BaseDataSourceSummaryPanel {
class ContainerPanel extends BaseDataSourceSummaryPanel {
/**
* Data payload for the Container panel.
*/
private static class ContainerPanelData {
private final DataSource dataSource;
private final Long unallocatedFilesSize;
/**
* Main constructor.
*
* @param dataSource The original datasource.
* @param unallocatedFilesSize The unallocated file size.
*/
ContainerPanelData(DataSource dataSource, Long unallocatedFilesSize) {
this.dataSource = dataSource;
this.unallocatedFilesSize = unallocatedFilesSize;
}
/**
* @return The original datasource.
*/
DataSource getDataSource() {
return dataSource;
}
/**
* @return The unallocated file size.
*/
Long getUnallocatedFilesSize() {
return unallocatedFilesSize;
}
}
// set of case events for which to call update (if the name changes, that will impact data shown)
private static final Set<Case.Events> CASE_EVENT_SET = new HashSet<>(Arrays.asList(
Case.Events.DATA_SOURCE_NAME_CHANGED
));
// governor for handling these updates
private static final UpdateGovernor CONTAINER_UPDATES = new DefaultUpdateGovernor() {
@Override
public Set<Case.Events> getCaseEventUpdates() {
return CASE_EVENT_SET;
}
@Override
public boolean isRefreshRequiredForCaseEvent(PropertyChangeEvent evt) {
return true;
}
};
//Because this panel was made using the gridbaglayout and netbean's Customize Layout tool it will be best to continue to modify it through that
private static final long serialVersionUID = 1L;
private static final Integer SIZE_COVERSION_CONSTANT = 1000;
private static final DecimalFormat APPROXIMATE_SIZE_FORMAT = new DecimalFormat("#.##");
private static final Logger logger = Logger.getLogger(DataSourceSummaryDetailsPanel.class.getName());
private static final Logger logger = Logger.getLogger(ContainerPanel.class.getName());
private final List<DataFetchComponents<DataSource, ?>> dataFetchComponents;
/**
* Creates new form DataSourceSummaryDetailsPanel
* Creates a new form ContainerPanel.
*/
@Messages({"DataSourceSummaryDetailsPanel.getDataSources.error.text=Failed to get the list of datasources for the current case.",
"DataSourceSummaryDetailsPanel.getDataSources.error.title=Load Failure"})
DataSourceSummaryDetailsPanel() {
ContainerPanel() {
this(new ContainerSummary());
}
/**
* Creates new form ContainerPanel.
*/
ContainerPanel(ContainerSummary containerSummary) {
super(containerSummary, CONTAINER_UPDATES);
dataFetchComponents = Arrays.asList(
new DataFetchComponents<>(
(dataSource) -> {
return new ContainerPanelData(
dataSource,
containerSummary.getSizeOfUnallocatedFiles(dataSource)
);
},
(result) -> {
if (result != null && result.getResultType() == ResultType.SUCCESS) {
ContainerPanelData data = result.getData();
DataSource dataSource = (data == null) ? null : data.getDataSource();
Long unallocatedFileSize = (data == null) ? null : data.getUnallocatedFilesSize();
updateDetailsPanelData(dataSource, unallocatedFileSize);
} else {
if (result == null) {
logger.log(Level.WARNING, "No data fetch result was provided to the ContainerPanel.");
} else {
logger.log(Level.WARNING, "An exception occurred while attempting to fetch data for the ContainerPanel.",
result.getException());
}
updateDetailsPanelData(null, null);
}
}
)
);
initComponents();
setDataSource(null);
}
@Override
protected void onNewDataSource(DataSource dataSource) {
if (dataSource == null || !Case.isCaseOpen()) {
updateDetailsPanelData(null, null, null, null);
} else {
updateDetailsPanelData(dataSource,
DataSourceDetailsSummary.getSizeOfUnallocatedFiles(dataSource),
DataSourceDetailsSummary.getOperatingSystems(dataSource),
DataSourceDetailsSummary.getDataSourceType(dataSource));
}
fetchInformation(dataSource);
}
@Override
protected void fetchInformation(DataSource dataSource) {
fetchInformation(dataFetchComponents, dataSource);
}
/**
@ -67,11 +162,10 @@ class DataSourceSummaryDetailsPanel extends BaseDataSourceSummaryPanel {
*
* @param selectedDataSource the DataSource to display details about.
*/
private void updateDetailsPanelData(DataSource selectedDataSource, Long unallocatedFilesSize, String osDetails, String usage) {
private void updateDetailsPanelData(DataSource selectedDataSource, Long unallocatedFilesSize) {
clearTableValues();
if (selectedDataSource != null) {
unallocatedSizeValue.setText(getSizeString(unallocatedFilesSize));
unallocatedSizeValue.setText(SizeRepresentationUtil.getSizeString(unallocatedFilesSize));
timeZoneValue.setText(selectedDataSource.getTimeZone());
displayNameValue.setText(selectedDataSource.getName());
originalNameValue.setText(selectedDataSource.getName());
@ -100,8 +194,8 @@ class DataSourceSummaryDetailsPanel extends BaseDataSourceSummaryPanel {
*/
private void setFieldsForImage(Image selectedImage) {
imageTypeValue.setText(selectedImage.getType().getName());
sizeValue.setText(getSizeString(selectedImage.getSize()));
sectorSizeValue.setText(getSizeString(selectedImage.getSsize()));
sizeValue.setText(SizeRepresentationUtil.getSizeString(selectedImage.getSize()));
sectorSizeValue.setText(SizeRepresentationUtil.getSizeString(selectedImage.getSsize()));
for (String path : selectedImage.getPaths()) {
((DefaultTableModel) filePathsTable.getModel()).addRow(new Object[]{path});
@ -139,55 +233,6 @@ class DataSourceSummaryDetailsPanel extends BaseDataSourceSummaryPanel {
}
}
/**
* Get a long size in bytes as a string formated to be read by users.
*
* @param size Long value representing a size in bytes
*
* @return return a string formated with a user friendly version of the size
* as a string, returns empty String when provided empty size
*/
@Messages({
"DataSourceSummaryDetailsPanel.units.bytes= bytes",
"DataSourceSummaryDetailsPanel.units.kilobytes= kB",
"DataSourceSummaryDetailsPanel.units.megabytes= MB",
"DataSourceSummaryDetailsPanel.units.gigabytes= GB",
"DataSourceSummaryDetailsPanel.units.terabytes= TB",
"DataSourceSummaryDetailsPanel.units.petabytes= PB"
})
private static String getSizeString(Long size) {
if (size == null) {
return "";
}
double approximateSize = size;
if (approximateSize < SIZE_COVERSION_CONSTANT) {
return String.valueOf(size) + Bundle.DataSourceSummaryDetailsPanel_units_bytes();
}
approximateSize /= SIZE_COVERSION_CONSTANT;
if (approximateSize < SIZE_COVERSION_CONSTANT) {
return APPROXIMATE_SIZE_FORMAT.format(approximateSize) + Bundle.DataSourceSummaryDetailsPanel_units_kilobytes()
+ " (" + String.valueOf(size) + Bundle.DataSourceSummaryDetailsPanel_units_bytes() + ")";
}
approximateSize /= SIZE_COVERSION_CONSTANT;
if (approximateSize < SIZE_COVERSION_CONSTANT) {
return APPROXIMATE_SIZE_FORMAT.format(approximateSize) + Bundle.DataSourceSummaryDetailsPanel_units_megabytes()
+ " (" + String.valueOf(size) + Bundle.DataSourceSummaryDetailsPanel_units_bytes() + ")";
}
approximateSize /= SIZE_COVERSION_CONSTANT;
if (approximateSize < SIZE_COVERSION_CONSTANT) {
return APPROXIMATE_SIZE_FORMAT.format(approximateSize) + Bundle.DataSourceSummaryDetailsPanel_units_gigabytes()
+ " (" + String.valueOf(size) + Bundle.DataSourceSummaryDetailsPanel_units_bytes() + ")";
}
approximateSize /= SIZE_COVERSION_CONSTANT;
if (approximateSize < SIZE_COVERSION_CONSTANT) {
return APPROXIMATE_SIZE_FORMAT.format(approximateSize) + Bundle.DataSourceSummaryDetailsPanel_units_terabytes()
+ " (" + String.valueOf(size) + Bundle.DataSourceSummaryDetailsPanel_units_bytes() + ")";
}
approximateSize /= SIZE_COVERSION_CONSTANT;
return APPROXIMATE_SIZE_FORMAT.format(approximateSize) + Bundle.DataSourceSummaryDetailsPanel_units_petabytes()
+ " (" + String.valueOf(size) + Bundle.DataSourceSummaryDetailsPanel_units_bytes() + ")";
}
/**
* Update the visibility of all fields and their labels based on whether
* they have contents. Empty fields have them and their contents hidden.
@ -287,7 +332,7 @@ class DataSourceSummaryDetailsPanel extends BaseDataSourceSummaryPanel {
jPanel1.setLayout(new java.awt.GridBagLayout());
org.openide.awt.Mnemonics.setLocalizedText(displayNameLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.displayNameLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(displayNameLabel, org.openide.util.NbBundle.getMessage(ContainerPanel.class, "ContainerPanel.displayNameLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
@ -296,7 +341,7 @@ class DataSourceSummaryDetailsPanel extends BaseDataSourceSummaryPanel {
gridBagConstraints.insets = new java.awt.Insets(10, 10, 0, 4);
jPanel1.add(displayNameLabel, gridBagConstraints);
org.openide.awt.Mnemonics.setLocalizedText(originalNameLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.originalNameLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(originalNameLabel, org.openide.util.NbBundle.getMessage(ContainerPanel.class, "ContainerPanel.originalNameLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 1;
@ -305,7 +350,7 @@ class DataSourceSummaryDetailsPanel extends BaseDataSourceSummaryPanel {
gridBagConstraints.insets = new java.awt.Insets(0, 10, 0, 4);
jPanel1.add(originalNameLabel, gridBagConstraints);
org.openide.awt.Mnemonics.setLocalizedText(sha1HashValue, org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.sha1HashValue.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(sha1HashValue, org.openide.util.NbBundle.getMessage(ContainerPanel.class, "ContainerPanel.sha1HashValue.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 12;
@ -316,7 +361,7 @@ class DataSourceSummaryDetailsPanel extends BaseDataSourceSummaryPanel {
gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 10);
jPanel1.add(sha1HashValue, gridBagConstraints);
org.openide.awt.Mnemonics.setLocalizedText(displayNameValue, org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.displayNameValue.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(displayNameValue, org.openide.util.NbBundle.getMessage(ContainerPanel.class, "ContainerPanel.displayNameValue.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 0;
@ -327,7 +372,7 @@ class DataSourceSummaryDetailsPanel extends BaseDataSourceSummaryPanel {
gridBagConstraints.insets = new java.awt.Insets(10, 0, 0, 10);
jPanel1.add(displayNameValue, gridBagConstraints);
org.openide.awt.Mnemonics.setLocalizedText(sha256HashValue, org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.sha256HashValue.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(sha256HashValue, org.openide.util.NbBundle.getMessage(ContainerPanel.class, "ContainerPanel.sha256HashValue.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 13;
@ -338,7 +383,7 @@ class DataSourceSummaryDetailsPanel extends BaseDataSourceSummaryPanel {
gridBagConstraints.insets = new java.awt.Insets(0, 0, 6, 10);
jPanel1.add(sha256HashValue, gridBagConstraints);
org.openide.awt.Mnemonics.setLocalizedText(originalNameValue, org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.originalNameValue.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(originalNameValue, org.openide.util.NbBundle.getMessage(ContainerPanel.class, "ContainerPanel.originalNameValue.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 1;
@ -349,8 +394,8 @@ class DataSourceSummaryDetailsPanel extends BaseDataSourceSummaryPanel {
gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 10);
jPanel1.add(originalNameValue, gridBagConstraints);
org.openide.awt.Mnemonics.setLocalizedText(deviceIdValue, org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.deviceIdValue.text")); // NOI18N
deviceIdValue.setToolTipText(org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.deviceIdValue.toolTipText")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(deviceIdValue, org.openide.util.NbBundle.getMessage(ContainerPanel.class, "ContainerPanel.deviceIdValue.text")); // NOI18N
deviceIdValue.setToolTipText(org.openide.util.NbBundle.getMessage(ContainerPanel.class, "ContainerPanel.deviceIdValue.toolTipText")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 2;
@ -382,7 +427,7 @@ class DataSourceSummaryDetailsPanel extends BaseDataSourceSummaryPanel {
filePathsTable.setTableHeader(null);
filePathsScrollPane.setViewportView(filePathsTable);
if (filePathsTable.getColumnModel().getColumnCount() > 0) {
filePathsTable.getColumnModel().getColumn(0).setHeaderValue(org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.filePathsTable.columnModel.title0")); // NOI18N
filePathsTable.getColumnModel().getColumn(0).setHeaderValue(org.openide.util.NbBundle.getMessage(ContainerPanel.class, "ContainerPanel.filePathsTable.columnModel.title0")); // NOI18N
}
gridBagConstraints = new java.awt.GridBagConstraints();
@ -396,7 +441,7 @@ class DataSourceSummaryDetailsPanel extends BaseDataSourceSummaryPanel {
gridBagConstraints.insets = new java.awt.Insets(6, 0, 10, 10);
jPanel1.add(filePathsScrollPane, gridBagConstraints);
org.openide.awt.Mnemonics.setLocalizedText(timeZoneValue, org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.timeZoneValue.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(timeZoneValue, org.openide.util.NbBundle.getMessage(ContainerPanel.class, "ContainerPanel.timeZoneValue.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 5;
@ -407,8 +452,8 @@ class DataSourceSummaryDetailsPanel extends BaseDataSourceSummaryPanel {
gridBagConstraints.insets = new java.awt.Insets(0, 0, 6, 10);
jPanel1.add(timeZoneValue, gridBagConstraints);
org.openide.awt.Mnemonics.setLocalizedText(imageTypeValue, org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.imageTypeValue.text")); // NOI18N
imageTypeValue.setToolTipText(org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.imageTypeValue.toolTipText")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(imageTypeValue, org.openide.util.NbBundle.getMessage(ContainerPanel.class, "ContainerPanel.imageTypeValue.text")); // NOI18N
imageTypeValue.setToolTipText(org.openide.util.NbBundle.getMessage(ContainerPanel.class, "ContainerPanel.imageTypeValue.toolTipText")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 7;
@ -419,8 +464,8 @@ class DataSourceSummaryDetailsPanel extends BaseDataSourceSummaryPanel {
gridBagConstraints.insets = new java.awt.Insets(6, 0, 0, 10);
jPanel1.add(imageTypeValue, gridBagConstraints);
org.openide.awt.Mnemonics.setLocalizedText(md5HashValue, org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.md5HashValue.text")); // NOI18N
md5HashValue.setToolTipText(org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.md5HashValue.toolTipText")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(md5HashValue, org.openide.util.NbBundle.getMessage(ContainerPanel.class, "ContainerPanel.md5HashValue.text")); // NOI18N
md5HashValue.setToolTipText(org.openide.util.NbBundle.getMessage(ContainerPanel.class, "ContainerPanel.md5HashValue.toolTipText")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 11;
@ -431,7 +476,7 @@ class DataSourceSummaryDetailsPanel extends BaseDataSourceSummaryPanel {
gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 10);
jPanel1.add(md5HashValue, gridBagConstraints);
org.openide.awt.Mnemonics.setLocalizedText(sectorSizeValue, org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.sectorSizeValue.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(sectorSizeValue, org.openide.util.NbBundle.getMessage(ContainerPanel.class, "ContainerPanel.sectorSizeValue.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 10;
@ -442,7 +487,7 @@ class DataSourceSummaryDetailsPanel extends BaseDataSourceSummaryPanel {
gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 10);
jPanel1.add(sectorSizeValue, gridBagConstraints);
org.openide.awt.Mnemonics.setLocalizedText(sizeValue, org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.sizeValue.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(sizeValue, org.openide.util.NbBundle.getMessage(ContainerPanel.class, "ContainerPanel.sizeValue.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 8;
@ -453,7 +498,7 @@ class DataSourceSummaryDetailsPanel extends BaseDataSourceSummaryPanel {
gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 10);
jPanel1.add(sizeValue, gridBagConstraints);
org.openide.awt.Mnemonics.setLocalizedText(filePathsLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.filePathsLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(filePathsLabel, org.openide.util.NbBundle.getMessage(ContainerPanel.class, "ContainerPanel.filePathsLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 14;
@ -463,7 +508,7 @@ class DataSourceSummaryDetailsPanel extends BaseDataSourceSummaryPanel {
gridBagConstraints.insets = new java.awt.Insets(6, 10, 10, 4);
jPanel1.add(filePathsLabel, gridBagConstraints);
org.openide.awt.Mnemonics.setLocalizedText(sha256HashLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.sha256HashLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(sha256HashLabel, org.openide.util.NbBundle.getMessage(ContainerPanel.class, "ContainerPanel.sha256HashLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 13;
@ -472,7 +517,7 @@ class DataSourceSummaryDetailsPanel extends BaseDataSourceSummaryPanel {
gridBagConstraints.insets = new java.awt.Insets(0, 10, 6, 4);
jPanel1.add(sha256HashLabel, gridBagConstraints);
org.openide.awt.Mnemonics.setLocalizedText(sha1HashLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.sha1HashLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(sha1HashLabel, org.openide.util.NbBundle.getMessage(ContainerPanel.class, "ContainerPanel.sha1HashLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 12;
@ -481,7 +526,7 @@ class DataSourceSummaryDetailsPanel extends BaseDataSourceSummaryPanel {
gridBagConstraints.insets = new java.awt.Insets(0, 10, 0, 4);
jPanel1.add(sha1HashLabel, gridBagConstraints);
org.openide.awt.Mnemonics.setLocalizedText(md5HashLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.md5HashLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(md5HashLabel, org.openide.util.NbBundle.getMessage(ContainerPanel.class, "ContainerPanel.md5HashLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 11;
@ -490,7 +535,7 @@ class DataSourceSummaryDetailsPanel extends BaseDataSourceSummaryPanel {
gridBagConstraints.insets = new java.awt.Insets(0, 10, 0, 4);
jPanel1.add(md5HashLabel, gridBagConstraints);
org.openide.awt.Mnemonics.setLocalizedText(sectorSizeLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.sectorSizeLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(sectorSizeLabel, org.openide.util.NbBundle.getMessage(ContainerPanel.class, "ContainerPanel.sectorSizeLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 10;
@ -499,7 +544,7 @@ class DataSourceSummaryDetailsPanel extends BaseDataSourceSummaryPanel {
gridBagConstraints.insets = new java.awt.Insets(0, 10, 0, 4);
jPanel1.add(sectorSizeLabel, gridBagConstraints);
org.openide.awt.Mnemonics.setLocalizedText(sizeLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.sizeLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(sizeLabel, org.openide.util.NbBundle.getMessage(ContainerPanel.class, "ContainerPanel.sizeLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 8;
@ -508,7 +553,7 @@ class DataSourceSummaryDetailsPanel extends BaseDataSourceSummaryPanel {
gridBagConstraints.insets = new java.awt.Insets(0, 10, 0, 4);
jPanel1.add(sizeLabel, gridBagConstraints);
org.openide.awt.Mnemonics.setLocalizedText(imageTypeLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.imageTypeLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(imageTypeLabel, org.openide.util.NbBundle.getMessage(ContainerPanel.class, "ContainerPanel.imageTypeLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 7;
@ -517,7 +562,7 @@ class DataSourceSummaryDetailsPanel extends BaseDataSourceSummaryPanel {
gridBagConstraints.insets = new java.awt.Insets(6, 10, 0, 4);
jPanel1.add(imageTypeLabel, gridBagConstraints);
org.openide.awt.Mnemonics.setLocalizedText(acquisitionDetailsLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.acquisitionDetailsLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(acquisitionDetailsLabel, org.openide.util.NbBundle.getMessage(ContainerPanel.class, "ContainerPanel.acquisitionDetailsLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 6;
@ -527,7 +572,7 @@ class DataSourceSummaryDetailsPanel extends BaseDataSourceSummaryPanel {
gridBagConstraints.insets = new java.awt.Insets(6, 10, 6, 4);
jPanel1.add(acquisitionDetailsLabel, gridBagConstraints);
org.openide.awt.Mnemonics.setLocalizedText(timeZoneLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.timeZoneLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(timeZoneLabel, org.openide.util.NbBundle.getMessage(ContainerPanel.class, "ContainerPanel.timeZoneLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 5;
@ -536,7 +581,7 @@ class DataSourceSummaryDetailsPanel extends BaseDataSourceSummaryPanel {
gridBagConstraints.insets = new java.awt.Insets(0, 10, 6, 4);
jPanel1.add(timeZoneLabel, gridBagConstraints);
org.openide.awt.Mnemonics.setLocalizedText(deviceIdLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.deviceIdLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(deviceIdLabel, org.openide.util.NbBundle.getMessage(ContainerPanel.class, "ContainerPanel.deviceIdLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 2;
@ -549,7 +594,7 @@ class DataSourceSummaryDetailsPanel extends BaseDataSourceSummaryPanel {
acquisitionDetailsTextArea.setBackground(javax.swing.UIManager.getDefaults().getColor("TextArea.disabledBackground"));
acquisitionDetailsTextArea.setColumns(20);
acquisitionDetailsTextArea.setRows(4);
acquisitionDetailsTextArea.setText(org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.acquisitionDetailsTextArea.text")); // NOI18N
acquisitionDetailsTextArea.setText(org.openide.util.NbBundle.getMessage(ContainerPanel.class, "ContainerPanel.acquisitionDetailsTextArea.text")); // NOI18N
acquisitionDetailsTextArea.setBorder(null);
acquisitionDetailsScrollPane.setViewportView(acquisitionDetailsTextArea);
@ -577,7 +622,7 @@ class DataSourceSummaryDetailsPanel extends BaseDataSourceSummaryPanel {
gridBagConstraints.weighty = 0.1;
jPanel1.add(filler2, gridBagConstraints);
org.openide.awt.Mnemonics.setLocalizedText(unallocatedSizeLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.unallocatedSizeLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(unallocatedSizeLabel, org.openide.util.NbBundle.getMessage(ContainerPanel.class, "ContainerPanel.unallocatedSizeLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 9;
@ -586,7 +631,7 @@ class DataSourceSummaryDetailsPanel extends BaseDataSourceSummaryPanel {
gridBagConstraints.insets = new java.awt.Insets(0, 10, 0, 4);
jPanel1.add(unallocatedSizeLabel, gridBagConstraints);
org.openide.awt.Mnemonics.setLocalizedText(unallocatedSizeValue, org.openide.util.NbBundle.getMessage(DataSourceSummaryDetailsPanel.class, "DataSourceSummaryDetailsPanel.unallocatedSizeValue.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(unallocatedSizeValue, org.openide.util.NbBundle.getMessage(ContainerPanel.class, "ContainerPanel.unallocatedSizeValue.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 9;

View File

@ -1,222 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.8" 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="scrollParent" alignment="0" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="scrollParent" alignment="0" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Container class="javax.swing.JScrollPane" name="scrollParent">
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Container class="javax.swing.JPanel" name="parentPanel">
<Properties>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[840, 320]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="fileTypePiePanel" min="-2" pref="400" max="-2" attributes="0"/>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="fileCountsByCategoryScrollPane" alignment="0" min="-2" pref="140" max="-2" attributes="0"/>
<Component id="byCategoryLabel" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="filesByCatParent" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="resultsByTypeLabel" min="-2" pref="79" max="-2" attributes="0"/>
<Group type="102" attributes="0">
<Component id="artifactCountsScrollPane" min="-2" pref="244" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="resultsByTypeParent" min="-2" 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" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="fileTypePiePanel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<Group type="103" groupAlignment="3" attributes="0">
<Component id="byCategoryLabel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="resultsByTypeLabel" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<EmptySpace max="32767" attributes="0"/>
<Component id="resultsByTypeParent" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="148" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="artifactCountsScrollPane" pref="0" max="32767" attributes="0"/>
<Group type="102" attributes="0">
<Component id="fileCountsByCategoryScrollPane" min="-2" pref="107" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="31" max="-2" attributes="0"/>
<Component id="filesByCatParent" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</Group>
</Group>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Container class="javax.swing.JScrollPane" name="fileCountsByCategoryScrollPane">
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
<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="fileCountsByCategoryTable">
</Component>
</SubComponents>
</Container>
<Component class="javax.swing.JLabel" name="byCategoryLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="DataSourceSummaryCountsPanel.byCategoryLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
</Component>
<Component class="javax.swing.JLabel" name="resultsByTypeLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="DataSourceSummaryCountsPanel.resultsByTypeLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
</Component>
<Container class="javax.swing.JScrollPane" name="artifactCountsScrollPane">
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
<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="artifactCountsTable">
<Properties>
<Property name="autoCreateRowSorter" type="boolean" value="true"/>
</Properties>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="fileTypePiePanel">
<Properties>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[400, 300]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="fileTypePieChart"/>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
</Container>
<Container class="javax.swing.JPanel" name="filesByCatParent">
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
</Layout>
</Container>
<Container class="javax.swing.JPanel" name="resultsByTypeParent">
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
</Layout>
</Container>
</SubComponents>
</Container>
</SubComponents>
</Container>
</SubComponents>
</Form>

View File

@ -1,289 +0,0 @@
/*
* 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.datasourcesummary.ui;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.NonEditableTableModel;
import java.util.Map;
import javax.swing.JLabel;
import javax.swing.table.DefaultTableCellRenderer;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataSourceCountsSummary;
import org.sleuthkit.datamodel.DataSource;
/**
* Panel for displaying summary information on the known files present in the
* specified DataSource
*/
@Messages({
"DataSourceSummaryCountsPanel.ArtifactCountsTableModel.type.header=Result Type",
"DataSourceSummaryCountsPanel.ArtifactCountsTableModel.count.header=Count",
"DataSourceSummaryCountsPanel.FilesByCategoryTableModel.type.header=File Type",
"DataSourceSummaryCountsPanel.FilesByCategoryTableModel.count.header=Count"
})
class DataSourceSummaryCountsPanel extends BaseDataSourceSummaryPanel {
private static final long serialVersionUID = 1L;
// Result returned for a data model if no data found.
private static final Object[][] EMPTY_PAIRS = new Object[][]{};
// column headers for file by category table
private static final Object[] FILE_BY_CATEGORY_COLUMN_HEADERS = new Object[]{
Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_type_header(),
Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_count_header()
};
// column headers for artifact counts table
private static final Object[] ARTIFACT_COUNTS_COLUMN_HEADERS = new Object[]{
Bundle.DataSourceSummaryCountsPanel_ArtifactCountsTableModel_type_header(),
Bundle.DataSourceSummaryCountsPanel_ArtifactCountsTableModel_count_header()
};
private final DefaultTableCellRenderer rightAlignedRenderer = new DefaultTableCellRenderer();
private final FileTypePieChart fileTypePieChart = new FileTypePieChart();
/**
* Creates new form DataSourceSummaryCountsPanel
*/
DataSourceSummaryCountsPanel() {
rightAlignedRenderer.setHorizontalAlignment(JLabel.RIGHT);
initComponents();
fileCountsByCategoryTable.getTableHeader().setReorderingAllowed(false);
artifactCountsTable.getTableHeader().setReorderingAllowed(false);
setDataSource(null);
}
@Override
protected void onNewDataSource(DataSource dataSource) {
if (dataSource == null || !Case.isCaseOpen()) {
updateCountsTableData(EMPTY_PAIRS, EMPTY_PAIRS);
} else {
updateCountsTableData(getFileCategoryModel(dataSource), getArtifactCountsModel(dataSource));
}
this.fileTypePieChart.setDataSource(dataSource);
}
/**
* Specify the DataSource to display file information for.
*
* @param fileCategoryDataModel The file category data model.
* @param artifactDataModel The artifact type data model.
*/
private void updateCountsTableData(Object[][] fileCategoryDataModel, Object[][] artifactDataModel) {
fileCountsByCategoryTable.setModel(new NonEditableTableModel(fileCategoryDataModel, FILE_BY_CATEGORY_COLUMN_HEADERS));
fileCountsByCategoryTable.getColumnModel().getColumn(1).setCellRenderer(rightAlignedRenderer);
fileCountsByCategoryTable.getColumnModel().getColumn(0).setPreferredWidth(130);
artifactCountsTable.setModel(new NonEditableTableModel(artifactDataModel, ARTIFACT_COUNTS_COLUMN_HEADERS));
artifactCountsTable.getColumnModel().getColumn(0).setPreferredWidth(230);
artifactCountsTable.getColumnModel().getColumn(1).setCellRenderer(rightAlignedRenderer);
this.repaint();
}
/**
* Determines the JTable data model for datasource file categories.
*
* @param dataSource The DataSource.
*
* @return The model to be used with a JTable.
*/
@Messages({
"DataSourceSummaryCountsPanel.FilesByCategoryTableModel.all.row=All",
"DataSourceSummaryCountsPanel.FilesByCategoryTableModel.allocated.row=Allocated",
"DataSourceSummaryCountsPanel.FilesByCategoryTableModel.unallocated.row=Unallocated",
"DataSourceSummaryCountsPanel.FilesByCategoryTableModel.slack.row=Slack",
"DataSourceSummaryCountsPanel.FilesByCategoryTableModel.directory.row=Directory"
})
private static Object[][] getFileCategoryModel(DataSource selectedDataSource) {
Long fileCount = zeroIfNull(DataSourceCountsSummary.getCountOfFiles(selectedDataSource));
Long unallocatedFiles = zeroIfNull(DataSourceCountsSummary.getCountOfUnallocatedFiles(selectedDataSource));
Long allocatedFiles = zeroIfNull(DataSourceCountsSummary.getCountOfAllocatedFiles(selectedDataSource));
Long slackFiles = zeroIfNull(DataSourceCountsSummary.getCountOfSlackFiles(selectedDataSource));
Long directories = zeroIfNull(DataSourceCountsSummary.getCountOfDirectories(selectedDataSource));
return new Object[][]{
new Object[]{Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_all_row(), fileCount},
new Object[]{Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_allocated_row(), allocatedFiles},
new Object[]{Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_unallocated_row(), unallocatedFiles},
new Object[]{Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_slack_row(), slackFiles},
new Object[]{Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_directory_row(), directories}
};
}
/**
* Returns 0 if value is null.
*
* @param origValue The original value.
*
* @return The value or 0 if null.
*/
private static Long zeroIfNull(Long origValue) {
return origValue == null ? 0 : origValue;
}
/**
* The counts of different artifact types found in a DataSource.
*
* @param selectedDataSource The DataSource.
*
* @return The JTable data model of counts of artifact types.
*/
private static Object[][] getArtifactCountsModel(DataSource selectedDataSource) {
Map<String, Long> artifactMapping = DataSourceCountsSummary.getCountsOfArtifactsByType(selectedDataSource);
if (artifactMapping == null) {
return EMPTY_PAIRS;
}
return artifactMapping.entrySet().stream()
.filter((entrySet) -> entrySet != null && entrySet.getKey() != null)
.sorted((a, b) -> a.getKey().compareTo(b.getKey()))
.map((entrySet) -> new Object[]{entrySet.getKey(), entrySet.getValue()})
.toArray(Object[][]::new);
}
/**
* 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() {
javax.swing.JScrollPane scrollParent = new javax.swing.JScrollPane();
javax.swing.JPanel parentPanel = new javax.swing.JPanel();
javax.swing.JScrollPane fileCountsByCategoryScrollPane = new javax.swing.JScrollPane();
fileCountsByCategoryTable = new javax.swing.JTable();
javax.swing.JLabel byCategoryLabel = new javax.swing.JLabel();
javax.swing.JLabel resultsByTypeLabel = new javax.swing.JLabel();
javax.swing.JScrollPane artifactCountsScrollPane = new javax.swing.JScrollPane();
artifactCountsTable = new javax.swing.JTable();
javax.swing.JPanel fileTypePiePanel = fileTypePieChart;
javax.swing.JPanel filesByCatParent = new javax.swing.JPanel();
javax.swing.JPanel resultsByTypeParent = new javax.swing.JPanel();
parentPanel.setMinimumSize(new java.awt.Dimension(840, 320));
fileCountsByCategoryScrollPane.setViewportView(fileCountsByCategoryTable);
org.openide.awt.Mnemonics.setLocalizedText(byCategoryLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryCountsPanel.class, "DataSourceSummaryCountsPanel.byCategoryLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(resultsByTypeLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryCountsPanel.class, "DataSourceSummaryCountsPanel.resultsByTypeLabel.text")); // NOI18N
artifactCountsTable.setAutoCreateRowSorter(true);
artifactCountsScrollPane.setViewportView(artifactCountsTable);
fileTypePiePanel.setPreferredSize(new java.awt.Dimension(400, 300));
javax.swing.GroupLayout filesByCatParentLayout = new javax.swing.GroupLayout(filesByCatParent);
filesByCatParent.setLayout(filesByCatParentLayout);
filesByCatParentLayout.setHorizontalGroup(
filesByCatParentLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 0, Short.MAX_VALUE)
);
filesByCatParentLayout.setVerticalGroup(
filesByCatParentLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 0, Short.MAX_VALUE)
);
javax.swing.GroupLayout resultsByTypeParentLayout = new javax.swing.GroupLayout(resultsByTypeParent);
resultsByTypeParent.setLayout(resultsByTypeParentLayout);
resultsByTypeParentLayout.setHorizontalGroup(
resultsByTypeParentLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 0, Short.MAX_VALUE)
);
resultsByTypeParentLayout.setVerticalGroup(
resultsByTypeParentLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 0, Short.MAX_VALUE)
);
javax.swing.GroupLayout parentPanelLayout = new javax.swing.GroupLayout(parentPanel);
parentPanel.setLayout(parentPanelLayout);
parentPanelLayout.setHorizontalGroup(
parentPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(parentPanelLayout.createSequentialGroup()
.addContainerGap()
.addComponent(fileTypePiePanel, javax.swing.GroupLayout.PREFERRED_SIZE, 400, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(18, 18, 18)
.addGroup(parentPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(fileCountsByCategoryScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 140, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(byCategoryLabel)
.addComponent(filesByCatParent, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGap(18, 18, 18)
.addGroup(parentPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(resultsByTypeLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 79, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGroup(parentPanelLayout.createSequentialGroup()
.addComponent(artifactCountsScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 244, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(resultsByTypeParent, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
parentPanelLayout.setVerticalGroup(
parentPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(parentPanelLayout.createSequentialGroup()
.addContainerGap()
.addGroup(parentPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(parentPanelLayout.createSequentialGroup()
.addComponent(fileTypePiePanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addGroup(parentPanelLayout.createSequentialGroup()
.addGroup(parentPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(byCategoryLabel)
.addComponent(resultsByTypeLabel))
.addGroup(parentPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, parentPanelLayout.createSequentialGroup()
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(resultsByTypeParent, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(148, 148, 148))
.addGroup(parentPanelLayout.createSequentialGroup()
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(parentPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(artifactCountsScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)
.addGroup(parentPanelLayout.createSequentialGroup()
.addComponent(fileCountsByCategoryScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 107, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(31, 31, 31)
.addComponent(filesByCatParent, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(0, 0, Short.MAX_VALUE)))
.addContainerGap())))))
);
scrollParent.setViewportView(parentPanel);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(scrollParent)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(scrollParent)
);
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JTable artifactCountsTable;
private javax.swing.JTable fileCountsByCategoryTable;
// End of variables declaration//GEN-END:variables
}

View File

@ -67,7 +67,7 @@
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
<SubComponents>
<Container class="javax.swing.JTabbedPane" name="dataSourceTabbedPane">
<Container class="javax.swing.JPanel" name="dataSourceTabbedPane">
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="dataSourceSummaryTabbedPane"/>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
@ -79,7 +79,7 @@
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout"/>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
</Container>
</SubComponents>
</Container>

View File

@ -20,11 +20,13 @@ package org.sleuthkit.autopsy.datasourcesummary.ui;
import java.awt.Frame;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.EnumSet;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;
import java.util.Set;
import javax.swing.WindowConstants;
import javax.swing.event.ListSelectionEvent;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.CaseDataSourcesSummary;
@ -43,6 +45,7 @@ final class DataSourceSummaryDialog extends javax.swing.JDialog implements Obser
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.DATA_SOURCE_ANALYSIS_COMPLETED);
private final DataSourceBrowser dataSourcesPanel;
private final DataSourceSummaryTabbedPane dataSourceSummaryTabbedPane;
private final PropertyChangeListener ingestEventListener;
/**
* Creates new form DataSourceSummaryDialog for displaying a summary of the
@ -67,8 +70,8 @@ final class DataSourceSummaryDialog extends javax.swing.JDialog implements Obser
this.repaint();
}
});
//add listener to refresh jobs with Started status when they complete
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, (PropertyChangeEvent evt) -> {
ingestEventListener = (PropertyChangeEvent evt) -> {
if (evt instanceof DataSourceAnalysisCompletedEvent) {
DataSourceAnalysisCompletedEvent dsEvent = (DataSourceAnalysisCompletedEvent) evt;
if (dsEvent.getResult() == Reason.ANALYSIS_COMPLETED) {
@ -77,10 +80,24 @@ final class DataSourceSummaryDialog extends javax.swing.JDialog implements Obser
dataSourcesPanel.refresh(dsEvent.getDataSource().getId(), null);
}
}
});
};
//add listener to refresh jobs with Started status when they complete
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, ingestEventListener);
// verify that dialog will call dispose on close:
// https://docs.oracle.com/javase/tutorial/uiswing/components/frame.html#windowevents
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
this.pack();
}
@Override
public void dispose() {
IngestManager.getInstance().removeIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, ingestEventListener);
this.dataSourceSummaryTabbedPane.close();
super.dispose();
}
/**
* Make this dialog an observer of the DataSourcesPanel.
*/
@ -104,7 +121,7 @@ final class DataSourceSummaryDialog extends javax.swing.JDialog implements Obser
closeButton = new javax.swing.JButton();
dataSourceSummarySplitPane = new javax.swing.JSplitPane();
javax.swing.JTabbedPane dataSourceTabbedPane = dataSourceSummaryTabbedPane;
javax.swing.JPanel dataSourceTabbedPane = dataSourceSummaryTabbedPane;
org.openide.awt.Mnemonics.setLocalizedText(closeButton, org.openide.util.NbBundle.getMessage(DataSourceSummaryDialog.class, "DataSourceSummaryDialog.closeButton.text")); // NOI18N
closeButton.addActionListener(new java.awt.event.ActionListener() {

View File

@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" 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"/>
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,44,0,0,1,-112"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignCardLayout"/>
<SubComponents>
<Container class="javax.swing.JTabbedPane" name="tabbedPane">
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignCardLayout" value="org.netbeans.modules.form.compat2.layouts.DesignCardLayout$CardConstraintsDescription">
<CardConstraints cardName="tabbedPane"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout"/>
</Container>
<Container class="javax.swing.JPanel" name="noDataSourcePane">
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignCardLayout" value="org.netbeans.modules.form.compat2.layouts.DesignCardLayout$CardConstraintsDescription">
<CardConstraints cardName="noDataSourcePane"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
<SubComponents>
<Component class="javax.swing.JLabel" name="noDataSourceLabel">
<Properties>
<Property name="horizontalAlignment" type="int" value="0"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="DataSourceSummaryTabbedPane.noDataSourceLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
<BorderConstraints direction="Center"/>
</Constraint>
</Constraints>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Form>

View File

@ -18,64 +18,142 @@
*/
package org.sleuthkit.autopsy.datasourcesummary.ui;
import java.util.ArrayList;
import java.awt.CardLayout;
import java.awt.Component;
import java.util.Arrays;
import java.util.List;
import javax.swing.JTabbedPane;
import org.apache.commons.lang3.tuple.Pair;
import java.util.function.Consumer;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.IngestJobInfoPanel;
import org.sleuthkit.datamodel.DataSource;
/**
* A tabbed pane showing the summary of a data source including tabs of:
* DataSourceSummaryCountsPanel, DataSourceSummaryDetailsPanel, and
* IngestJobInfoPanel.
* DataSourceSummaryCountsPanel, ContainerPanel, and IngestJobInfoPanel.
*/
@Messages({
"DataSourceSummaryTabbedPane_countsTab_title=Counts",
"DataSourceSummaryTabbedPane_typesTab_title=Types",
"DataSourceSummaryTabbedPane_detailsTab_title=Container",
"DataSourceSummaryTabbedPane_userActivityTab_title=User Activity",
"DataSourceSummaryTabbedPane_ingestHistoryTab_title=Ingest History",
"DataSourceSummaryTabbedPane_recentFileTab_title=Recent Files",
"DataSourceSummaryTabbedPane_pastCasesTab_title=Past Cases",
"DataSourceSummaryTabbedPane_analysisTab_title=Analysis"
})
public class DataSourceSummaryTabbedPane extends JTabbedPane {
public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
/**
* Records of tab information (i.e. title, component, function to call on
* new data source).
*/
private static class DataSourceTab {
private final String tabTitle;
private final Component component;
private final Consumer<DataSource> onDataSource;
private final Runnable onClose;
/**
* Main constructor.
*
* @param tabTitle The title of the tab.
* @param component The component to be displayed.
* @param onDataSource The function to be called on a new data source.
* @param onClose Called to cleanup resources when closing tabs.
*/
DataSourceTab(String tabTitle, Component component, Consumer<DataSource> onDataSource, Runnable onClose) {
this.tabTitle = tabTitle;
this.component = component;
this.onDataSource = onDataSource;
this.onClose = onClose;
}
/**
* Main constructor.
*
* @param tabTitle The title of the tab.
* @param panel The component to be displayed in the tab.
*/
DataSourceTab(String tabTitle, BaseDataSourceSummaryPanel panel) {
this.tabTitle = tabTitle;
this.component = panel;
this.onDataSource = panel::setDataSource;
this.onClose = panel::close;
}
/**
* @return The title for the tab.
*/
String getTabTitle() {
return tabTitle;
}
/**
* @return The component to display in the tab.
*/
Component getComponent() {
return component;
}
/**
* @return The function to be called on new data source.
*/
Consumer<DataSource> getOnDataSource() {
return onDataSource;
}
/**
* @return The action for closing resources in the tab.
*/
public Runnable getOnClose() {
return onClose;
}
}
private static final long serialVersionUID = 1L;
// A pair of the tab name and the corresponding BaseDataSourceSummaryTabs to be displayed.
private final List<Pair<String, BaseDataSourceSummaryPanel>> tabs = new ArrayList<>(Arrays.asList(
Pair.of(Bundle.DataSourceSummaryTabbedPane_countsTab_title(), new DataSourceSummaryCountsPanel()),
Pair.of(Bundle.DataSourceSummaryTabbedPane_userActivityTab_title(), new DataSourceSummaryUserActivityPanel()),
Pair.of(Bundle.DataSourceSummaryTabbedPane_recentFileTab_title(), new RecentFilesPanel()),
Pair.of(Bundle.DataSourceSummaryTabbedPane_analysisTab_title(), new AnalysisPanel())
));
// needs to match value provided for card layout in designed
private static final String TABBED_PANE = "tabbedPane";
private static final String NO_DATASOURCE_PANE = "noDataSourcePane";
private final IngestJobInfoPanel ingestHistoryPanel = new IngestJobInfoPanel();
private final List<DataSourceTab> tabs = Arrays.asList(
new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_typesTab_title(), new TypesPanel()),
new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_userActivityTab_title(), new UserActivityPanel()),
new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_analysisTab_title(), new AnalysisPanel()),
new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_recentFileTab_title(), new RecentFilesPanel()),
new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_pastCasesTab_title(), new PastCasesPanel()),
// do nothing on closing
new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_ingestHistoryTab_title(), ingestHistoryPanel, ingestHistoryPanel::setDataSource, () -> {
}),
new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_detailsTab_title(), new ContainerPanel())
);
private DataSource dataSource = null;
private CardLayout cardLayout;
/**
* Constructs a tabbed pane showing the summary of a data source.
* Creates new form TabPane
*/
public DataSourceSummaryTabbedPane() {
initComponent();
initComponents();
postInit();
}
private void initComponent() {
for (Pair<String, BaseDataSourceSummaryPanel> tab : tabs) {
addTab(tab.getKey(), tab.getValue());
/**
* Method called right after initComponents during initialization.
*/
private void postInit() {
// get the card layout
cardLayout = (CardLayout) this.getLayout();
// set up the tabs
for (DataSourceTab tab : tabs) {
tabbedPane.addTab(tab.getTabTitle(), tab.getComponent());
}
// IngestJobInfoPanel is not specifically a data source summary panel
// and is called separately for that reason.
addTab(Bundle.DataSourceSummaryTabbedPane_ingestHistoryTab_title(), ingestHistoryPanel);
// The Container tab should be last.
Pair<String, BaseDataSourceSummaryPanel> tab = Pair.of(Bundle.DataSourceSummaryTabbedPane_detailsTab_title(), new DataSourceSummaryDetailsPanel());
addTab(tab.getKey(), tab.getValue());
tabs.add(tab);
// set this to no datasource initially
cardLayout.show(this, NO_DATASOURCE_PANE);
}
/**
@ -94,13 +172,52 @@ public class DataSourceSummaryTabbedPane extends JTabbedPane {
*/
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
for (Pair<String, BaseDataSourceSummaryPanel> tab : tabs) {
tab.getValue().setDataSource(dataSource);
if (this.dataSource == null) {
cardLayout.show(this, NO_DATASOURCE_PANE);
} else {
for (DataSourceTab tab : tabs) {
tab.getOnDataSource().accept(dataSource);
}
cardLayout.show(this, TABBED_PANE);
}
// IngestJobInfoPanel is not specifically a data source summary panel
// and is called separately for that reason.
ingestHistoryPanel.setDataSource(dataSource);
}
/**
* Handle close events on each tab.
*/
public void close() {
for (DataSourceTab tab : tabs) {
tab.getOnClose().run();
}
}
/**
* 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() {
tabbedPane = new javax.swing.JTabbedPane();
javax.swing.JPanel noDataSourcePane = new javax.swing.JPanel();
javax.swing.JLabel noDataSourceLabel = new javax.swing.JLabel();
setLayout(new java.awt.CardLayout());
add(tabbedPane, "tabbedPane");
noDataSourcePane.setLayout(new java.awt.BorderLayout());
noDataSourceLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
org.openide.awt.Mnemonics.setLocalizedText(noDataSourceLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryTabbedPane.class, "DataSourceSummaryTabbedPane.noDataSourceLabel.text")); // NOI18N
noDataSourcePane.add(noDataSourceLabel, java.awt.BorderLayout.CENTER);
add(noDataSourcePane, "noDataSourcePane");
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JTabbedPane tabbedPane;
// End of variables declaration//GEN-END:variables
}

View File

@ -1,190 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 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.datasourcesummary.ui;
import java.awt.BorderLayout;
import java.awt.Font;
import javax.swing.JPanel;
import org.sleuthkit.datamodel.DataSource;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.labels.PieSectionLabelGenerator;
import org.jfree.chart.labels.StandardPieSectionLabelGenerator;
import org.jfree.chart.plot.PiePlot;
import org.jfree.data.general.DefaultPieDataset;
import java.text.DecimalFormat;
import java.util.Arrays;
import java.util.Set;
import java.util.stream.Collectors;
import javax.swing.JLabel;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.coreutils.FileTypeUtils;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataSourceMimeTypeSummary;
import static org.sleuthkit.autopsy.coreutils.FileTypeUtils.FileTypeCategory;
/**
* A Pie Chart that shows file mime types in a data source.
*/
class FileTypePieChart extends JPanel {
private static final long serialVersionUID = 1L;
private static final Font DEFAULT_FONT = new JLabel().getFont();
private static final Font DEFAULT_HEADER_FONT = new Font(DEFAULT_FONT.getName(), DEFAULT_FONT.getStyle(), (int) (DEFAULT_FONT.getSize() * 1.5));
private final DefaultPieDataset dataset = new DefaultPieDataset();
private DataSource dataSource;
// used for determining mime types that fall in the 'other' category
private static final Set<String> ALL_CATEGORY_MIME_TYPES = Arrays.asList(
FileTypeCategory.IMAGE,
FileTypeCategory.VIDEO,
FileTypeCategory.AUDIO,
FileTypeCategory.DOCUMENTS,
FileTypeCategory.EXECUTABLE)
.stream()
.flatMap((cat) -> cat.getMediaTypes().stream())
.collect(Collectors.toSet());
/**
* Default constructor for the pie chart.
*/
@Messages({
"DataSourceSummaryCountsPanel.byMimeTypeLabel.text=Files by MIME Type",
"DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.audio.row=Audio",
"DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.documents.row=Documents",
"DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.executables.row=Executables",
"DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.images.row=Images",
"DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.videos.row=Videos",
"DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_other_label=Other",
"DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_notAnalyzed_label=Not Analyzed"
})
FileTypePieChart() {
// Create chart
JFreeChart chart = ChartFactory.createPieChart(
Bundle.DataSourceSummaryCountsPanel_byMimeTypeLabel_text(),
dataset,
true,
true,
false);
chart.setBackgroundPaint(null);
chart.getLegend().setItemFont(DEFAULT_FONT);
chart.getTitle().setFont(DEFAULT_HEADER_FONT);
PiePlot plot = ((PiePlot) chart.getPlot());
//Format Label
PieSectionLabelGenerator labelGenerator = new StandardPieSectionLabelGenerator(
"{0}: {1} ({2})", new DecimalFormat("0"), new DecimalFormat("0.0%"));
plot.setLabelGenerator(labelGenerator);
plot.setLabelFont(DEFAULT_FONT);
plot.setBackgroundPaint(null);
plot.setOutlinePaint(null);
// Create Panel
ChartPanel panel = new ChartPanel(chart);
this.setLayout(new BorderLayout());
this.add(panel, BorderLayout.CENTER);
}
/**
* The datasource currently used as the model with this pie chart.
*
* @return The datasource currently being used as the model in this pie
* chart.
*/
DataSource getDataSource() {
return dataSource;
}
/**
* Sets datasource to visualize in the pie chart.
*
* @param dataSource The datasource to use in this pie chart.
*/
void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
this.dataset.clear();
if (dataSource != null) {
addIfPresent(Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_images_row(),
this.dataSource, FileTypeCategory.IMAGE);
addIfPresent(Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_videos_row(),
this.dataSource, FileTypeCategory.VIDEO);
addIfPresent(Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_audio_row(),
this.dataSource, FileTypeCategory.AUDIO);
addIfPresent(Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_documents_row(),
this.dataSource, FileTypeCategory.DOCUMENTS);
addIfPresent(Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_executables_row(),
this.dataSource, FileTypeCategory.EXECUTABLE);
addIfPresent(Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_other_label(),
DataSourceMimeTypeSummary.getCountOfFilesNotInMimeTypes(this.dataSource, ALL_CATEGORY_MIME_TYPES));
addIfPresent(Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_notAnalyzed_label(),
DataSourceMimeTypeSummary.getCountOfFilesWithNoMimeType(this.dataSource));
}
}
/**
* Adds count for file type category if there is a value. Uses fields
* 'dataSource' and 'dataset'.
*
* @param label The label for this pie slice.
* @param dataSource The data source.
* @param category The category for the pie slice.
*/
private void addIfPresent(String label, DataSource dataSource, FileTypeUtils.FileTypeCategory category) {
if (dataSource == null) {
return;
}
Long count = getCount(dataSource, category);
addIfPresent(label, count);
}
/**
* Adds count for a a label if the count is non-null and greater than 0.
*
* @param label The label.
* @param count The count.
*/
private void addIfPresent(String label, Long count) {
if (count != null && count > 0) {
this.dataset.setValue(label, count);
}
}
/**
* Retrieves the counts of files of a particular mime type for a particular
* DataSource.
*
* @param dataSource The DataSource.
* @param category The mime type category.
*
* @return The count.
*/
private static Long getCount(DataSource dataSource, FileTypeUtils.FileTypeCategory category) {
return DataSourceMimeTypeSummary.getCountOfFilesForMimeTypes(dataSource, category.getMediaTypes());
}
}

View File

@ -0,0 +1,219 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.4" 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="mainScrollPane" alignment="0" pref="400" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="mainScrollPane" alignment="0" pref="300" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Container class="javax.swing.JScrollPane" name="mainScrollPane">
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Container class="javax.swing.JPanel" name="mainContentPanel">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.EmptyBorderInfo">
<EmptyBorder bottom="10" left="10" right="10" top="10"/>
</Border>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBoxLayout">
<Property name="axis" type="int" value="3"/>
</Layout>
<SubComponents>
<Container class="javax.swing.JPanel" name="ingestRunningPanel">
<Properties>
<Property name="alignmentX" type="float" value="0.0"/>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[32767, 25]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[10, 25]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[10, 25]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="ingestRunningLabel"/>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
</Container>
<Component class="javax.swing.JLabel" name="notableFileLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="PastCasesPanel.notableFileLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<AccessibilityProperties>
<Property name="AccessibleContext.accessibleName" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="PastCasesPanel.notableFileLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</AccessibilityProperties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
</Component>
<Component class="javax.swing.Box$Filler" name="filler1">
<Properties>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 2]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 2]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 2]"/>
</Property>
<Property name="alignmentX" type="float" value="0.0"/>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
<AuxValue name="classDetails" type="java.lang.String" value="Box.Filler.VerticalStrut"/>
</AuxValues>
</Component>
<Container class="javax.swing.JPanel" name="notableFilePanel">
<Properties>
<Property name="alignmentX" type="float" value="0.0"/>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[32767, 106]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[100, 106]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[100, 106]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="notableFileTable"/>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
</Container>
<Component class="javax.swing.Box$Filler" name="filler2">
<Properties>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 20]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 20]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 20]"/>
</Property>
<Property name="alignmentX" type="float" value="0.0"/>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
<AuxValue name="classDetails" type="java.lang.String" value="Box.Filler.VerticalStrut"/>
</AuxValues>
</Component>
<Component class="javax.swing.JLabel" name="sameIdLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="PastCasesPanel.sameIdLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
</Component>
<Component class="javax.swing.Box$Filler" name="filler3">
<Properties>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 2]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 2]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 2]"/>
</Property>
<Property name="alignmentX" type="float" value="0.0"/>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
<AuxValue name="classDetails" type="java.lang.String" value="Box.Filler.VerticalStrut"/>
</AuxValues>
</Component>
<Container class="javax.swing.JPanel" name="sameIdPanel">
<Properties>
<Property name="alignmentX" type="float" value="0.0"/>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[32767, 106]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[100, 106]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[100, 106]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="sameIdTable"/>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
</Container>
<Component class="javax.swing.Box$Filler" name="filler5">
<Properties>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 32767]"/>
</Property>
<Property name="alignmentX" type="float" value="0.0"/>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
<AuxValue name="classDetails" type="java.lang.String" value="Box.Filler.VerticalGlue"/>
</AuxValues>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Container>
</SubComponents>
</Form>

View File

@ -0,0 +1,227 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 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.datasourcesummary.ui;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import org.apache.commons.lang3.tuple.Pair;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.centralrepository.ingestmodule.CentralRepoIngestModuleFactory;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.PastCasesSummary;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.PastCasesSummary.PastCasesResult;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.DefaultCellModel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult.ResultType;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel.ColumnModel;
import org.sleuthkit.datamodel.DataSource;
/**
* A tab shown in data source summary displaying information about a datasource
* and how it pertains to other cases.
*/
@Messages({
"PastCasesPanel_caseColumn_title=Case",
"PastCasesPanel_countColumn_title=Count",
"PastCasesPanel_onNoCrIngest_message=No results will be shown because the Central Repository module was not run."
})
public class PastCasesPanel extends BaseDataSourceSummaryPanel {
private static final long serialVersionUID = 1L;
private static final String CR_FACTORY = CentralRepoIngestModuleFactory.class.getName();
private static final String CR_NAME = CentralRepoIngestModuleFactory.getModuleName();
private static final ColumnModel<Pair<String, Long>> CASE_COL = new ColumnModel<>(
Bundle.PastCasesPanel_caseColumn_title(),
(pair) -> new DefaultCellModel(pair.getKey()),
300
);
private static final ColumnModel<Pair<String, Long>> COUNT_COL = new ColumnModel<>(
Bundle.PastCasesPanel_countColumn_title(),
(pair) -> new DefaultCellModel(String.valueOf(pair.getValue())),
100
);
private static final List<ColumnModel<Pair<String, Long>>> DEFAULT_COLUMNS = Arrays.asList(CASE_COL, COUNT_COL);
private final JTablePanel<Pair<String, Long>> notableFileTable = JTablePanel.getJTablePanel(DEFAULT_COLUMNS);
private final JTablePanel<Pair<String, Long>> sameIdTable = JTablePanel.getJTablePanel(DEFAULT_COLUMNS);
private final List<JTablePanel<?>> tables = Arrays.asList(
notableFileTable,
sameIdTable
);
private final List<DataFetchComponents<DataSource, ?>> dataFetchComponents;
private final IngestRunningLabel ingestRunningLabel = new IngestRunningLabel();
public PastCasesPanel() {
this(new PastCasesSummary());
}
/**
* Creates new form PastCasesPanel
*/
public PastCasesPanel(PastCasesSummary pastCaseData) {
// set up data acquisition methods
dataFetchComponents = Arrays.asList(
new DataFetchWorker.DataFetchComponents<>(
(dataSource) -> pastCaseData.getPastCasesData(dataSource),
(result) -> handleResult(result))
);
initComponents();
}
/**
* Handles displaying the result for each table by breaking apart subdata
* items into seperate results for each table.
*
* @param result The result.
*/
private void handleResult(DataFetchResult<PastCasesResult> result) {
showResultWithModuleCheck(notableFileTable, getSubResult(result, (res) -> res.getTaggedNotable()), CR_FACTORY, CR_NAME);
showResultWithModuleCheck(sameIdTable, getSubResult(result, (res) -> res.getSameIdsResults()), CR_FACTORY, CR_NAME);
}
/**
* Given an input data fetch result, creates an error result if the original
* is an error. Otherwise, uses the getSubResult function on the underlying
* data to create a new DataFetchResult.
*
* @param inputResult The input result.
* @param getSubComponent The means of getting the data given the original
* data.
*
* @return The new result with the error of the original or the processed
* data.
*/
private <O> DataFetchResult<O> getSubResult(DataFetchResult<PastCasesResult> inputResult, Function<PastCasesResult, O> getSubResult) {
if (inputResult == null) {
return null;
} else if (inputResult.getResultType() == ResultType.SUCCESS) {
O innerData = (inputResult.getData() == null) ? null : getSubResult.apply(inputResult.getData());
return DataFetchResult.getSuccessResult(innerData);
} else {
return DataFetchResult.getErrorResult(inputResult.getException());
}
}
@Override
protected void fetchInformation(DataSource dataSource) {
fetchInformation(dataFetchComponents, dataSource);
}
@Override
protected void onNewDataSource(DataSource dataSource) {
onNewDataSource(dataFetchComponents, tables, dataSource);
}
@Override
public void close() {
ingestRunningLabel.unregister();
super.close();
}
/**
* 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() {
javax.swing.JScrollPane mainScrollPane = new javax.swing.JScrollPane();
javax.swing.JPanel mainContentPanel = new javax.swing.JPanel();
javax.swing.JPanel ingestRunningPanel = ingestRunningLabel;
javax.swing.JLabel notableFileLabel = new javax.swing.JLabel();
javax.swing.Box.Filler filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2));
javax.swing.JPanel notableFilePanel = notableFileTable;
javax.swing.Box.Filler filler2 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 20), new java.awt.Dimension(0, 20), new java.awt.Dimension(0, 20));
javax.swing.JLabel sameIdLabel = new javax.swing.JLabel();
javax.swing.Box.Filler filler3 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2));
javax.swing.JPanel sameIdPanel = sameIdTable;
javax.swing.Box.Filler filler5 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 32767));
mainContentPanel.setBorder(javax.swing.BorderFactory.createEmptyBorder(10, 10, 10, 10));
mainContentPanel.setLayout(new javax.swing.BoxLayout(mainContentPanel, javax.swing.BoxLayout.PAGE_AXIS));
ingestRunningPanel.setAlignmentX(0.0F);
ingestRunningPanel.setMaximumSize(new java.awt.Dimension(32767, 25));
ingestRunningPanel.setMinimumSize(new java.awt.Dimension(10, 25));
ingestRunningPanel.setPreferredSize(new java.awt.Dimension(10, 25));
mainContentPanel.add(ingestRunningPanel);
org.openide.awt.Mnemonics.setLocalizedText(notableFileLabel, org.openide.util.NbBundle.getMessage(PastCasesPanel.class, "PastCasesPanel.notableFileLabel.text")); // NOI18N
mainContentPanel.add(notableFileLabel);
notableFileLabel.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(PastCasesPanel.class, "PastCasesPanel.notableFileLabel.text")); // NOI18N
filler1.setAlignmentX(0.0F);
mainContentPanel.add(filler1);
notableFilePanel.setAlignmentX(0.0F);
notableFilePanel.setMaximumSize(new java.awt.Dimension(32767, 106));
notableFilePanel.setMinimumSize(new java.awt.Dimension(100, 106));
notableFilePanel.setPreferredSize(new java.awt.Dimension(100, 106));
mainContentPanel.add(notableFilePanel);
filler2.setAlignmentX(0.0F);
mainContentPanel.add(filler2);
org.openide.awt.Mnemonics.setLocalizedText(sameIdLabel, org.openide.util.NbBundle.getMessage(PastCasesPanel.class, "PastCasesPanel.sameIdLabel.text")); // NOI18N
mainContentPanel.add(sameIdLabel);
filler3.setAlignmentX(0.0F);
mainContentPanel.add(filler3);
sameIdPanel.setAlignmentX(0.0F);
sameIdPanel.setMaximumSize(new java.awt.Dimension(32767, 106));
sameIdPanel.setMinimumSize(new java.awt.Dimension(100, 106));
sameIdPanel.setPreferredSize(new java.awt.Dimension(100, 106));
mainContentPanel.add(sameIdPanel);
filler5.setAlignmentX(0.0F);
mainContentPanel.add(filler5);
mainScrollPane.setViewportView(mainContentPanel);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(mainScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(mainScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 300, Short.MAX_VALUE)
);
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
// End of variables declaration//GEN-END:variables
}

View File

@ -31,6 +31,11 @@
<SubComponents>
<Container class="javax.swing.JPanel" name="tablePanel">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.EmptyBorderInfo">
<EmptyBorder bottom="10" left="10" right="10" top="10"/>
</Border>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[400, 400]"/>
</Property>
@ -45,13 +50,39 @@
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
<SubComponents>
<Container class="javax.swing.JPanel" name="ingestRunningPanel">
<Properties>
<Property name="alignmentX" type="float" value="0.0"/>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[32767, 25]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[10, 25]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[10, 25]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="ingestRunningLabel"/>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
</Container>
<Container class="javax.swing.JPanel" name="openedDocPane">
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new JTablePanel&lt;RecentFileDetails&gt;()"/>
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="1" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="5" insetsLeft="5" insetsBottom="0" insetsRight="5" anchor="11" weightX="1.0" weightY="1.0"/>
<GridBagConstraints gridX="0" gridY="2" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="2" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="11" weightX="1.0" weightY="1.0"/>
</Constraint>
</Constraints>
@ -63,7 +94,7 @@
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="3" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="5" insetsLeft="5" insetsBottom="0" insetsRight="5" anchor="11" weightX="1.0" weightY="1.0"/>
<GridBagConstraints gridX="0" gridY="4" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="2" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="11" weightX="1.0" weightY="1.0"/>
</Constraint>
</Constraints>
@ -75,7 +106,7 @@
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="5" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="5" insetsLeft="5" insetsBottom="10" insetsRight="5" anchor="11" weightX="1.0" weightY="1.0"/>
<GridBagConstraints gridX="0" gridY="6" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="2" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="11" weightX="1.0" weightY="1.0"/>
</Constraint>
</Constraints>
@ -93,7 +124,7 @@
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="10" insetsLeft="5" insetsBottom="0" insetsRight="5" anchor="11" weightX="0.0" weightY="0.0"/>
<GridBagConstraints gridX="0" gridY="1" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="11" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
@ -109,7 +140,7 @@
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="2" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="15" insetsLeft="5" insetsBottom="0" insetsRight="5" anchor="10" weightX="0.0" weightY="0.0"/>
<GridBagConstraints gridX="0" gridY="3" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="20" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
@ -125,7 +156,7 @@
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="4" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="15" insetsLeft="5" insetsBottom="0" insetsRight="5" anchor="11" weightX="0.0" weightY="0.0"/>
<GridBagConstraints gridX="0" gridY="5" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="20" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="11" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>

View File

@ -21,16 +21,15 @@ package org.sleuthkit.autopsy.datasourcesummary.ui;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.IngestModuleCheckUtil;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary.RecentAttachmentDetails;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary.RecentDownloadDetails;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary.RecentFileDetails;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.DefaultCellModel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel.ColumnModel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ListTableModel;
@ -42,17 +41,22 @@ import org.sleuthkit.datamodel.DataSource;
public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
private static final long serialVersionUID = 1L;
private static final String EMAIL_PARSER_FACTORY = "org.sleuthkit.autopsy.thunderbirdparser.EmailParserModuleFactory";
private static final String EMAIL_PARSER_MODULE_NAME = Bundle.RecentFilePanel_emailParserModuleName();
private final List<JTablePanel<?>> tablePanelList = new ArrayList<>();
private final List<DataFetchWorker.DataFetchComponents<DataSource, ?>> dataFetchComponents = new ArrayList<>();
private final IngestRunningLabel ingestRunningLabel = new IngestRunningLabel();
private final RecentFilesSummary dataHandler;
@Messages({
"RecentFilesPanel_col_head_date=Date",
"RecentFilePanel_col_header_domain=Domain",
"RecentFilePanel_col_header_path=Path",
"RecentFilePanel_col_header_sender=Sender"
"RecentFilePanel_col_header_sender=Sender",
"RecentFilePanel_emailParserModuleName=Email Parser"
})
/**
@ -66,33 +70,27 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
* Creates new form RecentFilesPanel
*/
public RecentFilesPanel(RecentFilesSummary dataHandler) {
super(dataHandler);
this.dataHandler = dataHandler;
initComponents();
initalizeTables();
}
@Override
protected void fetchInformation(DataSource dataSource) {
fetchInformation(dataFetchComponents, dataSource);
}
@Override
protected void onNewDataSource(DataSource dataSource) {
// if no data source is present or the case is not open,
// set results for tables to null.
if (dataSource == null || !Case.isCaseOpen()) {
this.dataFetchComponents.forEach((item) -> item.getResultHandler()
.accept(DataFetchResult.getSuccessResult(null)));
onNewDataSource(dataFetchComponents, tablePanelList, dataSource);
}
} else {
// set tables to display loading screen
tablePanelList.forEach((table) -> table.showDefaultLoadingMessage());
// create swing workers to run for each table
List<DataFetchWorker<?, ?>> workers = dataFetchComponents
.stream()
.map((components) -> new DataFetchWorker<>(components, dataSource))
.collect(Collectors.toList());
// submit swing workers to run
submit(workers);
}
@Override
public void close() {
ingestRunningLabel.unregister();
super.close();
}
/**
@ -127,13 +125,17 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
JTablePanel<RecentFileDetails> pane = (JTablePanel<RecentFileDetails>) openedDocPane;
pane.setModel(tableModel);
pane.setColumnModel(JTablePanel.getTableColumnModel(list));
pane.setKeyFunction((recentFile) -> recentFile.getPath());
tablePanelList.add(pane);
DataFetchWorker.DataFetchComponents<DataSource, List<RecentFileDetails>> worker
= new DataFetchWorker.DataFetchComponents<>(
(dataSource) -> dataHandler.getRecentlyOpenedDocuments(dataSource, 10),
(result) -> pane.showDataFetchResult(result, JTablePanel.getDefaultErrorMessage(),
Bundle.RecentFilePanel_no_open_documents()));
(result) -> {
showResultWithModuleCheck(pane, result,
IngestModuleCheckUtil.RECENT_ACTIVITY_FACTORY,
IngestModuleCheckUtil.RECENT_ACTIVITY_MODULE_NAME);
});
dataFetchComponents.add(worker);
}
@ -161,14 +163,18 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
JTablePanel<RecentDownloadDetails> pane = (JTablePanel<RecentDownloadDetails>) downloadsPane;
pane.setModel(tableModel);
pane.setKeyFunction((download) -> download.getPath());
pane.setColumnModel(JTablePanel.getTableColumnModel(list));
tablePanelList.add(pane);
DataFetchWorker.DataFetchComponents<DataSource, List<RecentDownloadDetails>> worker
= new DataFetchWorker.DataFetchComponents<>(
(dataSource) -> dataHandler.getRecentDownloads(dataSource, 10),
(result) -> pane.showDataFetchResult(result, JTablePanel.getDefaultErrorMessage(),
Bundle.RecentFilePanel_no_open_documents()));
(result) -> {
showResultWithModuleCheck(pane, result,
IngestModuleCheckUtil.RECENT_ACTIVITY_FACTORY,
IngestModuleCheckUtil.RECENT_ACTIVITY_MODULE_NAME);
});
dataFetchComponents.add(worker);
}
@ -196,14 +202,15 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
JTablePanel<RecentAttachmentDetails> pane = (JTablePanel<RecentAttachmentDetails>) attachmentsPane;
pane.setModel(tableModel);
pane.setKeyFunction((attachment) -> attachment.getPath());
pane.setColumnModel(JTablePanel.getTableColumnModel(list));
tablePanelList.add(pane);
DataFetchWorker.DataFetchComponents<DataSource, List<RecentAttachmentDetails>> worker
= new DataFetchWorker.DataFetchComponents<>(
(dataSource) -> dataHandler.getRecentAttachments(dataSource, 10),
(result) -> pane.showDataFetchResult(result, JTablePanel.getDefaultErrorMessage(),
Bundle.RecentFilePanel_no_open_documents()));
(result) -> showResultWithModuleCheck(pane, result, EMAIL_PARSER_FACTORY, EMAIL_PARSER_MODULE_NAME)
);
dataFetchComponents.add(worker);
}
@ -220,6 +227,7 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
javax.swing.JScrollPane scrollPane = new javax.swing.JScrollPane();
javax.swing.JPanel tablePanel = new javax.swing.JPanel();
javax.swing.JPanel ingestRunningPanel = ingestRunningLabel;
openedDocPane = new JTablePanel<RecentFileDetails>();
downloadsPane = new JTablePanel<RecentDownloadDetails>();
attachmentsPane = new JTablePanel<RecentAttachmentDetails>();
@ -229,61 +237,72 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
setLayout(new java.awt.BorderLayout());
tablePanel.setBorder(javax.swing.BorderFactory.createEmptyBorder(10, 10, 10, 10));
tablePanel.setMinimumSize(new java.awt.Dimension(400, 400));
tablePanel.setPreferredSize(new java.awt.Dimension(600, 400));
tablePanel.setLayout(new java.awt.GridBagLayout());
ingestRunningPanel.setAlignmentX(0.0F);
ingestRunningPanel.setMaximumSize(new java.awt.Dimension(32767, 25));
ingestRunningPanel.setMinimumSize(new java.awt.Dimension(10, 25));
ingestRunningPanel.setPreferredSize(new java.awt.Dimension(10, 25));
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 1;
gridBagConstraints.gridy = 0;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
tablePanel.add(ingestRunningPanel, gridBagConstraints);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 2;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.weighty = 1.0;
gridBagConstraints.insets = new java.awt.Insets(5, 5, 0, 5);
gridBagConstraints.insets = new java.awt.Insets(2, 0, 0, 0);
tablePanel.add(openedDocPane, gridBagConstraints);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 3;
gridBagConstraints.gridy = 4;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.weighty = 1.0;
gridBagConstraints.insets = new java.awt.Insets(5, 5, 0, 5);
gridBagConstraints.insets = new java.awt.Insets(2, 0, 0, 0);
tablePanel.add(downloadsPane, gridBagConstraints);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 5;
gridBagConstraints.gridy = 6;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.weighty = 1.0;
gridBagConstraints.insets = new java.awt.Insets(5, 5, 10, 5);
gridBagConstraints.insets = new java.awt.Insets(2, 0, 0, 0);
tablePanel.add(attachmentsPane, gridBagConstraints);
org.openide.awt.Mnemonics.setLocalizedText(openDocsLabel, org.openide.util.NbBundle.getMessage(RecentFilesPanel.class, "RecentFilesPanel.openDocsLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.gridy = 1;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH;
gridBagConstraints.insets = new java.awt.Insets(10, 5, 0, 5);
tablePanel.add(openDocsLabel, gridBagConstraints);
org.openide.awt.Mnemonics.setLocalizedText(downloadLabel, org.openide.util.NbBundle.getMessage(RecentFilesPanel.class, "RecentFilesPanel.downloadLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 2;
gridBagConstraints.gridy = 3;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.insets = new java.awt.Insets(15, 5, 0, 5);
gridBagConstraints.insets = new java.awt.Insets(20, 0, 0, 0);
tablePanel.add(downloadLabel, gridBagConstraints);
org.openide.awt.Mnemonics.setLocalizedText(attachmentLabel, org.openide.util.NbBundle.getMessage(RecentFilesPanel.class, "RecentFilesPanel.attachmentLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 4;
gridBagConstraints.gridy = 5;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH;
gridBagConstraints.insets = new java.awt.Insets(15, 5, 0, 5);
gridBagConstraints.insets = new java.awt.Insets(20, 0, 0, 0);
tablePanel.add(attachmentLabel, gridBagConstraints);
scrollPane.setViewportView(tablePanel);
@ -297,5 +316,4 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
private javax.swing.JPanel downloadsPane;
private javax.swing.JPanel openedDocPane;
// End of variables declaration//GEN-END:variables
}

View File

@ -0,0 +1,103 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 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.datasourcesummary.ui;
import java.text.DecimalFormat;
import java.util.Arrays;
import java.util.List;
import org.openide.util.NbBundle;
/**
* This class provides utilities for representing storage size in most relevant
* units (i.e. bytes, megabytes, etc.).
*/
public final class SizeRepresentationUtil {
private static final int SIZE_CONVERSION_CONSTANT = 1000;
private static final DecimalFormat APPROXIMATE_SIZE_FORMAT = new DecimalFormat("#.##");
private static List<String> UNITS = Arrays.asList(
Bundle.SizeRepresentationUtil_units_bytes(),
Bundle.SizeRepresentationUtil_units_kilobytes(),
Bundle.SizeRepresentationUtil_units_megabytes(),
Bundle.SizeRepresentationUtil_units_gigabytes(),
Bundle.SizeRepresentationUtil_units_terabytes(),
Bundle.SizeRepresentationUtil_units_petabytes()
);
/**
* Get a long size in bytes as a string formated to be read by users.
*
* @param size Long value representing a size in bytes.
*
* @return Return a string formated with a user friendly version of the size
* as a string, returns empty String when provided empty size.
*/
public static String getSizeString(Long size) {
return getSizeString(size, APPROXIMATE_SIZE_FORMAT, true);
}
/**
* Get a long size in bytes as a string formated to be read by users.
*
* @param size Long value representing a size in byte.s
* @param format The means of formatting the number.
* @param showFullSize Optionally show the number of bytes in the
* datasource.
*
* @return Return a string formated with a user friendly version of the size
* as a string, returns empty String when provided empty size.
*/
@NbBundle.Messages({
"SizeRepresentationUtil_units_bytes= bytes",
"SizeRepresentationUtil_units_kilobytes= kB",
"SizeRepresentationUtil_units_megabytes= MB",
"SizeRepresentationUtil_units_gigabytes= GB",
"SizeRepresentationUtil_units_terabytes= TB",
"SizeRepresentationUtil_units_petabytes= PB"
})
public static String getSizeString(Long size, DecimalFormat format, boolean showFullSize) {
if (size == null) {
return "";
}
double approximateSize = size;
int unitsIndex = 0;
for (; unitsIndex < UNITS.size(); unitsIndex++) {
if (approximateSize < SIZE_CONVERSION_CONSTANT) {
break;
} else {
approximateSize /= SIZE_CONVERSION_CONSTANT;
}
}
String fullSize = size + UNITS.get(0);
String closestUnitSize = format.format(approximateSize) + UNITS.get(unitsIndex);
if (unitsIndex == 0) {
return fullSize;
} else if (showFullSize) {
return String.format("%s (%s)", closestUnitSize, fullSize);
} else {
return closestUnitSize;
}
}
private SizeRepresentationUtil() {
}
}

View File

@ -0,0 +1,285 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.8" 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"/>
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,2,20,0,0,1,-112"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
<SubComponents>
<Container class="javax.swing.JScrollPane" name="scrollParent">
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
<BorderConstraints direction="Center"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Container class="javax.swing.JPanel" name="contentParent">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.EmptyBorderInfo">
<EmptyBorder bottom="10" left="10" right="10" top="10"/>
</Border>
</Property>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[32787, 32787]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[400, 490]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBoxLayout">
<Property name="axis" type="int" value="3"/>
</Layout>
<SubComponents>
<Container class="javax.swing.JPanel" name="ingestRunningPanel">
<Properties>
<Property name="alignmentX" type="float" value="0.0"/>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[32767, 25]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[10, 25]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[10, 25]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="ingestRunningLabel"/>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
</Container>
<Container class="javax.swing.JPanel" name="usagePanel">
<Properties>
<Property name="alignmentX" type="float" value="0.0"/>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[32767, 20]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[10, 20]"/>
</Property>
<Property name="name" type="java.lang.String" value="" noResource="true"/>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[800, 20]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="usageLabel"/>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
</Container>
<Container class="javax.swing.JPanel" name="osPanel">
<Properties>
<Property name="alignmentX" type="float" value="0.0"/>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[32767, 20]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[10, 20]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[800, 20]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="osLabel"/>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
</Container>
<Container class="javax.swing.JPanel" name="sizePanel">
<Properties>
<Property name="alignmentX" type="float" value="0.0"/>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[32767, 20]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[10, 20]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[800, 20]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="sizeLabel"/>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
</Container>
<Container class="javax.swing.JPanel" name="fileMimeTypesPanel">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.EmptyBorderInfo">
<EmptyBorder bottom="5" left="5" right="5" top="5"/>
</Border>
</Property>
<Property name="alignmentX" type="float" value="0.0"/>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[400, 300]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[400, 300]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[400, 300]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="fileMimeTypesChart"/>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
</Container>
<Component class="javax.swing.Box$Filler" name="filler2">
<Properties>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[32767, 5]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 5]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 5]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
<AuxValue name="classDetails" type="java.lang.String" value="Box.Filler.VerticalStrut"/>
</AuxValues>
</Component>
<Container class="javax.swing.JPanel" name="allocatedPanel">
<Properties>
<Property name="alignmentX" type="float" value="0.0"/>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[32767, 16]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[10, 16]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[800, 16]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="allocatedLabel"/>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
</Container>
<Container class="javax.swing.JPanel" name="unallocatedPanel">
<Properties>
<Property name="alignmentX" type="float" value="0.0"/>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[32767, 16]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[10, 16]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[800, 16]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="unallocatedLabel"/>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
</Container>
<Container class="javax.swing.JPanel" name="slackPanel">
<Properties>
<Property name="alignmentX" type="float" value="0.0"/>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[32767, 16]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[10, 16]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[800, 16]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="slackLabel"/>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
</Container>
<Container class="javax.swing.JPanel" name="directoriesPanel">
<Properties>
<Property name="alignmentX" type="float" value="0.0"/>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[32767, 16]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[10, 16]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[800, 16]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="directoriesLabel"/>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
</Container>
<Component class="javax.swing.Box$Filler" name="filler3">
<Properties>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 32767]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
<AuxValue name="classDetails" type="java.lang.String" value="Box.Filler.VerticalGlue"/>
</AuxValues>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Container>
</SubComponents>
</Form>

View File

@ -0,0 +1,590 @@
/*
* 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.datasourcesummary.ui;
import java.awt.BorderLayout;
import java.awt.Color;
import java.sql.SQLException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.swing.JLabel;
import org.apache.commons.lang3.StringUtils;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.coreutils.FileTypeUtils.FileTypeCategory;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TypesSummary;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.ContainerSummary;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.IngestModuleCheckUtil;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.MimeTypeSummary;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.AbstractLoadableComponent;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult.ResultType;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableComponent;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.PieChartPanel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.PieChartPanel.PieChartItem;
import org.sleuthkit.autopsy.modules.filetypeid.FileTypeIdModuleFactory;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Panel for displaying summary information on the known files present in the
* specified DataSource.
*/
@Messages({
"TypesPanel_artifactsTypesPieChart_title=Artifact Types",
"TypesPanel_filesByCategoryTable_allocatedRow_title=Allocated Files",
"TypesPanel_filesByCategoryTable_unallocatedRow_title=Unallocated Files",
"TypesPanel_filesByCategoryTable_slackRow_title=Slack Files",
"TypesPanel_filesByCategoryTable_directoryRow_title=Directories",
"TypesPanel_fileMimeTypesChart_title=File Types",
"TypesPanel_fileMimeTypesChart_audio_title=Audio",
"TypesPanel_fileMimeTypesChart_documents_title=Documents",
"TypesPanel_fileMimeTypesChart_executables_title=Executables",
"TypesPanel_fileMimeTypesChart_images_title=Images",
"TypesPanel_fileMimeTypesChart_videos_title=Videos",
"TypesPanel_fileMimeTypesChart_other_title=Other",
"TypesPanel_fileMimeTypesChart_unknown_title=Unknown",
"TypesPanel_fileMimeTypesChart_notAnalyzed_title=Not Analyzed",
"TypesPanel_usageLabel_title=Usage",
"TypesPanel_osLabel_title=OS",
"TypesPanel_sizeLabel_title=Size"})
class TypesPanel extends BaseDataSourceSummaryPanel {
/**
* A label that allows for displaying loading messages and can be used with
* a DataFetchResult. Text displays as "<key>:<value | message>".
*/
private static class LoadableLabel extends AbstractLoadableComponent<String> {
private static final long serialVersionUID = 1L;
private final JLabel label = new JLabel();
private final String key;
/**
* Main constructor for the label.
*
* @param key The key to be displayed.
*/
LoadableLabel(String key) {
this.key = key;
setLayout(new BorderLayout());
add(label, BorderLayout.CENTER);
this.showResults(null);
}
private void setValue(String value) {
String formattedKey = StringUtils.isBlank(key) ? "" : key;
String formattedValue = StringUtils.isBlank(value) ? "" : value;
label.setText(String.format("%s: %s", formattedKey, formattedValue));
}
@Override
protected void setMessage(boolean visible, String message) {
setValue(message);
}
@Override
protected void setResults(String data) {
setValue(data);
}
}
/**
* Data for types pie chart.
*/
private static class TypesPieChartData {
private final List<PieChartItem> pieSlices;
private final boolean usefulContent;
/**
* Main constructor.
*
* @param pieSlices The pie slices.
* @param usefulContent True if this is useful content; false if there
* is 0 mime type information.
*/
public TypesPieChartData(List<PieChartItem> pieSlices, boolean usefulContent) {
this.pieSlices = pieSlices;
this.usefulContent = usefulContent;
}
/**
* @return The pie chart data.
*/
public List<PieChartItem> getPieSlices() {
return pieSlices;
}
/**
* @return Whether or not the data is usefulContent.
*/
public boolean isUsefulContent() {
return usefulContent;
}
}
/**
* Information concerning a particular category in the file types pie chart.
*/
private static class TypesPieCategory {
private final String label;
private final Set<String> mimeTypes;
private final Color color;
/**
* Main constructor.
*
* @param label The label for this slice.
* @param mimeTypes The mime types associated with this slice.
* @param color The color associated with this slice.
*/
TypesPieCategory(String label, Set<String> mimeTypes, Color color) {
this.label = label;
this.mimeTypes = mimeTypes;
this.color = color;
}
/**
* Constructor that accepts FileTypeCategory.
*
* @param label The label for this slice.
* @param mimeTypes The mime types associated with this slice.
* @param color The color associated with this slice.
*/
TypesPieCategory(String label, FileTypeCategory fileCategory, Color color) {
this(label, fileCategory.getMediaTypes(), color);
}
/**
* @return The label for this category.
*/
String getLabel() {
return label;
}
/**
* @return The mime types associated with this category.
*/
Set<String> getMimeTypes() {
return mimeTypes;
}
/**
* @return The color associated with this category.
*/
Color getColor() {
return color;
}
}
private static final long serialVersionUID = 1L;
private static final DecimalFormat INTEGER_SIZE_FORMAT = new DecimalFormat("#");
private static final DecimalFormat COMMA_FORMATTER = new DecimalFormat("#,###");
private static final String FILE_TYPE_FACTORY = FileTypeIdModuleFactory.class.getCanonicalName();
private static final String FILE_TYPE_MODULE_NAME = FileTypeIdModuleFactory.getModuleName();
private static final Logger logger = Logger.getLogger(TypesPanel.class.getName());
private static final Color IMAGES_COLOR = new Color(156, 39, 176);
private static final Color VIDEOS_COLOR = Color.YELLOW;
private static final Color AUDIO_COLOR = Color.BLUE;
private static final Color DOCUMENTS_COLOR = Color.GREEN;
private static final Color EXECUTABLES_COLOR = new Color(0, 188, 212);
private static final Color UNKNOWN_COLOR = Color.ORANGE;
private static final Color OTHER_COLOR = new Color(78, 52, 46);
private static final Color NOT_ANALYZED_COLOR = Color.WHITE;
// All file type categories.
private static final List<TypesPieCategory> FILE_MIME_TYPE_CATEGORIES = Arrays.asList(
new TypesPieCategory(Bundle.TypesPanel_fileMimeTypesChart_images_title(), FileTypeCategory.IMAGE.getMediaTypes(), IMAGES_COLOR),
new TypesPieCategory(Bundle.TypesPanel_fileMimeTypesChart_videos_title(), FileTypeCategory.VIDEO.getMediaTypes(), VIDEOS_COLOR),
new TypesPieCategory(Bundle.TypesPanel_fileMimeTypesChart_audio_title(), FileTypeCategory.AUDIO.getMediaTypes(), AUDIO_COLOR),
new TypesPieCategory(Bundle.TypesPanel_fileMimeTypesChart_documents_title(), FileTypeCategory.DOCUMENTS.getMediaTypes(), DOCUMENTS_COLOR),
new TypesPieCategory(Bundle.TypesPanel_fileMimeTypesChart_executables_title(), FileTypeCategory.EXECUTABLE.getMediaTypes(), EXECUTABLES_COLOR),
new TypesPieCategory(Bundle.TypesPanel_fileMimeTypesChart_unknown_title(), new HashSet<>(Arrays.asList("application/octet-stream")), UNKNOWN_COLOR)
);
private final LoadableLabel usageLabel = new LoadableLabel(Bundle.TypesPanel_usageLabel_title());
private final LoadableLabel osLabel = new LoadableLabel(Bundle.TypesPanel_osLabel_title());
private final LoadableLabel sizeLabel = new LoadableLabel(Bundle.TypesPanel_sizeLabel_title());
private final PieChartPanel fileMimeTypesChart = new PieChartPanel(Bundle.TypesPanel_fileMimeTypesChart_title());
private final LoadableLabel allocatedLabel = new LoadableLabel(Bundle.TypesPanel_filesByCategoryTable_allocatedRow_title());
private final LoadableLabel unallocatedLabel = new LoadableLabel(Bundle.TypesPanel_filesByCategoryTable_unallocatedRow_title());
private final LoadableLabel slackLabel = new LoadableLabel(Bundle.TypesPanel_filesByCategoryTable_slackRow_title());
private final LoadableLabel directoriesLabel = new LoadableLabel(Bundle.TypesPanel_filesByCategoryTable_directoryRow_title());
// all loadable components
private final List<LoadableComponent<?>> loadables = Arrays.asList(
usageLabel,
osLabel,
sizeLabel,
fileMimeTypesChart,
allocatedLabel,
unallocatedLabel,
slackLabel,
directoriesLabel
);
private final IngestRunningLabel ingestRunningLabel = new IngestRunningLabel();
// all of the means for obtaining data for the gui components.
private final List<DataFetchComponents<DataSource, ?>> dataFetchComponents;
/**
* Creates a new TypesPanel.
*/
public TypesPanel() {
this(new MimeTypeSummary(), new TypesSummary(), new ContainerSummary());
}
@Override
public void close() {
ingestRunningLabel.unregister();
super.close();
}
/**
* Creates a new TypesPanel.
*
* @param mimeTypeData The service for mime types.
* @param typeData The service for file types data.
* @param containerData The service for container information.
*/
public TypesPanel(
MimeTypeSummary mimeTypeData,
TypesSummary typeData,
ContainerSummary containerData) {
super(mimeTypeData, typeData, containerData);
this.dataFetchComponents = Arrays.asList(
// usage label worker
new DataFetchWorker.DataFetchComponents<>(
containerData::getDataSourceType,
(result) -> {
showResultWithModuleCheck(
usageLabel,
result,
StringUtils::isNotBlank,
IngestModuleCheckUtil.RECENT_ACTIVITY_FACTORY,
IngestModuleCheckUtil.RECENT_ACTIVITY_MODULE_NAME);
}),
// os label worker
new DataFetchWorker.DataFetchComponents<>(
containerData::getOperatingSystems,
(result) -> {
showResultWithModuleCheck(
osLabel,
result,
StringUtils::isNotBlank,
IngestModuleCheckUtil.RECENT_ACTIVITY_FACTORY,
IngestModuleCheckUtil.RECENT_ACTIVITY_MODULE_NAME);
}),
// size label worker
new DataFetchWorker.DataFetchComponents<>(
(dataSource) -> {
Long size = dataSource == null ? null : dataSource.getSize();
return SizeRepresentationUtil.getSizeString(size, INTEGER_SIZE_FORMAT, false);
},
sizeLabel::showDataFetchResult),
// file types worker
new DataFetchWorker.DataFetchComponents<>(
(dataSource) -> getMimeTypeCategoriesModel(mimeTypeData, dataSource),
this::showMimeTypeCategories),
// allocated files worker
new DataFetchWorker.DataFetchComponents<>(
(dataSource) -> getStringOrZero(typeData.getCountOfAllocatedFiles(dataSource)),
allocatedLabel::showDataFetchResult),
// unallocated files worker
new DataFetchWorker.DataFetchComponents<>(
(dataSource) -> getStringOrZero(typeData.getCountOfUnallocatedFiles(dataSource)),
unallocatedLabel::showDataFetchResult),
// slack files worker
new DataFetchWorker.DataFetchComponents<>(
(dataSource) -> getStringOrZero(typeData.getCountOfSlackFiles(dataSource)),
slackLabel::showDataFetchResult),
// directories worker
new DataFetchWorker.DataFetchComponents<>(
(dataSource) -> getStringOrZero(typeData.getCountOfDirectories(dataSource)),
directoriesLabel::showDataFetchResult)
);
initComponents();
}
@Override
protected void fetchInformation(DataSource dataSource) {
fetchInformation(dataFetchComponents, dataSource);
}
@Override
protected void onNewDataSource(DataSource dataSource) {
onNewDataSource(dataFetchComponents, loadables, dataSource);
}
/**
* Gets all the data for the file type pie chart.
*
* @param mimeTypeData The means of acquiring data.
* @param dataSource The datasource.
*
* @return The pie chart items.
*/
private TypesPieChartData getMimeTypeCategoriesModel(MimeTypeSummary mimeTypeData, DataSource dataSource)
throws SQLException, SleuthkitCaseProviderException, TskCoreException {
if (dataSource == null) {
return null;
}
// for each category of file types, get the counts of files
List<PieChartItem> fileCategoryItems = new ArrayList<>();
long categoryTotalCount = 0;
for (TypesPieCategory cat : FILE_MIME_TYPE_CATEGORIES) {
long thisValue = getLongOrZero(mimeTypeData.getCountOfFilesForMimeTypes(dataSource, cat.getMimeTypes()));
categoryTotalCount += thisValue;
fileCategoryItems.add(new PieChartItem(
cat.getLabel(),
thisValue,
cat.getColor()));
}
// get a count of all files with no mime type
long noMimeTypeCount = getLongOrZero(mimeTypeData.getCountOfFilesWithNoMimeType(dataSource));
// get a count of all regular files
long allRegularFiles = getLongOrZero(mimeTypeData.getCountOfAllRegularFiles(dataSource));
// create entry for mime types in other category
long otherCount = allRegularFiles - (categoryTotalCount + noMimeTypeCount);
PieChartItem otherPieItem = new PieChartItem(Bundle.TypesPanel_fileMimeTypesChart_other_title(),
otherCount, OTHER_COLOR);
// check at this point to see if these are all 0; if so, we don't have useful content.
boolean usefulContent = categoryTotalCount > 0 || otherCount > 0;
// create entry for not analyzed mime types category
PieChartItem notAnalyzedItem = new PieChartItem(Bundle.TypesPanel_fileMimeTypesChart_notAnalyzed_title(),
noMimeTypeCount, NOT_ANALYZED_COLOR);
// combine categories with 'other' and 'not analyzed'
List<PieChartItem> items = Stream.concat(
fileCategoryItems.stream(),
Stream.of(otherPieItem, notAnalyzedItem))
// remove items that have no value
.filter(slice -> slice.getValue() > 0)
.collect(Collectors.toList());
return new TypesPieChartData(items, usefulContent);
}
/**
* Handles properly showing data for the mime type categories pie chart
* accounting for whether there are any files with mime types specified and
* whether or not the current data source has been ingested with the file
* type ingest module.
*
* @param result The result to be shown.
*/
private void showMimeTypeCategories(DataFetchResult<TypesPieChartData> result) {
// if result is null check for ingest module and show empty results.
if (result == null) {
showPieResultWithModuleCheck(null);
return;
}
// if error, show error
if (result.getResultType() == ResultType.ERROR) {
this.fileMimeTypesChart.showDataFetchResult(DataFetchResult.getErrorResult(result.getException()));
return;
}
TypesPieChartData data = result.getData();
if (data == null) {
// if no data, do an ingest module check with empty results
showPieResultWithModuleCheck(null);
} else if (!data.isUsefulContent()) {
// if no useful data, do an ingest module check and show data
showPieResultWithModuleCheck(data.getPieSlices());
} else {
// otherwise, show the data
this.fileMimeTypesChart.showDataFetchResult(DataFetchResult.getSuccessResult(data.getPieSlices()));
}
}
/**
* Shows a message in the fileMimeTypesChart about the data source not being
* ingested with the file type ingest module if the data source has not been
* ingested with that module. Also shows data if present.
*
* @param items The list of items to show.
*/
private void showPieResultWithModuleCheck(List<PieChartItem> items) {
boolean hasBeenIngested = false;
try {
hasBeenIngested = this.getIngestModuleCheckUtil().isModuleIngested(getDataSource(), FILE_TYPE_FACTORY);
} catch (TskCoreException | SleuthkitCaseProviderException ex) {
logger.log(Level.WARNING, "There was an error fetching whether or not the current data source has been ingested with the file type ingest module.", ex);
}
if (hasBeenIngested) {
this.fileMimeTypesChart.showDataFetchResult(DataFetchResult.getSuccessResult(items));
} else {
this.fileMimeTypesChart.showDataWithMessage(items, getDefaultNoIngestMessage(FILE_TYPE_MODULE_NAME));
}
}
/**
* Returns the long value or zero if longVal is null.
*
* @param longVal The long value.
*
* @return The long value or 0 if provided value is null.
*/
private static long getLongOrZero(Long longVal) {
return longVal == null ? 0 : longVal;
}
/**
* Returns string value of long with comma separators. If null returns a
* string of '0'.
*
* @param longVal The long value.
*
* @return The string value of the long.
*/
private static String getStringOrZero(Long longVal) {
return longVal == null ? "0" : COMMA_FORMATTER.format(longVal);
}
/**
* 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() {
javax.swing.JScrollPane scrollParent = new javax.swing.JScrollPane();
javax.swing.JPanel contentParent = new javax.swing.JPanel();
javax.swing.JPanel ingestRunningPanel = ingestRunningLabel;
javax.swing.JPanel usagePanel = usageLabel;
javax.swing.JPanel osPanel = osLabel;
javax.swing.JPanel sizePanel = sizeLabel;
javax.swing.JPanel fileMimeTypesPanel = fileMimeTypesChart;
javax.swing.Box.Filler filler2 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 5), new java.awt.Dimension(0, 5), new java.awt.Dimension(32767, 5));
javax.swing.JPanel allocatedPanel = allocatedLabel;
javax.swing.JPanel unallocatedPanel = unallocatedLabel;
javax.swing.JPanel slackPanel = slackLabel;
javax.swing.JPanel directoriesPanel = directoriesLabel;
javax.swing.Box.Filler filler3 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 32767));
setLayout(new java.awt.BorderLayout());
contentParent.setBorder(javax.swing.BorderFactory.createEmptyBorder(10, 10, 10, 10));
contentParent.setMaximumSize(new java.awt.Dimension(32787, 32787));
contentParent.setMinimumSize(new java.awt.Dimension(400, 490));
contentParent.setLayout(new javax.swing.BoxLayout(contentParent, javax.swing.BoxLayout.PAGE_AXIS));
ingestRunningPanel.setAlignmentX(0.0F);
ingestRunningPanel.setMaximumSize(new java.awt.Dimension(32767, 25));
ingestRunningPanel.setMinimumSize(new java.awt.Dimension(10, 25));
ingestRunningPanel.setPreferredSize(new java.awt.Dimension(10, 25));
contentParent.add(ingestRunningPanel);
usagePanel.setAlignmentX(0.0F);
usagePanel.setMaximumSize(new java.awt.Dimension(32767, 20));
usagePanel.setMinimumSize(new java.awt.Dimension(10, 20));
usagePanel.setName(""); // NOI18N
usagePanel.setPreferredSize(new java.awt.Dimension(800, 20));
contentParent.add(usagePanel);
osPanel.setAlignmentX(0.0F);
osPanel.setMaximumSize(new java.awt.Dimension(32767, 20));
osPanel.setMinimumSize(new java.awt.Dimension(10, 20));
osPanel.setPreferredSize(new java.awt.Dimension(800, 20));
contentParent.add(osPanel);
sizePanel.setAlignmentX(0.0F);
sizePanel.setMaximumSize(new java.awt.Dimension(32767, 20));
sizePanel.setMinimumSize(new java.awt.Dimension(10, 20));
sizePanel.setPreferredSize(new java.awt.Dimension(800, 20));
contentParent.add(sizePanel);
fileMimeTypesPanel.setBorder(javax.swing.BorderFactory.createEmptyBorder(5, 5, 5, 5));
fileMimeTypesPanel.setAlignmentX(0.0F);
fileMimeTypesPanel.setMaximumSize(new java.awt.Dimension(400, 300));
fileMimeTypesPanel.setMinimumSize(new java.awt.Dimension(400, 300));
fileMimeTypesPanel.setPreferredSize(new java.awt.Dimension(400, 300));
contentParent.add(fileMimeTypesPanel);
contentParent.add(filler2);
allocatedPanel.setAlignmentX(0.0F);
allocatedPanel.setMaximumSize(new java.awt.Dimension(32767, 16));
allocatedPanel.setMinimumSize(new java.awt.Dimension(10, 16));
allocatedPanel.setPreferredSize(new java.awt.Dimension(800, 16));
contentParent.add(allocatedPanel);
unallocatedPanel.setAlignmentX(0.0F);
unallocatedPanel.setMaximumSize(new java.awt.Dimension(32767, 16));
unallocatedPanel.setMinimumSize(new java.awt.Dimension(10, 16));
unallocatedPanel.setPreferredSize(new java.awt.Dimension(800, 16));
contentParent.add(unallocatedPanel);
slackPanel.setAlignmentX(0.0F);
slackPanel.setMaximumSize(new java.awt.Dimension(32767, 16));
slackPanel.setMinimumSize(new java.awt.Dimension(10, 16));
slackPanel.setPreferredSize(new java.awt.Dimension(800, 16));
contentParent.add(slackPanel);
directoriesPanel.setAlignmentX(0.0F);
directoriesPanel.setMaximumSize(new java.awt.Dimension(32767, 16));
directoriesPanel.setMinimumSize(new java.awt.Dimension(10, 16));
directoriesPanel.setPreferredSize(new java.awt.Dimension(800, 16));
contentParent.add(directoriesPanel);
contentParent.add(filler3);
scrollParent.setViewportView(contentParent);
add(scrollParent, java.awt.BorderLayout.CENTER);
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
// End of variables declaration//GEN-END:variables
}

View File

@ -60,11 +60,32 @@
<Property name="axis" type="int" value="3"/>
</Layout>
<SubComponents>
<Container class="javax.swing.JPanel" name="ingestRunningPanel">
<Properties>
<Property name="alignmentX" type="float" value="0.0"/>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[32767, 25]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[10, 25]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[10, 25]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="ingestRunningLabel"/>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
</Container>
<Component class="javax.swing.JLabel" name="programsRunLabel">
<Properties>
<Property name="horizontalAlignment" type="int" value="2"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="DataSourceSummaryUserActivityPanel.programsRunLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="UserActivityPanel.programsRunLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="alignmentX" type="float" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="Component.LEFT_ALIGNMENT" type="code"/>
@ -136,7 +157,7 @@
<Properties>
<Property name="horizontalAlignment" type="int" value="2"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="DataSourceSummaryUserActivityPanel.recentDomainsLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="UserActivityPanel.recentDomainsLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<AuxValues>
@ -205,7 +226,7 @@
<Properties>
<Property name="horizontalAlignment" type="int" value="2"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="DataSourceSummaryUserActivityPanel.topWebSearchLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="UserActivityPanel.topWebSearchLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<AuxValues>
@ -274,7 +295,7 @@
<Properties>
<Property name="horizontalAlignment" type="int" value="2"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="DataSourceSummaryUserActivityPanel.topDevicesAttachedLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="UserActivityPanel.topDevicesAttachedLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<AuxValues>
@ -343,7 +364,7 @@
<Properties>
<Property name="horizontalAlignment" type="int" value="2"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="DataSourceSummaryUserActivityPanel.recentAccountsLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="UserActivityPanel.recentAccountsLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<AuxValues>

View File

@ -25,21 +25,19 @@ import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;
import org.apache.commons.lang.StringUtils;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataSourceUserActivitySummary;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataSourceTopProgramsSummary;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataSourceUserActivitySummary.TopAccountResult;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataSourceUserActivitySummary.TopDeviceAttachedResult;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataSourceUserActivitySummary.TopWebSearchResult;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TopDomainsResult;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TopProgramsResult;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.IngestModuleCheckUtil;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TopProgramsSummary;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopAccountResult;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopDeviceAttachedResult;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopWebSearchResult;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TopProgramsSummary.TopProgramsResult;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopDomainsResult;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.DefaultCellModel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel.ColumnModel;
import org.sleuthkit.datamodel.DataSource;
@ -48,24 +46,24 @@ import org.sleuthkit.datamodel.DataSource;
* A panel to display user activity.
*/
@Messages({
"DataSourceSummaryUserActivityPanel_tab_title=User Activity",
"DataSourceSummaryUserActivityPanel_TopProgramsTableModel_name_header=Program",
"DataSourceSummaryUserActivityPanel_TopProgramsTableModel_folder_header=Folder",
"DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header=Run Times",
"DataSourceSummaryUserActivityPanel_TopProgramsTableModel_lastrun_header=Last Run",
"DataSourceSummaryUserActivityPanel_TopDomainsTableModel_domain_header=Domain",
"DataSourceSummaryUserActivityPanel_TopDomainsTableModel_url_header=URL",
"DataSourceSummaryUserActivityPanel_TopDomainsTableModel_lastAccess_header=Last Access",
"DataSourceSummaryUserActivityPanel_noDataExists=No communication data exists",
"DataSourceSummaryUserActivityPanel_TopWebSearchTableModel_searchString_header=Search String",
"DataSourceSummaryUserActivityPanel_TopWebSearchTableModel_dateAccessed_header=Date Accessed",
"DataSourceSummaryUserActivityPanel_TopWebSearchTableModel_translatedResult_header=Translated",
"DataSourceSummaryUserActivityPanel_TopDeviceAttachedTableModel_deviceId_header=Device Id",
"DataSourceSummaryUserActivityPanel_TopDeviceAttachedTableModel_makeModel_header=Make and Model",
"DataSourceSummaryUserActivityPanel_TopDeviceAttachedTableModel_dateAccessed_header=Last Accessed",
"DataSourceSummaryUserActivityPanel_TopAccountTableModel_accountType_header=Account Type",
"DataSourceSummaryUserActivityPanel_TopAccountTableModel_lastAccess_header=Last Accessed",})
public class DataSourceSummaryUserActivityPanel extends BaseDataSourceSummaryPanel {
"UserActivityPanel_tab_title=User Activity",
"UserActivityPanel_TopProgramsTableModel_name_header=Program",
"UserActivityPanel_TopProgramsTableModel_folder_header=Folder",
"UserActivityPanel_TopProgramsTableModel_count_header=Run Times",
"UserActivityPanel_TopProgramsTableModel_lastrun_header=Last Run",
"UserActivityPanel_TopDomainsTableModel_domain_header=Domain",
"UserActivityPanel_TopDomainsTableModel_count_header=Visits",
"UserActivityPanel_TopDomainsTableModel_lastAccess_header=Last Accessed",
"UserActivityPanel_TopWebSearchTableModel_searchString_header=Search String",
"UserActivityPanel_TopWebSearchTableModel_dateAccessed_header=Date Accessed",
"UserActivityPanel_TopWebSearchTableModel_translatedResult_header=Translated",
"UserActivityPanel_TopDeviceAttachedTableModel_deviceId_header=Device Id",
"UserActivityPanel_TopDeviceAttachedTableModel_makeModel_header=Make and Model",
"UserActivityPanel_TopDeviceAttachedTableModel_dateAccessed_header=Last Accessed",
"UserActivityPanel_TopAccountTableModel_accountType_header=Account Type",
"UserActivityPanel_TopAccountTableModel_lastAccess_header=Last Accessed",
"UserActivityPanel_noDataExists=No communication data exists"})
public class UserActivityPanel extends BaseDataSourceSummaryPanel {
private static final long serialVersionUID = 1L;
private static final DateFormat DATETIME_FORMAT = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.getDefault());
@ -74,6 +72,8 @@ public class DataSourceSummaryUserActivityPanel extends BaseDataSourceSummaryPan
private static final int TOP_SEARCHES_COUNT = 10;
private static final int TOP_ACCOUNTS_COUNT = 5;
private static final int TOP_DEVICES_COUNT = 10;
private static final String ANDROID_FACTORY = "org.python.proxies.module$AndroidModuleFactory";
private static final String ANDROID_MODULE_NAME = "Android Analyzer";
/**
* Gets a string formatted date or returns empty string if the date is null.
@ -89,8 +89,8 @@ public class DataSourceSummaryUserActivityPanel extends BaseDataSourceSummaryPan
// set up recent programs table
private final JTablePanel<TopProgramsResult> topProgramsTable = JTablePanel.getJTablePanel(Arrays.asList(
// program name column
new ColumnModel<>(
Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_name_header(),
new ColumnModel<TopProgramsResult>(
Bundle.UserActivityPanel_TopProgramsTableModel_name_header(),
(prog) -> {
return new DefaultCellModel(prog.getProgramName())
.setTooltip(prog.getProgramPath());
@ -98,7 +98,7 @@ public class DataSourceSummaryUserActivityPanel extends BaseDataSourceSummaryPan
250),
// program folder column
new ColumnModel<>(
Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_folder_header(),
Bundle.UserActivityPanel_TopProgramsTableModel_folder_header(),
(prog) -> {
return new DefaultCellModel(
getShortFolderName(
@ -109,7 +109,7 @@ public class DataSourceSummaryUserActivityPanel extends BaseDataSourceSummaryPan
150),
// run count column
new ColumnModel<>(
Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header(),
Bundle.UserActivityPanel_TopProgramsTableModel_count_header(),
(prog) -> {
String runTimes = prog.getRunTimes() == null ? "" : Long.toString(prog.getRunTimes());
return new DefaultCellModel(runTimes);
@ -117,69 +117,75 @@ public class DataSourceSummaryUserActivityPanel extends BaseDataSourceSummaryPan
80),
// last run date column
new ColumnModel<>(
Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_lastrun_header(),
Bundle.UserActivityPanel_TopProgramsTableModel_lastrun_header(),
(prog) -> new DefaultCellModel(getFormatted(prog.getLastRun())),
150)
));
))
.setKeyFunction((prog) -> prog.getProgramPath() + ":" + prog.getProgramName());
// set up recent domains table
private final JTablePanel<TopDomainsResult> recentDomainsTable = JTablePanel.getJTablePanel(Arrays.asList(
// domain column
new ColumnModel<>(
Bundle.DataSourceSummaryUserActivityPanel_TopDomainsTableModel_domain_header(),
new ColumnModel<TopDomainsResult>(
Bundle.UserActivityPanel_TopDomainsTableModel_domain_header(),
(recentDomain) -> new DefaultCellModel(recentDomain.getDomain()),
250),
// url column
// count column
new ColumnModel<>(
Bundle.DataSourceSummaryUserActivityPanel_TopDomainsTableModel_url_header(),
(recentDomain) -> new DefaultCellModel(recentDomain.getUrl()),
250),
Bundle.UserActivityPanel_TopDomainsTableModel_count_header(),
(recentDomain) -> {
String visitTimes = recentDomain.getVisitTimes() == null ? "" : Long.toString(recentDomain.getVisitTimes());
return new DefaultCellModel(visitTimes);
},
100),
// last accessed column
new ColumnModel<>(
Bundle.DataSourceSummaryUserActivityPanel_TopDomainsTableModel_lastAccess_header(),
Bundle.UserActivityPanel_TopDomainsTableModel_lastAccess_header(),
(recentDomain) -> new DefaultCellModel(getFormatted(recentDomain.getLastVisit())),
150)
));
))
.setKeyFunction((domain) -> domain.getDomain());
// top web searches table
private final JTablePanel<TopWebSearchResult> topWebSearchesTable = JTablePanel.getJTablePanel(Arrays.asList(
// search string column
new ColumnModel<>(
Bundle.DataSourceSummaryUserActivityPanel_TopWebSearchTableModel_searchString_header(),
new ColumnModel<TopWebSearchResult>(
Bundle.UserActivityPanel_TopWebSearchTableModel_searchString_header(),
(webSearch) -> new DefaultCellModel(webSearch.getSearchString()),
250
),
// last accessed
new ColumnModel<>(
Bundle.DataSourceSummaryUserActivityPanel_TopWebSearchTableModel_dateAccessed_header(),
Bundle.UserActivityPanel_TopWebSearchTableModel_dateAccessed_header(),
(webSearch) -> new DefaultCellModel(getFormatted(webSearch.getDateAccessed())),
150
),
// translated value
new ColumnModel<>(
Bundle.DataSourceSummaryUserActivityPanel_TopWebSearchTableModel_translatedResult_header(),
Bundle.UserActivityPanel_TopWebSearchTableModel_translatedResult_header(),
(webSearch) -> new DefaultCellModel(webSearch.getTranslatedResult()),
250
)
));
))
.setKeyFunction((query) -> query.getSearchString());
// top devices attached table
private final JTablePanel<TopDeviceAttachedResult> topDevicesAttachedTable = JTablePanel.getJTablePanel(Arrays.asList(
// device id column
new ColumnModel<>(
Bundle.DataSourceSummaryUserActivityPanel_TopDeviceAttachedTableModel_deviceId_header(),
new ColumnModel<TopDeviceAttachedResult>(
Bundle.UserActivityPanel_TopDeviceAttachedTableModel_deviceId_header(),
(device) -> new DefaultCellModel(device.getDeviceId()),
250
),
// last accessed
new ColumnModel<>(
Bundle.DataSourceSummaryUserActivityPanel_TopDeviceAttachedTableModel_dateAccessed_header(),
Bundle.UserActivityPanel_TopDeviceAttachedTableModel_dateAccessed_header(),
(device) -> new DefaultCellModel(getFormatted(device.getDateAccessed())),
150
),
// make and model
new ColumnModel<>(
Bundle.DataSourceSummaryUserActivityPanel_TopDeviceAttachedTableModel_makeModel_header(),
Bundle.UserActivityPanel_TopDeviceAttachedTableModel_makeModel_header(),
(device) -> {
String make = StringUtils.isBlank(device.getDeviceMake()) ? "" : device.getDeviceMake().trim();
String model = StringUtils.isBlank(device.getDeviceModel()) ? "" : device.getDeviceModel().trim();
@ -190,23 +196,25 @@ public class DataSourceSummaryUserActivityPanel extends BaseDataSourceSummaryPan
},
250
)
));
))
.setKeyFunction((topDevice) -> topDevice.getDeviceId());
// top accounts table
private final JTablePanel<TopAccountResult> topAccountsTable = JTablePanel.getJTablePanel(Arrays.asList(
// account type column
new ColumnModel<>(
Bundle.DataSourceSummaryUserActivityPanel_TopAccountTableModel_accountType_header(),
new ColumnModel<TopAccountResult>(
Bundle.UserActivityPanel_TopAccountTableModel_accountType_header(),
(account) -> new DefaultCellModel(account.getAccountType()),
250
),
// last accessed
new ColumnModel<>(
Bundle.DataSourceSummaryUserActivityPanel_TopAccountTableModel_lastAccess_header(),
Bundle.UserActivityPanel_TopAccountTableModel_lastAccess_header(),
(account) -> new DefaultCellModel(getFormatted(account.getLastAccess())),
150
)
));
))
.setKeyFunction((topAccount) -> topAccount.getAccountType());
private final List<JTablePanel<?>> tables = Arrays.asList(
topProgramsTable,
@ -216,56 +224,75 @@ public class DataSourceSummaryUserActivityPanel extends BaseDataSourceSummaryPan
topAccountsTable
);
private final IngestRunningLabel ingestRunningLabel = new IngestRunningLabel();
private final List<DataFetchComponents<DataSource, ?>> dataFetchComponents;
private final DataSourceTopProgramsSummary topProgramsData;
private final TopProgramsSummary topProgramsData;
/**
* Creates a new DataSourceSummaryUserActivityPanel.
* Creates a new UserActivityPanel.
*/
public DataSourceSummaryUserActivityPanel() {
this(new DataSourceTopProgramsSummary(), new DataSourceUserActivitySummary());
public UserActivityPanel() {
this(new TopProgramsSummary(), new UserActivitySummary());
}
/**
* Creates a new DataSourceSummaryUserActivityPanel.
* Creates a new UserActivityPanel.
*
* @param topProgramsData Class from which to obtain top programs data.
* @param userActivityData Class from which to obtain remaining user
* activity data.
*/
public DataSourceSummaryUserActivityPanel(
DataSourceTopProgramsSummary topProgramsData,
DataSourceUserActivitySummary userActivityData) {
public UserActivityPanel(
TopProgramsSummary topProgramsData,
UserActivitySummary userActivityData) {
super(topProgramsData, userActivityData);
this.topProgramsData = topProgramsData;
// set up data acquisition methods
this.dataFetchComponents = Arrays.asList(
// top programs query
new DataFetchComponents<>(
new DataFetchComponents<DataSource, List<TopProgramsResult>>(
(dataSource) -> topProgramsData.getTopPrograms(dataSource, TOP_PROGS_COUNT),
(result) -> topProgramsTable.showDataFetchResult(result, JTablePanel.getDefaultErrorMessage(),
Bundle.DataSourceSummaryUserActivityPanel_noDataExists())),
(result) -> {
showResultWithModuleCheck(topProgramsTable, result,
IngestModuleCheckUtil.RECENT_ACTIVITY_FACTORY,
IngestModuleCheckUtil.RECENT_ACTIVITY_MODULE_NAME);
}),
// top domains query
new DataFetchComponents<>(
new DataFetchComponents<DataSource, List<TopDomainsResult>>(
(dataSource) -> userActivityData.getRecentDomains(dataSource, TOP_DOMAINS_COUNT),
(result) -> recentDomainsTable.showDataFetchResult(result, JTablePanel.getDefaultErrorMessage(),
Bundle.DataSourceSummaryUserActivityPanel_noDataExists())),
(result) -> {
showResultWithModuleCheck(recentDomainsTable, result,
IngestModuleCheckUtil.RECENT_ACTIVITY_FACTORY,
IngestModuleCheckUtil.RECENT_ACTIVITY_MODULE_NAME);
}),
// top web searches query
new DataFetchComponents<>(
new DataFetchComponents<DataSource, List<TopWebSearchResult>>(
(dataSource) -> userActivityData.getMostRecentWebSearches(dataSource, TOP_SEARCHES_COUNT),
(result) -> topWebSearchesTable.showDataFetchResult(result, JTablePanel.getDefaultErrorMessage(),
Bundle.DataSourceSummaryUserActivityPanel_noDataExists())),
(result) -> {
showResultWithModuleCheck(topWebSearchesTable, result,
IngestModuleCheckUtil.RECENT_ACTIVITY_FACTORY,
IngestModuleCheckUtil.RECENT_ACTIVITY_MODULE_NAME);
}),
// top devices query
new DataFetchComponents<>(
new DataFetchComponents<DataSource, List<TopDeviceAttachedResult>>(
(dataSource) -> userActivityData.getRecentDevices(dataSource, TOP_DEVICES_COUNT),
(result) -> topDevicesAttachedTable.showDataFetchResult(result, JTablePanel.getDefaultErrorMessage(),
Bundle.DataSourceSummaryUserActivityPanel_noDataExists())),
(result) -> {
showResultWithModuleCheck(topDevicesAttachedTable, result,
IngestModuleCheckUtil.RECENT_ACTIVITY_FACTORY,
IngestModuleCheckUtil.RECENT_ACTIVITY_MODULE_NAME);
}),
// top accounts query
new DataFetchComponents<>(
new DataFetchComponents<DataSource, List<TopAccountResult>>(
(dataSource) -> userActivityData.getRecentAccounts(dataSource, TOP_ACCOUNTS_COUNT),
(result) -> topAccountsTable.showDataFetchResult(result, JTablePanel.getDefaultErrorMessage(),
Bundle.DataSourceSummaryUserActivityPanel_noDataExists()))
(result) -> {
showResultWithModuleCheck(topAccountsTable, result,
ANDROID_FACTORY,
ANDROID_MODULE_NAME);
})
);
initComponents();
@ -283,27 +310,20 @@ public class DataSourceSummaryUserActivityPanel extends BaseDataSourceSummaryPan
return this.topProgramsData.getShortFolderName(path, appName);
}
@Override
protected void fetchInformation(DataSource dataSource) {
fetchInformation(dataFetchComponents, dataSource);
}
@Override
protected void onNewDataSource(DataSource dataSource) {
// if no data source is present or the case is not open,
// set results for tables to null.
if (dataSource == null || !Case.isCaseOpen()) {
this.dataFetchComponents.forEach((item) -> item.getResultHandler()
.accept(DataFetchResult.getSuccessResult(null)));
onNewDataSource(dataFetchComponents, tables, dataSource);
}
} else {
// set tables to display loading screen
this.tables.forEach((table) -> table.showDefaultLoadingMessage());
// create swing workers to run for each table
List<DataFetchWorker<?, ?>> workers = dataFetchComponents
.stream()
.map((components) -> new DataFetchWorker<>(components, dataSource))
.collect(Collectors.toList());
// submit swing workers to run
submit(workers);
}
@Override
public void close() {
ingestRunningLabel.unregister();
super.close();
}
/**
@ -317,6 +337,7 @@ public class DataSourceSummaryUserActivityPanel extends BaseDataSourceSummaryPan
javax.swing.JScrollPane contentScrollPane = new javax.swing.JScrollPane();
javax.swing.JPanel contentPanel = new javax.swing.JPanel();
javax.swing.JPanel ingestRunningPanel = ingestRunningLabel;
javax.swing.JLabel programsRunLabel = new javax.swing.JLabel();
javax.swing.Box.Filler filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2));
javax.swing.JPanel topProgramsTablePanel = topProgramsTable;
@ -347,8 +368,14 @@ public class DataSourceSummaryUserActivityPanel extends BaseDataSourceSummaryPan
contentPanel.setMinimumSize(new java.awt.Dimension(10, 450));
contentPanel.setLayout(new javax.swing.BoxLayout(contentPanel, javax.swing.BoxLayout.PAGE_AXIS));
ingestRunningPanel.setAlignmentX(0.0F);
ingestRunningPanel.setMaximumSize(new java.awt.Dimension(32767, 25));
ingestRunningPanel.setMinimumSize(new java.awt.Dimension(10, 25));
ingestRunningPanel.setPreferredSize(new java.awt.Dimension(10, 25));
contentPanel.add(ingestRunningPanel);
programsRunLabel.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
org.openide.awt.Mnemonics.setLocalizedText(programsRunLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryUserActivityPanel.class, "DataSourceSummaryUserActivityPanel.programsRunLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(programsRunLabel, org.openide.util.NbBundle.getMessage(UserActivityPanel.class, "UserActivityPanel.programsRunLabel.text")); // NOI18N
programsRunLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
contentPanel.add(programsRunLabel);
contentPanel.add(filler1);
@ -361,7 +388,7 @@ public class DataSourceSummaryUserActivityPanel extends BaseDataSourceSummaryPan
contentPanel.add(filler3);
recentDomainsLabel.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
org.openide.awt.Mnemonics.setLocalizedText(recentDomainsLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryUserActivityPanel.class, "DataSourceSummaryUserActivityPanel.recentDomainsLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(recentDomainsLabel, org.openide.util.NbBundle.getMessage(UserActivityPanel.class, "UserActivityPanel.recentDomainsLabel.text")); // NOI18N
contentPanel.add(recentDomainsLabel);
contentPanel.add(filler2);
@ -373,7 +400,7 @@ public class DataSourceSummaryUserActivityPanel extends BaseDataSourceSummaryPan
contentPanel.add(filler4);
topWebSearchLabel.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
org.openide.awt.Mnemonics.setLocalizedText(topWebSearchLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryUserActivityPanel.class, "DataSourceSummaryUserActivityPanel.topWebSearchLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(topWebSearchLabel, org.openide.util.NbBundle.getMessage(UserActivityPanel.class, "UserActivityPanel.topWebSearchLabel.text")); // NOI18N
contentPanel.add(topWebSearchLabel);
contentPanel.add(filler5);
@ -385,7 +412,7 @@ public class DataSourceSummaryUserActivityPanel extends BaseDataSourceSummaryPan
contentPanel.add(filler6);
topDevicesAttachedLabel.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
org.openide.awt.Mnemonics.setLocalizedText(topDevicesAttachedLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryUserActivityPanel.class, "DataSourceSummaryUserActivityPanel.topDevicesAttachedLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(topDevicesAttachedLabel, org.openide.util.NbBundle.getMessage(UserActivityPanel.class, "UserActivityPanel.topDevicesAttachedLabel.text")); // NOI18N
contentPanel.add(topDevicesAttachedLabel);
contentPanel.add(filler7);
@ -397,7 +424,7 @@ public class DataSourceSummaryUserActivityPanel extends BaseDataSourceSummaryPan
contentPanel.add(filler8);
recentAccountsLabel.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
org.openide.awt.Mnemonics.setLocalizedText(recentAccountsLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryUserActivityPanel.class, "DataSourceSummaryUserActivityPanel.recentAccountsLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(recentAccountsLabel, org.openide.util.NbBundle.getMessage(UserActivityPanel.class, "UserActivityPanel.recentAccountsLabel.text")); // NOI18N
contentPanel.add(recentAccountsLabel);
contentPanel.add(filler9);

View File

@ -0,0 +1,170 @@
/*
* 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.datasourcesummary.uiutils;
import java.util.Collection;
import java.util.logging.Level;
import javax.swing.JPanel;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.Logger;
/**
* Abstract class for common methods of a loadable component.
*/
@NbBundle.Messages({
"AbstractLoadableComponent_loadingMessage_defaultText=Loading results...",
"AbstractLoadableComponent_errorMessage_defaultText=There was an error loading results.",
"AbstractLoadableComponent_noDataExists_defaultText=No data exists.",})
public abstract class AbstractLoadableComponent<T> extends JPanel implements LoadableComponent<T> {
private static final long serialVersionUID = 1L;
/**
* The default loading message.
*/
public static final String DEFAULT_LOADING_MESSAGE = Bundle.AbstractLoadableComponent_loadingMessage_defaultText();
/**
* The default error message.
*/
public static final String DEFAULT_ERROR_MESSAGE = Bundle.AbstractLoadableComponent_errorMessage_defaultText();
/**
* The default 'no results' message.
*/
public static final String DEFAULT_NO_RESULTS_MESSAGE = Bundle.AbstractLoadableComponent_noDataExists_defaultText();
private static final Logger logger = Logger.getLogger(AbstractLoadableComponent.class.getName());
/**
* @return The default error message.
*/
public static String getDefaultErrorMessage() {
return DEFAULT_ERROR_MESSAGE;
}
/**
* @return The default message for no results.
*/
public static String getDefaultNoResultsMessage() {
return DEFAULT_NO_RESULTS_MESSAGE;
}
/**
* Clears the results from the underlying JTable and shows the provided
* message.
*
* @param message The message to be shown.
*/
public synchronized void showMessage(String message) {
setResults(null);
setMessage(true, message);
repaint();
}
/**
* Shows a default loading message on the table. This will clear any results
* in the table.
*/
public void showDefaultLoadingMessage() {
showMessage(DEFAULT_LOADING_MESSAGE);
}
/**
* Shows the list as rows of data in the table. If overlay message will be
* cleared if present.
*
* @param data The data to be shown where each item represents a row of
* data.
*/
public synchronized void showResults(T data) {
setMessage(false, null);
setResults(data);
repaint();
}
/**
* Shows the data in a DataFetchResult. If there was an error during the
* operation, the errorMessage will be displayed. If the operation completed
* successfully and no data is present, noResultsMessage will be shown.
* Otherwise, the data will be shown as rows in the table.
*
* @param result The DataFetchResult.
* @param errorMessage The error message to be shown in the event of an
* error.
* @param noResultsMessage The message to be shown if there are no results
* but the operation completed successfully.
*/
public void showDataFetchResult(DataFetchResult<T> result, String errorMessage, String noResultsMessage) {
if (result == null) {
logger.log(Level.SEVERE, "Null data fetch result received.");
return;
}
switch (result.getResultType()) {
case SUCCESS:
T data = result.getData();
if (data == null || (data instanceof Collection<?> && ((Collection<?>) data).isEmpty())) {
showMessage(noResultsMessage);
} else {
showResults(data);
}
break;
case ERROR:
// if there is an error, log accordingly, set result list to
// empty and display error message
logger.log(Level.WARNING, "An exception was caused while results were loaded.", result.getException());
showMessage(errorMessage);
break;
default:
// an unknown loading state was specified. log accordingly.
logger.log(Level.SEVERE, "No known loading state was found in result.");
break;
}
}
/**
* Shows the data in a DataFetchResult. If there was an error during the
* operation, the DEFAULT_ERROR_MESSAGE will be displayed. If the operation
* completed successfully and no data is present, DEFAULT_NO_RESULTS_MESSAGE
* will be shown. Otherwise, the data will be shown as rows in the table.
*
* @param result The DataFetchResult.
*/
public void showDataFetchResult(DataFetchResult<T> result) {
showDataFetchResult(result, DEFAULT_ERROR_MESSAGE, DEFAULT_NO_RESULTS_MESSAGE);
}
/**
* Sets the message and visibility of the message. Repaint does not need to
* be handled in this method.
*
* @param visible The visibility of the message.
* @param message The message to be displayed if visible.
*/
protected abstract void setMessage(boolean visible, String message);
/**
* Sets the data to be shown in the JTable. Repaint does not need to be
* handled in this method.
*
* @param data The list of data objects to be shown.
*/
protected abstract void setResults(T data);
}

View File

@ -0,0 +1,106 @@
/*
* 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.datasourcesummary.uiutils;
import java.awt.Graphics;
import javax.swing.JLabel;
/**
* Base class for drawing a message overlay. Contains a paint method for
* painting a JLabel using a java.awt.Graphics object.
*/
public class BaseMessageOverlay {
private final JLabel label;
private boolean visible = false;
/**
* Main constructor for the Overlay.
*/
public BaseMessageOverlay() {
label = new JLabel();
label.setHorizontalAlignment(JLabel.CENTER);
label.setVerticalAlignment(JLabel.CENTER);
label.setOpaque(false);
}
/**
* @return Whether or not this message overlay should be visible.
*/
public boolean isVisible() {
return visible;
}
/**
* Sets this layer visible when painted. In order to be shown in UI, this
* component needs to be repainted.
*
* @param visible Whether or not it is visible.
*/
public void setVisible(boolean visible) {
this.visible = visible;
}
/**
* Sets the message to be displayed in the child jlabel.
*
* @param message The message to be displayed.
*/
public void setMessage(String message) {
label.setText(String.format("<html><div style='text-align: center;'>%s</div></html>",
message == null ? "" : message));
}
/**
* Paints the jlabel at full width and height with the graphics object.
*
* @param g The graphics object.
* @param width The width.
* @param height The height.
*/
public void paintOverlay(Graphics g, int width, int height) {
paintOverlay(g, width, height, null);
}
/**
* Paints the jlabel at full width and height with the graphics object.
*
* @param g The graphics object.
* @param parentWidth The width of the component.
* @param parentHeight The height of the component.
* @param labelMaxWidth The maximum width of the label drawn for the
* overlay. The label will be vertically and
* horizontally centered.
*/
public void paintOverlay(Graphics g, int parentWidth, int parentHeight, Integer labelMaxWidth) {
if (!visible) {
return;
}
int labelWidth = (labelMaxWidth == null) ? parentWidth : Math.min(labelMaxWidth, parentWidth);
int leftPad = (parentWidth - labelWidth) / 2;
// paint the jlabel if visible.
g.translate(leftPad, 0);
label.setBounds(0, 0, labelWidth, parentHeight);
g.translate(0, 0);
label.paint(g);
}
}

View File

@ -1,3 +1,5 @@
JTablePanel_errorMessage_defaultText=There was an error loading results.
JTablePanel_loadingMessage_defaultText=Loading results...
JTablePanel_noDataExists_defaultText=No data exists.
AbstractLoadableComponent_errorMessage_defaultText=There was an error loading results.
AbstractLoadableComponent_loadingMessage_defaultText=Loading results...
AbstractLoadableComponent_noDataExists_defaultText=No data exists.
IngestRunningLabel_defaultMessage=Ingest is currently running.
PieChartPanel_noDataLabel=No Data

View File

@ -20,7 +20,6 @@ package org.sleuthkit.autopsy.datasourcesummary.uiutils;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
import java.util.logging.Level;
import javax.swing.SwingWorker;
import org.sleuthkit.autopsy.coreutils.Logger;
@ -71,6 +70,7 @@ public class DataFetchWorker<A, R> extends SwingWorker<R, Void> {
}
private static final Logger logger = Logger.getLogger(DataFetchWorker.class.getName());
private static final int MAX_INNER_EXCEPTION_DEPTH = 100;
private final A args;
private final DataFetcher<A, R> processor;
@ -128,14 +128,17 @@ public class DataFetchWorker<A, R> extends SwingWorker<R, Void> {
return;
} catch (ExecutionException ex) {
Throwable inner = ex.getCause();
// if cancelled during operation, simply return
if (inner instanceof InterruptedException) {
return;
for (int i = 0; i < MAX_INNER_EXCEPTION_DEPTH; i++) {
if (inner == null) {
break;
} else if (inner instanceof InterruptedException) {
// if cancelled during operation, simply return
return;
} else {
inner = inner.getCause();
}
}
// otherwise, there is an error to log
logger.log(Level.WARNING, "There was an error while fetching results.", ex);
// and pass the result to the client
resultHandler.accept(DataFetchResult.getErrorResult(inner));
return;

View File

@ -0,0 +1,60 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 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.datasourcesummary.uiutils;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.IngestManager.IngestJobEvent;
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
/**
* An UpdateGovernor that provides a means of providing a set of artifact type
* id's that should trigger an update.
*/
public interface DefaultArtifactUpdateGovernor extends DefaultUpdateGovernor {
Set<IngestJobEvent> INGEST_JOB_EVENTS = new HashSet<>(
Arrays.asList(IngestJobEvent.COMPLETED, IngestJobEvent.CANCELLED));
@Override
default boolean isRefreshRequired(ModuleDataEvent evt) {
if (evt == null || evt.getBlackboardArtifactType() == null) {
return false;
}
return getArtifactTypeIdsForRefresh().contains(evt.getBlackboardArtifactType().getTypeID());
}
@Override
default boolean isRefreshRequired(IngestManager.IngestJobEvent evt) {
return (evt != null && INGEST_JOB_EVENTS.contains(evt));
}
@Override
default Set<IngestJobEvent> getIngestJobEventUpdates() {
return INGEST_JOB_EVENTS;
}
/**
* @return The set of artifact type id's that should trigger an update.
*/
Set<Integer> getArtifactTypeIdsForRefresh();
}

View File

@ -0,0 +1,70 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 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.datasourcesummary.uiutils;
import java.beans.PropertyChangeEvent;
import java.util.Collections;
import java.util.Set;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.ModuleContentEvent;
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
import org.sleuthkit.datamodel.AbstractFile;
/**
* The default UpdateGovernor where no updates will be triggered unless
* overridden.
*/
public interface DefaultUpdateGovernor extends UpdateGovernor {
@Override
default boolean isRefreshRequiredForCaseEvent(PropertyChangeEvent evt) {
return false;
}
@Override
default boolean isRefreshRequired(ModuleContentEvent evt) {
return false;
}
@Override
default boolean isRefreshRequired(ModuleDataEvent evt) {
return false;
}
@Override
default boolean isRefreshRequired(IngestManager.IngestJobEvent evt) {
return false;
}
@Override
default Set<Case.Events> getCaseEventUpdates() {
return Collections.emptySet();
}
@Override
default Set<IngestManager.IngestJobEvent> getIngestJobEventUpdates() {
return Collections.emptySet();
}
@Override
default boolean isRefreshRequired(AbstractFile evt) {
return false;
}
}

View File

@ -0,0 +1,199 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 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.datasourcesummary.uiutils;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Set;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.guiutils.RefreshThrottler;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.IngestManager.IngestJobEvent;
import org.sleuthkit.autopsy.ingest.ModuleContentEvent;
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
import org.sleuthkit.datamodel.AbstractFile;
/**
* Handles ingest and case events, and determines whether they should trigger an
* update.
*/
public class EventUpdateHandler {
/**
* The refresh throttler that handles ingest events.
*/
private final RefreshThrottler refreshThrottler = new RefreshThrottler(new RefreshThrottler.Refresher() {
@Override
public void refresh() {
// delegate to EventUpdateHandler method.
EventUpdateHandler.this.onRefresh();
}
@Override
public boolean isRefreshRequired(PropertyChangeEvent evt) {
String eventType = evt.getPropertyName();
if (Case.isCaseOpen()) {
if (IngestManager.IngestModuleEvent.DATA_ADDED.toString().equals(eventType) && evt.getOldValue() instanceof ModuleDataEvent) {
ModuleDataEvent dataEvent = (ModuleDataEvent) evt.getOldValue();
return EventUpdateHandler.this.isRefreshRequired(dataEvent);
} else if (IngestManager.IngestModuleEvent.CONTENT_CHANGED.toString().equals(eventType) && evt.getOldValue() instanceof ModuleContentEvent) {
ModuleContentEvent contentEvent = (ModuleContentEvent) evt.getOldValue();
return EventUpdateHandler.this.isRefreshRequired(contentEvent);
} else if (IngestManager.IngestModuleEvent.FILE_DONE.toString().equals(eventType) && evt.getNewValue() instanceof AbstractFile) {
AbstractFile analyzedFile = (AbstractFile) evt.getNewValue();
return EventUpdateHandler.this.isRefreshRequired(analyzedFile);
}
}
return false;
}
});
/**
* Handler for case event updates.
*/
private final PropertyChangeListener caseEventsListener = (evt) -> {
if (isRefreshRequiredForCaseEvent(evt)) {
onRefresh();
}
};
private final PropertyChangeListener ingestJobEventsListener = (evt) -> {
if (evt == null) {
return;
}
String eventName = evt.getPropertyName();
for (IngestJobEvent ingestEvt : IngestJobEvent.values()) {
if (ingestEvt.name().equals(eventName) && isRefreshRequired(ingestEvt)) {
onRefresh();
}
}
};
private final UpdateGovernor governor;
private final Set<Case.Events> caseEvents;
private final Set<IngestJobEvent> ingestEvents;
private final Runnable onUpdate;
/**
* Constructor.
*
* @param onUpdate The function to call if an update should be required.
* @param governor The item used to determine if an update is required. If
* the governor requires an update, then onUpdate is
* triggered.
*/
public EventUpdateHandler(Runnable onUpdate, UpdateGovernor governor) {
if (onUpdate == null) {
throw new IllegalArgumentException("onUpdate parameter must be non-null.");
}
this.onUpdate = onUpdate;
this.governor = governor;
this.caseEvents = governor.getCaseEventUpdates();
this.ingestEvents = governor.getIngestJobEventUpdates();
}
/**
* Handles whether or not a ModuleDataEvent should trigger an update.
*
* @param evt The ModuleDataEvent.
*
* @return True if an update should occur.
*/
protected boolean isRefreshRequired(ModuleDataEvent evt) {
return governor.isRefreshRequired(evt);
}
/**
* Handles whether or not a ModuleContentEvent should trigger an update.
*
* @param evt The ModuleContentEvent.
*
* @return True if an update should occur.
*/
protected boolean isRefreshRequired(ModuleContentEvent evt) {
return governor.isRefreshRequired(evt);
}
/**
* Handles whether or not a newly added AbstractFile should trigger an
* update.
*
* @param evt The AbstractFile.
*
* @return True if an update should occur.
*/
protected boolean isRefreshRequired(AbstractFile evt) {
return governor.isRefreshRequired(evt);
}
/**
* Handles whether or not a IngestJobEvent should trigger an update.
*
* @param evt The IngestJobEvent.
*
* @return True if an update should occur.
*/
protected boolean isRefreshRequired(IngestJobEvent evt) {
return governor.isRefreshRequired(evt);
}
/**
* Handles whether or not a case event should trigger an update.
*
* @param evt The case event.
*
* @return True if an update should occur.
*/
protected boolean isRefreshRequiredForCaseEvent(PropertyChangeEvent evt) {
return governor.isRefreshRequiredForCaseEvent(evt);
}
/**
* Method called that triggers refresh.
*/
protected void onRefresh() {
onUpdate.run();
}
/**
* Registers ingest and case event listeners.
*/
public void register() {
if (!caseEvents.isEmpty()) {
Case.addEventTypeSubscriber(caseEvents, caseEventsListener);
}
IngestManager.getInstance().addIngestJobEventListener(ingestEvents, ingestJobEventsListener);
refreshThrottler.registerForIngestModuleEvents();
}
/**
* Unregisters ingest and case event listeners.
*/
public void unregister() {
if (!caseEvents.isEmpty()) {
Case.removeEventTypeSubscriber(caseEvents, caseEventsListener);
}
IngestManager.getInstance().removeIngestJobEventListener(ingestEvents, ingestJobEventsListener);
refreshThrottler.unregisterEventListener();
}
}

View File

@ -0,0 +1,167 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 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.datasourcesummary.uiutils;
import java.awt.BorderLayout;
import java.beans.PropertyChangeListener;
import java.net.URL;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Set;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.ingest.IngestManager;
/**
* JLabel that shows ingest is running.
*/
@Messages({
"IngestRunningLabel_defaultMessage=Ingest is currently running."
})
public class IngestRunningLabel extends JPanel {
private static final long serialVersionUID = 1L;
public static final String DEFAULT_MESSAGE = Bundle.IngestRunningLabel_defaultMessage();
private static final URL DEFAULT_ICON = IngestRunningLabel.class.getResource("/org/sleuthkit/autopsy/modules/filetypeid/warning16.png");
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(
IngestManager.IngestJobEvent.STARTED,
IngestManager.IngestJobEvent.CANCELLED,
IngestManager.IngestJobEvent.COMPLETED
);
private static Set<IngestRunningLabel> activeLabels = new HashSet<>();
private static PropertyChangeListener classListener = null;
private static Object lockObject = new Object();
/**
* Setup ingest event listener for the current label.
*
* @param label The label.
*/
private static void setupListener(IngestRunningLabel label) {
synchronized (lockObject) {
// if listener is not initialized, initialize it.
if (classListener == null) {
classListener = (evt) -> {
if (evt == null) {
return;
}
if (evt.getPropertyName().equals(IngestManager.IngestJobEvent.STARTED.toString())) {
// ingest started
notifyListeners(true);
} else if (evt.getPropertyName().equals(IngestManager.IngestJobEvent.CANCELLED.toString())
|| evt.getPropertyName().equals(IngestManager.IngestJobEvent.COMPLETED.toString())) {
// ingest cancelled or finished
notifyListeners(false);
}
};
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, classListener);
}
// add the item to the set
activeLabels.add(label);
}
}
/**
* Notifies all listening instances of an update in ingest state.
*
* @param ingestIsRunning Whether or not ingest is running currently.
*/
private static void notifyListeners(boolean ingestIsRunning) {
synchronized (lockObject) {
for (IngestRunningLabel label : activeLabels) {
label.refreshState(ingestIsRunning);
}
}
}
/**
* Removes a label from listening events.
*
* @param label The label to remove from listening events.
*/
private static void removeListener(IngestRunningLabel label) {
synchronized (lockObject) {
activeLabels.remove(label);
if (activeLabels.isEmpty() && classListener != null) {
IngestManager.getInstance().removeIngestJobEventListener(classListener);
classListener = null;
}
}
}
/**
* Main constructor with default message and showing icon.
*/
public IngestRunningLabel() {
this(DEFAULT_MESSAGE, true);
}
/**
* Constructor.
*
* @param message The message to be shown.
* @param showWarningIcon Whether or not to show warning icon.
*/
public IngestRunningLabel(String message, boolean showWarningIcon) {
JLabel jlabel = new JLabel();
jlabel.setText(message);
if (showWarningIcon) {
jlabel.setIcon(new ImageIcon(DEFAULT_ICON));
}
setLayout(new BorderLayout());
add(jlabel, BorderLayout.NORTH);
setupListener(this);
refreshState();
}
/**
* Refresh state of this label based on ingest status.
*/
protected final void refreshState() {
refreshState(IngestManager.getInstance().isIngestRunning());
}
/**
* Refresh state of this label based on ingest status.
*
* @param ingestIsRunning True if ingest is running.
*/
protected final void refreshState(boolean ingestIsRunning) {
setVisible(ingestIsRunning);
}
/**
* Unregister this instance from listening for ingest status changes.
*/
public void unregister() {
removeListener(this);
}
}

View File

@ -23,31 +23,22 @@ import java.awt.Graphics;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import java.util.logging.Level;
import java.util.stream.Collectors;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JLayer;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.plaf.LayerUI;
import javax.swing.table.DefaultTableColumnModel;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.CellModel;
/**
* A table that displays a list of items and also can display messages for
* loading, load error, and not loaded.
*/
@Messages({
"JTablePanel_loadingMessage_defaultText=Loading results...",
"JTablePanel_errorMessage_defaultText=There was an error loading results.",
"JTablePanel_noDataExists_defaultText=No data exists.",})
public class JTablePanel<T> extends JPanel {
public class JTablePanel<T> extends AbstractLoadableComponent<List<T>> {
/**
* JTables don't allow displaying messages. So this LayerUI is used to
@ -58,27 +49,7 @@ public class JTablePanel<T> extends JPanel {
private static class Overlay extends LayerUI<JComponent> {
private static final long serialVersionUID = 1L;
private final JLabel label;
private boolean visible;
/**
* Main constructor for the Overlay.
*/
Overlay() {
label = new JLabel();
label.setHorizontalAlignment(JLabel.CENTER);
label.setVerticalAlignment(JLabel.CENTER);
label.setOpaque(false);
}
/**
* @return Whether or not this message overlay should be visible.
*/
boolean isVisible() {
return visible;
}
private final BaseMessageOverlay overlayDelegate = new BaseMessageOverlay();
/**
* Sets this layer visible when painted. In order to be shown in UI,
@ -87,7 +58,7 @@ public class JTablePanel<T> extends JPanel {
* @param visible Whether or not it is visible.
*/
void setVisible(boolean visible) {
this.visible = visible;
overlayDelegate.setVisible(visible);
}
/**
@ -96,25 +67,13 @@ public class JTablePanel<T> extends JPanel {
* @param message The message to be displayed.
*/
void setMessage(String message) {
label.setText(String.format("<html><div style='text-align: center;'>%s</div></html>",
message == null ? "" : message));
overlayDelegate.setMessage(message);
}
@Override
public void paint(Graphics g, JComponent c) {
// Paint the underlying view.
super.paint(g, c);
if (!visible) {
return;
}
int w = c.getWidth();
int h = c.getHeight();
// paint the jlabel if visible.
label.setBounds(0, 0, w, h);
label.paint(g);
overlayDelegate.paintOverlay(g, c.getWidth(), c.getHeight());
}
}
@ -179,12 +138,6 @@ public class JTablePanel<T> extends JPanel {
private static final long serialVersionUID = 1L;
private static final Logger logger = Logger.getLogger(JTablePanel.class.getName());
private static final String DEFAULT_LOADING_MESSAGE = Bundle.JTablePanel_loadingMessage_defaultText();
private static final String DEFAULT_ERROR_MESSAGE = Bundle.JTablePanel_errorMessage_defaultText();
private static final String DEFAULT_NO_RESULTS_MESSAGE = Bundle.JTablePanel_noDataExists_defaultText();
private static final CellModelTableCellRenderer DEFAULT_CELL_RENDERER = new CellModelTableCellRenderer();
/**
@ -231,7 +184,7 @@ public class JTablePanel<T> extends JPanel {
.map((colModel) -> colModel.getCellRenderer())
.collect(Collectors.toList());
return new DefaultListTableModel<>(columnRenderers);
return new DefaultListTableModel<T>(columnRenderers);
}
/**
@ -248,24 +201,11 @@ public class JTablePanel<T> extends JPanel {
return resultTable.setColumnModel(getTableColumnModel(columns));
}
/**
* @return The default error message.
*/
public static String getDefaultErrorMessage() {
return DEFAULT_ERROR_MESSAGE;
}
/**
* @return The default message for no results.
*/
public static String getDefaultNoResultsMessage() {
return DEFAULT_NO_RESULTS_MESSAGE;
}
private JScrollPane tableScrollPane;
private Overlay overlayLayer;
private ListTableModel<T> tableModel;
private JTable table;
private Function<T, ? extends Object> keyFunction = (rowItem) -> rowItem;
/**
* Panel constructor.
@ -289,14 +229,17 @@ public class JTablePanel<T> extends JPanel {
* setResultList.
*
* @param tableModel
*
* @return As a utility, returns this.
*/
public final void setModel(ListTableModel<T> tableModel) {
public final JTablePanel<T> setModel(ListTableModel<T> tableModel) {
if (tableModel == null) {
throw new IllegalArgumentException("Null table model passed to setModel");
}
this.tableModel = tableModel;
table.setModel(tableModel);
return this;
}
/**
@ -319,124 +262,68 @@ public class JTablePanel<T> extends JPanel {
}
/**
* Sets the data to be shown in the JTable. Repaint is not handled in this
* method and should be handled separately.
*
* @param data The list of data objects to be shown.
* @return The function for determining the key for a data row. This key is
* used to maintain current selection in the table despite changing
* rows.
*/
private void setResultList(List<T> data) {
public Function<T, ? extends Object> getKeyFunction() {
return keyFunction;
}
if(tableModel == null) {
throw new IllegalStateException("ListTableModel has not be initialized");
/**
* Sets the function for determining the key for a data row. This key is
* used to maintain current selection in the table despite changing rows.
*
* @param keyFunction The function to determine the key of a row.
*
* @return As a utility, returns this.
*/
public JTablePanel<T> setKeyFunction(Function<T, ? extends Object> keyFunction) {
if (keyFunction == null) {
throw new IllegalArgumentException("Key function must be non-null");
}
this.keyFunction = keyFunction;
return this;
}
@Override
protected synchronized void setResults(List<T> data) {
// get previously selected value
int prevSelectedRow = this.table.getSelectedRow();
List<T> tableRows = this.tableModel.getDataRows();
T prevValue = (tableRows != null && prevSelectedRow >= 0 && prevSelectedRow < tableRows.size())
? this.tableModel.getDataRows().get(prevSelectedRow)
: null;
Object prevKeyValue = (prevValue == null) ? null : this.keyFunction.apply(prevValue);
// set the list of data to be shown as either the data or an empty list
// on null.
List<T> dataToSet = (data == null) ? Collections.emptyList() : data;
// since the data is being reset, scroll to the top.
tableScrollPane.getVerticalScrollBar().setValue(0);
// set the underlying table model's data.
this.tableModel.setDataRows(dataToSet);
// set the row to selected value if the value is found
if (prevKeyValue != null) {
for (int objIndex = 0; objIndex < dataToSet.size(); objIndex++) {
Object thisKey = this.keyFunction.apply(dataToSet.get(objIndex));
if (prevKeyValue.equals(thisKey)) {
this.table.setRowSelectionInterval(objIndex, objIndex);
break;
}
}
}
}
/**
* Sets the message and visibility of the overlay. Repaint is not handled in
* this method and should be handled separately.
*
* @param visible The visibility of the overlay.
* @param message The message in the overlay.
*/
private void setOverlay(boolean visible, String message) {
@Override
protected void setMessage(boolean visible, String message) {
this.overlayLayer.setVisible(visible);
this.overlayLayer.setMessage(message);
}
/**
* Clears the results from the underlying JTable and shows the provided
* message.
*
* @param message The message to be shown.
*/
public synchronized void showMessage(String message) {
setResultList(null);
setOverlay(true, message);
repaint();
}
/**
* Shows a default loading message on the table. This will clear any results
* in the table.
*/
public void showDefaultLoadingMessage() {
showMessage(DEFAULT_LOADING_MESSAGE);
}
/**
* Shows the list as rows of data in the table. If overlay message will be
* cleared if present.
*
* @param data The data to be shown where each item represents a row of
* data.
*/
public synchronized void showResults(List<T> data) {
setOverlay(false, null);
setResultList(data);
repaint();
}
/**
* Shows the data in a DataFetchResult. If there was an error during the
* operation, the errorMessage will be displayed. If the operation completed
* successfully and no data is present, noResultsMessage will be shown.
* Otherwise, the data will be shown as rows in the table.
*
* @param result The DataFetchResult.
* @param errorMessage The error message to be shown in the event of an
* error.
* @param noResultsMessage The message to be shown if there are no results
* but the operation completed successfully.
*/
public void showDataFetchResult(DataFetchResult<List<T>> result, String errorMessage, String noResultsMessage) {
if (result == null) {
logger.log(Level.SEVERE, "Null data processor result received.");
return;
}
switch (result.getResultType()) {
case SUCCESS:
if (result.getData() == null || result.getData().isEmpty()) {
showMessage(noResultsMessage);
} else {
showResults(result.getData());
}
break;
case ERROR:
// if there is an error, log accordingly, set result list to
// empty and display error message
logger.log(Level.WARNING, "An exception was caused while results were loaded.", result.getException());
showMessage(errorMessage);
break;
default:
// an unknown loading state was specified. log accordingly.
logger.log(Level.SEVERE, "No known loading state was found in result.");
break;
}
}
/**
* Shows the data in a DataFetchResult. If there was an error during the
* operation, the DEFAULT_ERROR_MESSAGE will be displayed. If the operation
* completed successfully and no data is present, DEFAULT_NO_RESULTS_MESSAGE
* will be shown. Otherwise, the data will be shown as rows in the table.
*
* @param result The DataFetchResult.
*/
public void showDataFetchResult(DataFetchResult<List<T>> result) {
showDataFetchResult(result, DEFAULT_ERROR_MESSAGE, DEFAULT_NO_RESULTS_MESSAGE);
}
/**
* Initialize the gui components.
*/

View File

@ -0,0 +1,73 @@
/*
* 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.datasourcesummary.uiutils;
/**
* Interface for a loadable component that can show messages, results, or a
* DataFetchResult.
*/
public interface LoadableComponent<T> {
/**
* Clears the results from the underlying JTable and shows the provided
* message.
*
* @param message The message to be shown.
*/
void showMessage(String message);
/**
* Shows a default loading message on the table. This will clear any results
* in the table.
*/
void showDefaultLoadingMessage();
/**
* Shows the list as rows of data in the table. If overlay message will be
* cleared if present.
*
* @param data The data to be shown where each item represents a row of
* data.
*/
void showResults(T data);
/**
* Shows the data in a DataFetchResult. If there was an error during the
* operation, the errorMessage will be displayed. If the operation completed
* successfully and no data is present, noResultsMessage will be shown.
* Otherwise, the data will be shown as rows in the table.
*
* @param result The DataFetchResult.
* @param errorMessage The error message to be shown in the event of an
* error.
* @param noResultsMessage The message to be shown if there are no results
* but the operation completed successfully.
*/
void showDataFetchResult(DataFetchResult<T> result, String errorMessage, String noResultsMessage);
/**
* Shows the data in a DataFetchResult. If there was an error during the
* operation, the DEFAULT_ERROR_MESSAGE will be displayed. If the operation
* completed successfully and no data is present, DEFAULT_NO_RESULTS_MESSAGE
* will be shown. Otherwise, the data will be shown as rows in the table.
*
* @param result The DataFetchResult.
*/
void showDataFetchResult(DataFetchResult<T> result);
}

View File

@ -0,0 +1,253 @@
/*
* 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.datasourcesummary.uiutils;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.text.DecimalFormat;
import java.util.List;
import javax.swing.JLabel;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.labels.PieSectionLabelGenerator;
import org.jfree.chart.labels.StandardPieSectionLabelGenerator;
import org.jfree.chart.panel.AbstractOverlay;
import org.jfree.chart.panel.Overlay;
import org.jfree.chart.plot.PiePlot;
import org.jfree.data.general.DefaultPieDataset;
import org.openide.util.NbBundle.Messages;
/**
* A pie chart panel.
*/
@Messages({
"PieChartPanel_noDataLabel=No Data"
})
public class PieChartPanel extends AbstractLoadableComponent<List<PieChartPanel.PieChartItem>> {
/**
* An individual pie chart slice in the pie chart.
*/
public static class PieChartItem {
private final String label;
private final double value;
private final Color color;
/**
* Main constructor.
*
* @param label The label for this pie slice.
* @param value The value for this item.
* @param color The color for the pie slice. Can be null for
* auto-determined.
*/
public PieChartItem(String label, double value, Color color) {
this.label = label;
this.value = value;
this.color = color;
}
/**
* @return The label for this item.
*/
public String getLabel() {
return label;
}
/**
* @return The value for this item.
*/
public double getValue() {
return value;
}
/**
* @return The color for the pie slice or null for auto-determined.
*/
public Color getColor() {
return color;
}
}
/**
* A JFreeChart message overlay that can show a message for the purposes of
* the LoadableComponent.
*/
private static class MessageOverlay extends AbstractOverlay implements Overlay {
private static final long serialVersionUID = 1L;
private final BaseMessageOverlay overlay = new BaseMessageOverlay();
// multiply this value by the smaller dimension (height or width) of the component
// to determine width of text to be displayed.
private static final double MESSAGE_WIDTH_FACTOR = .6;
/**
* Sets this layer visible when painted. In order to be shown in UI,
* this component needs to be repainted.
*
* @param visible Whether or not it is visible.
*/
void setVisible(boolean visible) {
overlay.setVisible(visible);
}
/**
* Sets the message to be displayed in the child jlabel.
*
* @param message The message to be displayed.
*/
void setMessage(String message) {
overlay.setMessage(message);
}
@Override
public void paintOverlay(Graphics2D gd, ChartPanel cp) {
int labelWidth = (int) (Math.min(cp.getWidth(), cp.getHeight()) * MESSAGE_WIDTH_FACTOR);
overlay.paintOverlay(gd, cp.getWidth(), cp.getHeight(), labelWidth);
}
}
private static final long serialVersionUID = 1L;
private static final Font DEFAULT_FONT = new JLabel().getFont();
/**
* It appears that JFreeChart will show nothing if all values are zero. So
* this is a value close to zero but not to be displayed.
*/
private static final double NEAR_ZERO = Math.ulp(1d);
private static final Color NO_DATA_COLOR = Color.WHITE;
private static final double DEFAULT_CHART_PADDING = .1;
private static final Font DEFAULT_HEADER_FONT = new Font(DEFAULT_FONT.getName(), DEFAULT_FONT.getStyle(), (int) (DEFAULT_FONT.getSize() * 1.5));
private static final PieSectionLabelGenerator DEFAULT_LABEL_GENERATOR
= new StandardPieSectionLabelGenerator(
"{0}: {1} ({2})", new DecimalFormat("#,###"), new DecimalFormat("0.0%"));
private final MessageOverlay overlay = new MessageOverlay();
private final DefaultPieDataset dataset = new DefaultPieDataset();
private final JFreeChart chart;
private final PiePlot plot;
/**
* Main constructor.
*/
public PieChartPanel() {
this(null);
}
/**
* Main constructor for the pie chart.
*
* @param title The title for this pie chart.
*/
public PieChartPanel(String title) {
// Create chart
this.chart = ChartFactory.createPieChart(
title,
dataset,
false,
false,
false);
chart.setBackgroundPaint(null);
chart.getTitle().setFont(DEFAULT_HEADER_FONT);
this.plot = ((PiePlot) chart.getPlot());
plot.setInteriorGap(DEFAULT_CHART_PADDING);
plot.setLabelGenerator(DEFAULT_LABEL_GENERATOR);
plot.setLabelFont(DEFAULT_FONT);
plot.setBackgroundPaint(null);
plot.setOutlinePaint(null);
// Create Panel
ChartPanel panel = new ChartPanel(chart);
panel.addOverlay(overlay);
panel.setPopupMenu(null);
this.setLayout(new BorderLayout());
this.add(panel, BorderLayout.CENTER);
}
/**
* @return The title for this chart if one exists.
*/
public String getTitle() {
return (this.chart == null || this.chart.getTitle() == null)
? null
: this.chart.getTitle().getText();
}
/**
* Sets the title for this pie chart.
*
* @param title The title.
*
* @return As a utility, returns this.
*/
public PieChartPanel setTitle(String title) {
this.chart.getTitle().setText(title);
return this;
}
@Override
protected void setMessage(boolean visible, String message) {
this.overlay.setVisible(visible);
this.overlay.setMessage(message);
}
@Override
protected void setResults(List<PieChartPanel.PieChartItem> data) {
this.dataset.clear();
this.plot.clearSectionPaints(false);
if (data != null && !data.isEmpty()) {
for (PieChartPanel.PieChartItem slice : data) {
this.dataset.setValue(slice.getLabel(), slice.getValue());
if (slice.getColor() != null) {
this.plot.setSectionPaint(slice.getLabel(), slice.getColor());
}
}
} else {
// show a no data label if no data.
// this in fact shows a very small number for the value
// that should be way below rounding error for formatters
this.dataset.setValue(Bundle.PieChartPanel_noDataLabel(), NEAR_ZERO);
this.plot.setSectionPaint(Bundle.PieChartPanel_noDataLabel(), NO_DATA_COLOR);
}
}
/**
* Shows a message on top of data.
*
* @param data The data.
* @param message The message.
*/
public synchronized void showDataWithMessage(List<PieChartPanel.PieChartItem> data, String message) {
setResults(data);
setMessage(true, message);
repaint();
}
}

View File

@ -71,4 +71,22 @@ public class SwingWorkerSequentialExecutor {
workers = Collections.emptyList();
futures = Collections.emptyList();
}
/**
* Returns whether or not any of the workers provided are still running.
*
* @return Whether or not any of the submitted workers are still running.
*/
public synchronized boolean isRunning() {
// borrowed from this stack overflow answer:
// https://stackoverflow.com/a/33845730
for (Future<?> future : futures) {
if (!future.isDone()) {
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,90 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 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.datasourcesummary.uiutils;
import java.beans.PropertyChangeEvent;
import java.util.Set;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.ingest.IngestManager.IngestJobEvent;
import org.sleuthkit.autopsy.ingest.ModuleContentEvent;
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
import org.sleuthkit.datamodel.AbstractFile;
/**
* Interface for determiining when data should update based on autopsy (i.e.
* case/ingest) events.
*/
public interface UpdateGovernor {
/**
* @return The set of Case Events for which data should be updated.
*/
Set<Case.Events> getCaseEventUpdates();
/**
* @return The set of Ingest Job Events for which data should be updated.
*/
Set<IngestJobEvent> getIngestJobEventUpdates();
/**
* Given a module data event, whether or not an update should occur.
*
* @param evt The ModuleDataEvent that is occurring.
*
* @return Whether or not this event should trigger an update.
*/
boolean isRefreshRequired(ModuleDataEvent evt);
/**
* Given a module content event, whether or not an update should occur.
*
* @param evt The ModuleContentEvent.
*
* @return Whether or not this event should trigger an update.
*/
boolean isRefreshRequired(ModuleContentEvent evt);
/**
* Given an ingest job event, determines whether or not an update should
* occur.
*
* @param evt The event.
*
* @return Whether or not this event should trigger an update.
*/
boolean isRefreshRequired(IngestJobEvent evt);
/**
* Given a case event, whether or not an update should occur.
*
* @param evt The event.
*
* @return Whether or not this event should trigger an update.
*/
boolean isRefreshRequiredForCaseEvent(PropertyChangeEvent evt);
/**
* Whether or not a newly added AbstractFile should trigger an update.
*
* @param evt The AbstractFile.
*
* @return True if an update should occur.
*/
boolean isRefreshRequired(AbstractFile evt);
}

View File

@ -1,106 +0,0 @@
FileSearchDialog.jLabel1.text=File Type
FileSearchDialog.dsCheckBox.text=Data source
FileSearchDialog.cancelButton.text=Cancel
FileSearchDialog.freqCheckBox.text=CR Frequency
FileSearchDialog.sizeCheckBox.text=Size
FileSearchDialog.kwCheckBox.text=Keyword
FileSearchDialog.addParentButton.text=Add
FileSearchDialog.deleteParentButton.text=Delete
FileSearchDialog.parentFullRadioButton.text=Full
FileSearchDialog.parentSubstringRadioButton.text=Substring
FileSearchDialog.jLabel2.text=(All will be used)
FileSearchDialog.jLabel3.text=Group by attribute:
FileSearchDialog.jLabel4.text=Order groups by:
FileSearchDialog.orderAttrRadioButton.text=Attribute
FileSearchDialog.orderSizeRadioButton.text=Group Size
FileSearchDialog.jLabel5.text=Order files by:
FileSearchDialog.parentCheckBox.text=Parent
FileSearchPanel.sortingPanel.border.title=Grouping
FileSearchPanel.addButton.text=Add
FileSearchPanel.substringRadioButton.text=Substring
FileSearchPanel.fullRadioButton.text=Full
FileSearchPanel.parentCheckbox.text=Parent Folder:
FileSearchPanel.keywordCheckbox.text=Keyword:
FileSearchPanel.crFrequencyCheckbox.text=Past Occurrences:
FileSearchPanel.dataSourceCheckbox.text=Data Source:
FileSearchPanel.sizeCheckbox.text=File Size:
FileSearchPanel.orderGroupsByLabel.text=Order Groups By:
FileSearchPanel.filtersScrollPane.border.title=Filters
FileSearchPanel.parentLabel.text=(All will be used)
FileSearchPanel.deleteButton.text=Delete
FileSearchPanel.orderByLabel.text=Order Within Groups By:
FileSearchPanel.groupByLabel.text=Group By:
FileSearchDialog.searchButton.text=Search
FileSearchDialog.hashCheckBox.text=Hash Set
FileSearchDialog.intCheckBox.text=Interesting Items
FileSearchDialog.tagsCheckBox.text=Tags
FileSearchDialog.objCheckBox.text=Objects
FileSearchDialog.exifCheckBox.text=Must contain EXIF data
FileSearchDialog.notableCheckBox.text=Must have been tagged as notable
FileSearchDialog.scoreCheckBox.text=Has score
FileSearchPanel.hashSetCheckbox.text=Hash Set:
FileSearchPanel.tagsCheckbox.text=Tag:
FileSearchPanel.interestingItemsCheckbox.text=Interesting Item:
FileSearchPanel.scoreCheckbox.text=Has Score:
FileSearchPanel.notableCheckbox.text=Must have been tagged as notable
FileSearchPanel.objectsCheckbox.text=Object Detected:
ResultsPanel.currentPageLabel.text=Page: -
ResultsPanel.pageControlsLabel.text=Pages:
ResultsPanel.gotoPageLabel.text=Go to Page:
ResultsPanel.pageSizeLabel.text=Page Size:
DiscoveryExtractAction.title.extractFiles.text=Extract File
FileSearchPanel.includeRadioButton.text=Include
FileSearchPanel.excludeRadioButton.text=Exclude
FileSearchPanel.knownFilesCheckbox.toolTipText=
FileSearchPanel.knownFilesCheckbox.text=Hide known files
GroupListPanel.groupKeyList.border.title=Groups
FileSearchPanel.stepThreeLabel.text=Step 3: Choose display settings
DocumentPanel.fileSizeLabel.toolTipText=
DocumentPanel.isDeletedLabel.toolTipText=
ImageThumbnailPanel.isDeletedLabel.toolTipText=
FileSearchPanel.userCreatedCheckbox.text=Possibly User Created
DiscoveryDialog.documentsButton.text=Documents
DiscoveryDialog.videosButton.text=Videos
DiscoveryDialog.imagesButton.text=Images
DiscoveryDialog.searchButton.text=Search
DetailsPanel.instancesList.border.title=Instances
SizeFilterPanel.sizeCheckbox.text=File Size:
DataSourceFilterPanel.dataSourceCheckbox.text=Data Source:
UserCreatedFilterPanel.userCreatedCheckbox.text=Possibly User Created
# To change this license header, choose License Headers in Project Properties.
# To change this template file, choose Tools | Templates
# and open the template in the editor.
HashSetFilterPanel.hashSetCheckbox.text=Hash Set:
InterestingItemFilterPanel.interestingItemsCheckbox.text=Interesting Item:
ParentFolderFilterPanel.parentCheckbox.text=Parent Folder:
ParentFolderFilterPanel.deleteButton.text=Delete
ParentFolderFilterPanel.excludeRadioButton.text=Exclude
ParentFolderFilterPanel.includeRadioButton.text=Include
ParentFolderFilterPanel.substringRadioButton.text=Substring
ParentFolderFilterPanel.fullRadioButton.text=Full
ParentFolderFilterPanel.parentLabel.text=(All will be used)
ParentFolderFilterPanel.addButton.text=Add
ParentFolderFilterPanel.parentCheckbox.text_1=Parent Folder:
ParentFolderFilterPanel.addButton.text_1=Add
ParentFolderFilterPanel.deleteButton.text_1=Delete
ParentFolderFilterPanel.excludeRadioButton.text_1=Exclude
ParentFolderFilterPanel.substringRadioButton.text_1=Substring
ParentFolderFilterPanel.includeRadioButton.text_1=Include
ParentFolderFilterPanel.fullRadioButton.text_1=Full
ParentFolderFilterPanel.parentLabel.text_1=(All will be used)
InterestingItemsFilterPanel.interestingItemsCheckbox.text=Interesting Item:
UserCreatedFilterPanel.userCreatedCheckbox.text_1=Possibly User Created
PastOccurrencesFilterPanel.pastOccurrencesCheckbox.text=Past Occurrences:
ObjectDetectedFilterPanel.text=Object Detected:
DiscoveryDialog.sortingPanel.border.title=Step 3: Choose display settings
DiscoveryDialog.groupByLabel.text=Group By:
DiscoveryDialog.orderByLabel.text=Order Within Groups By:
DiscoveryDialog.orderGroupsByLabel.text=Order Groups By:
ImageFilterPanel.imageFiltersSplitPane.toolTipText=
DocumentFilterPanel.documentsFiltersSplitPane.border.title=Step 2: Filter which documents to show
ImageFilterPanel.imageFiltersSplitPane.border.title=Step 2: Filter which images to show
VideoFilterPanel.videoFiltersSplitPane.border.title=Step 2: Filter which videos to show
DiscoveryDialog.step1Label.text=Step 1: Choose result type
ResultsSplitPaneDivider.hideButton.text=
ResultsSplitPaneDivider.showButton.text=
ResultsSplitPaneDivider.detailsLabel.text=Details Area

View File

@ -1,280 +0,0 @@
CTL_OpenDiscoveryAction=Discovery
# {0} - dataSourceName
DataSourceModuleWrapper.exifModule.text=Exif Parser module was not run on data source: {0}\n
# {0} - dataSourceName
DataSourceModuleWrapper.fileTypeModule.text=File Type Identification module was not run on data source: {0}\n
# {0} - dataSourceName
DataSourceModuleWrapper.hashModule.text=Hash Lookup module was not run on data source: {0}\n
DiscoveryDialog.name.text=Discovery
DiscoveryTopComponent.cancelButton.text=Cancel Search
DiscoveryTopComponent.name=\ Discovery
DiscoveryTopComponent.newSearch.text=New Search
DiscoveryTopComponent.searchCancelled.text=Search has been cancelled.
# {0} - search
DiscoveryTopComponent.searchComplete.text=Results with {0}
# {0} - searchType
DiscoveryTopComponent.searchInProgress.text=Performing search for results of type {0}. Please wait.
DiscoveryUiUtility.bytes.text=bytes
DiscoveryUiUtility.gigaBytes.text=GB
DiscoveryUiUtility.kiloBytes.text=KB
DiscoveryUiUtility.megaBytes.text=MB
# {0} - fileSize
# {1} - units
DiscoveryUiUtility.sizeLabel.text=Size: {0} {1}
DiscoveryUiUtility.terraBytes.text=TB
# {0} - otherInstanceCount
DocumentPanel.nameLabel.more.text=\ and {0} more
DocumentPanel.noImageExtraction.text=0 of ? images
DocumentPanel.numberOfImages.noImages=No images
# {0} - numberOfImages
DocumentPanel.numberOfImages.text=1 of {0} images
DocumentWrapper.previewInitialValue=Preview not generated yet.
FileGroup.groupSortingAlgorithm.groupName.text=Group Name
FileGroup.groupSortingAlgorithm.groupSize.text=Group Size
# {0} - Data source name
# {1} - Data source ID
FileSearch.DataSourceGroupKey.datasourceAndID={0}(ID: {1})
# {0} - Data source ID
FileSearch.DataSourceGroupKey.idOnly=Data source (ID: {0})
FileSearch.documentSummary.noBytes=No bytes read for document, unable to display preview.
FileSearch.documentSummary.noPreview=No preview available.
FileSearch.FileTagGroupKey.noSets=None
# {0} - file name
FileSearch.genVideoThumb.progress.text=extracting temporary file {0}
FileSearch.GroupingAttributeType.datasource.displayName=Data Source
FileSearch.GroupingAttributeType.fileType.displayName=File Type
FileSearch.GroupingAttributeType.frequency.displayName=Past Occurrences
FileSearch.GroupingAttributeType.hash.displayName=Hash Set
FileSearch.GroupingAttributeType.interestingItem.displayName=Interesting Item
FileSearch.GroupingAttributeType.keywordList.displayName=Keyword
FileSearch.GroupingAttributeType.none.displayName=None
FileSearch.GroupingAttributeType.object.displayName=Object Detected
FileSearch.GroupingAttributeType.parent.displayName=Parent Folder
FileSearch.GroupingAttributeType.size.displayName=File Size
FileSearch.GroupingAttributeType.tag.displayName=Tag
FileSearch.HashHitsGroupKey.noHashHits=None
FileSearch.InterestingItemGroupKey.noSets=None
FileSearch.KeywordListGroupKey.noKeywords=None
FileSearch.NoGroupingGroupKey.allFiles=All Files
FileSearch.ObjectDetectedGroupKey.noSets=None
FileSearchData.FileSize.100kbto1mb=: 100KB-1MB
FileSearchData.FileSize.100mbto1gb=: 100MB-1GB
FileSearchData.FileSize.10PlusGb=: 10GB+
FileSearchData.FileSize.16kbto100kb=: 16-100KB
FileSearchData.FileSize.1gbto5gb=: 1-5GB
FileSearchData.FileSize.1mbto50mb=: 1-50MB
FileSearchData.FileSize.200PlusMb=: 200MB+
FileSearchData.FileSize.500kbto100mb=: 500KB-100MB
FileSearchData.FileSize.50mbto200mb=: 50-200MB
FileSearchData.FileSize.5gbto10gb=: 5-10GB
FileSearchData.FileSize.LARGE.displayName=Large
FileSearchData.FileSize.MEDIUM.displayName=Medium
FileSearchData.FileSize.SMALL.displayName=Small
FileSearchData.FileSize.upTo16kb=: 0-16KB
FileSearchData.FileSize.upTo500kb=: 0-500KB
FileSearchData.FileSize.XLARGE.displayName=XLarge
FileSearchData.FileSize.XSMALL.displayName=XSmall
FileSearchData.FileSize.XXLARGE.displayName=XXLarge
FileSearchData.FileType.Audio.displayName=Audio
FileSearchData.FileType.Documents.displayName=Documents
FileSearchData.FileType.Executables.displayName=Executables
FileSearchData.FileType.Image.displayName=Image
FileSearchData.FileType.Other.displayName=Other/Unknown
FileSearchData.FileType.Video.displayName=Video
FileSearchData.Frequency.common.displayName=Common (11 - 100)
FileSearchData.Frequency.known.displayName=Known (NSRL)
FileSearchData.Frequency.rare.displayName=Rare (2-10)
FileSearchData.Frequency.unique.displayName=Unique (1)
FileSearchData.Frequency.unknown.displayName=Unknown
FileSearchData.Frequency.verycommon.displayName=Very Common (100+)
FileSearchData.Score.interesting.displayName=Interesting
FileSearchData.Score.notable.displayName=Notable
FileSearchData.Score.unknown.displayName=Unknown
FileSearchDialog.jLabel1.text=File Type
FileSearchDialog.dsCheckBox.text=Data source
FileSearchDialog.cancelButton.text=Cancel
FileSearchDialog.freqCheckBox.text=CR Frequency
FileSearchDialog.sizeCheckBox.text=Size
FileSearchDialog.kwCheckBox.text=Keyword
FileSearchDialog.addParentButton.text=Add
FileSearchDialog.deleteParentButton.text=Delete
FileSearchDialog.parentFullRadioButton.text=Full
FileSearchDialog.parentSubstringRadioButton.text=Substring
FileSearchDialog.jLabel2.text=(All will be used)
FileSearchDialog.jLabel3.text=Group by attribute:
FileSearchDialog.jLabel4.text=Order groups by:
FileSearchDialog.orderAttrRadioButton.text=Attribute
FileSearchDialog.orderSizeRadioButton.text=Group Size
FileSearchDialog.jLabel5.text=Order files by:
FileSearchDialog.parentCheckBox.text=Parent
FileSearchFiltering.concatenateSetNamesForDisplay.comma=,
# {0} - Data source name
# {1} - Data source ID
FileSearchFiltering.DataSourceFilter.datasource={0}({1})
# {0} - filters
FileSearchFiltering.DataSourceFilter.desc=Data source(s): {0}
FileSearchFiltering.DataSourceFilter.or=,
# {0} - filters
FileSearchFiltering.FileTypeFilter.desc=Type: {0}
FileSearchFiltering.FileTypeFilter.or=,
# {0} - filters
FileSearchFiltering.FrequencyFilter.desc=Past occurrences: {0}
FileSearchFiltering.FrequencyFilter.or=,
# {0} - filters
FileSearchFiltering.HashSetFilter.desc=Hash set hits in set(s): {0}
# {0} - filters
FileSearchFiltering.InterestingItemSetFilter.desc=Interesting item hits in set(s): {0}
# {0} - filters
FileSearchFiltering.KeywordListFilter.desc=Keywords in list(s): {0}
FileSearchFiltering.KnownFilter.desc=which are not known
# {0} - filters
FileSearchFiltering.ObjectDetectionFilter.desc=Objects detected in set(s): {0}
# {0} - filters
FileSearchFiltering.ParentFilter.desc=Paths matching: {0}
FileSearchFiltering.ParentFilter.exact=(exact match)
FileSearchFiltering.ParentFilter.excluded=(excluded)
FileSearchFiltering.ParentFilter.included=(included)
FileSearchFiltering.ParentFilter.or=,
FileSearchFiltering.ParentFilter.substring=(substring)
FileSearchFiltering.ParentSearchTerm.excludeString=\ (exclude)
FileSearchFiltering.ParentSearchTerm.fullString=\ (exact)
FileSearchFiltering.ParentSearchTerm.includeString=\ (include)
FileSearchFiltering.ParentSearchTerm.subString=\ (substring)
FileSearchFiltering.PreviouslyNotableFilter.desc=that were previously marked as notable
# {0} - filters
FileSearchFiltering.ScoreFilter.desc=Score(s) of : {0}
# {0} - filters
FileSearchFiltering.SizeFilter.desc=Size(s): {0}
FileSearchFiltering.SizeFilter.or=,
# {0} - tag names
FileSearchFiltering.TagsFilter.desc=Tagged {0}
FileSearchFiltering.TagsFilter.or=,
FileSearchFiltering.UserCreatedFilter.desc=that contain EXIF data
FileSearchPanel.sortingPanel.border.title=Grouping
FileSearchPanel.addButton.text=Add
FileSearchPanel.substringRadioButton.text=Substring
FileSearchPanel.fullRadioButton.text=Full
FileSearchPanel.parentCheckbox.text=Parent Folder:
FileSearchPanel.keywordCheckbox.text=Keyword:
FileSearchPanel.crFrequencyCheckbox.text=Past Occurrences:
FileSearchPanel.dataSourceCheckbox.text=Data Source:
FileSearchPanel.sizeCheckbox.text=File Size:
FileSearchPanel.orderGroupsByLabel.text=Order Groups By:
FileSearchPanel.filtersScrollPane.border.title=Filters
FileSearchPanel.parentLabel.text=(All will be used)
FileSearchPanel.deleteButton.text=Delete
FileSearchPanel.orderByLabel.text=Order Within Groups By:
FileSearchPanel.groupByLabel.text=Group By:
FileSearchDialog.searchButton.text=Search
FileSearchDialog.hashCheckBox.text=Hash Set
FileSearchDialog.intCheckBox.text=Interesting Items
FileSearchDialog.tagsCheckBox.text=Tags
FileSearchDialog.objCheckBox.text=Objects
FileSearchDialog.exifCheckBox.text=Must contain EXIF data
FileSearchDialog.notableCheckBox.text=Must have been tagged as notable
FileSearchDialog.scoreCheckBox.text=Has score
FileSearchPanel.hashSetCheckbox.text=Hash Set:
FileSearchPanel.tagsCheckbox.text=Tag:
FileSearchPanel.interestingItemsCheckbox.text=Interesting Item:
FileSearchPanel.scoreCheckbox.text=Has Score:
FileSearchPanel.notableCheckbox.text=Must have been tagged as notable
FileSearchPanel.objectsCheckbox.text=Object Detected:
FileSorter.SortingMethod.datasource.displayName=Data Source
FileSorter.SortingMethod.filename.displayName=File Name
FileSorter.SortingMethod.filesize.displayName=File Size
FileSorter.SortingMethod.filetype.displayName=File Type
FileSorter.SortingMethod.frequency.displayName=Central Repo Frequency
FileSorter.SortingMethod.fullPath.displayName=Full Path
FileSorter.SortingMethod.keywordlist.displayName=Keyword List Names
GroupsListPanel.noResults.message.text=No results were found for the selected filters.\n\nReminder:\n -The File Type Identification module must be run on each data source you want to find results in.\n -The Hash Lookup module must be run on each data source if you want to filter by past occurrence.\n -The Exif module must be run on each data source if you are filtering by User Created content.
GroupsListPanel.noResults.title.text=No results found
ImageThumbnailPanel.isDeleted.text=All instances of file are deleted.
# {0} - otherInstanceCount
ImageThumbnailPanel.nameLabel.more.text=\ and {0} more
OpenDiscoveryAction.resultsIncomplete.text=Discovery results may be incomplete
ResultFile.score.interestingResult.description=At least one instance of the file has an interesting result associated with it.
ResultFile.score.notableFile.description=At least one instance of the file was recognized as notable.
ResultFile.score.notableTaggedFile.description=At least one instance of the file is tagged with a notable tag.
ResultFile.score.taggedFile.description=At least one instance of the file has been tagged.
# {0} - currentPage
# {1} - totalPages
ResultsPanel.currentPage.displayValue=Page: {0} of {1}
ResultsPanel.currentPageLabel.text=Page: -
ResultsPanel.documentPreview.text=Document preview creation cancelled.
# {0} - selectedPage
# {1} - maxPage
ResultsPanel.invalidPageNumber.message=The selected page number {0} does not exist. Please select a value from 1 to {1}.
ResultsPanel.invalidPageNumber.title=Invalid Page Number
ResultsPanel.openInExternalViewer.name=Open in External Viewer
ResultsPanel.pageControlsLabel.text=Pages:
ResultsPanel.gotoPageLabel.text=Go to Page:
ResultsPanel.pageSizeLabel.text=Page Size:
DiscoveryExtractAction.title.extractFiles.text=Extract File
FileSearchPanel.includeRadioButton.text=Include
FileSearchPanel.excludeRadioButton.text=Exclude
FileSearchPanel.knownFilesCheckbox.toolTipText=
FileSearchPanel.knownFilesCheckbox.text=Hide known files
GroupListPanel.groupKeyList.border.title=Groups
FileSearchPanel.stepThreeLabel.text=Step 3: Choose display settings
DocumentPanel.fileSizeLabel.toolTipText=
DocumentPanel.isDeletedLabel.toolTipText=
ImageThumbnailPanel.isDeletedLabel.toolTipText=
FileSearchPanel.userCreatedCheckbox.text=Possibly User Created
DiscoveryDialog.documentsButton.text=Documents
DiscoveryDialog.videosButton.text=Videos
DiscoveryDialog.imagesButton.text=Images
DiscoveryDialog.searchButton.text=Search
DetailsPanel.instancesList.border.title=Instances
ResultsPanel.unableToCreate.text=Unable to create summary.
ResultsPanel.viewFileInDir.name=View File in Directory
SizeFilterPanel.sizeCheckbox.text=File Size:
DataSourceFilterPanel.dataSourceCheckbox.text=Data Source:
UserCreatedFilterPanel.userCreatedCheckbox.text=Possibly User Created
# To change this license header, choose License Headers in Project Properties.
# To change this template file, choose Tools | Templates
# and open the template in the editor.
HashSetFilterPanel.hashSetCheckbox.text=Hash Set:
InterestingItemFilterPanel.interestingItemsCheckbox.text=Interesting Item:
ParentFolderFilterPanel.parentCheckbox.text=Parent Folder:
ParentFolderFilterPanel.deleteButton.text=Delete
ParentFolderFilterPanel.excludeRadioButton.text=Exclude
ParentFolderFilterPanel.includeRadioButton.text=Include
ParentFolderFilterPanel.substringRadioButton.text=Substring
ParentFolderFilterPanel.fullRadioButton.text=Full
ParentFolderFilterPanel.parentLabel.text=(All will be used)
ParentFolderFilterPanel.addButton.text=Add
ParentFolderFilterPanel.parentCheckbox.text_1=Parent Folder:
ParentFolderFilterPanel.addButton.text_1=Add
ParentFolderFilterPanel.deleteButton.text_1=Delete
ParentFolderFilterPanel.excludeRadioButton.text_1=Exclude
ParentFolderFilterPanel.substringRadioButton.text_1=Substring
ParentFolderFilterPanel.includeRadioButton.text_1=Include
ParentFolderFilterPanel.fullRadioButton.text_1=Full
ParentFolderFilterPanel.parentLabel.text_1=(All will be used)
InterestingItemsFilterPanel.interestingItemsCheckbox.text=Interesting Item:
UserCreatedFilterPanel.userCreatedCheckbox.text_1=Possibly User Created
PastOccurrencesFilterPanel.pastOccurrencesCheckbox.text=Past Occurrences:
ObjectDetectedFilterPanel.text=Object Detected:
DiscoveryDialog.sortingPanel.border.title=Step 3: Choose display settings
DiscoveryDialog.groupByLabel.text=Group By:
DiscoveryDialog.orderByLabel.text=Order Within Groups By:
DiscoveryDialog.orderGroupsByLabel.text=Order Groups By:
ImageFilterPanel.imageFiltersSplitPane.toolTipText=
DocumentFilterPanel.documentsFiltersSplitPane.border.title=Step 2: Filter which documents to show
ImageFilterPanel.imageFiltersSplitPane.border.title=Step 2: Filter which images to show
VideoFilterPanel.videoFiltersSplitPane.border.title=Step 2: Filter which videos to show
DiscoveryDialog.step1Label.text=Step 1: Choose result type
ResultsSplitPaneDivider.hideButton.text=
ResultsSplitPaneDivider.showButton.text=
ResultsSplitPaneDivider.detailsLabel.text=Details Area
VideoThumbnailPanel.bytes.text=bytes
VideoThumbnailPanel.deleted.text=All instances of file are deleted.
VideoThumbnailPanel.gigaBytes.text=GB
VideoThumbnailPanel.kiloBytes.text=KB
VideoThumbnailPanel.megaBytes.text=MB
# {0} - otherInstanceCount
VideoThumbnailPanel.nameLabel.more.text=\ and {0} more
# {0} - fileSize
# {1} - units
VideoThumbnailPanel.sizeLabel.text=Size: {0} {1}
VideoThumbnailPanel.terraBytes.text=TB

View File

@ -1,253 +0,0 @@
/*
* Autopsy
*
* Copyright 2020 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.discovery;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Point;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import org.openide.util.ImageUtilities;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.IngestJobInfo;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Utility class for the various user interface elements used by Discovery.
*/
final class DiscoveryUiUtils {
private final static Logger logger = Logger.getLogger(DiscoveryUiUtils.class.getName());
private static final int BYTE_UNIT_CONVERSION = 1000;
private static final int ICON_SIZE = 16;
private static final String RED_CIRCLE_ICON_PATH = "org/sleuthkit/autopsy/images/red-circle-exclamation.png";
private static final String YELLOW_CIRCLE_ICON_PATH = "org/sleuthkit/autopsy/images/yellow-circle-yield.png";
private static final String DELETE_ICON_PATH = "org/sleuthkit/autopsy/images/file-icon-deleted.png";
private static final String UNSUPPORTED_DOC_PATH = "org/sleuthkit/autopsy/images/image-extraction-not-supported.png";
private static final ImageIcon INTERESTING_SCORE_ICON = new ImageIcon(ImageUtilities.loadImage(YELLOW_CIRCLE_ICON_PATH, false));
private static final ImageIcon NOTABLE_SCORE_ICON = new ImageIcon(ImageUtilities.loadImage(RED_CIRCLE_ICON_PATH, false));
private static final ImageIcon DELETED_ICON = new ImageIcon(ImageUtilities.loadImage(DELETE_ICON_PATH, false));
private static final ImageIcon UNSUPPORTED_DOCUMENT_THUMBNAIL = new ImageIcon(ImageUtilities.loadImage(UNSUPPORTED_DOC_PATH, false));
@NbBundle.Messages({"# {0} - fileSize",
"# {1} - units",
"DiscoveryUiUtility.sizeLabel.text=Size: {0} {1}",
"DiscoveryUiUtility.bytes.text=bytes",
"DiscoveryUiUtility.kiloBytes.text=KB",
"DiscoveryUiUtility.megaBytes.text=MB",
"DiscoveryUiUtility.gigaBytes.text=GB",
"DiscoveryUiUtility.terraBytes.text=TB"})
/**
* Convert a size in bytes to a string with representing the size in the
* largest units which represent the value as being greater than or equal to
* one. Result will be rounded down to the nearest whole number of those
* units.
*
* @param bytes Size in bytes.
*/
static String getFileSizeString(long bytes) {
long size = bytes;
int unitsSwitchValue = 0;
while (size > BYTE_UNIT_CONVERSION && unitsSwitchValue < 4) {
size /= BYTE_UNIT_CONVERSION;
unitsSwitchValue++;
}
String units;
switch (unitsSwitchValue) {
case 1:
units = Bundle.DiscoveryUiUtility_kiloBytes_text();
break;
case 2:
units = Bundle.DiscoveryUiUtility_megaBytes_text();
break;
case 3:
units = Bundle.DiscoveryUiUtility_gigaBytes_text();
break;
case 4:
units = Bundle.DiscoveryUiUtility_terraBytes_text();
break;
default:
units = Bundle.DiscoveryUiUtility_bytes_text();
break;
}
return Bundle.DiscoveryUiUtility_sizeLabel_text(size, units);
}
/**
* Get the image to use when the document type does not support image
* extraction.
*
* @return An image that indicates we don't know if there are images.
*/
static ImageIcon getUnsupportedImageThumbnail() {
return UNSUPPORTED_DOCUMENT_THUMBNAIL;
}
/**
* Get the names of the sets which exist in the case database for the
* specified artifact and attribute types.
*
* @param artifactType The artifact type to get the list of sets for.
* @param setNameAttribute The attribute type which contains the set names.
*
* @return A list of set names which exist in the case for the specified
* artifact and attribute types.
*
* @throws TskCoreException
*/
static List<String> getSetNames(BlackboardArtifact.ARTIFACT_TYPE artifactType, BlackboardAttribute.ATTRIBUTE_TYPE setNameAttribute) throws TskCoreException {
List<BlackboardArtifact> arts = Case.getCurrentCase().getSleuthkitCase().getBlackboardArtifacts(artifactType);
List<String> setNames = new ArrayList<>();
for (BlackboardArtifact art : arts) {
for (BlackboardAttribute attr : art.getAttributes()) {
if (attr.getAttributeType().getTypeID() == setNameAttribute.getTypeID()) {
String setName = attr.getValueString();
if (!setNames.contains(setName)) {
setNames.add(setName);
}
}
}
}
Collections.sort(setNames);
return setNames;
}
/**
* Helper method to see if point is on the icon.
*
* @param comp The component to check if the cursor is over the icon of
* @param point The point the cursor is at.
*
* @return True if the point is over the icon, false otherwise.
*/
static boolean isPointOnIcon(Component comp, Point point) {
return comp instanceof JComponent && point.x >= comp.getX() && point.x <= comp.getX() + ICON_SIZE && point.y >= comp.getY() && point.y <= comp.getY() + ICON_SIZE;
}
/**
* Method to set the icon and tool tip text for a label to show deleted
* status.
*
* @param isDeleted True if the label should reflect deleted status,
* false otherwise.
* @param isDeletedLabel The label to set the icon and tooltip for.
*/
static void setDeletedIcon(boolean isDeleted, javax.swing.JLabel isDeletedLabel) {
if (isDeleted) {
isDeletedLabel.setIcon(DELETED_ICON);
isDeletedLabel.setToolTipText(Bundle.ImageThumbnailPanel_isDeleted_text());
} else {
isDeletedLabel.setIcon(null);
isDeletedLabel.setToolTipText(null);
}
}
/**
* Method to set the icon and tool tip text for a label to show the score.
*
* @param resultFile The result file which the label should reflect the
* score of.
* @param scoreLabel The label to set the icon and tooltip for.
*/
static void setScoreIcon(ResultFile resultFile, javax.swing.JLabel scoreLabel) {
switch (resultFile.getScore()) {
case NOTABLE_SCORE:
scoreLabel.setIcon(NOTABLE_SCORE_ICON);
break;
case INTERESTING_SCORE:
scoreLabel.setIcon(INTERESTING_SCORE_ICON);
break;
case NO_SCORE: // empty case - this is interpreted as an intentional fall-through
default:
scoreLabel.setIcon(null);
break;
}
scoreLabel.setToolTipText(resultFile.getScoreDescription());
}
/**
* Get the size of the icons used by the UI.
*
* @return
*/
static int getIconSize() {
return ICON_SIZE;
}
/**
* Helper method to display an error message when the results of the
* Discovery Top component may be incomplete.
*/
static void displayErrorMessage(DiscoveryDialog dialog) {
//check if modules run and assemble message
try {
SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
Map<Long, DataSourceModulesWrapper> dataSourceIngestModules = new HashMap<>();
for (DataSource dataSource : skCase.getDataSources()) {
dataSourceIngestModules.put(dataSource.getId(), new DataSourceModulesWrapper(dataSource.getName()));
}
for (IngestJobInfo jobInfo : skCase.getIngestJobs()) {
dataSourceIngestModules.get(jobInfo.getObjectId()).updateModulesRun(jobInfo);
}
String message = "";
for (DataSourceModulesWrapper dsmodulesWrapper : dataSourceIngestModules.values()) {
message += dsmodulesWrapper.getMessage();
}
if (!message.isEmpty()) {
JScrollPane messageScrollPane = new JScrollPane();
JTextPane messageTextPane = new JTextPane();
messageTextPane.setText(message);
messageTextPane.setVisible(true);
messageTextPane.setEditable(false);
messageTextPane.setCaretPosition(0);
messageScrollPane.setMaximumSize(new Dimension(600, 100));
messageScrollPane.setPreferredSize(new Dimension(600, 100));
messageScrollPane.setViewportView(messageTextPane);
JOptionPane.showMessageDialog(dialog, messageScrollPane, Bundle.OpenDiscoveryAction_resultsIncomplete_text(), JOptionPane.PLAIN_MESSAGE);
}
} catch (NoCurrentCaseException | TskCoreException ex) {
logger.log(Level.WARNING, "Exception while determining which modules have been run for Discovery", ex);
}
dialog.validateDialog();
}
/**
* Private constructor for DiscoveryUiUtils utility class.
*/
private DiscoveryUiUtils() {
//private constructor in a utility class intentionally left blank
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,444 +0,0 @@
/*
* 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.discovery;
import com.google.common.collect.ImmutableSet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.FileTypeUtils;
/**
* Utility enums for FileSearch.
*/
final class FileSearchData {
private final static long BYTES_PER_MB = 1000000;
/**
* Enum representing how often the file occurs in the Central Repository.
*/
@NbBundle.Messages({
"FileSearchData.Frequency.unique.displayName=Unique (1)",
"FileSearchData.Frequency.rare.displayName=Rare (2-10)",
"FileSearchData.Frequency.common.displayName=Common (11 - 100)",
"FileSearchData.Frequency.verycommon.displayName=Very Common (100+)",
"FileSearchData.Frequency.known.displayName=Known (NSRL)",
"FileSearchData.Frequency.unknown.displayName=Unknown",})
enum Frequency {
UNIQUE(0, 1, Bundle.FileSearchData_Frequency_unique_displayName()),
RARE(1, 10, Bundle.FileSearchData_Frequency_rare_displayName()),
COMMON(2, 100, Bundle.FileSearchData_Frequency_common_displayName()),
VERY_COMMON(3, 0, Bundle.FileSearchData_Frequency_verycommon_displayName()),
KNOWN(4, 0, Bundle.FileSearchData_Frequency_known_displayName()),
UNKNOWN(5, 0, Bundle.FileSearchData_Frequency_unknown_displayName());
private final int ranking;
private final String displayName;
private final int maxOccur;
Frequency(int ranking, int maxOccur, String displayName) {
this.ranking = ranking;
this.maxOccur = maxOccur;
this.displayName = displayName;
}
/**
* Get the rank for sorting.
*
* @return the rank (lower should be displayed first)
*/
int getRanking() {
return ranking;
}
/**
* Get the enum matching the given occurrence count.
*
* @param count Number of times a file is in the Central Repository.
*
* @return the corresponding enum
*/
static Frequency fromCount(long count) {
if (count <= UNIQUE.getMaxOccur()) {
return UNIQUE;
} else if (count <= RARE.getMaxOccur()) {
return RARE;
} else if (count <= COMMON.getMaxOccur()) {
return COMMON;
}
return VERY_COMMON;
}
/**
* Get the list of enums that are valid for filtering when a CR is
* enabled.
*
* @return enums that can be used to filter with a CR.
*/
static List<Frequency> getOptionsForFilteringWithCr() {
return Arrays.asList(UNIQUE, RARE, COMMON, VERY_COMMON, KNOWN);
}
/**
* Get the list of enums that are valid for filtering when no CR is
* enabled.
*
* @return enums that can be used to filter without a CR.
*/
static List<Frequency> getOptionsForFilteringWithoutCr() {
return Arrays.asList(KNOWN, UNKNOWN);
}
@Override
public String toString() {
return displayName;
}
/**
* @return the maxOccur
*/
int getMaxOccur() {
return maxOccur;
}
}
/**
* Enum representing the file size
*/
@NbBundle.Messages({
"FileSearchData.FileSize.XXLARGE.displayName=XXLarge",
"FileSearchData.FileSize.XLARGE.displayName=XLarge",
"FileSearchData.FileSize.LARGE.displayName=Large",
"FileSearchData.FileSize.MEDIUM.displayName=Medium",
"FileSearchData.FileSize.SMALL.displayName=Small",
"FileSearchData.FileSize.XSMALL.displayName=XSmall",
"FileSearchData.FileSize.10PlusGb=: 10GB+",
"FileSearchData.FileSize.5gbto10gb=: 5-10GB",
"FileSearchData.FileSize.1gbto5gb=: 1-5GB",
"FileSearchData.FileSize.100mbto1gb=: 100MB-1GB",
"FileSearchData.FileSize.200PlusMb=: 200MB+",
"FileSearchData.FileSize.50mbto200mb=: 50-200MB",
"FileSearchData.FileSize.500kbto100mb=: 500KB-100MB",
"FileSearchData.FileSize.1mbto50mb=: 1-50MB",
"FileSearchData.FileSize.100kbto1mb=: 100KB-1MB",
"FileSearchData.FileSize.16kbto100kb=: 16-100KB",
"FileSearchData.FileSize.upTo500kb=: 0-500KB",
"FileSearchData.FileSize.upTo16kb=: 0-16KB",})
enum FileSize {
XXLARGE_VIDEO(0, 10000 * BYTES_PER_MB, -1, Bundle.FileSearchData_FileSize_XXLARGE_displayName(), Bundle.FileSearchData_FileSize_10PlusGb()),
XLARGE_VIDEO(1, 5000 * BYTES_PER_MB, 10000 * BYTES_PER_MB, Bundle.FileSearchData_FileSize_XLARGE_displayName(), Bundle.FileSearchData_FileSize_5gbto10gb()),
LARGE_VIDEO(2, 1000 * BYTES_PER_MB, 5000 * BYTES_PER_MB, Bundle.FileSearchData_FileSize_LARGE_displayName(), Bundle.FileSearchData_FileSize_1gbto5gb()),
MEDIUM_VIDEO(3, 100 * BYTES_PER_MB, 1000 * BYTES_PER_MB, Bundle.FileSearchData_FileSize_MEDIUM_displayName(), Bundle.FileSearchData_FileSize_100mbto1gb()),
SMALL_VIDEO(4, 500000, 100 * BYTES_PER_MB, Bundle.FileSearchData_FileSize_SMALL_displayName(), Bundle.FileSearchData_FileSize_500kbto100mb()),
XSMALL_VIDEO(5, 0, 500000, Bundle.FileSearchData_FileSize_XSMALL_displayName(), Bundle.FileSearchData_FileSize_upTo500kb()),
XXLARGE_IMAGE(6, 200 * BYTES_PER_MB, -1, Bundle.FileSearchData_FileSize_XXLARGE_displayName(), Bundle.FileSearchData_FileSize_200PlusMb()),
XLARGE_IMAGE(7, 50 * BYTES_PER_MB, 200 * BYTES_PER_MB, Bundle.FileSearchData_FileSize_XLARGE_displayName(), Bundle.FileSearchData_FileSize_50mbto200mb()),
LARGE_IMAGE(8, 1 * BYTES_PER_MB, 50 * BYTES_PER_MB, Bundle.FileSearchData_FileSize_LARGE_displayName(), Bundle.FileSearchData_FileSize_1mbto50mb()),
MEDIUM_IMAGE(9, 100000, 1 * BYTES_PER_MB, Bundle.FileSearchData_FileSize_MEDIUM_displayName(), Bundle.FileSearchData_FileSize_100kbto1mb()),
SMALL_IMAGE(10, 16000, 100000, Bundle.FileSearchData_FileSize_SMALL_displayName(), Bundle.FileSearchData_FileSize_16kbto100kb()),
XSMALL_IMAGE(11, 0, 16000, Bundle.FileSearchData_FileSize_XSMALL_displayName(), Bundle.FileSearchData_FileSize_upTo16kb());
private final int ranking; // Must be unique for each value
private final long minBytes; // Note that the size must be strictly greater than this to match
private final long maxBytes;
private final String sizeGroup;
private final String displaySize;
final static long NO_MAXIMUM = -1;
FileSize(int ranking, long minB, long maxB, String displayName, String displaySize) {
this.ranking = ranking;
this.minBytes = minB;
if (maxB >= 0) {
this.maxBytes = maxB;
} else {
this.maxBytes = NO_MAXIMUM;
}
this.sizeGroup = displayName;
this.displaySize = displaySize;
}
/**
* Get the enum corresponding to the given file size for image files.
* The file size must be strictly greater than minBytes.
*
* @param size the file size
*
* @return the enum whose range contains the file size
*/
static FileSize fromImageSize(long size) {
if (size > XXLARGE_IMAGE.getMinBytes()) {
return XXLARGE_IMAGE;
} else if (size > XLARGE_IMAGE.getMinBytes()) {
return XLARGE_IMAGE;
} else if (size > LARGE_IMAGE.getMinBytes()) {
return LARGE_IMAGE;
} else if (size > MEDIUM_IMAGE.getMinBytes()) {
return MEDIUM_IMAGE;
} else if (size > SMALL_IMAGE.getMinBytes()) {
return SMALL_IMAGE;
} else {
return XSMALL_IMAGE;
}
}
/**
* Get the enum corresponding to the given file size for video files.
* The file size must be strictly greater than minBytes.
*
* @param size the file size
*
* @return the enum whose range contains the file size
*/
static FileSize fromVideoSize(long size) {
if (size > XXLARGE_VIDEO.getMinBytes()) {
return XXLARGE_VIDEO;
} else if (size > XLARGE_VIDEO.getMinBytes()) {
return XLARGE_VIDEO;
} else if (size > LARGE_VIDEO.getMinBytes()) {
return LARGE_VIDEO;
} else if (size > MEDIUM_VIDEO.getMinBytes()) {
return MEDIUM_VIDEO;
} else if (size > SMALL_VIDEO.getMinBytes()) {
return SMALL_VIDEO;
} else {
return XSMALL_VIDEO;
}
}
/**
* Get the upper limit of the range.
*
* @return the maximum file size that will fit in this range.
*/
long getMaxBytes() {
return maxBytes;
}
/**
* Get the lower limit of the range.
*
* @return the maximum file size that is not part of this range
*/
long getMinBytes() {
return minBytes;
}
/**
* Get the rank for sorting.
*
* @return the rank (lower should be displayed first)
*/
int getRanking() {
return ranking;
}
@Override
public String toString() {
return sizeGroup + displaySize;
}
String getSizeGroup(){
return sizeGroup;
}
/**
* Get the list of enums that are valid for most file sizes.
*
* @return Enums that can be used to filter most file including images
* by size.
*/
static List<FileSize> getDefaultSizeOptions() {
return Arrays.asList(XXLARGE_IMAGE, XLARGE_IMAGE, LARGE_IMAGE, MEDIUM_IMAGE, SMALL_IMAGE, XSMALL_IMAGE);
}
/**
* Get the list of enums that are valid for video sizes.
*
* @return enums that can be used to filter videos by size.
*/
static List<FileSize> getOptionsForVideos() {
return Arrays.asList(XXLARGE_VIDEO, XLARGE_VIDEO, LARGE_VIDEO, MEDIUM_VIDEO, SMALL_VIDEO, XSMALL_VIDEO);
}
}
//Discovery uses a different list of document mime types than FileTypeUtils.FileTypeCategory.DOCUMENTS
private static final ImmutableSet<String> DOCUMENT_MIME_TYPES
= new ImmutableSet.Builder<String>()
.add("text/html", //NON-NLS
"text/csv", //NON-NLS
"application/rtf", //NON-NLS
"application/pdf", //NON-NLS
"application/xhtml+xml", //NON-NLS
"application/x-msoffice", //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> IMAGE_UNSUPPORTED_DOC_TYPES
= new ImmutableSet.Builder<String>()
.add("application/pdf", //NON-NLS
"application/xhtml+xml").build(); //NON-NLS
static Collection<String> getDocTypesWithoutImageExtraction() {
return Collections.unmodifiableCollection(IMAGE_UNSUPPORTED_DOC_TYPES);
}
/**
* Enum representing the file type. We don't simply use
* FileTypeUtils.FileTypeCategory because: - Some file types categories
* overlap - It is convenient to have the "OTHER" option for files that
* don't match the given types
*/
@NbBundle.Messages({
"FileSearchData.FileType.Audio.displayName=Audio",
"FileSearchData.FileType.Video.displayName=Video",
"FileSearchData.FileType.Image.displayName=Image",
"FileSearchData.FileType.Documents.displayName=Documents",
"FileSearchData.FileType.Executables.displayName=Executables",
"FileSearchData.FileType.Other.displayName=Other/Unknown"})
enum FileType {
IMAGE(0, Bundle.FileSearchData_FileType_Image_displayName(), FileTypeUtils.FileTypeCategory.IMAGE.getMediaTypes()),
AUDIO(1, Bundle.FileSearchData_FileType_Audio_displayName(), FileTypeUtils.FileTypeCategory.AUDIO.getMediaTypes()),
VIDEO(2, Bundle.FileSearchData_FileType_Video_displayName(), FileTypeUtils.FileTypeCategory.VIDEO.getMediaTypes()),
EXECUTABLE(3, Bundle.FileSearchData_FileType_Executables_displayName(), FileTypeUtils.FileTypeCategory.EXECUTABLE.getMediaTypes()),
DOCUMENTS(4, Bundle.FileSearchData_FileType_Documents_displayName(), DOCUMENT_MIME_TYPES),
OTHER(5, Bundle.FileSearchData_FileType_Other_displayName(), new ArrayList<>());
private final int ranking; // For ordering in the UI
private final String displayName;
private final Collection<String> mediaTypes;
FileType(int value, String displayName, Collection<String> mediaTypes) {
this.ranking = value;
this.displayName = displayName;
this.mediaTypes = mediaTypes;
}
/**
* Get the MIME types matching this category.
*
* @return Collection of MIME type strings
*/
Collection<String> getMediaTypes() {
return Collections.unmodifiableCollection(mediaTypes);
}
@Override
public String toString() {
return displayName;
}
/**
* Get the rank for sorting.
*
* @return the rank (lower should be displayed first)
*/
int getRanking() {
return ranking;
}
/**
* Get the enum matching the given MIME type.
*
* @param mimeType The MIME type for the file
*
* @return the corresponding enum (will be OTHER if no types matched)
*/
static FileType fromMIMEtype(String mimeType) {
for (FileType type : FileType.values()) {
if (type.getMediaTypes().contains(mimeType)) {
return type;
}
}
return OTHER;
}
/**
* Get the list of enums that are valid for filtering.
*
* @return enums that can be used to filter
*/
static List<FileType> getOptionsForFiltering() {
return Arrays.asList(IMAGE, VIDEO);
}
}
/**
* Enum representing the score of the file.
*/
@NbBundle.Messages({
"FileSearchData.Score.notable.displayName=Notable",
"FileSearchData.Score.interesting.displayName=Interesting",
"FileSearchData.Score.unknown.displayName=Unknown",})
enum Score {
NOTABLE(0, Bundle.FileSearchData_Score_notable_displayName()),
INTERESTING(1, Bundle.FileSearchData_Score_interesting_displayName()),
UNKNOWN(2, Bundle.FileSearchData_Score_unknown_displayName());
private final int ranking;
private final String displayName;
Score(int ranking, String displayName) {
this.ranking = ranking;
this.displayName = displayName;
}
/**
* Get the rank for sorting.
*
* @return the rank (lower should be displayed first)
*/
int getRanking() {
return ranking;
}
/**
* Get the list of enums that are valid for filtering.
*
* @return enums that can be used to filter
*/
static List<Score> getOptionsForFiltering() {
return Arrays.asList(NOTABLE, INTERESTING);
}
@Override
public String toString() {
return displayName;
}
}
private FileSearchData() {
// Class should not be instantiated
}
}

View File

@ -1,292 +0,0 @@
/*
* 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.discovery;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.openide.util.NbBundle;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Class used to sort ResultFiles using the supplied method.
*/
class FileSorter implements Comparator<ResultFile> {
private final List<Comparator<ResultFile>> comparators = new ArrayList<>();
/**
* Set up the sorter using the supplied sorting method. The sorting is
* defined by a list of ResultFile comparators. These comparators will be
* run in order until one returns a non-zero result.
*
* @param method The method that should be used to sort the files
*/
FileSorter(SortingMethod method) {
// Set up the primary comparators that should applied to the files
switch (method) {
case BY_DATA_SOURCE:
comparators.add(getDataSourceComparator());
break;
case BY_FILE_SIZE:
comparators.add(getFileSizeComparator());
break;
case BY_FILE_TYPE:
comparators.add(getFileTypeComparator());
comparators.add(getMIMETypeComparator());
break;
case BY_FREQUENCY:
comparators.add(getFrequencyComparator());
break;
case BY_KEYWORD_LIST_NAMES:
comparators.add(getKeywordListNameComparator());
break;
case BY_FULL_PATH:
comparators.add(getParentPathComparator());
break;
case BY_FILE_NAME:
comparators.add(getFileNameComparator());
break;
default:
// The default comparator will be added afterward
break;
}
// Add the default comparator to the end. This will ensure a consistent sort
// order regardless of the order the files were added to the list.
comparators.add(getDefaultComparator());
}
@Override
public int compare(ResultFile file1, ResultFile file2) {
int result = 0;
for (Comparator<ResultFile> comp : comparators) {
result = comp.compare(file1, file2);
if (result != 0) {
return result;
}
}
// The files are the same
return result;
}
/**
* Compare files using data source ID. Will order smallest to largest.
*
* @return -1 if file1 has the lower data source ID, 0 if equal, 1 otherwise
*/
private static Comparator<ResultFile> getDataSourceComparator() {
return (ResultFile file1, ResultFile file2) -> Long.compare(file1.getFirstInstance().getDataSourceObjectId(), file2.getFirstInstance().getDataSourceObjectId());
}
/**
* Compare files using their FileType enum. Orders based on the ranking in
* the FileType enum.
*
* @return -1 if file1 has the lower FileType value, 0 if equal, 1 otherwise
*/
private static Comparator<ResultFile> getFileTypeComparator() {
return (ResultFile file1, ResultFile file2) -> Integer.compare(file1.getFileType().getRanking(), file2.getFileType().getRanking());
}
/**
* Compare files using a concatenated version of keyword list names.
* Alphabetical by the list names with files with no keyword list hits going
* last.
*
* @return -1 if file1 has the earliest combined keyword list name, 0 if
* equal, 1 otherwise
*/
private static Comparator<ResultFile> getKeywordListNameComparator() {
return (ResultFile file1, ResultFile file2) -> {
// Put empty lists at the bottom
if (file1.getKeywordListNames().isEmpty()) {
if (file2.getKeywordListNames().isEmpty()) {
return 0;
}
return 1;
} else if (file2.getKeywordListNames().isEmpty()) {
return -1;
}
String list1 = String.join(",", file1.getKeywordListNames());
String list2 = String.join(",", file2.getKeywordListNames());
return compareStrings(list1, list2);
};
}
/**
* Compare files based on parent path. Order alphabetically.
*
* @return -1 if file1's path comes first alphabetically, 0 if equal, 1
* otherwise
*/
private static Comparator<ResultFile> getParentPathComparator() {
return new Comparator<ResultFile>() {
@Override
public int compare(ResultFile file1, ResultFile file2) {
String file1ParentPath;
try {
file1ParentPath = file1.getFirstInstance().getParent().getUniquePath();
} catch (TskCoreException ingored) {
file1ParentPath = file1.getFirstInstance().getParentPath();
}
String file2ParentPath;
try {
file2ParentPath = file2.getFirstInstance().getParent().getUniquePath();
} catch (TskCoreException ingored) {
file2ParentPath = file2.getFirstInstance().getParentPath();
}
return compareStrings(file1ParentPath.toLowerCase(), file2ParentPath.toLowerCase());
}
};
}
/**
* Compare files based on number of occurrences in the central repository.
* Order from most rare to least rare Frequency enum.
*
* @return -1 if file1's rarity is lower than file2, 0 if equal, 1 otherwise
*/
private static Comparator<ResultFile> getFrequencyComparator() {
return (ResultFile file1, ResultFile file2) -> Integer.compare(file1.getFrequency().getRanking(), file2.getFrequency().getRanking());
}
/**
* Compare files based on MIME type. Order is alphabetical.
*
* @return -1 if file1's MIME type comes before file2's, 0 if equal, 1
* otherwise
*/
private static Comparator<ResultFile> getMIMETypeComparator() {
return (ResultFile file1, ResultFile file2) -> compareStrings(file1.getFirstInstance().getMIMEType(), file2.getFirstInstance().getMIMEType());
}
/**
* Compare files based on size. Order large to small.
*
* @return -1 if file1 is larger than file2, 0 if equal, 1 otherwise
*/
private static Comparator<ResultFile> getFileSizeComparator() {
return (ResultFile file1, ResultFile file2) -> -1 * Long.compare(file1.getFirstInstance().getSize(), file2.getFirstInstance().getSize()) // Sort large to small
;
}
/**
* Compare files based on file name. Order alphabetically.
*
* @return -1 if file1 comes before file2, 0 if equal, 1 otherwise
*/
private static Comparator<ResultFile> getFileNameComparator() {
return (ResultFile file1, ResultFile file2) -> compareStrings(file1.getFirstInstance().getName().toLowerCase(), file2.getFirstInstance().getName().toLowerCase());
}
/**
* A final default comparison between two ResultFile objects. Currently this
* is on file name and then object ID. It can be changed but should always
* include something like the object ID to ensure a consistent sorting when
* the rest of the compared fields are the same.
*
* @return -1 if file1 comes before file2, 0 if equal, 1 otherwise
*/
private static Comparator<ResultFile> getDefaultComparator() {
return (ResultFile file1, ResultFile file2) -> {
// Compare file names and then object ID (to ensure a consistent sort)
int result = getFileNameComparator().compare(file1, file2);
if (result == 0) {
return Long.compare(file1.getFirstInstance().getId(), file2.getFirstInstance().getId());
}
return result;
};
}
/**
* Compare two strings alphabetically. Nulls are allowed.
*
* @param s1
* @param s2
*
* @return -1 if s1 comes before s2, 0 if equal, 1 otherwise
*/
private static int compareStrings(String s1, String s2) {
String string1 = s1 == null ? "" : s1;
String string2 = s2 == null ? "" : s2;
return string1.compareTo(string2);
}
/**
* Enum for selecting the primary method for sorting result files.
*/
@NbBundle.Messages({
"FileSorter.SortingMethod.datasource.displayName=Data Source",
"FileSorter.SortingMethod.filename.displayName=File Name",
"FileSorter.SortingMethod.filesize.displayName=File Size",
"FileSorter.SortingMethod.filetype.displayName=File Type",
"FileSorter.SortingMethod.frequency.displayName=Central Repo Frequency",
"FileSorter.SortingMethod.keywordlist.displayName=Keyword List Names",
"FileSorter.SortingMethod.fullPath.displayName=Full Path"})
enum SortingMethod {
BY_FILE_NAME(new ArrayList<>(),
Bundle.FileSorter_SortingMethod_filename_displayName()), // Sort alphabetically by file name
BY_DATA_SOURCE(new ArrayList<>(),
Bundle.FileSorter_SortingMethod_datasource_displayName()), // Sort in increasing order of data source ID
BY_FILE_SIZE(new ArrayList<>(),
Bundle.FileSorter_SortingMethod_filesize_displayName()), // Sort in decreasing order of size
BY_FILE_TYPE(Arrays.asList(new FileSearch.FileTypeAttribute()),
Bundle.FileSorter_SortingMethod_filetype_displayName()), // Sort in order of file type (defined in FileType enum), with secondary sort on MIME type
BY_FREQUENCY(Arrays.asList(new FileSearch.FrequencyAttribute()),
Bundle.FileSorter_SortingMethod_frequency_displayName()), // Sort by decreasing rarity in the central repository
BY_KEYWORD_LIST_NAMES(Arrays.asList(new FileSearch.KeywordListAttribute()),
Bundle.FileSorter_SortingMethod_keywordlist_displayName()), // Sort alphabetically by list of keyword list names found
BY_FULL_PATH(new ArrayList<>(),
Bundle.FileSorter_SortingMethod_fullPath_displayName()); // Sort alphabetically by path
private final String displayName;
private final List<FileSearch.AttributeType> requiredAttributes;
SortingMethod(List<FileSearch.AttributeType> attributes, String displayName) {
this.requiredAttributes = attributes;
this.displayName = displayName;
}
@Override
public String toString() {
return displayName;
}
List<FileSearch.AttributeType> getRequiredAttributes() {
return Collections.unmodifiableList(requiredAttributes);
}
/**
* Get the list of enums that are valid for ordering images.
*
* @return enums that can be used to ordering images.
*/
static List<SortingMethod> getOptionsForOrdering() {
return Arrays.asList(BY_FILE_SIZE, BY_FULL_PATH, BY_FILE_NAME, BY_DATA_SOURCE);
}
}
}

View File

@ -0,0 +1,74 @@
/*
* Autopsy
*
* Copyright 2020 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.discovery.search;
import java.util.ArrayList;
import java.util.List;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.datamodel.SleuthkitCase;
/**
* Base class for the filters.
*/
public abstract class AbstractFilter {
/**
* Returns part of a query on the table that can be AND-ed with other pieces
*
* @return the SQL query or an empty string if there is no SQL query for
* this filter.
*/
public abstract String getWhereClause();
/**
* Indicates whether this filter needs to use the secondary, non-SQL method
* applyAlternateFilter().
*
* @return false by default
*/
public boolean useAlternateFilter() {
return false;
}
/**
* Run a secondary filter that does not operate on table.
*
* @param currentResults The current list of matching results; empty if no
* filters have yet been run.
* @param caseDb The case database
* @param centralRepoDb The central repo database. Can be null if the
* filter does not require it.
*
* @return The list of results that match this filter (and any that came
* before it)
*
* @throws DiscoveryException
*/
public List<Result> applyAlternateFilter(List<Result> currentResults, SleuthkitCase caseDb,
CentralRepository centralRepoDb) throws DiscoveryException {
return new ArrayList<>();
}
/**
* Get a description of the selected filter.
*
* @return A description of the filter
*/
public abstract String getDesc();
}

View File

@ -0,0 +1,132 @@
DiscoveryAttributes.GroupingAttributeType.datasource.displayName=Data Source
DiscoveryAttributes.GroupingAttributeType.fileType.displayName=File Type
DiscoveryAttributes.GroupingAttributeType.firstDate.displayName=First Activity Date
DiscoveryAttributes.GroupingAttributeType.frequency.displayName=Past Occurrences
DiscoveryAttributes.GroupingAttributeType.hash.displayName=Hash Set
DiscoveryAttributes.GroupingAttributeType.interestingItem.displayName=Interesting Item
DiscoveryAttributes.GroupingAttributeType.keywordList.displayName=Keyword
DiscoveryAttributes.GroupingAttributeType.mostRecentDate.displayName=Most Recent Activity Date
DiscoveryAttributes.GroupingAttributeType.none.displayName=None
DiscoveryAttributes.GroupingAttributeType.numberOfVisits.displayName=Number of Visits
DiscoveryAttributes.GroupingAttributeType.object.displayName=Object Detected
DiscoveryAttributes.GroupingAttributeType.parent.displayName=Parent Folder
DiscoveryAttributes.GroupingAttributeType.size.displayName=File Size
DiscoveryAttributes.GroupingAttributeType.tag.displayName=Tag
# {0} - Data source name
# {1} - Data source ID
DiscoveryKeyUtils.DataSourceGroupKey.datasourceAndID={0}(ID: {1})
# {0} - Data source ID
DiscoveryKeyUtils.DataSourceGroupKey.idOnly=Data source (ID: {0})
DiscoveryKeyUtils.FileTagGroupKey.noSets=None
DiscoveryKeyUtils.FirstActivityDateGroupKey.noDate=No Date Available
DiscoveryKeyUtils.HashHitsGroupKey.noHashHits=None
DiscoveryKeyUtils.InterestingItemGroupKey.noSets=None
DiscoveryKeyUtils.KeywordListGroupKey.noKeywords=None
DiscoveryKeyUtils.MostRecentActivityDateGroupKey.noDate=No Date Available
DiscoveryKeyUtils.NoGroupingGroupKey.allFiles=All Files
# {0} - totalVisits
DiscoveryKeyUtils.NumberOfVisitsGroupKey.displayName={0} visits
DiscoveryKeyUtils.NumberOfVisitsGroupKey.noVisits=No visits
DiscoveryKeyUtils.ObjectDetectedGroupKey.noSets=None
FileGroup.groupSortingAlgorithm.groupName.text=Group Name
FileGroup.groupSortingAlgorithm.groupSize.text=Group Size
FileSearch.documentSummary.noBytes=No bytes read for document, unable to display preview.
FileSearch.documentSummary.noPreview=No preview available.
FileSearchFiltering.concatenateSetNamesForDisplay.comma=,
# {0} - filters
FileSearchFiltering.HashSetFilter.desc=Hash set hits in set(s): {0}
FileSearchFiltering.KnownFilter.desc=which are not known
FileSearchFiltering.PreviouslyNotableFilter.desc=that were previously marked as notable
# {0} - tag names
FileSearchFiltering.TagsFilter.desc=Tagged {0}
FileSearchFiltering.TagsFilter.or=,
FileSearchFiltering.UserCreatedFilter.desc=that contain EXIF data
FileSorter.SortingMethod.datasource.displayName=Data Source
FileSorter.SortingMethod.domain.displayName=Domain
FileSorter.SortingMethod.filename.displayName=File Name
FileSorter.SortingMethod.filesize.displayName=File Size
FileSorter.SortingMethod.filetype.displayName=File Type
FileSorter.SortingMethod.frequency.displayName=Central Repo Frequency
FileSorter.SortingMethod.fullPath.displayName=Full Path
FileSorter.SortingMethod.keywordlist.displayName=Keyword List Names
ResultFile.score.interestingResult.description=At least one instance of the file has an interesting result associated with it.
ResultFile.score.notableFile.description=At least one instance of the file was recognized as notable.
ResultFile.score.notableTaggedFile.description=At least one instance of the file is tagged with a notable tag.
ResultFile.score.taggedFile.description=At least one instance of the file has been tagged.
SearchData.AttributeType.Domain.displayName=Domain
SearchData.FileSize.100kbto1mb=: 100KB-1MB
SearchData.FileSize.100mbto1gb=: 100MB-1GB
SearchData.FileSize.10PlusGb=: 10GB+
SearchData.FileSize.16kbto100kb=: 16-100KB
SearchData.FileSize.1gbto5gb=: 1-5GB
SearchData.FileSize.1mbto50mb=: 1-50MB
SearchData.FileSize.200PlusMb=: 200MB+
SearchData.FileSize.500kbto100mb=: 500KB-100MB
SearchData.FileSize.50mbto200mb=: 50-200MB
SearchData.FileSize.5gbto10gb=: 5-10GB
SearchData.FileSize.LARGE.displayName=Large
SearchData.FileSize.MEDIUM.displayName=Medium
SearchData.FileSize.SMALL.displayName=Small
SearchData.FileSize.upTo16kb=: 0-16KB
SearchData.FileSize.upTo500kb=: 0-500KB
SearchData.FileSize.XLARGE.displayName=XLarge
SearchData.FileSize.XSMALL.displayName=XSmall
SearchData.FileSize.XXLARGE.displayName=XXLarge
SearchData.FileType.Audio.displayName=Audio
SearchData.FileType.Documents.displayName=Document
SearchData.FileType.Executables.displayName=Executable
SearchData.FileType.Image.displayName=Image
SearchData.FileType.Other.displayName=Other/Unknown
SearchData.FileType.Video.displayName=Video
SearchData.Frequency.common.displayName=Common (11 - 100)
SearchData.Frequency.known.displayName=Known (NSRL)
SearchData.Frequency.rare.displayName=Rare (2-10)
SearchData.Frequency.unique.displayName=Unique (1)
SearchData.Frequency.unknown.displayName=Unknown
SearchData.Frequency.verycommon.displayName=Very Common (100+)
SearchData.Score.interesting.displayName=Interesting
SearchData.Score.notable.displayName=Notable
SearchData.Score.unknown.displayName=Unknown
# {0} - artifactTypes
SearchFiltering.artifactTypeFilter.desc=Result type(s): {0}
SearchFiltering.artifactTypeFilter.or=,
# {0} - Data source name
# {1} - Data source ID
SearchFiltering.DataSourceFilter.datasource={0}({1})
# {0} - filters
SearchFiltering.DataSourceFilter.desc=Data source(s): {0}
SearchFiltering.DataSourceFilter.or=,
# {0} - startDate
SearchFiltering.dateRangeFilter.after=after: {0}
SearchFiltering.dateRangeFilter.and=\ and
# {0} - endDate
SearchFiltering.dateRangeFilter.before=before: {0}
SearchFiltering.dateRangeFilter.lable=Activity date
# {0} - filters
SearchFiltering.FileTypeFilter.desc=Type: {0}
SearchFiltering.FileTypeFilter.or=,
# {0} - filters
SearchFiltering.FrequencyFilter.desc=Past occurrences: {0}
SearchFiltering.FrequencyFilter.or=,
# {0} - filters
SearchFiltering.InterestingItemSetFilter.desc=Interesting item hits in set(s): {0}
# {0} - filters
SearchFiltering.KeywordListFilter.desc=Keywords in list(s): {0}
# {0} - filters
SearchFiltering.ObjectDetectionFilter.desc=Objects detected in set(s): {0}
# {0} - filters
SearchFiltering.ParentFilter.desc=Paths matching: {0}
SearchFiltering.ParentFilter.exact=(exact match)
SearchFiltering.ParentFilter.excluded=(excluded)
SearchFiltering.ParentFilter.included=(included)
SearchFiltering.ParentFilter.or=,
SearchFiltering.ParentFilter.substring=(substring)
SearchFiltering.ParentSearchTerm.excludeString=\ (exclude)
SearchFiltering.ParentSearchTerm.fullString=\ (exact)
SearchFiltering.ParentSearchTerm.includeString=\ (include)
SearchFiltering.ParentSearchTerm.subString=\ (substring)
# {0} - filters
SearchFiltering.ScoreFilter.desc=Score(s) of : {0}
# {0} - filters
SearchFiltering.SizeFilter.desc=Size(s): {0}
SearchFiltering.SizeFilter.or=,

View File

@ -0,0 +1,892 @@
/*
* Autopsy
*
* Copyright 2020 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.discovery.search;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoDbUtil;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeNormalizationException;
import org.sleuthkit.autopsy.centralrepository.datamodel.InstanceTableCallback;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.CaseDbAccessManager;
import org.sleuthkit.datamodel.ContentTag;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
import java.util.StringJoiner;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeNormalizer;
/**
* Class which contains the search attributes which can be specified for
* Discovery.
*/
public class DiscoveryAttributes {
private final static Logger logger = Logger.getLogger(DiscoveryAttributes.class.getName());
/**
* Base class for the grouping attributes.
*/
public abstract static class AttributeType {
/**
* For a given Result, return the key for the group it belongs to for
* this attribute type.
*
* @param result The result to be grouped.
*
* @return The key for the group this result goes in.
*/
public abstract DiscoveryKeyUtils.GroupKey getGroupKey(Result result);
/**
* Add any extra data to the ResultFile object from this attribute.
*
* @param results The list of results to enhance.
* @param caseDb The case database.
* @param centralRepoDb The central repository database. Can be null if
* not needed.
*
* @throws DiscoveryException
*/
public void addAttributeToResults(List<Result> results, SleuthkitCase caseDb, CentralRepository centralRepoDb) throws DiscoveryException {
// Default is to do nothing
}
}
/**
* Attribute for grouping/sorting by file size.
*/
public static class FileSizeAttribute extends AttributeType {
@Override
public DiscoveryKeyUtils.GroupKey getGroupKey(Result result) {
return new DiscoveryKeyUtils.FileSizeGroupKey(result);
}
}
/**
* Attribute for grouping/sorting by parent path.
*/
public static class ParentPathAttribute extends AttributeType {
@Override
public DiscoveryKeyUtils.GroupKey getGroupKey(Result file) {
return new DiscoveryKeyUtils.ParentPathGroupKey((ResultFile) file);
}
}
/**
* Default attribute used to make one group.
*/
static class NoGroupingAttribute extends AttributeType {
@Override
public DiscoveryKeyUtils.GroupKey getGroupKey(Result result) {
return new DiscoveryKeyUtils.NoGroupingGroupKey();
}
}
/**
* Attribute for grouping/sorting by data source.
*/
static class DataSourceAttribute extends AttributeType {
@Override
public DiscoveryKeyUtils.GroupKey getGroupKey(Result result) {
return new DiscoveryKeyUtils.DataSourceGroupKey(result);
}
}
/**
* Attribute for grouping/sorting by file type.
*/
static class FileTypeAttribute extends AttributeType {
@Override
public DiscoveryKeyUtils.GroupKey getGroupKey(Result file) {
return new DiscoveryKeyUtils.FileTypeGroupKey(file);
}
}
/**
* Attribute for grouping/sorting by keyword lists.
*/
static class KeywordListAttribute extends AttributeType {
@Override
public DiscoveryKeyUtils.GroupKey getGroupKey(Result file) {
return new DiscoveryKeyUtils.KeywordListGroupKey((ResultFile) file);
}
@Override
public void addAttributeToResults(List<Result> results, SleuthkitCase caseDb,
CentralRepository centralRepoDb) throws DiscoveryException {
// Get pairs of (object ID, keyword list name) for all files in the list of files that have
// keyword list hits.
String selectQuery = createSetNameClause(results, BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID(),
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID());
SetKeywordListNamesCallback callback = new SetKeywordListNamesCallback(results);
try {
caseDb.getCaseDbAccessManager().select(selectQuery, callback);
} catch (TskCoreException ex) {
throw new DiscoveryException("Error looking up keyword list attributes", ex); // NON-NLS
}
}
/**
* Callback to process the results of the CaseDbAccessManager select
* query. Will add the keyword list names to the list of ResultFile
* objects.
*/
private static class SetKeywordListNamesCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback {
List<Result> resultFiles;
/**
* Create the callback.
*
* @param resultFiles List of files to add keyword list names to.
*/
SetKeywordListNamesCallback(List<Result> resultFiles) {
this.resultFiles = resultFiles;
}
@Override
public void process(ResultSet rs) {
try {
// Create a temporary map of object ID to ResultFile
Map<Long, ResultFile> tempMap = new HashMap<>();
for (Result result : resultFiles) {
if (result.getType() == SearchData.Type.DOMAIN) {
break;
}
ResultFile file = (ResultFile) result;
tempMap.put(file.getFirstInstance().getId(), file);
}
while (rs.next()) {
try {
Long objId = rs.getLong("object_id"); // NON-NLS
String keywordListName = rs.getString("set_name"); // NON-NLS
tempMap.get(objId).addKeywordListName(keywordListName);
} catch (SQLException ex) {
logger.log(Level.SEVERE, "Unable to get object_id or set_name from result set", ex); // NON-NLS
}
}
} catch (SQLException ex) {
logger.log(Level.SEVERE, "Failed to get keyword list names", ex); // NON-NLS
}
}
}
}
/**
* Attribute for grouping/sorting by frequency in the central repository.
*/
static class FrequencyAttribute extends AttributeType {
static final int BATCH_SIZE = 50; // Number of hashes to look up at one time
static final int DOMAIN_BATCH_SIZE = 500; // Number of domains to look up at one time
@Override
public DiscoveryKeyUtils.GroupKey getGroupKey(Result file) {
return new DiscoveryKeyUtils.FrequencyGroupKey(file);
}
@Override
public void addAttributeToResults(List<Result> results, SleuthkitCase caseDb,
CentralRepository centralRepoDb) throws DiscoveryException {
if (centralRepoDb == null) {
for (Result result : results) {
if (result.getFrequency() == SearchData.Frequency.UNKNOWN && result.getKnown() == TskData.FileKnown.KNOWN) {
result.setFrequency(SearchData.Frequency.KNOWN);
}
}
} else {
processResultFilesForCR(results, centralRepoDb);
}
}
/**
* Private helper method for adding Frequency attribute when CR is
* enabled.
*
* @param files The list of ResultFiles to caluclate frequency
* for.
* @param centralRepoDb The central repository currently in use.
*/
private void processResultFilesForCR(List<Result> results,
CentralRepository centralRepoDb) throws DiscoveryException {
List<ResultFile> currentFiles = new ArrayList<>();
Set<String> hashesToLookUp = new HashSet<>();
List<ResultDomain> domainsToQuery = new ArrayList<>();
for (Result result : results) {
// If frequency was already calculated, skip...
if (result.getFrequency() == SearchData.Frequency.UNKNOWN) {
if (result.getKnown() == TskData.FileKnown.KNOWN) {
result.setFrequency(SearchData.Frequency.KNOWN);
}
if (result.getType() != SearchData.Type.DOMAIN) {
ResultFile file = (ResultFile) result;
if (file.getFirstInstance().getMd5Hash() != null
&& !file.getFirstInstance().getMd5Hash().isEmpty()) {
hashesToLookUp.add(file.getFirstInstance().getMd5Hash());
currentFiles.add(file);
}
if (hashesToLookUp.size() >= BATCH_SIZE) {
computeFrequency(hashesToLookUp, currentFiles, centralRepoDb);
hashesToLookUp.clear();
currentFiles.clear();
}
} else {
domainsToQuery.add((ResultDomain) result);
if (domainsToQuery.size() == DOMAIN_BATCH_SIZE) {
queryDomainFrequency(domainsToQuery, centralRepoDb);
domainsToQuery.clear();
}
}
}
}
queryDomainFrequency(domainsToQuery, centralRepoDb);
computeFrequency(hashesToLookUp, currentFiles, centralRepoDb);
}
}
/**
* Query to get the frequency of a domain.
*
* @param domainsToQuery List of domains to check the frequency of.
* @param centralRepository The central repository to query.
*
* @throws DiscoveryException
*/
private static void queryDomainFrequency(List<ResultDomain> domainsToQuery, CentralRepository centralRepository) throws DiscoveryException {
if (domainsToQuery.isEmpty()) {
return;
}
try {
final Map<String, List<ResultDomain>> resultDomainTable = new HashMap<>();
final StringJoiner joiner = new StringJoiner(", ");
final CorrelationAttributeInstance.Type attributeType = centralRepository.getCorrelationTypeById(CorrelationAttributeInstance.DOMAIN_TYPE_ID);
for (ResultDomain domainInstance : domainsToQuery) {
try {
final String domainValue = domainInstance.getDomain();
final String normalizedDomain = CorrelationAttributeNormalizer.normalize(attributeType, domainValue);
final List<ResultDomain> bucket = resultDomainTable.getOrDefault(normalizedDomain, new ArrayList<>());
bucket.add(domainInstance);
resultDomainTable.put(normalizedDomain, bucket);
joiner.add("'" + normalizedDomain + "'");
} catch (CorrelationAttributeNormalizationException ex) {
logger.log(Level.INFO, String.format("Domain [%s] failed normalization, skipping...", domainInstance.getDomain()));
}
}
final String tableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(attributeType);
final String domainFrequencyQuery = " value AS domain_name, COUNT(*) AS frequency "
+ "FROM " + tableName + " "
+ "WHERE value IN (" + joiner + ") "
+ "GROUP BY value";
final DomainFrequencyCallback frequencyCallback = new DomainFrequencyCallback(resultDomainTable);
centralRepository.processSelectClause(domainFrequencyQuery, frequencyCallback);
if (frequencyCallback.getCause() != null) {
throw frequencyCallback.getCause();
}
} catch (CentralRepoException | SQLException ex) {
throw new DiscoveryException("Fatal exception encountered querying the CR.", ex);
}
}
/**
* Callback to get the frequency of domain.
*/
private static class DomainFrequencyCallback implements InstanceTableCallback {
private final Map<String, List<ResultDomain>> domainLookup;
private SQLException sqlCause;
/**
* Construct a new DomainFrequencyCallback.
*
* @param domainLookup The map to get domain from.
*/
private DomainFrequencyCallback(Map<String, List<ResultDomain>> domainLookup) {
this.domainLookup = domainLookup;
}
@Override
public void process(ResultSet resultSet) {
try {
while (resultSet.next()) {
String domain = resultSet.getString("domain_name");
Long frequency = resultSet.getLong("frequency");
List<ResultDomain> domainInstances = domainLookup.get(domain);
for (ResultDomain domainInstance : domainInstances) {
domainInstance.setFrequency(SearchData.Frequency.fromCount(frequency));
}
}
} catch (SQLException ex) {
this.sqlCause = ex;
}
}
/**
* Get the SQL exception if one occurred during this callback.
*
* @return
*/
SQLException getCause() {
return this.sqlCause;
}
}
/**
* Callback to use with findInterCaseValuesByCount which generates a list of
* values for common property search
*/
private static class FrequencyCallback implements InstanceTableCallback {
private final List<ResultFile> files;
/**
* Construct a new FrequencyCallback.
*
* @param files List of files to add hash set names to.
*/
private FrequencyCallback(List<ResultFile> files) {
this.files = new ArrayList<>(files);
}
@Override
public void process(ResultSet resultSet) {
try {
while (resultSet.next()) {
String hash = resultSet.getString(1);
int count = resultSet.getInt(2);
for (Iterator<ResultFile> iterator = files.iterator(); iterator.hasNext();) {
ResultFile file = iterator.next();
if (file.getFirstInstance().getMd5Hash().equalsIgnoreCase(hash)) {
file.setFrequency(SearchData.Frequency.fromCount(count));
iterator.remove();
}
}
}
// The files left had no matching entries in the CR, so mark them as unique
for (ResultFile file : files) {
file.setFrequency(SearchData.Frequency.UNIQUE);
}
} catch (SQLException ex) {
logger.log(Level.WARNING, "Error getting frequency counts from Central Repository", ex); // NON-NLS
}
}
}
/**
* Attribute for grouping/sorting by hash set lists.
*/
static class HashHitsAttribute extends AttributeType {
@Override
public DiscoveryKeyUtils.GroupKey getGroupKey(Result result) {
if (result.getType() == SearchData.Type.DOMAIN) {
return null;
}
return new DiscoveryKeyUtils.HashHitsGroupKey((ResultFile) result);
}
@Override
public void addAttributeToResults(List<Result> results, SleuthkitCase caseDb,
CentralRepository centralRepoDb) throws DiscoveryException {
// Get pairs of (object ID, hash set name) for all files in the list of files that have
// hash set hits.
String selectQuery = createSetNameClause(results, BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID(),
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID());
HashSetNamesCallback callback = new HashSetNamesCallback(results);
try {
caseDb.getCaseDbAccessManager().select(selectQuery, callback);
} catch (TskCoreException ex) {
throw new DiscoveryException("Error looking up hash set attributes", ex); // NON-NLS
}
}
/**
* Callback to process the results of the CaseDbAccessManager select
* query. Will add the hash set names to the list of ResultFile objects.
*/
private static class HashSetNamesCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback {
List<Result> results;
/**
* Create the callback.
*
* @param resultFiles List of files to add hash set names to.
*/
HashSetNamesCallback(List<Result> results) {
this.results = results;
}
@Override
public void process(ResultSet rs) {
try {
// Create a temporary map of object ID to ResultFile
Map<Long, ResultFile> tempMap = new HashMap<>();
for (Result result : results) {
if (result.getType() == SearchData.Type.DOMAIN) {
return;
}
ResultFile file = (ResultFile) result;
tempMap.put(file.getFirstInstance().getId(), file);
}
while (rs.next()) {
try {
Long objId = rs.getLong("object_id"); // NON-NLS
String hashSetName = rs.getString("set_name"); // NON-NLS
tempMap.get(objId).addHashSetName(hashSetName);
} catch (SQLException ex) {
logger.log(Level.SEVERE, "Unable to get object_id or set_name from result set", ex); // NON-NLS
}
}
} catch (SQLException ex) {
logger.log(Level.SEVERE, "Failed to get hash set names", ex); // NON-NLS
}
}
}
}
/**
* Attribute for grouping/sorting by interesting item set lists.
*/
static class InterestingItemAttribute extends AttributeType {
@Override
public DiscoveryKeyUtils.GroupKey getGroupKey(Result file) {
return new DiscoveryKeyUtils.InterestingItemGroupKey((ResultFile) file);
}
@Override
public void addAttributeToResults(List<Result> results, SleuthkitCase caseDb,
CentralRepository centralRepoDb) throws DiscoveryException {
// Get pairs of (object ID, interesting item set name) for all files in the list of files that have
// interesting file set hits.
String selectQuery = createSetNameClause(results, BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID(),
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID());
InterestingFileSetNamesCallback callback = new InterestingFileSetNamesCallback(results);
try {
caseDb.getCaseDbAccessManager().select(selectQuery, callback);
} catch (TskCoreException ex) {
throw new DiscoveryException("Error looking up interesting file set attributes", ex); // NON-NLS
}
}
/**
* Callback to process the results of the CaseDbAccessManager select
* query. Will add the interesting file set names to the list of
* ResultFile objects.
*/
private static class InterestingFileSetNamesCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback {
List<Result> results;
/**
* Create the callback.
*
* @param resultFiles List of files to add interesting file set
* names to.
*/
InterestingFileSetNamesCallback(List<Result> results) {
this.results = results;
}
@Override
public void process(ResultSet rs) {
try {
// Create a temporary map of object ID to ResultFile
Map<Long, ResultFile> tempMap = new HashMap<>();
for (Result result : results) {
if (result.getType() == SearchData.Type.DOMAIN) {
return;
}
ResultFile file = (ResultFile) result;
tempMap.put(file.getFirstInstance().getId(), file);
}
while (rs.next()) {
try {
Long objId = rs.getLong("object_id"); // NON-NLS
String setName = rs.getString("set_name"); // NON-NLS
tempMap.get(objId).addInterestingSetName(setName);
} catch (SQLException ex) {
logger.log(Level.SEVERE, "Unable to get object_id or set_name from result set", ex); // NON-NLS
}
}
} catch (SQLException ex) {
logger.log(Level.SEVERE, "Failed to get interesting file set names", ex); // NON-NLS
}
}
}
}
/**
* Attribute for grouping/sorting by date of most recent activity.
*/
static class MostRecentActivityDateAttribute extends AttributeType {
@Override
public DiscoveryKeyUtils.GroupKey getGroupKey(Result result) {
return new DiscoveryKeyUtils.MostRecentActivityDateGroupKey(result);
}
}
/**
* Attribute for grouping/sorting by date of first activity.
*/
static class FirstActivityDateAttribute extends AttributeType {
@Override
public DiscoveryKeyUtils.GroupKey getGroupKey(Result result) {
return new DiscoveryKeyUtils.FirstActivityDateGroupKey(result);
}
}
/**
* Attribute for grouping/sorting by number of visits.
*/
static class NumberOfVisitsAttribute extends AttributeType {
@Override
public DiscoveryKeyUtils.GroupKey getGroupKey(Result result) {
return new DiscoveryKeyUtils.NumberOfVisitsGroupKey(result);
}
}
/**
* Attribute for grouping/sorting by objects detected.
*/
static class ObjectDetectedAttribute extends AttributeType {
@Override
public DiscoveryKeyUtils.GroupKey getGroupKey(Result file) {
return new DiscoveryKeyUtils.ObjectDetectedGroupKey((ResultFile) file);
}
@Override
public void addAttributeToResults(List<Result> results, SleuthkitCase caseDb,
CentralRepository centralRepoDb) throws DiscoveryException {
// Get pairs of (object ID, object type name) for all files in the list of files that have
// objects detected
String selectQuery = createSetNameClause(results, BlackboardArtifact.ARTIFACT_TYPE.TSK_OBJECT_DETECTED.getTypeID(),
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION.getTypeID());
ObjectDetectedNamesCallback callback = new ObjectDetectedNamesCallback(results);
try {
caseDb.getCaseDbAccessManager().select(selectQuery, callback);
} catch (TskCoreException ex) {
throw new DiscoveryException("Error looking up object detected attributes", ex); // NON-NLS
}
}
/**
* Callback to process the results of the CaseDbAccessManager select
* query. Will add the object type names to the list of ResultFile
* objects.
*/
private static class ObjectDetectedNamesCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback {
List<Result> results;
/**
* Create the callback.
*
* @param resultFiles List of files to add object detected names to.
*/
ObjectDetectedNamesCallback(List<Result> results) {
this.results = results;
}
@Override
public void process(ResultSet rs) {
try {
// Create a temporary map of object ID to ResultFile
Map<Long, ResultFile> tempMap = new HashMap<>();
for (Result result : results) {
if (result.getType() == SearchData.Type.DOMAIN) {
return;
}
ResultFile file = (ResultFile) result;
tempMap.put(file.getFirstInstance().getId(), file);
}
while (rs.next()) {
try {
Long objId = rs.getLong("object_id"); // NON-NLS
String setName = rs.getString("set_name"); // NON-NLS
tempMap.get(objId).addObjectDetectedName(setName);
} catch (SQLException ex) {
logger.log(Level.SEVERE, "Unable to get object_id or set_name from result set", ex); // NON-NLS
}
}
} catch (SQLException ex) {
logger.log(Level.SEVERE, "Failed to get object detected names", ex); // NON-NLS
}
}
}
}
/**
* Attribute for grouping/sorting by tag name.
*/
static class FileTagAttribute extends AttributeType {
@Override
public DiscoveryKeyUtils.GroupKey getGroupKey(Result file) {
return new DiscoveryKeyUtils.FileTagGroupKey((ResultFile) file);
}
@Override
public void addAttributeToResults(List<Result> results, SleuthkitCase caseDb,
CentralRepository centralRepoDb) throws DiscoveryException {
try {
for (Result result : results) {
if (result.getType() == SearchData.Type.DOMAIN) {
return;
}
ResultFile file = (ResultFile) result;
List<ContentTag> contentTags = caseDb.getContentTagsByContent(file.getFirstInstance());
for (ContentTag tag : contentTags) {
result.addTagName(tag.getName().getDisplayName());
}
}
} catch (TskCoreException ex) {
throw new DiscoveryException("Error looking up file tag attributes", ex); // NON-NLS
}
}
}
/**
* Enum for the attribute types that can be used for grouping.
*/
@NbBundle.Messages({
"DiscoveryAttributes.GroupingAttributeType.fileType.displayName=File Type",
"DiscoveryAttributes.GroupingAttributeType.frequency.displayName=Past Occurrences",
"DiscoveryAttributes.GroupingAttributeType.keywordList.displayName=Keyword",
"DiscoveryAttributes.GroupingAttributeType.size.displayName=File Size",
"DiscoveryAttributes.GroupingAttributeType.datasource.displayName=Data Source",
"DiscoveryAttributes.GroupingAttributeType.parent.displayName=Parent Folder",
"DiscoveryAttributes.GroupingAttributeType.hash.displayName=Hash Set",
"DiscoveryAttributes.GroupingAttributeType.interestingItem.displayName=Interesting Item",
"DiscoveryAttributes.GroupingAttributeType.tag.displayName=Tag",
"DiscoveryAttributes.GroupingAttributeType.object.displayName=Object Detected",
"DiscoveryAttributes.GroupingAttributeType.mostRecentDate.displayName=Most Recent Activity Date",
"DiscoveryAttributes.GroupingAttributeType.firstDate.displayName=First Activity Date",
"DiscoveryAttributes.GroupingAttributeType.numberOfVisits.displayName=Number of Visits",
"DiscoveryAttributes.GroupingAttributeType.none.displayName=None"})
public enum GroupingAttributeType {
FILE_SIZE(new FileSizeAttribute(), Bundle.DiscoveryAttributes_GroupingAttributeType_size_displayName()),
FREQUENCY(new FrequencyAttribute(), Bundle.DiscoveryAttributes_GroupingAttributeType_frequency_displayName()),
KEYWORD_LIST_NAME(new KeywordListAttribute(), Bundle.DiscoveryAttributes_GroupingAttributeType_keywordList_displayName()),
DATA_SOURCE(new DataSourceAttribute(), Bundle.DiscoveryAttributes_GroupingAttributeType_datasource_displayName()),
PARENT_PATH(new ParentPathAttribute(), Bundle.DiscoveryAttributes_GroupingAttributeType_parent_displayName()),
HASH_LIST_NAME(new HashHitsAttribute(), Bundle.DiscoveryAttributes_GroupingAttributeType_hash_displayName()),
INTERESTING_ITEM_SET(new InterestingItemAttribute(), Bundle.DiscoveryAttributes_GroupingAttributeType_interestingItem_displayName()),
FILE_TAG(new FileTagAttribute(), Bundle.DiscoveryAttributes_GroupingAttributeType_tag_displayName()),
OBJECT_DETECTED(new ObjectDetectedAttribute(), Bundle.DiscoveryAttributes_GroupingAttributeType_object_displayName()),
MOST_RECENT_DATE(new MostRecentActivityDateAttribute(), Bundle.DiscoveryAttributes_GroupingAttributeType_mostRecentDate_displayName()),
FIRST_DATE(new FirstActivityDateAttribute(), Bundle.DiscoveryAttributes_GroupingAttributeType_firstDate_displayName()),
NUMBER_OF_VISITS(new NumberOfVisitsAttribute(), Bundle.DiscoveryAttributes_GroupingAttributeType_numberOfVisits_displayName()),
NO_GROUPING(new NoGroupingAttribute(), Bundle.DiscoveryAttributes_GroupingAttributeType_none_displayName());
private final AttributeType attributeType;
private final String displayName;
/**
* Construct a new GroupingAttributeType enum value.
*
* @param attributeType The type of attribute this enum value was
* constructed for.
* @param displayName The display name for this grouping attribute
* type.
*/
GroupingAttributeType(AttributeType attributeType, String displayName) {
this.attributeType = attributeType;
this.displayName = displayName;
}
@Override
public String toString() {
return displayName;
}
/**
* Get the type of attribute this enum value was constructed for.
*
* @return The type of attribute this enum value was constructed for.
*/
public AttributeType getAttributeType() {
return attributeType;
}
/**
* Get the list of enums that are valid for grouping files.
*
* @return Enums that can be used to group files.
*/
public static List<GroupingAttributeType> getOptionsForGroupingForFiles() {
return Arrays.asList(FILE_SIZE, FREQUENCY, PARENT_PATH, OBJECT_DETECTED, HASH_LIST_NAME, INTERESTING_ITEM_SET);
}
/**
* Get the list of enums that are valid for grouping files.
*
* @return Enums that can be used to group files.
*/
public static List<GroupingAttributeType> getOptionsForGroupingForDomains() {
return Arrays.asList(FREQUENCY, MOST_RECENT_DATE, FIRST_DATE, NUMBER_OF_VISITS);
}
}
/**
* Computes the CR frequency of all the given hashes and updates the list of
* files.
*
* @param hashesToLookUp Hashes to find the frequency of.
* @param currentFiles List of files to update with frequencies.
* @param centralRepoDb The central repository being used.
*/
private static void computeFrequency(Set<String> hashesToLookUp, List<ResultFile> currentFiles, CentralRepository centralRepoDb) {
if (hashesToLookUp.isEmpty()) {
return;
}
String hashes = String.join("','", hashesToLookUp);
hashes = "'" + hashes + "'";
try {
CorrelationAttributeInstance.Type attributeType = centralRepoDb.getCorrelationTypeById(CorrelationAttributeInstance.FILES_TYPE_ID);
String tableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(attributeType);
String selectClause = " value, COUNT(value) FROM "
+ "(SELECT DISTINCT case_id, value FROM " + tableName
+ " WHERE value IN ("
+ hashes
+ ")) AS foo GROUP BY value";
FrequencyCallback callback = new FrequencyCallback(currentFiles);
centralRepoDb.processSelectClause(selectClause, callback);
} catch (CentralRepoException ex) {
logger.log(Level.WARNING, "Error getting frequency counts from Central Repository", ex); // NON-NLS
}
}
/**
* Private helper method to create a set name clause to be used in queries.
*
* @param results The list of results to create the set name clause
* for.
* @param artifactTypeID The Blackboard Artifact type ID for the artifact
* type.
* @param setNameAttrID The set name attribute id.
*
* @return The String to use as a set name clause in queries.
*
* @throws DiscoveryException
*/
private static String createSetNameClause(List<Result> results,
int artifactTypeID, int setNameAttrID) throws DiscoveryException {
// Concatenate the object IDs in the list of files
String objIdList = ""; // NON-NLS
for (Result result : results) {
if (result.getType() == SearchData.Type.DOMAIN) {
break;
}
ResultFile file = (ResultFile) result;
if (!objIdList.isEmpty()) {
objIdList += ","; // NON-NLS
}
objIdList += "\'" + file.getFirstInstance().getId() + "\'"; // NON-NLS
}
// Get pairs of (object ID, set name) for all files in the list of files that have
// the given artifact type.
return "blackboard_artifacts.obj_id AS object_id, blackboard_attributes.value_text AS set_name "
+ "FROM blackboard_artifacts "
+ "INNER JOIN blackboard_attributes ON blackboard_artifacts.artifact_id=blackboard_attributes.artifact_id "
+ "WHERE blackboard_attributes.artifact_type_id=\'" + artifactTypeID + "\' "
+ "AND blackboard_attributes.attribute_type_id=\'" + setNameAttrID + "\' "
+ "AND blackboard_artifacts.obj_id IN (" + objIdList
+ ") "; // NON-NLS
}
/**
* Private constructor for DiscoveryAttributes class.
*/
private DiscoveryAttributes() {
// Class should not be instantiated
}
}

View File

@ -16,20 +16,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.discovery;
package org.sleuthkit.autopsy.discovery.search;
import com.google.common.eventbus.EventBus;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.sleuthkit.autopsy.discovery.FileSearch.GroupKey;
import org.sleuthkit.autopsy.discovery.FileSearchData.FileType;
import org.sleuthkit.autopsy.discovery.search.DiscoveryKeyUtils.GroupKey;
import org.sleuthkit.autopsy.discovery.search.SearchData.Type;
import org.sleuthkit.datamodel.AbstractFile;
/**
* Class to handle event bus and events for discovery tool.
*/
final class DiscoveryEventUtils {
public final class DiscoveryEventUtils {
private final static EventBus discoveryEventBus = new EventBus();
@ -38,7 +38,7 @@ final class DiscoveryEventUtils {
*
* @return The discovery event bus.
*/
static EventBus getDiscoveryEventBus() {
public static EventBus getDiscoveryEventBus() {
return discoveryEventBus;
}
@ -52,60 +52,74 @@ final class DiscoveryEventUtils {
/**
* Event to signal the start of a search being performed.
*/
static final class SearchStartedEvent {
public static final class SearchStartedEvent {
private final FileType fileType;
private final Type type;
/**
* Construct a new SearchStartedEvent
* Construct a new SearchStartedEvent.
*
* @param type The type of file the search event is for.
* @param type The type of result the search event is for.
*/
SearchStartedEvent(FileType type) {
this.fileType = type;
public SearchStartedEvent(Type type) {
this.type = type;
}
/**
* Get the type of file the search is being performed for.
* Get the type of result the search is being performed for.
*
* @return The type of files being searched for.
* @return The type of results being searched for.
*/
FileType getType() {
return fileType;
public Type getType() {
return type;
}
}
/**
* Event to signal that the Instances list should have selection cleared.
*/
static final class ClearInstanceSelectionEvent {
public static final class ClearInstanceSelectionEvent {
/**
* Construct a new ClearInstanceSelectionEvent.
*/
ClearInstanceSelectionEvent() {
public ClearInstanceSelectionEvent() {
//no arg constructor
}
}
/**
* Event to signal that any background tasks currently running should
* be cancelled.
*/
public static final class CancelBackgroundTasksEvent {
public CancelBackgroundTasksEvent() {
//no-arg constructor
}
}
/**
* Event to signal that the Instances list should be populated.
*/
static final class PopulateInstancesListEvent {
public static final class PopulateInstancesListEvent {
private final List<AbstractFile> instances;
/**
* Construct a new PopulateInstancesListEvent.
*/
PopulateInstancesListEvent(List<AbstractFile> files) {
public PopulateInstancesListEvent(List<AbstractFile> files) {
instances = files;
}
/**
* @return the instances
* Get the list of AbstractFiles for the instances list.
*
* @return The list of AbstractFiles for the instances list.
*/
List<AbstractFile> getInstances() {
public List<AbstractFile> getInstances() {
return Collections.unmodifiableList(instances);
}
}
@ -113,33 +127,33 @@ final class DiscoveryEventUtils {
/**
* Event to signal the completion of a search being performed.
*/
static final class SearchCompleteEvent {
public static final class SearchCompleteEvent {
private final Map<GroupKey, Integer> groupMap;
private final List<FileSearchFiltering.FileFilter> searchFilters;
private final FileSearch.AttributeType groupingAttribute;
private final FileGroup.GroupSortingAlgorithm groupSort;
private final FileSorter.SortingMethod fileSortMethod;
private final List<AbstractFilter> searchFilters;
private final DiscoveryAttributes.AttributeType groupingAttribute;
private final Group.GroupSortingAlgorithm groupSort;
private final ResultsSorter.SortingMethod sortMethod;
/**
* Construct a new SearchCompleteEvent,
*
* @param groupMap The map of groups which were found by the
* search.
* @param searchFilters The search filters which were used by the
* @param searchfilters The search filters which were used by the
* search.
* @param groupingAttribute The grouping attribute used by the search.
* @param groupSort The sorting algorithm used for groups.
* @param fileSortMethod The sorting method used for files.
* @param sortMethod The sorting method used for results.
*/
SearchCompleteEvent(Map<GroupKey, Integer> groupMap, List<FileSearchFiltering.FileFilter> searchfilters,
FileSearch.AttributeType groupingAttribute, FileGroup.GroupSortingAlgorithm groupSort,
FileSorter.SortingMethod fileSortMethod) {
public SearchCompleteEvent(Map<GroupKey, Integer> groupMap, List<AbstractFilter> searchfilters,
DiscoveryAttributes.AttributeType groupingAttribute, Group.GroupSortingAlgorithm groupSort,
ResultsSorter.SortingMethod sortMethod) {
this.groupMap = groupMap;
this.searchFilters = searchfilters;
this.groupingAttribute = groupingAttribute;
this.groupSort = groupSort;
this.fileSortMethod = fileSortMethod;
this.sortMethod = sortMethod;
}
/**
@ -147,16 +161,16 @@ final class DiscoveryEventUtils {
*
* @return The map of groups which were found by the search.
*/
Map<GroupKey, Integer> getGroupMap() {
public Map<GroupKey, Integer> getGroupMap() {
return Collections.unmodifiableMap(groupMap);
}
/**
* Get the file filters used by the search.
* Get the filters used by the search.
*
* @return The search filters which were used by the search.
*/
List<FileSearchFiltering.FileFilter> getFilters() {
public List<AbstractFilter> getFilters() {
return Collections.unmodifiableList(searchFilters);
}
@ -165,7 +179,7 @@ final class DiscoveryEventUtils {
*
* @return The grouping attribute used by the search.
*/
FileSearch.AttributeType getGroupingAttr() {
public DiscoveryAttributes.AttributeType getGroupingAttr() {
return groupingAttribute;
}
@ -174,17 +188,17 @@ final class DiscoveryEventUtils {
*
* @return The sorting algorithm used for groups.
*/
FileGroup.GroupSortingAlgorithm getGroupSort() {
public Group.GroupSortingAlgorithm getGroupSort() {
return groupSort;
}
/**
* Get the sorting method used for files.
* Get the sorting method used for results.
*
* @return The sorting method used for files.
* @return The sorting method used for results.
*/
FileSorter.SortingMethod getFileSort() {
return fileSortMethod;
public ResultsSorter.SortingMethod getResultSort() {
return sortMethod;
}
}
@ -193,31 +207,31 @@ final class DiscoveryEventUtils {
* Event to signal the completion of page retrieval and include the page
* contents.
*/
static final class PageRetrievedEvent {
public static final class PageRetrievedEvent {
private final List<ResultFile> results;
private final List<Result> results;
private final int page;
private final FileType resultType;
private final Type resultType;
/**
* Construct a new PageRetrievedEvent.
*
* @param resultType The type of files which exist in the page.
* @param resultType The type of results which exist in the page.
* @param page The number of the page which was retrieved.
* @param results The list of files in the page retrieved.
* @param results The list of results in the page retrieved.
*/
PageRetrievedEvent(FileType resultType, int page, List<ResultFile> results) {
public PageRetrievedEvent(Type resultType, int page, List<Result> results) {
this.results = results;
this.page = page;
this.resultType = resultType;
}
/**
* Get the list of files in the page retrieved.
* Get the list of results in the page retrieved.
*
* @return The list of files in the page retrieved.
* @return The list of results in the page retrieved.
*/
List<ResultFile> getSearchResults() {
public List<Result> getSearchResults() {
return Collections.unmodifiableList(results);
}
@ -226,16 +240,16 @@ final class DiscoveryEventUtils {
*
* @return The number of the page which was retrieved.
*/
int getPageNumber() {
public int getPageNumber() {
return page;
}
/**
* Get the type of files which exist in the page.
* Get the type of results which exist in the page.
*
* @return The type of files which exist in the page.
* @return The type of results which exist in the page.
*/
FileType getType() {
public Type getType() {
return resultType;
}
}
@ -243,25 +257,25 @@ final class DiscoveryEventUtils {
/**
* Event to signal that there were no results for the search.
*/
static final class NoResultsEvent {
public static final class NoResultsEvent {
/**
* Construct a new NoResultsEvent.
*/
NoResultsEvent() {
public NoResultsEvent() {
//no arg constructor
}
}
/**
* Event to signal that a search has been cancelled
* Event to signal that a search has been cancelled.
*/
static final class SearchCancelledEvent {
public static final class SearchCancelledEvent {
/**
* Construct a new SearchCancelledEvent.
*/
SearchCancelledEvent() {
public SearchCancelledEvent() {
//no arg constructor
}
@ -270,15 +284,15 @@ final class DiscoveryEventUtils {
/**
* Event to signal that a group has been selected.
*/
static final class GroupSelectedEvent {
public static final class GroupSelectedEvent {
private final FileType resultType;
private final Type resultType;
private final GroupKey groupKey;
private final int groupSize;
private final List<FileSearchFiltering.FileFilter> searchfilters;
private final FileSearch.AttributeType groupingAttribute;
private final FileGroup.GroupSortingAlgorithm groupSort;
private final FileSorter.SortingMethod fileSortMethod;
private final List<AbstractFilter> searchfilters;
private final DiscoveryAttributes.AttributeType groupingAttribute;
private final Group.GroupSortingAlgorithm groupSort;
private final ResultsSorter.SortingMethod sortMethod;
/**
* Construct a new GroupSelectedEvent.
@ -287,31 +301,32 @@ final class DiscoveryEventUtils {
* search.
* @param groupingAttribute The grouping attribute used by the search.
* @param groupSort The sorting algorithm used for groups.
* @param fileSortMethod The sorting method used for files.
* @param sortMethod The sorting method used for results.
* @param groupKey The key associated with the group which was
* selected.
* @param groupSize The number of files in the group which was
* @param groupSize The number of results in the group which was
* selected.
* @param resultType The type of files which exist in the group.
* @param resultType The type of results which exist in the
* group.
*/
GroupSelectedEvent(List<FileSearchFiltering.FileFilter> searchfilters,
FileSearch.AttributeType groupingAttribute, FileGroup.GroupSortingAlgorithm groupSort,
FileSorter.SortingMethod fileSortMethod, GroupKey groupKey, int groupSize, FileType resultType) {
public GroupSelectedEvent(List<AbstractFilter> searchfilters,
DiscoveryAttributes.AttributeType groupingAttribute, Group.GroupSortingAlgorithm groupSort,
ResultsSorter.SortingMethod sortMethod, GroupKey groupKey, int groupSize, Type resultType) {
this.searchfilters = searchfilters;
this.groupingAttribute = groupingAttribute;
this.groupSort = groupSort;
this.fileSortMethod = fileSortMethod;
this.sortMethod = sortMethod;
this.groupKey = groupKey;
this.groupSize = groupSize;
this.resultType = resultType;
}
/**
* Get the type of files which exist in the group.
* Get the type of results which exist in the group.
*
* @return The type of files which exist in the group.
* @return The type of results which exist in the group.
*/
FileType getResultType() {
public Type getResultType() {
return resultType;
}
@ -322,16 +337,16 @@ final class DiscoveryEventUtils {
* @return The group key which is used to uniquely identify the group
* selected.
*/
GroupKey getGroupKey() {
public GroupKey getGroupKey() {
return groupKey;
}
/**
* Get the number of files in the group which was selected.
* Get the number of results in the group which was selected.
*
* @return The number of files in the group which was selected.
* @return The number of results in the group which was selected.
*/
int getGroupSize() {
public int getGroupSize() {
return groupSize;
}
@ -340,25 +355,25 @@ final class DiscoveryEventUtils {
*
* @return The sorting algorithm used for groups.
*/
FileGroup.GroupSortingAlgorithm getGroupSort() {
public Group.GroupSortingAlgorithm getGroupSort() {
return groupSort;
}
/**
* Get the sorting method used for files in the group.
* Get the sorting method used for results in the group.
*
* @return The sorting method used for files.
* @return The sorting method used for results.
*/
FileSorter.SortingMethod getFileSort() {
return fileSortMethod;
public ResultsSorter.SortingMethod getResultSort() {
return sortMethod;
}
/**
* Get the file filters which were used by the search
* Get the result filters which were used by the search.
*
* @return The search filters which were used by the search.
*/
List<FileSearchFiltering.FileFilter> getFilters() {
public List<AbstractFilter> getFilters() {
return Collections.unmodifiableList(searchfilters);
}
@ -367,7 +382,7 @@ final class DiscoveryEventUtils {
*
* @return The grouping attribute used by the search.
*/
FileSearch.AttributeType getGroupingAttr() {
public DiscoveryAttributes.AttributeType getGroupingAttr() {
return groupingAttribute;
}
@ -376,7 +391,7 @@ final class DiscoveryEventUtils {
/**
* Event to signal that the visibility of the Details area should change.
*/
static class DetailsVisibleEvent {
public static class DetailsVisibleEvent {
private final boolean showDetailsArea;
@ -386,7 +401,7 @@ final class DiscoveryEventUtils {
* @param isVisible True if the details area should be visible, false
* otherwise.
*/
DetailsVisibleEvent(boolean isVisible) {
public DetailsVisibleEvent(boolean isVisible) {
showDetailsArea = isVisible;
}
@ -395,7 +410,7 @@ final class DiscoveryEventUtils {
*
* @return True if the details area should be visible, false otherwise.
*/
boolean isShowDetailsArea() {
public boolean isShowDetailsArea() {
return showDetailsArea;
}
}

View File

@ -16,12 +16,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.discovery;
package org.sleuthkit.autopsy.discovery.search;
/**
* Exception type used for FileSearch.
* Exception type used for Discovery search.
*/
final public class FileSearchException extends Exception {
final public class DiscoveryException extends Exception {
private static final long serialVersionUID = 1L;
@ -30,7 +30,7 @@ final public class FileSearchException extends Exception {
*
* @param message The message to associate with this exception.
*/
FileSearchException(String message) {
DiscoveryException(String message) {
super(message);
}
@ -40,7 +40,7 @@ final public class FileSearchException extends Exception {
* @param message The message to associate with this exception.
* @param cause The Throwable cause of the exception.
*/
FileSearchException(String message, Throwable cause) {
DiscoveryException(String message, Throwable cause) {
super(message, cause);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,155 @@
/*
* Autopsy
*
* Copyright 2020 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.discovery.search;
import java.awt.Image;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.discovery.search.DiscoveryKeyUtils.GroupKey;
import org.sleuthkit.datamodel.SleuthkitCase;
/**
* Main class to perform the domain search.
*/
public class DomainSearch {
private final DomainSearchCache searchCache;
private final DomainSearchThumbnailCache thumbnailCache;
/**
* Construct a new DomainSearch object.
*/
public DomainSearch() {
this(new DomainSearchCache(), new DomainSearchThumbnailCache());
}
/**
* Construct a new DomainSearch object with an existing DomainSearchCache
* and DomainSearchThumbnailCache.
*
* @param cache The DomainSearchCache to use for this DomainSearch.
* @param thumbnailCache The DomainSearchThumnailCache to use for this
* DomainSearch.
*/
DomainSearch(DomainSearchCache cache, DomainSearchThumbnailCache thumbnailCache) {
this.searchCache = cache;
this.thumbnailCache = thumbnailCache;
}
/**
* Run the domain search to get the group keys and sizes. Clears cache of
* search results, caching new results for access at later time.
*
* @param userName The name of the user performing the search.
* @param filters The filters to apply.
* @param groupAttributeType The attribute to use for grouping.
* @param groupSortingType The method to use to sort the groups.
* @param domainSortingMethod The method to use to sort the domains within
* the groups.
* @param caseDb The case database.
* @param centralRepoDb The central repository database. Can be null
* if not needed.
*
* @return A LinkedHashMap grouped and sorted according to the parameters.
*
* @throws DiscoveryException
*/
public Map<GroupKey, Integer> getGroupSizes(String userName,
List<AbstractFilter> filters,
DiscoveryAttributes.AttributeType groupAttributeType,
Group.GroupSortingAlgorithm groupSortingType,
ResultsSorter.SortingMethod domainSortingMethod,
SleuthkitCase caseDb, CentralRepository centralRepoDb) throws DiscoveryException {
final Map<GroupKey, List<Result>> searchResults = searchCache.get(
userName, filters, groupAttributeType, groupSortingType,
domainSortingMethod, caseDb, centralRepoDb);
// Transform the cached results into a map of group key to group size.
final LinkedHashMap<GroupKey, Integer> groupSizes = new LinkedHashMap<>();
for (GroupKey groupKey : searchResults.keySet()) {
groupSizes.put(groupKey, searchResults.get(groupKey).size());
}
return groupSizes;
}
/**
* Get the domains from the specified group from the cache, if the the group
* was not cached perform a search caching the groups.
*
* @param userName The name of the user performing the search.
* @param filters The filters to apply.
* @param groupAttributeType The attribute to use for grouping.
* @param groupSortingType The method to use to sort the groups.
* @param domainSortingMethod The method to use to sort the Domains within
* the groups.
* @param groupKey The key which uniquely identifies the group to
* get entries from.
* @param startingEntry The first entry to return.
* @param numberOfEntries The number of entries to return.
* @param caseDb The case database.
* @param centralRepoDb The central repository database. Can be null
* if not needed.
*
* @return A LinkedHashMap grouped and sorted according to the parameters.
*
* @throws DiscoveryException
*/
public List<Result> getDomainsInGroup(String userName,
List<AbstractFilter> filters,
DiscoveryAttributes.AttributeType groupAttributeType,
Group.GroupSortingAlgorithm groupSortingType,
ResultsSorter.SortingMethod domainSortingMethod,
GroupKey groupKey, int startingEntry, int numberOfEntries,
SleuthkitCase caseDb, CentralRepository centralRepoDb) throws DiscoveryException {
final Map<GroupKey, List<Result>> searchResults = searchCache.get(
userName, filters, groupAttributeType, groupSortingType,
domainSortingMethod, caseDb, centralRepoDb);
final List<Result> domainsInGroup = searchResults.get(groupKey);
final List<Result> page = new ArrayList<>();
for (int i = startingEntry; (i < startingEntry + numberOfEntries)
&& (i < domainsInGroup.size()); i++) {
page.add(domainsInGroup.get(i));
}
return page;
}
/**
* Get a thumbnail representation of a domain name. See
* DomainSearchThumbnailRequest for more details.
*
* @param thumbnailRequest Thumbnail request for domain.
*
* @return An Image instance or null if no thumbnail is available.
*
* @throws DiscoveryException If there is an error with Discovery related
* processing.
*/
public Image getThumbnail(DomainSearchThumbnailRequest thumbnailRequest) throws DiscoveryException {
return thumbnailCache.get(thumbnailRequest);
}
}

View File

@ -0,0 +1,56 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 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.discovery.search;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.LoadingCache;
import java.util.List;
import java.util.concurrent.ExecutionException;
import org.sleuthkit.datamodel.BlackboardArtifact;
/**
* Caches artifact requests.
*/
public class DomainSearchArtifactsCache {
private static final int MAXIMUM_CACHE_SIZE = 500;
private static final LoadingCache<DomainSearchArtifactsRequest, List<BlackboardArtifact>> cache
= CacheBuilder.newBuilder()
.maximumSize(MAXIMUM_CACHE_SIZE)
.build(new DomainSearchArtifactsLoader());
/**
* Get artifact instances that match the requested criteria. If the request
* is new, the results will be automatically loaded.
*
* @param request Artifact request, specifies type, Case, and domain name.
*
* @return A list of matching artifacts.
*
* @throws DiscoveryException Any error that occurs during the loading
* process.
*/
public List<BlackboardArtifact> get(DomainSearchArtifactsRequest request) throws DiscoveryException {
try {
return cache.get(request);
} catch (ExecutionException ex) {
throw new DiscoveryException("Error fetching artifacts from cache", ex);
}
}
}

View File

@ -0,0 +1,64 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 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.discovery.search;
import com.google.common.cache.CacheLoader;
import java.util.List;
import java.util.ArrayList;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
import org.sleuthkit.datamodel.BlackboardAttribute.Type;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.BlackboardArtifact;
/**
* Loads artifacts for the given request. Searches TSK_DOMAIN and TSK_URL
* attributes for the requested domain name. TSK_DOMAIN is exact match (ignoring
* case). TSK_URL is sub-string match (ignoring case).
*/
public class DomainSearchArtifactsLoader extends CacheLoader<DomainSearchArtifactsRequest, List<BlackboardArtifact>> {
private static final Type TSK_DOMAIN = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DOMAIN);
private static final Type TSK_URL = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_URL);
@Override
public List<BlackboardArtifact> load(DomainSearchArtifactsRequest artifactsRequest) throws TskCoreException, InterruptedException {
final SleuthkitCase caseDb = artifactsRequest.getSleuthkitCase();
final String normalizedDomain = artifactsRequest.getDomain().toLowerCase();
final List<BlackboardArtifact> artifacts = caseDb.getBlackboardArtifacts(artifactsRequest.getArtifactType());
final List<BlackboardArtifact> matchingDomainArtifacts = new ArrayList<>();
for (BlackboardArtifact artifact : artifacts) {
if(Thread.currentThread().isInterrupted()) {
throw new InterruptedException();
}
final BlackboardAttribute tskDomain = artifact.getAttribute(TSK_DOMAIN);
final BlackboardAttribute tskUrl = artifact.getAttribute(TSK_URL);
if (tskDomain != null && tskDomain.getValueString().equalsIgnoreCase(normalizedDomain)) {
matchingDomainArtifacts.add(artifact);
} else if (tskUrl != null && tskUrl.getValueString().toLowerCase().contains(normalizedDomain)) {
matchingDomainArtifacts.add(artifact);
}
}
return matchingDomainArtifacts;
}
}

View File

@ -0,0 +1,93 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 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.discovery.search;
import java.util.Objects;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
/**
* Requests artifacts of a specific type and domain from a given Case.
*/
public class DomainSearchArtifactsRequest {
private final SleuthkitCase sleuthkitCase;
private final String domain;
private final ARTIFACT_TYPE artifactType;
/**
* Construct a new DomainSearchArtifactsRequest object.
*
* @param sleuthkitCase The case database for the search.
* @param domain The domain that artifacts are being requested for.
* @param artifactType The type of artifact being requested.
*/
public DomainSearchArtifactsRequest(SleuthkitCase sleuthkitCase,
String domain, ARTIFACT_TYPE artifactType) {
this.sleuthkitCase = sleuthkitCase;
this.domain = domain;
this.artifactType = artifactType;
}
/**
* Get the case database for the search.
*
* @return The case database for the search.
*/
public SleuthkitCase getSleuthkitCase() {
return sleuthkitCase;
}
/**
* Get the domain that artifacts are being requested for.
*
* @return The domain that artifacts are being requested for.
*/
public String getDomain() {
return domain;
}
/**
* Get the type of artifact being requested.
*
* @return The type of artifact being requested.
*/
public ARTIFACT_TYPE getArtifactType() {
return artifactType;
}
@Override
public boolean equals(Object other) {
if (other == this) {
return true;
}
if (!(other instanceof DomainSearchArtifactsRequest)) {
return false;
}
DomainSearchArtifactsRequest otherRequest = (DomainSearchArtifactsRequest) other;
return this.sleuthkitCase == otherRequest.getSleuthkitCase()
&& this.domain.equals(otherRequest.getDomain())
&& this.artifactType == otherRequest.getArtifactType();
}
@Override
public int hashCode() {
return 79 * 5 + Objects.hash(this.domain, this.artifactType);
}
}

View File

@ -0,0 +1,77 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 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.discovery.search;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.LoadingCache;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.discovery.search.DiscoveryKeyUtils.GroupKey;
import org.sleuthkit.autopsy.discovery.search.DiscoveryKeyUtils.SearchKey;
import org.sleuthkit.datamodel.SleuthkitCase;
/**
* Caches results for domain searches initiated by the user in the Discovery
* panel. Uses a Guava Cache as a backing data structure. See
* DomainSearchCacheLoader for database querying in the event of a cache miss.
*/
class DomainSearchCache {
private static final int MAXIMUM_CACHE_SIZE = 10;
private static final LoadingCache<SearchKey, Map<GroupKey, List<Result>>> cache
= CacheBuilder.newBuilder()
.maximumSize(MAXIMUM_CACHE_SIZE)
.build(new DomainSearchCacheLoader());
/**
* Get domain search results matching the given parameters. If no results
* are found, the cache will automatically load them.
*
*
* @param userName The name of the user performing the search.
* @param filters The filters to apply.
* @param groupAttributeType The attribute to use for grouping.
* @param groupSortingType The method to use to sort the groups.
* @param fileSortingMethod The method to use to sort the domains within
* the groups.
* @param caseDb The case database.
* @param centralRepoDb The central repository database. Can be null if
* not needed.
*
* @return Domain search results matching the given parameters.
*
* @throws DiscoveryException
*/
Map<GroupKey, List<Result>> get(String userName,
List<AbstractFilter> filters,
DiscoveryAttributes.AttributeType groupAttributeType,
Group.GroupSortingAlgorithm groupSortingType,
ResultsSorter.SortingMethod domainSortingMethod,
SleuthkitCase caseDb, CentralRepository centralRepoDb) throws DiscoveryException {
try {
final SearchKey searchKey = new SearchKey(userName, filters, groupAttributeType,
groupSortingType, domainSortingMethod, caseDb, centralRepoDb);
return cache.get(searchKey);
} catch (ExecutionException ex) {
throw new DiscoveryException("Error fetching results from cache", ex.getCause());
}
}
}

View File

@ -0,0 +1,365 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 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.discovery.search;
import com.google.common.cache.CacheLoader;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.StringJoiner;
import org.apache.commons.lang3.tuple.Pair;
import org.sleuthkit.autopsy.discovery.search.DiscoveryAttributes.AttributeType;
import org.sleuthkit.autopsy.discovery.search.DiscoveryAttributes.DataSourceAttribute;
import org.sleuthkit.autopsy.discovery.search.DiscoveryKeyUtils.GroupKey;
import org.sleuthkit.autopsy.discovery.search.DiscoveryKeyUtils.SearchKey;
import org.sleuthkit.autopsy.discovery.search.SearchFiltering.ArtifactDateRangeFilter;
import org.sleuthkit.autopsy.discovery.search.SearchFiltering.ArtifactTypeFilter;
import org.sleuthkit.autopsy.discovery.search.SearchFiltering.DataSourceFilter;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY;
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN;
import org.sleuthkit.datamodel.CaseDbAccessManager;
import org.sleuthkit.datamodel.CaseDbAccessManager.CaseDbAccessQueryCallback;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Loads domain search results for cache misses. This loader is a Guava cache
* loader, which will be used in tandem with the DomainSearchCache, which is
* backed by a Guava LoadingCache.
*/
class DomainSearchCacheLoader extends CacheLoader<SearchKey, Map<GroupKey, List<Result>>> {
@Override
public Map<GroupKey, List<Result>> load(SearchKey key) throws DiscoveryException, SQLException, TskCoreException, InterruptedException {
List<Result> domainResults = getResultDomainsFromDatabase(key);
// Grouping by CR Frequency, for example, will require further processing
// in order to make the correct decision. The attribute types that require
// more information implement their logic by overriding `addAttributeToResults`.
List<AttributeType> searchAttributes = new ArrayList<>();
searchAttributes.add(key.getGroupAttributeType());
searchAttributes.addAll(key.getFileSortingMethod().getRequiredAttributes());
for (AttributeType attr : searchAttributes) {
if (Thread.currentThread().isInterrupted()) {
throw new InterruptedException();
}
attr.addAttributeToResults(domainResults,
key.getSleuthkitCase(), key.getCentralRepository());
}
// Apply secondary in memory filters
for (AbstractFilter filter : key.getFilters()) {
if (Thread.currentThread().isInterrupted()) {
throw new InterruptedException();
}
if (filter.useAlternateFilter()) {
domainResults = filter.applyAlternateFilter(domainResults, key.getSleuthkitCase(), key.getCentralRepository());
}
}
// Sort the ResultDomains by the requested criteria.
final SearchResults searchResults = new SearchResults(
key.getGroupSortingType(),
key.getGroupAttributeType(),
key.getFileSortingMethod());
searchResults.add(domainResults);
return searchResults.toLinkedHashMap();
}
/**
* Queries for domain names from the case database.
*
* @param key The SearchKey passed to the cache.
*
* @return A list of results corresponding to the domains found in the case
* database.
*/
List<Result> getResultDomainsFromDatabase(SearchKey key) throws TskCoreException, SQLException, DiscoveryException, InterruptedException {
// Filters chosen in the UI are aggregated into SQL statements to be used in
// the queries that follow.
final Pair<String, String> filterClauses = createWhereAndHavingClause(key.getFilters());
final String whereClause = filterClauses.getLeft();
final String havingClause = filterClauses.getRight();
// You may think of each row of this result as a TSK_DOMAIN attribute, where the parent
// artifact type is within the (optional) filter and the parent artifact
// had a date time attribute that was within the (optional) filter. With this
// table in hand, we can simply group by domain and apply aggregate functions
// to get, for example, # of downloads, # of visits in last 60, etc.
final String domainsTable
= "SELECT LOWER(MAX(value_text)) AS domain,"
+ " MAX(value_int64) AS date,"
+ " artifact_id AS parent_artifact_id,"
+ " MAX(artifact_type_id) AS parent_artifact_type_id "
+ "FROM blackboard_attributes "
+ "WHERE " + whereClause + " "
+ "GROUP BY artifact_id "
+ "HAVING " + havingClause;
// Needed to populate the visitsInLast60 data.
final Instant currentTime = Instant.now();
final Instant sixtyDaysAgo = currentTime.minus(60, ChronoUnit.DAYS);
// Check the group attribute, if by data source then the GROUP BY clause
// should group by data source id before grouping by domain.
final AttributeType groupAttribute = key.getGroupAttributeType();
final String groupByClause = (groupAttribute instanceof DataSourceAttribute)
? "data_source_obj_id, domain" : "domain";
final Optional<AbstractFilter> dataSourceFilter = key.getFilters().stream()
.filter(filter -> filter instanceof DataSourceFilter)
.findFirst();
String dataSourceWhereClause = null;
if (dataSourceFilter.isPresent()) {
dataSourceWhereClause = dataSourceFilter.get().getWhereClause();
}
// This query just processes the domains table, performing additional
// groupings and applying aggregate functions to calculate discovery data.
final String domainsQuery
= /*
* SELECT
*/ " domain,"
+ " MIN(date) AS activity_start,"
+ " MAX(date) AS activity_end,"
+ " SUM(CASE "
+ " WHEN artifact_type_id = " + TSK_WEB_DOWNLOAD.getTypeID() + " THEN 1 "
+ " ELSE 0 "
+ " END) AS fileDownloads,"
+ " SUM(CASE "
+ " WHEN artifact_type_id = " + TSK_WEB_HISTORY.getTypeID() + " THEN 1 "
+ " ELSE 0 "
+ " END) AS totalVisits,"
+ " SUM(CASE "
+ " WHEN artifact_type_id = " + TSK_WEB_HISTORY.getTypeID() + " AND"
+ " date BETWEEN " + sixtyDaysAgo.getEpochSecond() + " AND " + currentTime.getEpochSecond() + " THEN 1 "
+ " ELSE 0 "
+ " END) AS last60,"
+ " MAX(data_source_obj_id) AS dataSource "
+ "FROM blackboard_artifacts"
+ " JOIN (" + domainsTable + ") AS domains_table"
+ " ON artifact_id = parent_artifact_id "
+ // Add the data source where clause here if present.
((dataSourceWhereClause != null) ? "WHERE " + dataSourceWhereClause + " " : "")
+ "GROUP BY " + groupByClause;
final SleuthkitCase caseDb = key.getSleuthkitCase();
final CaseDbAccessManager dbManager = caseDb.getCaseDbAccessManager();
final DomainCallback domainCallback = new DomainCallback(caseDb);
dbManager.select(domainsQuery, domainCallback);
if (domainCallback.getSQLException() != null) {
throw domainCallback.getSQLException();
}
if (domainCallback.getTskCoreException() != null) {
throw domainCallback.getTskCoreException();
}
if (domainCallback.getInterruptedException() != null) {
throw domainCallback.getInterruptedException();
}
return domainCallback.getResultDomains();
}
/**
* A utility method to transform filters into the necessary SQL statements
* for the domainsTable query. The complexity of that query requires this
* transformation process to be conditional. The date time filter is a good
* example of the type of conditional handling that follows in the method
* below. If no dateTime filter is supplied, then in order for the query to
* be correct, an additional clause needs to be added in.
*
* @param filters The list of filters to apply create the where clause from.
*
* @return The whereClause and havingClause as a pair. These methods are one
* to stress that these clauses are tightly coupled.
*/
Pair<String, String> createWhereAndHavingClause(List<AbstractFilter> filters) {
final StringJoiner whereClause = new StringJoiner(" OR ");
final StringJoiner havingClause = new StringJoiner(" AND ");
String artifactTypeFilter = null;
boolean hasDateTimeFilter = false;
for (AbstractFilter filter : filters) {
if (filter instanceof ArtifactTypeFilter) {
artifactTypeFilter = filter.getWhereClause();
} else if (!(filter instanceof DataSourceFilter) && !filter.useAlternateFilter()) {
if (filter instanceof ArtifactDateRangeFilter) {
hasDateTimeFilter = true;
}
whereClause.add("(" + filter.getWhereClause() + ")");
havingClause.add("SUM(CASE WHEN " + filter.getWhereClause() + " THEN 1 ELSE 0 END) > 0");
}
}
if (!hasDateTimeFilter) {
whereClause.add(ArtifactDateRangeFilter.createAttributeTypeClause());
}
String domainAttributeFilter = "attribute_type_id = " + TSK_DOMAIN.getTypeID()
+ " AND value_text <> ''";
whereClause.add("(" + domainAttributeFilter + ")");
havingClause.add("SUM(CASE WHEN " + domainAttributeFilter + " THEN 1 ELSE 0 END) > 0");
return Pair.of(
whereClause.toString() + ((artifactTypeFilter != null) ? " AND (" + artifactTypeFilter + ")" : ""),
havingClause.toString()
);
}
/**
* Callback to handle the result set of the domain query. This callback is
* responsible for mapping result set rows into ResultDomain objects for
* display.
*/
private class DomainCallback implements CaseDbAccessQueryCallback {
private final List<Result> resultDomains;
private final SleuthkitCase skc;
private SQLException sqlCause;
private TskCoreException coreCause;
private InterruptedException interruptedException;
private final Set<String> bannedDomains = new HashSet<String>() {
{
add("localhost");
add("127.0.0.1");
}
};
/**
* Construct a new DomainCallback object.
*
* @param skc The case database for the query being performed.
*/
private DomainCallback(SleuthkitCase skc) {
this.resultDomains = new ArrayList<>();
this.skc = skc;
}
@Override
public void process(ResultSet resultSet) {
try {
resultSet.setFetchSize(500);
while (resultSet.next()) {
if (Thread.currentThread().isInterrupted()) {
throw new InterruptedException();
}
String domain = resultSet.getString("domain");
if (bannedDomains.contains(domain)) {
// Skip banned domains
// Domain names are lowercased in the SQL query
continue;
}
Long activityStart = resultSet.getLong("activity_start");
if (resultSet.wasNull()) {
activityStart = null;
}
Long activityEnd = resultSet.getLong("activity_end");
if (resultSet.wasNull()) {
activityEnd = null;
}
Long filesDownloaded = resultSet.getLong("fileDownloads");
if (resultSet.wasNull()) {
filesDownloaded = null;
}
Long totalVisits = resultSet.getLong("totalVisits");
if (resultSet.wasNull()) {
totalVisits = null;
}
Long visitsInLast60 = resultSet.getLong("last60");
if (resultSet.wasNull()) {
visitsInLast60 = null;
}
Long dataSourceID = resultSet.getLong("dataSource");
Content dataSource = skc.getContentById(dataSourceID);
resultDomains.add(new ResultDomain(domain, activityStart,
activityEnd, totalVisits, visitsInLast60, filesDownloaded, dataSource));
}
} catch (SQLException ex) {
this.sqlCause = ex;
} catch (TskCoreException ex) {
this.coreCause = ex;
} catch (InterruptedException ex) {
this.interruptedException = ex;
}
}
/**
* Get the list of Result object for the domains which were in the
* search results.
*
* @return The list of Result object for the domains which were in the
* search results.
*/
private List<Result> getResultDomains() {
return Collections.unmodifiableList(this.resultDomains);
}
/**
* Get the SQLEception in an exception occurred.
*
* @return The SQLEception in an exception occurred.
*/
private SQLException getSQLException() {
return this.sqlCause;
}
/**
* Get the TskCoreException if a SQL exception occurred.
*
* @return The TskCoreException if a tsk core exception occurred.
*/
private TskCoreException getTskCoreException() {
return this.coreCause;
}
/**
* Get the interrupted exception if the processing thread was
* interrupted.
*
* @return The interrupted exception or null if none was thrown.
*/
private InterruptedException getInterruptedException() {
return this.interruptedException;
}
}
}

View File

@ -0,0 +1,56 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 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.discovery.search;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.LoadingCache;
import java.awt.Image;
import java.util.concurrent.ExecutionException;
/**
* Caches thumbnail requests.
*/
public class DomainSearchThumbnailCache {
private static final int MAXIMUM_CACHE_SIZE = 500;
private static final LoadingCache<DomainSearchThumbnailRequest, Image> cache
= CacheBuilder.newBuilder()
.maximumSize(MAXIMUM_CACHE_SIZE)
.build(new DomainSearchThumbnailLoader());
/**
* Get a thumbnail for the requested domain. If the request is new, the
* thumbnail will be automatically loaded.
*
* @param request Requested domain to thumbnail.
*
* @return The thumbnail Image instance, or null if no thumbnail is
* available.
*
* @throws DiscoveryException If any error occurs during thumbnail
* generation.
*/
public Image get(DomainSearchThumbnailRequest request) throws DiscoveryException {
try {
return cache.get(request);
} catch (ExecutionException ex) {
throw new DiscoveryException("Error fetching artifacts from cache", ex);
}
}
}

Some files were not shown because too many files have changed in this diff Show More