merged in develop

This commit is contained in:
Kelly Kelly 2020-11-16 12:53:28 -05:00
commit b826764de0
157 changed files with 6680 additions and 1104 deletions

3
.gitignore vendored Normal file → Executable file
View File

@ -99,4 +99,5 @@ hs_err_pid*.log
/thirdparty/yara/YaraJNIWrapper/dist/
/thirdparty/yara/YaraJNIWrapper/build/
/thirdparty/yara/YaraJNIWrapper/nbproject/private/
thirdparty/yara/yarabridge/.vs/
/thirdparty/yara/yarabridge/.vs/

View File

@ -166,7 +166,7 @@
<get src="https://drive.google.com/uc?id=1ns2olaWsBu_c4EoE4Seh8t_B3U5RnLKd" dest="${test-input}/CommonFilesAttrs_img1_v1.vhd" skipexisting="true"/>
</target>
<target name="get-deps" depends="init-ivy,getTSKJars,get-thirdparty-dependencies,get-InternalPythonModules, download-binlist">
<mkdir dir="${ext.dir}"/>
<copy file="${thirdparty.dir}/LICENSE-2.0.txt" todir="${ext.dir}" />
@ -199,5 +199,110 @@
<globmapper from="*" to="*-MERGED"/>
</copy>
</target>
<!--sets up integration test system properties, calls underlying test-init and then sets up the pathing jar-->
<target name="test-init" depends="projectized-common.test-init,getTestDataFiles,qa-functional-pathing-jar,unit-test-path-simplification" />
<!--
The paths specified in 'module.run.classpath' are incorporated into the manifest of a jar and then the path to the
jar is used as part of the classpath for '-do-junit' instead of 'module.run.classpath'. This was done to prevent
classpath length issues on windows. More information on this technique can be found here:
https://stackoverflow.com/a/201969.
-->
<target name="qa-functional-pathing-jar" depends="projectized-common.test-init">
<sequential>
<!--set up pathing jar based on module.run.classpath as classpath-->
<path id="test.qa-functional.pathing-jar.module-cp.classpath" path="${module.run.classpath}"/>
<pathconvert pathsep=" " refid="test.qa-functional.pathing-jar.module-cp.classpath" property="test.qa-functional.pathing-jar.module-cp.classpathstr"/>
<property name="test.qa-functional.pathing-jar.module-cp.loc" value="${cluster}/test.qa-functional.pathing.module-cp.jar"/>
<jar destfile="${test.qa-functional.pathing-jar.module-cp.loc}">
<manifest>
<attribute name="Class-Path" value="${test.qa-functional.pathing-jar.module-cp.classpathstr}"/>
</manifest>
</jar>
<!--grab properties from common.xml:test-init so that "test.qa-functional.run.cp" can be properly formed-->
<property name="build.test.qa-functional.dir" location="${build.dir}/test/qa-functional"/>
<property name="build.test.qa-functional.classes.dir" location="${build.test.qa-functional.dir}/classes"/>
<property name="test.qa-functional.cp.extra" value=""/>
<!--set up "test.qa-functional.run.cp" to be used by common.xml:-do-junit-->
<path id="test.qa-functional.run.cp">
<pathelement path="${build.test.qa-functional.classes.dir}"/>
<!-- Cannot use <path refid="cp"/> since that uses ${module.classpath} and we want ${module.run.classpath}: -->
<pathelement path="${test.qa-functional.runtime.cp}"/>
<pathelement path="${cp.extra}"/>
<pathelement location="${cluster}/${module.jar}"/>
<path refid="test.unit.lib.cp"/>
<!-- for compatibility with property based classpath-->
<pathelement path="${test.qa-functional.pathing-jar.module-cp.loc}"/>
<pathelement path="${test.qa-functional.run.cp.extra}"/>
<pathelement path="${test.qa-functional.cp.extra}"/>
<pathelement path="${test.extra.nb.javac.deps}"/>
</path>
</sequential>
</target>
<!--
This specifies the classpath for unit tests using * notation
(i.e. https://stackoverflow.com/questions/219585/including-all-the-jars-in-a-directory-within-the-java-classpath).
This solution involves taking the initial module.run.classpath property and simplifying it to the directories containing jars
(i.e. instead of “/dir/lib1.jar:/dir/lib2.jar:/dir/lib3.jar” it becomes “/dir/*” ).
More information on module.run.classpath can be found in “netbeans-plat\11.3\harness\README” and it appears that
“netbeans-plat\11.3\harness\build.xml:build-init target is in charge of setting the module.run.classpath variable.
More information in Jira: 6970.
-->
<target name="unit-test-path-simplification" depends="projectized-common.test-init">
<sequential>
<script language="javascript">
<![CDATA[
var moduleRunClasspath = project.getProperty("module.run.classpath");
var directories = [];
// searches for jar file parent directories with path separators of \ or /
var individualPathRegex = /^\s*(.+?[\\\/])[^\\\/]+?\.jar\s*$/i;
// split on ':' but not 'C:\'
var classPathRegex = /((C:\\)?.+?)(:|$)/gi;
var match;
while((match = classPathRegex.exec(moduleRunClasspath)) !== null) {
var thisPath = match[1];
var pathMatch = thisPath.match(individualPathRegex);
// find unique matches
if (pathMatch && directories.indexOf(pathMatch[1]) < 0) {
directories.push(pathMatch[1]);
}
}
// suffix with *
for (var i = 0; i < directories.length; i++) {
directories[i] = directories[i] + "*";
}
// set project property
project.setNewProperty("test.unit.abbreviatedModuleRunClassPath", directories.join(":"));
]]>
</script>
<!--grab properties from common.xml:test-init so that "test.unit.run.cp" can be properly formed-->
<property name="build.test.unit.dir" location="${build.dir}/test/unit"/>
<property name="build.test.unit.classes.dir" location="${build.test.unit.dir}/classes"/>
<property name="test.unit.cp.extra" value=""/>
<!--set up "test.unit.run.cp" to be used by common.xml:-do-junit-->
<path id="test.unit.run.cp">
<pathelement path="${build.test.unit.classes.dir}"/>
<!-- Cannot use <path refid="cp"/> since that uses ${module.classpath} and we want ${module.run.classpath}: -->
<pathelement path="${test.unit.runtime.cp}"/>
<pathelement path="${cp.extra}"/>
<pathelement location="${cluster}/${module.jar}"/>
<path refid="test.unit.lib.cp"/>
<!-- for compatibility with property based classpath-->
<pathelement path="${test.unit.abbreviatedModuleRunClassPath}"/>
<pathelement path="${test.unit.run.cp.extra}"/>
<pathelement path="${test.unit.cp.extra}"/>
<pathelement path="${test.extra.nb.javac.deps}"/>
</path>
</sequential>
</target>
</project>

View File

@ -232,7 +232,8 @@
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
<specification-version>1.0</specification-version>
<release-version>1</release-version>
<specification-version>23</specification-version>
</run-dependency>
</dependency>
<dependency>

View File

@ -96,7 +96,7 @@ Metadata.tableRowTitle.mimeType=MIME Type
Metadata.tableRowTitle.name=Name
Metadata.tableRowTitle.sectorSize=Sector Size
Metadata.tableRowTitle.sha1=SHA1
Metadata.tableRowTitle.sha256=SHA256
Metadata.tableRowTitle.sha256=SHA-256
Metadata.tableRowTitle.size=Size
Metadata.tableRowTitle.fileNameAlloc=File Name Allocation
Metadata.tableRowTitle.metadataAlloc=Metadata Allocation

View File

@ -137,7 +137,7 @@ public class Metadata extends javax.swing.JPanel implements DataContentViewer {
"Metadata.tableRowTitle.mimeType=MIME Type",
"Metadata.nodeText.truncated=(results truncated)",
"Metadata.tableRowTitle.sha1=SHA1",
"Metadata.tableRowTitle.sha256=SHA256",
"Metadata.tableRowTitle.sha256=SHA-256",
"Metadata.tableRowTitle.imageType=Type",
"Metadata.tableRowTitle.sectorSize=Sector Size",
"Metadata.tableRowTitle.timezone=Time Zone",
@ -182,6 +182,11 @@ public class Metadata extends javax.swing.JPanel implements DataContentViewer {
md5 = NbBundle.getMessage(this.getClass(), "Metadata.tableRowContent.md5notCalc");
}
addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.md5"), md5);
String sha256 = file.getSha256Hash();
if (sha256 == null) {
sha256 = NbBundle.getMessage(this.getClass(), "Metadata.tableRowContent.md5notCalc");
}
addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.sha256"), sha256);
addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.hashLookupResults"), file.getKnown().toString());
addAcquisitionDetails(sb, dataSource);

View File

@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.contentviewers;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
@ -97,6 +98,9 @@ final class PDFViewer implements FileTypeViewer {
// Add the IcePDF view to the center of our container.
this.container.add(icePdfPanel, BorderLayout.CENTER);
// Disable all components until the document is ready to view.
enableComponents(container, false);
// Document is the 'M' in IcePDFs MVC set up. Read the data needed to
// populate the model in the background.
@ -122,12 +126,13 @@ final class PDFViewer implements FileTypeViewer {
// will cause UI widgets to be updated.
try {
Document doc = get();
controller.openDocument(doc, null);
controller.openDocument(doc, file.getName());
// This makes the PDF viewer appear as one continuous
// document, which is the default for most popular PDF viewers.
controller.setPageViewMode(DocumentViewControllerImpl.ONE_COLUMN_VIEW, true);
// This makes it possible to select text by left clicking and dragging.
controller.setDisplayTool(DocumentViewModelImpl.DISPLAY_TOOL_TEXT_SELECTION);
enableComponents(container, true);
} catch (InterruptedException ex) {
// Do nothing.
} catch (ExecutionException ex) {
@ -140,10 +145,28 @@ final class PDFViewer implements FileTypeViewer {
file.getId(), file.getName()), ex);
showErrorDialog();
}
} catch (Throwable ex) {
logger.log(Level.WARNING, String.format("PDF content viewer "
+ "was unable to open document with id %d and name %s",
file.getId(), file.getName()), ex);
}
}
}.execute();
}
/**
* Recursively enable/disable all components in this content viewer.
* This will disable/enable all internal IcePDF Swing components too.
*/
private void enableComponents(Container container, boolean enabled) {
Component[] components = container.getComponents();
for(Component component : components) {
component.setEnabled(enabled);
if (component instanceof Container) {
enableComponents((Container)component, enabled);
}
}
}
@Override
public Component getComponent() {

View File

@ -44,7 +44,7 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
* A class to help display a communication artifact in a panel using a
* gridbaglayout.
*/
final class CommunicationArtifactViewerHelper {
public final class CommunicationArtifactViewerHelper {
// Number of columns in the gridbag layout.
private final static int MAX_COLS = 4;
@ -63,12 +63,12 @@ final class CommunicationArtifactViewerHelper {
*
* @param panel Panel to update.
* @param gridbagLayout Layout to use.
* @param constraints Constrains to use.
* @param constraints Constraints to use.
* @param headerString Heading string to display.
*
* @return JLabel Heading label added.
*/
static JLabel addHeader(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints, String headerString) {
public static JLabel addHeader(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints, String headerString) {
Insets savedInsets = constraints.insets;
@ -109,6 +109,23 @@ final class CommunicationArtifactViewerHelper {
return headingLabel;
}
/**
* Add a key value row to the specified panel with the specified layout and
* constraints.
*
*
* @param panel Panel to update.
* @param gridbagLayout Layout to use.
* @param constraints Constraints to use.
* @param keyString Key name to display.
* @param valueString Value string to display.
*
*/
public static void addNameValueRow(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints, String keyString, String valueString) {
addKey(panel, gridbagLayout, constraints, keyString);
addValue(panel, gridbagLayout, constraints, valueString);
}
/**
* Adds the given component to the panel.
*
@ -116,7 +133,7 @@ final class CommunicationArtifactViewerHelper {
*
* @param panel Panel to update.
* @param gridbagLayout Layout to use.
* @param constraints Constrains to use.
* @param constraints Constraints to use.
* @param component Component to add.
*/
static void addComponent(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints, JComponent component) {
@ -132,7 +149,7 @@ final class CommunicationArtifactViewerHelper {
*
* @param panel Panel to update.
* @param gridbagLayout Layout to use.
* @param constraints Constrains to use.
* @param constraints Constraints to use.
*/
static void addLineEndGlue(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints) {
// Place the filler just past the last column.
@ -159,9 +176,9 @@ final class CommunicationArtifactViewerHelper {
*
* @param panel Panel to update.
* @param gridbagLayout Layout to use.
* @param constraints Constrains to use.
* @param constraints Constraints to use.
*/
static void addPageEndGlue(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints) {
public static void addPageEndGlue(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints) {
constraints.gridx = 0;
@ -185,7 +202,7 @@ final class CommunicationArtifactViewerHelper {
*
* @param panel Panel to update.
* @param gridbagLayout Layout to use.
* @param constraints Constrains to use.
* @param constraints Constraints to use.
*/
static void addBlankLine(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints) {
constraints.gridy++;
@ -203,7 +220,7 @@ final class CommunicationArtifactViewerHelper {
*
* @param panel Panel to update.
* @param gridbagLayout Layout to use.
* @param constraints Constrains to use.
* @param constraints Constraints to use.
* @param keyString Key name to display.
*
* @return Label added.
@ -217,7 +234,7 @@ final class CommunicationArtifactViewerHelper {
*
* @param panel Panel to update.
* @param gridbagLayout Layout to use.
* @param constraints Constrains to use.
* @param constraints Constraints to use.
* @param keyString Key name to display.
* @param gridx column index, must be less than MAX_COLS - 1.
*
@ -246,8 +263,8 @@ final class CommunicationArtifactViewerHelper {
*
* @param panel Panel to update.
* @param gridbagLayout Layout to use.
* @param constraints Constrains to use.
* @param keyString Value string to display.
* @param constraints Constraints to use.
* @param valueString Value string to display.
*
* @return Label added.
*/
@ -260,7 +277,7 @@ final class CommunicationArtifactViewerHelper {
*
* @param panel Panel to update.
* @param gridbagLayout Layout to use.
* @param constraints Constrains to use.
* @param constraints Constraints to use.
* @param keyString Value string to display.
* @param gridx Column index, must be less than MAX_COLS;
*
@ -367,7 +384,7 @@ final class CommunicationArtifactViewerHelper {
*
* @param panel Panel to update.
* @param gridbagLayout Layout to use.
* @param constraints Constrains to use.
* @param constraints Constraints to use.
* @param accountIdentifier Account identifier to search the persona.
*
* @return List of AccountPersonaSearcherData objects.
@ -435,7 +452,7 @@ final class CommunicationArtifactViewerHelper {
*
* @param panel Panel to update.
* @param gridbagLayout Layout to use.
* @param constraints Constrains to use.
* @param constraints Constraints to use.
* @param contactId Contact name to display.
*
* @return A JLabel with the contact information.

View File

@ -54,15 +54,14 @@ import com.google.gson.JsonArray;
import java.util.Locale;
import java.util.Map;
import javax.swing.SwingUtilities;
import org.sleuthkit.autopsy.discovery.ui.AbstractArtifactDetailsPanel;
//import org.sleuthkit.autopsy.contentviewers.Bundle;
/**
* This class displays a Blackboard artifact as a table listing all it's
* attributes names and values.
* This class displays a Blackboard artifact as a table of its attributes.
*/
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
public class DefaultArtifactContentViewer extends javax.swing.JPanel implements ArtifactContentViewer {
public class DefaultArtifactContentViewer extends AbstractArtifactDetailsPanel implements ArtifactContentViewer {
@NbBundle.Messages({
"DefaultArtifactContentViewer.attrsTableHeader.type=Type",
@ -71,11 +70,11 @@ public class DefaultArtifactContentViewer extends javax.swing.JPanel implements
"DataContentViewerArtifact.failedToGetSourcePath.message=Failed to get source file path from case database",
"DataContentViewerArtifact.failedToGetAttributes.message=Failed to get some or all attributes from case database"
})
private final static Logger logger = Logger.getLogger(DefaultArtifactContentViewer.class.getName());
private static final long serialVersionUID = 1L;
private static final String[] COLUMN_HEADERS = {
Bundle.DefaultArtifactContentViewer_attrsTableHeader_type(),
Bundle.DefaultArtifactContentViewer_attrsTableHeader_value(),
@ -124,7 +123,7 @@ public class DefaultArtifactContentViewer extends javax.swing.JPanel implements
// do nothing
}
@Override
@Override
public void columnMarginChanged(ChangeEvent e) {
updateRowHeights(); //When the user changes column width we may need to resize row height
}
@ -153,12 +152,12 @@ public class DefaultArtifactContentViewer extends javax.swing.JPanel implements
Component comp = resultsTable.prepareRenderer(
resultsTable.getCellRenderer(row, valueColIndex), row, valueColIndex);
final int rowHeight;
if (comp instanceof JTextArea) {
if (comp instanceof JTextArea) {
final JTextArea tc = (JTextArea) comp;
final View rootView = tc.getUI().getRootView(tc);
java.awt.Insets i = tc.getInsets();
rootView.setSize(resultsTable.getColumnModel().getColumn(valueColIndex)
.getWidth() - (i.left + i.right +CELL_RIGHT_MARGIN), //current width minus borders
.getWidth() - (i.left + i.right + CELL_RIGHT_MARGIN), //current width minus borders
Integer.MAX_VALUE);
rowHeight = (int) rootView.getPreferredSpan(View.Y_AXIS);
} else {
@ -267,7 +266,7 @@ public class DefaultArtifactContentViewer extends javax.swing.JPanel implements
* Resets the components to an empty view state.
*/
private void resetComponents() {
((DefaultTableModel) resultsTable.getModel()).setRowCount(0);
}
@ -279,7 +278,7 @@ public class DefaultArtifactContentViewer extends javax.swing.JPanel implements
@Override
public void setArtifact(BlackboardArtifact artifact) {
try {
ResultsTableArtifact resultsTableArtifact = new ResultsTableArtifact(artifact, artifact.getParent());
ResultsTableArtifact resultsTableArtifact = artifact == null ? null : new ResultsTableArtifact(artifact, artifact.getParent());
SwingUtilities.invokeLater(new Runnable() {
@Override
@ -289,7 +288,7 @@ public class DefaultArtifactContentViewer extends javax.swing.JPanel implements
});
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Error getting parent content for artifact (artifact_id=%d, obj_id=%d)", artifact.getArtifactID(), artifact.getObjectID()), ex);
logger.log(Level.SEVERE, String.format("Error getting parent content for artifact (artifact_id=%d, obj_id=%d)", artifact.getArtifactID(), artifact.getObjectID()), ex);
}
}
@ -301,7 +300,7 @@ public class DefaultArtifactContentViewer extends javax.swing.JPanel implements
}
/**
* This class is a container to hold the data necessary for the artifact
* This class is a container to hold the data necessary for the artifact
* being viewed.
*/
private class ResultsTableArtifact {
@ -340,20 +339,20 @@ public class DefaultArtifactContentViewer extends javax.swing.JPanel implements
*/
String value;
switch (attr.getAttributeType().getValueType()) {
// Use Autopsy date formatting settings, not TSK defaults
case DATETIME:
value = epochTimeToString(attr.getValueLong());
break;
case JSON:
case JSON:
// Get the attribute's JSON value and convert to indented multiline display string
String jsonVal = attr.getValueString();
JsonParser parser = new JsonParser();
JsonObject json = parser.parse(jsonVal).getAsJsonObject();
value = toJsonDisplayString(json, "");
break;
case STRING:
case INTEGER:
case LONG:
@ -398,43 +397,43 @@ public class DefaultArtifactContentViewer extends javax.swing.JPanel implements
String getArtifactDisplayName() {
return artifactDisplayName;
}
private static final String INDENT_RIGHT = " ";
private static final String NEW_LINE = "\n";
/**
* Recursively converts a JSON element into an indented multi-line
* display string.
*
* @param element JSON element to convert
* @param element JSON element to convert
* @param startIndent Starting indentation for the element.
*
* @return A multi-line display string.
*/
private String toJsonDisplayString(JsonElement element, String startIndent) {
StringBuilder sb = new StringBuilder("");
JsonObject obj = element.getAsJsonObject();
for (Map.Entry<String, JsonElement> entry : obj.entrySet()) {
appendJsonElementToString(entry.getKey(), entry.getValue(), startIndent, sb );
appendJsonElementToString(entry.getKey(), entry.getValue(), startIndent, sb);
}
String returnString = sb.toString();
if (startIndent.length() == 0 && returnString.startsWith(NEW_LINE)) {
if (startIndent.length() == 0 && returnString.startsWith(NEW_LINE)) {
returnString = returnString.substring(NEW_LINE.length());
}
return returnString;
}
/**
* Converts the given JSON element into string and appends to the given string builder.
*
* Converts the given JSON element into string and appends to the given
* string builder.
*
* @param jsonKey
* @param jsonElement
* @param startIndent Starting indentation for the element.
* @param sb String builder to append to.
* @param sb String builder to append to.
*/
private void appendJsonElementToString(String jsonKey, JsonElement jsonElement, String startIndent, StringBuilder sb) {
if (jsonElement.isJsonArray()) {
@ -463,11 +462,12 @@ public class DefaultArtifactContentViewer extends javax.swing.JPanel implements
sb.append(NEW_LINE).append(String.format("%s%s = null", startIndent, jsonKey));
}
}
/**
* Converts epoch time to readable string.
*
*
* @param epochTime epoch time value to be converted to string.
*
* @return String with human readable time.
*/
private String epochTimeToString(long epochTime) {
@ -482,21 +482,20 @@ public class DefaultArtifactContentViewer extends javax.swing.JPanel implements
}
/**
* Updates the table view with the given artifact data.
*
* Updates the table view with the given artifact data.
*
* It should be called on EDT.
*
* @param resultsTableArtifact Artifact data to display in the view.
*/
private void updateView(ResultsTableArtifact resultsTableArtifact) {
this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
DefaultTableModel tModel = ((DefaultTableModel) resultsTable.getModel());
tModel.setDataVector(resultsTableArtifact.getRows(), COLUMN_HEADERS);
String[][] rows = resultsTableArtifact == null ? new String[0][0] : resultsTableArtifact.getRows();
tModel.setDataVector(rows, COLUMN_HEADERS);
updateColumnSizes();
updateRowHeights();
resultsTable.clearSelection();
this.setCursor(null);
}

View File

@ -357,7 +357,9 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
|| (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID())
|| (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_OBJECT_DETECTED.getTypeID())
|| (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_METADATA_EXIF.getTypeID())
|| (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_EXT_MISMATCH_DETECTED.getTypeID())) {
|| (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_EXT_MISMATCH_DETECTED.getTypeID())
|| (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID())
|| (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_WEB_CACHE.getTypeID())) {
return 3;
} else {
return 6;

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2012-2014 Basis Technology Corp.
* Copyright 2012-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -54,6 +54,40 @@ import org.xml.sax.SAXException;
* -Loading documents from disk
*/
public class XMLUtil {
private static DocumentBuilder getDocumentBuilder() throws ParserConfigurationException {
// See JIRA-6958 for details about class loading and jaxb.
ClassLoader original = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(XMLUtil.class.getClassLoader());
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
return builderFactory.newDocumentBuilder();
} finally {
Thread.currentThread().setContextClassLoader(original);
}
}
private static SchemaFactory getSchemaFactory(String schemaLanguage) {
// See JIRA-6958 for details about class loading and jaxb.
ClassLoader original = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(XMLUtil.class.getClassLoader());
return SchemaFactory.newInstance(schemaLanguage);
} finally {
Thread.currentThread().setContextClassLoader(original);
}
}
private static TransformerFactory getTransformerFactory() {
// See JIRA-6958 for details about class loading and jaxb.
ClassLoader original = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(XMLUtil.class.getClassLoader());
return TransformerFactory.newInstance();
} finally {
Thread.currentThread().setContextClassLoader(original);
}
}
/**
* Creates a W3C DOM.
@ -63,9 +97,7 @@ public class XMLUtil {
* @throws ParserConfigurationException
*/
public static Document createDocument() throws ParserConfigurationException {
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = builderFactory.newDocumentBuilder();
return builder.newDocument();
return getDocumentBuilder().newDocument();
}
/**
@ -100,8 +132,7 @@ public class XMLUtil {
* @throws IOException
*/
public static Document loadDocument(String docPath) throws ParserConfigurationException, SAXException, IOException {
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = builderFactory.newDocumentBuilder();
DocumentBuilder builder = getDocumentBuilder();
Document doc = builder.parse(new FileInputStream(docPath));
return doc;
}
@ -119,7 +150,7 @@ public class XMLUtil {
public static <T> void validateDocument(final Document doc, Class<T> clazz, String schemaResourceName) throws SAXException, IOException {
PlatformUtil.extractResourceToUserConfigDir(clazz, schemaResourceName, false);
File schemaFile = new File(Paths.get(PlatformUtil.getUserConfigDirectory(), schemaResourceName).toAbsolutePath().toString());
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
SchemaFactory schemaFactory = getSchemaFactory(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = schemaFactory.newSchema(schemaFile);
Validator validator = schema.newValidator();
validator.validate(new DOMSource(doc), new DOMResult());
@ -140,7 +171,7 @@ public class XMLUtil {
* @throws IOException
*/
public static void saveDocument(final Document doc, String encoding, String docPath) throws TransformerConfigurationException, FileNotFoundException, UnsupportedEncodingException, TransformerException, IOException {
TransformerFactory xf = TransformerFactory.newInstance();
TransformerFactory xf = getTransformerFactory();
xf.setAttribute("indent-number", 1); //NON-NLS
Transformer xformer = xf.newTransformer();
xformer.setOutputProperty(OutputKeys.METHOD, "xml"); //NON-NLS
@ -178,7 +209,7 @@ public class XMLUtil {
try {
PlatformUtil.extractResourceToUserConfigDir(clazz, schemaFile, false);
File schemaLoc = new File(PlatformUtil.getUserConfigDirectory() + File.separator + schemaFile);
SchemaFactory schm = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
SchemaFactory schm = getSchemaFactory(XMLConstants.W3C_XML_SCHEMA_NS_URI);
try {
Schema schema = schm.newSchema(schemaLoc);
Validator validator = schema.newValidator();
@ -226,10 +257,9 @@ public class XMLUtil {
*/
// TODO: Deprecate.
public static <T> Document loadDoc(Class<T> clazz, String xmlPath) {
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
Document ret = null;
try {
DocumentBuilder builder = builderFactory.newDocumentBuilder();
DocumentBuilder builder = getDocumentBuilder();
ret = builder.parse(new FileInputStream(xmlPath));
} catch (ParserConfigurationException e) {
Logger.getLogger(clazz.getName()).log(Level.SEVERE, "Error loading XML file " + xmlPath + " : can't initialize parser.", e); //NON-NLS
@ -268,7 +298,7 @@ public class XMLUtil {
*/
// TODO: Deprecate.
public static <T> boolean saveDoc(Class<T> clazz, String xmlPath, String encoding, final Document doc) {
TransformerFactory xf = TransformerFactory.newInstance();
TransformerFactory xf = getTransformerFactory();
xf.setAttribute("indent-number", 1); //NON-NLS
boolean success = false;
try {

View File

@ -278,6 +278,7 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
"AbstractAbstractFileNode.typeMetaColLbl=Type(Meta)",
"AbstractAbstractFileNode.knownColLbl=Known",
"AbstractAbstractFileNode.md5HashColLbl=MD5 Hash",
"AbstractAbstractFileNode.sha256HashColLbl=SHA-256 Hash",
"AbstractAbstractFileNode.objectId=Object ID",
"AbstractAbstractFileNode.mimeType=MIME Type",
"AbstractAbstractFileNode.extensionColLbl=Extension"})
@ -305,6 +306,7 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
TYPE_META(AbstractAbstractFileNode_typeMetaColLbl()),
KNOWN(AbstractAbstractFileNode_knownColLbl()),
MD5HASH(AbstractAbstractFileNode_md5HashColLbl()),
SHA256HASH(AbstractAbstractFileNode_sha256HashColLbl()),
ObjectID(AbstractAbstractFileNode_objectId()),
MIMETYPE(AbstractAbstractFileNode_mimeType()),
EXTENSION(AbstractAbstractFileNode_extensionColLbl());
@ -358,6 +360,7 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
properties.add(new NodeProperty<>(KNOWN.toString(), KNOWN.toString(), NO_DESCR, content.getKnown().getName()));
properties.add(new NodeProperty<>(LOCATION.toString(), LOCATION.toString(), NO_DESCR, getContentPath(content)));
properties.add(new NodeProperty<>(MD5HASH.toString(), MD5HASH.toString(), NO_DESCR, StringUtils.defaultString(content.getMd5Hash())));
properties.add(new NodeProperty<>(SHA256HASH.toString(), SHA256HASH.toString(), NO_DESCR, StringUtils.defaultString(content.getSha256Hash())));
properties.add(new NodeProperty<>(MIMETYPE.toString(), MIMETYPE.toString(), NO_DESCR, StringUtils.defaultString(content.getMIMEType())));
properties.add(new NodeProperty<>(EXTENSION.toString(), EXTENSION.toString(), NO_DESCR, content.getNameExtension()));
@ -577,6 +580,7 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
map.put(FLAGS_META.toString(), content.getMetaFlagsAsString());
map.put(KNOWN.toString(), content.getKnown().getName());
map.put(MD5HASH.toString(), StringUtils.defaultString(content.getMd5Hash()));
map.put(SHA256HASH.toString(), StringUtils.defaultString(content.getSha256Hash()));
map.put(MIMETYPE.toString(), StringUtils.defaultString(content.getMIMEType()));
map.put(EXTENSION.toString(), content.getNameExtension());
}

View File

@ -128,8 +128,8 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
};
private final BlackboardArtifact artifact;
private Content srcContent;
private volatile String translatedSourceName;
private Content srcContent;
private volatile String translatedSourceName;
/*
* A method has been provided to allow the injection of properties into this
@ -284,17 +284,49 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
*/
private static Lookup createLookup(BlackboardArtifact artifact) {
final long objectID = artifact.getObjectID();
Content content = null;
try {
Content content = contentCache.get(objectID, () -> artifact.getSleuthkitCase().getContentById(objectID));
if (artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID() || artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CACHE.getTypeID()) {
content = getPathIdFile(artifact);
}
if (content == null) {
return Lookups.fixed(artifact);
} else {
return Lookups.fixed(artifact, content);
content = contentCache.get(objectID, () -> artifact.getSleuthkitCase().getContentById(objectID));
}
} catch (ExecutionException ex) {
logger.log(Level.SEVERE, MessageFormat.format("Error getting source content (artifact objID={0}", artifact.getId()), ex); //NON-NLS
return Lookups.fixed(artifact);
content = null;
}
if (content == null) {
return Lookups.fixed(artifact);
} else {
return Lookups.fixed(artifact, content);
}
}
/**
* Private helper method to allow content specified in a path id attribute
* to be retrieved.
*
* @param artifact The artifact for which content may be specified as a tsk
* path attribute.
*
* @return The Content specified by the artifact's path id attribute or null
* if there was no content available.
*
* @throws ExecutionException Error retrieving the file specified by the
* path id from the cache.
*/
private static Content getPathIdFile(BlackboardArtifact artifact) throws ExecutionException {
try {
BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID));
if (attribute != null) {
return contentCache.get(attribute.getValueLong(), () -> artifact.getSleuthkitCase().getContentById(attribute.getValueLong()));
}
} catch (TskCoreException ex) {
logger.log(Level.WARNING, MessageFormat.format("Error getting content for path id attrbiute for artifact: ", artifact.getId()), ex); //NON-NLS
}
return null;
}
/**

View File

@ -30,6 +30,7 @@ AbstractAbstractFileNode.modifiedTimeColLbl=Modified Time
AbstractAbstractFileNode.nameColLbl=Name
AbstractAbstractFileNode.objectId=Object ID
AbstractAbstractFileNode.originalName=Original Name
AbstractAbstractFileNode.sha256HashColLbl=SHA-256 Hash
AbstractAbstractFileNode.sizeColLbl=Size
AbstractAbstractFileNode.tagsProperty.displayName=Tags
AbstractAbstractFileNode.typeDirColLbl=Type(Dir)

View File

@ -529,9 +529,11 @@ final public class Accounts implements AutopsyVisitableItem {
+ getRejectedArtifactFilterClause(); //NON-NLS
try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
ResultSet rs = results.getResultSet();) {
List<Long> tempList = new ArrayList<>();
while (rs.next()) {
list.add(rs.getLong("artifact_id")); //NON-NLS
tempList.add(rs.getLong("artifact_id")); // NON-NLS
}
list.addAll(tempList);
} catch (TskCoreException | SQLException ex) {
LOGGER.log(Level.SEVERE, "Error querying for account artifacts.", ex); //NON-NLS
}

View File

@ -398,6 +398,20 @@ final class DataSourceInfoUtilities {
BlackboardAttribute attr = getAttributeOrNull(artifact, attributeType);
return (attr == null) ? null : attr.getValueLong();
}
/**
* Retrieves the int value of a certain attribute type from an artifact.
*
* @param artifact The artifact.
* @param attributeType The attribute type.
*
* @return The 'getValueInt()' value or null if the attribute could not be
* retrieved.
*/
static Integer getIntOrNull(BlackboardArtifact artifact, Type attributeType) {
BlackboardAttribute attr = getAttributeOrNull(artifact, attributeType);
return (attr == null) ? null : attr.getValueInt();
}
/**
* Retrieves the long value of a certain attribute type from an artifact and

View File

@ -118,7 +118,7 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
dataSource,
DATETIME_ATT,
DataSourceInfoUtilities.SortOrder.DESCENDING,
10);
maxCount);
List<RecentFileDetails> fileDetails = new ArrayList<>();
for (BlackboardArtifact artifact : artifactList) {
@ -134,12 +134,11 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
} else if (attribute.getAttributeType().equals(PATH_ATT)) {
path = attribute.getValueString();
}
if (accessedTime != null) {
fileDetails.add(new RecentFileDetails(path, accessedTime));
}
}
if (accessedTime != null && accessedTime != 0) {
fileDetails.add(new RecentFileDetails(path, accessedTime));
}
}
return fileDetails;
@ -190,7 +189,7 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
path = attribute.getValueString();
}
}
if (accessedTime != null) {
if (accessedTime != null && accessedTime != 0L) {
fileDetails.add(new RecentDownloadDetails(path, accessedTime, domain));
}
}
@ -215,6 +214,10 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
return Collections.emptyList();
}
if (maxCount < 0) {
throw new IllegalArgumentException("Invalid maxCount passed to getRecentAttachments, value must be equal to or greater than 0");
}
return createListFromMap(buildAttachmentMap(dataSource), maxCount);
}
@ -241,7 +244,7 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
}
BlackboardArtifact messageArtifact = skCase.getBlackboardArtifact(attribute.getValueLong());
if (isMessageArtifact(messageArtifact)) {
if (messageArtifact != null && isMessageArtifact(messageArtifact)) {
Content content = artifact.getParent();
if (content instanceof AbstractFile) {
String sender;

View File

@ -0,0 +1,318 @@
/*
* 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.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
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.TimeZone;
import org.joda.time.Interval;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultUpdateGovernor;
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.TimelineEvent;
import org.sleuthkit.datamodel.TimelineEventType;
import org.sleuthkit.datamodel.TimelineFilter;
import org.sleuthkit.datamodel.TimelineFilter.DataSourcesFilter;
import org.sleuthkit.datamodel.TimelineFilter.RootFilter;
import org.sleuthkit.datamodel.TimelineManager;
import org.sleuthkit.datamodel.TskCoreException;
import java.util.function.Supplier;
import org.sleuthkit.autopsy.core.UserPreferences;
/**
* Provides data source summary information pertaining to Timeline data.
*/
public class TimelineSummary implements DefaultUpdateGovernor {
private static final long DAY_SECS = 24 * 60 * 60;
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS = new HashSet<>(
Arrays.asList(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED));
private static final Set<TimelineEventType> FILE_SYSTEM_EVENTS
= new HashSet<>(Arrays.asList(
TimelineEventType.FILE_MODIFIED,
TimelineEventType.FILE_ACCESSED,
TimelineEventType.FILE_CREATED,
TimelineEventType.FILE_CHANGED));
private final SleuthkitCaseProvider caseProvider;
private final Supplier<TimeZone> timeZoneProvider;
/**
* Default constructor.
*/
public TimelineSummary() {
this(SleuthkitCaseProvider.DEFAULT, () -> TimeZone.getTimeZone(UserPreferences.getTimeZoneForDisplays()));
}
/**
* Construct object with given SleuthkitCaseProvider
*
* @param caseProvider SleuthkitCaseProvider provider, cannot be null.
* @param timeZoneProvider The timezone provider, cannot be null.
*/
public TimelineSummary(SleuthkitCaseProvider caseProvider, Supplier<TimeZone> timeZoneProvider) {
this.caseProvider = caseProvider;
this.timeZoneProvider = timeZoneProvider;
}
@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;
}
/**
* Retrieves timeline summary data.
*
* @param dataSource The data source for which timeline data will be
* retrieved.
* @param recentDaysNum The maximum number of most recent days' activity to
* include.
* @return The retrieved data.
* @throws SleuthkitCaseProviderException
* @throws TskCoreException
*/
public TimelineSummaryData getData(DataSource dataSource, int recentDaysNum) throws SleuthkitCaseProviderException, TskCoreException {
TimeZone timeZone = this.timeZoneProvider.get();
TimelineManager timelineManager = this.caseProvider.get().getTimelineManager();
// get a mapping of days from epoch to the activity for that day
Map<Long, DailyActivityAmount> dateCounts = getTimelineEventsByDay(dataSource, timelineManager, timeZone);
// get minimum and maximum usage date by iterating through
Long minDay = null;
Long maxDay = null;
for (long daysFromEpoch : dateCounts.keySet()) {
minDay = (minDay == null) ? daysFromEpoch : Math.min(minDay, daysFromEpoch);
maxDay = (maxDay == null) ? daysFromEpoch : Math.max(maxDay, daysFromEpoch);
}
// if no min date or max date, no usage; return null.
if (minDay == null || maxDay == null) {
return null;
}
Date minDate = new Date(minDay * 1000 * DAY_SECS);
Date maxDate = new Date(maxDay * 1000 * DAY_SECS);
// The minimum recent day will be within recentDaysNum from the maximum day
// (+1 since maxDay included) or the minimum day of activity
long minRecentDay = Math.max(maxDay - recentDaysNum + 1, minDay);
// get most recent days activity
List<DailyActivityAmount> mostRecentActivityAmt = getMostRecentActivityAmounts(dateCounts, minRecentDay, maxDay);
return new TimelineSummaryData(minDate, maxDate, mostRecentActivityAmt);
}
/**
* Given activity by day, converts to most recent days' activity handling
* empty values.
*
* @param dateCounts The day from epoch mapped to activity amounts for that
* day.
* @param minRecentDay The minimum recent day in days from epoch.
* @param maxDay The maximum recent day in days from epoch;
* @return The most recent daily activity amounts.
*/
private List<DailyActivityAmount> getMostRecentActivityAmounts(Map<Long, DailyActivityAmount> dateCounts, long minRecentDay, long maxDay) {
List<DailyActivityAmount> mostRecentActivityAmt = new ArrayList<>();
for (long curRecentDay = minRecentDay; curRecentDay <= maxDay; curRecentDay++) {
DailyActivityAmount prevCounts = dateCounts.get(curRecentDay);
DailyActivityAmount countsHandleNotFound = prevCounts != null
? prevCounts
: new DailyActivityAmount(new Date(curRecentDay * DAY_SECS * 1000), 0, 0);
mostRecentActivityAmt.add(countsHandleNotFound);
}
return mostRecentActivityAmt;
}
/**
* Fetches timeline events per day for a particular data source.
*
* @param dataSource The data source.
* @param timelineManager The timeline manager to use while fetching the
* data.
* @param timeZone The time zone to use to determine which day activity
* belongs.
* @return A Map mapping days from epoch to the activity for that day.
* @throws TskCoreException
*/
private Map<Long, DailyActivityAmount> getTimelineEventsByDay(DataSource dataSource, TimelineManager timelineManager, TimeZone timeZone) throws TskCoreException {
DataSourcesFilter dataSourceFilter = new DataSourcesFilter();
dataSourceFilter.addSubFilter(new TimelineFilter.DataSourceFilter(dataSource.getName(), dataSource.getId()));
RootFilter dataSourceRootFilter = new RootFilter(
null,
null,
null,
null,
null,
dataSourceFilter,
null,
Collections.emptySet());
// get events for data source
long curRunTime = System.currentTimeMillis();
List<TimelineEvent> events = timelineManager.getEvents(new Interval(1, curRunTime), dataSourceRootFilter);
// get counts of events per day (left is file system events, right is everything else)
Map<Long, DailyActivityAmount> dateCounts = new HashMap<>();
for (TimelineEvent evt : events) {
long curSecondsFromEpoch = evt.getTime();
long curDaysFromEpoch = Instant.ofEpochMilli(curSecondsFromEpoch * 1000)
.atZone(timeZone.toZoneId())
.toLocalDate()
.toEpochDay();
DailyActivityAmount prevAmt = dateCounts.get(curDaysFromEpoch);
long prevFileEvtCount = prevAmt == null ? 0 : prevAmt.getFileActivityCount();
long prevArtifactEvtCount = prevAmt == null ? 0 : prevAmt.getArtifactActivityCount();
Date thisDay = prevAmt == null ? new Date(curDaysFromEpoch * 1000 * DAY_SECS) : prevAmt.getDay();
boolean isFileEvt = FILE_SYSTEM_EVENTS.contains(evt.getEventType());
long curFileEvtCount = prevFileEvtCount + (isFileEvt ? 1 : 0);
long curArtifactEvtCount = prevArtifactEvtCount + (isFileEvt ? 0 : 1);
dateCounts.put(curDaysFromEpoch, new DailyActivityAmount(thisDay, curFileEvtCount, curArtifactEvtCount));
}
return dateCounts;
}
/**
* All the data to be represented in the timeline summary tab.
*/
public static class TimelineSummaryData {
private final Date minDate;
private final Date maxDate;
private final List<DailyActivityAmount> histogramActivity;
/**
* Main constructor.
*
* @param minDate Earliest usage date recorded for the data source.
* @param maxDate Latest usage date recorded for the data source.
* @param recentDaysActivity A list of activity prior to and including
* the latest usage date by day.
*/
TimelineSummaryData(Date minDate, Date maxDate, List<DailyActivityAmount> recentDaysActivity) {
this.minDate = minDate;
this.maxDate = maxDate;
this.histogramActivity = (recentDaysActivity == null) ? Collections.emptyList() : Collections.unmodifiableList(recentDaysActivity);
}
/**
* @return Earliest usage date recorded for the data source.
*/
public Date getMinDate() {
return minDate;
}
/**
* @return Latest usage date recorded for the data source.
*/
public Date getMaxDate() {
return maxDate;
}
/**
* @return A list of activity prior to and including the latest usage
* date by day.
*/
public List<DailyActivityAmount> getMostRecentDaysActivity() {
return histogramActivity;
}
}
/**
* Represents the amount of usage based on timeline events for a day.
*/
public static class DailyActivityAmount {
private final Date day;
private final long fileActivityCount;
private final long artifactActivityCount;
/**
* Main constructor.
*
* @param day The day for which activity is being measured.
* @param fileActivityCount The amount of file activity timeline events.
* @param artifactActivityCount The amount of artifact timeline events.
*/
DailyActivityAmount(Date day, long fileActivityCount, long artifactActivityCount) {
this.day = day;
this.fileActivityCount = fileActivityCount;
this.artifactActivityCount = artifactActivityCount;
}
/**
* @return The day for which activity is being measured.
*/
public Date getDay() {
return day;
}
/**
* @return The amount of file activity timeline events.
*/
public long getFileActivityCount() {
return fileActivityCount;
}
/**
* @return The amount of artifact timeline events.
*/
public long getArtifactActivityCount() {
return artifactActivityCount;
}
}
}

View File

@ -1,370 +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 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.stream.Collectors;
import org.apache.commons.lang.StringUtils;
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 TopProgramsSummary implements DefaultArtifactUpdateGovernor {
private static final Set<Integer> ARTIFACT_UPDATE_TYPE_IDS = new HashSet<>(Arrays.asList(
ARTIFACT_TYPE.TSK_PROG_RUN.getTypeID()
));
/**
* A SQL join type.
*/
private enum JoinType {
LEFT,
RIGHT,
INNER,
OUTER
}
/**
* A blackboard attribute value column.
*/
private enum AttributeColumn {
value_text,
value_int32,
value_int64
}
/**
* The suffix joined to a key name for use as an identifier of a query.
*/
private static final String QUERY_SUFFIX = "_query";
/**
* Functions that determine the folder name of a list of path elements. If
* not matched, function returns null.
*/
private static final List<Function<List<String>, String>> SHORT_FOLDER_MATCHERS = Arrays.asList(
// handle Program Files and Program Files (x86) - if true, return the next folder
(pathList) -> {
if (pathList.size() < 2) {
return null;
}
String rootParent = pathList.get(0).toUpperCase();
if ("PROGRAM FILES".equals(rootParent) || "PROGRAM FILES (X86)".equals(rootParent)) {
return pathList.get(1);
} else {
return null;
}
},
// if there is a folder named "APPLICATION DATA" or "APPDATA"
(pathList) -> {
for (String pathEl : pathList) {
String uppered = pathEl.toUpperCase();
if ("APPLICATION DATA".equals(uppered) || "APPDATA".equals(uppered)) {
return "AppData";
}
}
return null;
}
);
/**
* Creates a sql statement querying the blackboard attributes table for a
* particular attribute type and returning a specified value. That query
* also joins with the blackboard artifact table.
*
* @param joinType The type of join statement to create.
* @param attributeColumn The blackboard attribute column that should be
* returned.
* @param attrType The attribute type to query for.
* @param keyName The aliased name of the attribute to return. This
* is also used to calculate the alias of the query
* same as getFullKey.
* @param bbaName The blackboard artifact table alias.
*
* @return The generated sql statement.
*/
private static String getAttributeJoin(JoinType joinType, AttributeColumn attributeColumn, BlackboardAttribute.ATTRIBUTE_TYPE attrType, String keyName, String bbaName) {
String queryName = keyName + QUERY_SUFFIX;
String innerQueryName = "inner_attribute_" + queryName;
return "\n" + joinType + " JOIN (\n"
+ " SELECT \n"
+ " " + innerQueryName + ".artifact_id,\n"
+ " " + innerQueryName + "." + attributeColumn + " AS " + keyName + "\n"
+ " FROM blackboard_attributes " + innerQueryName + "\n"
+ " WHERE " + innerQueryName + ".attribute_type_id = " + attrType.getTypeID() + " -- " + attrType.name() + "\n"
+ ") " + queryName + " ON " + queryName + ".artifact_id = " + bbaName + ".artifact_id\n";
}
/**
* Given a column key, creates the full name for the column key.
*
* @param key The column key.
*
* @return The full identifier for the column key.
*/
private static String getFullKey(String key) {
return key + QUERY_SUFFIX + "." + key;
}
/**
* Constructs a SQL 'where' statement from a list of clauses and puts
* parenthesis around each clause.
*
* @param clauses The clauses
*
* @return The generated 'where' statement.
*/
private static String getWhereString(List<String> clauses) {
if (clauses.isEmpty()) {
return "";
}
List<String> parenthesized = clauses.stream()
.map(c -> "(" + c + ")")
.collect(Collectors.toList());
return "\nWHERE " + String.join("\n AND ", parenthesized) + "\n";
}
/**
* Generates a [column] LIKE sql clause.
*
* @param column The column identifier.
* @param likeString The string that will be used as column comparison.
* @param isLike if false, the statement becomes NOT LIKE.
*
* @return The generated statement.
*/
private static String getLikeClause(String column, String likeString, boolean isLike) {
return column + (isLike ? "" : " NOT") + " LIKE '" + likeString + "'";
}
private final SleuthkitCaseProvider provider;
public TopProgramsSummary() {
this(SleuthkitCaseProvider.DEFAULT);
}
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
* count.
*
* @param dataSource The data source.
* @param count The number of programs to return.
*
* @return The top results objects found.
*
* @throws SleuthkitCaseProviderException
* @throws TskCoreException
* @throws SQLException
*/
public List<TopProgramsResult> getTopPrograms(DataSource dataSource, int count)
throws SleuthkitCaseProviderException, TskCoreException, SQLException {
if (dataSource == null || count <= 0) {
return Collections.emptyList();
}
// ntosboot should be ignored
final String ntosBootIdentifier = "NTOSBOOT";
// programs in windows directory to be ignored
final String windowsDir = "/WINDOWS%";
final String nameParam = "name";
final String pathParam = "path";
final String runCountParam = "run_count";
final String lastRunParam = "last_run";
String bbaQuery = "bba";
final String query = "SELECT\n"
+ " " + getFullKey(nameParam) + " AS " + nameParam + ",\n"
+ " " + getFullKey(pathParam) + " AS " + pathParam + ",\n"
+ " MAX(" + getFullKey(runCountParam) + ") AS " + runCountParam + ",\n"
+ " MAX(" + getFullKey(lastRunParam) + ") AS " + lastRunParam + "\n"
+ "FROM blackboard_artifacts " + bbaQuery + "\n"
+ getAttributeJoin(JoinType.INNER, AttributeColumn.value_text, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, nameParam, bbaQuery)
+ getAttributeJoin(JoinType.LEFT, AttributeColumn.value_text, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH, pathParam, bbaQuery)
+ getAttributeJoin(JoinType.LEFT, AttributeColumn.value_int32, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COUNT, runCountParam, bbaQuery)
+ getAttributeJoin(JoinType.LEFT, AttributeColumn.value_int64, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, lastRunParam, bbaQuery)
+ getWhereString(Arrays.asList(
bbaQuery + ".artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_PROG_RUN.getTypeID(),
bbaQuery + ".data_source_obj_id = " + dataSource.getId(),
// exclude ntosBootIdentifier from results
getLikeClause(getFullKey(nameParam), ntosBootIdentifier, false),
// exclude windows directory items from results
getFullKey(pathParam) + " IS NULL OR " + getLikeClause(getFullKey(pathParam), windowsDir, false)
))
+ "GROUP BY " + getFullKey(nameParam) + ", " + getFullKey(pathParam) + "\n"
+ "ORDER BY \n"
+ " MAX(" + getFullKey(runCountParam) + ") DESC,\n"
+ " MAX(" + getFullKey(lastRunParam) + ") DESC,\n"
+ " " + getFullKey(nameParam) + " ASC";
DataSourceInfoUtilities.ResultSetHandler<List<TopProgramsResult>> handler = (resultSet) -> {
List<TopProgramsResult> progResults = new ArrayList<>();
boolean quitAtCount = false;
while (resultSet.next() && (!quitAtCount || progResults.size() < count)) {
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));
}
return progResults;
};
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 applicationName The application name.
*
* @return The short folder name or empty string if not found.
*/
public String getShortFolderName(String strPath, String applicationName) {
if (strPath == null) {
return "";
}
List<String> pathEls = new ArrayList<>(Arrays.asList(applicationName));
File file = new File(strPath);
while (file != null && StringUtils.isNotBlank(file.getName())) {
pathEls.add(file.getName());
file = file.getParentFile();
}
Collections.reverse(pathEls);
for (Function<List<String>, String> matchEntry : SHORT_FOLDER_MATCHERS) {
String result = matchEntry.apply(pathEls);
if (StringUtils.isNotBlank(result)) {
return result;
}
}
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

@ -18,6 +18,7 @@
*/
package org.sleuthkit.autopsy.datasourcesummary.datamodel;
import java.io.File;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultArtifactUpdateGovernor;
import java.util.ArrayList;
import java.util.Arrays;
@ -30,6 +31,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.logging.Level;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@ -54,6 +56,36 @@ import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
*/
public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
/**
* Functions that determine the folder name of a list of path elements. If
* not matched, function returns null.
*/
private static final List<Function<List<String>, String>> SHORT_FOLDER_MATCHERS = Arrays.asList(
// handle Program Files and Program Files (x86) - if true, return the next folder
(pathList) -> {
if (pathList.size() < 2) {
return null;
}
String rootParent = pathList.get(0).toUpperCase();
if ("PROGRAM FILES".equals(rootParent) || "PROGRAM FILES (X86)".equals(rootParent)) {
return pathList.get(1);
} else {
return null;
}
},
// if there is a folder named "APPLICATION DATA" or "APPDATA"
(pathList) -> {
for (String pathEl : pathList) {
String uppered = pathEl.toUpperCase();
if ("APPLICATION DATA".equals(uppered) || "APPDATA".equals(uppered)) {
return "AppData";
}
}
return null;
}
);
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);
@ -69,17 +101,51 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
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 BlackboardAttribute.Type TYPE_PROG_NAME = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_PROG_NAME);
private static final BlackboardAttribute.Type TYPE_PATH = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_PATH);
private static final BlackboardAttribute.Type TYPE_COUNT = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_COUNT);
private static final String NTOS_BOOT_IDENTIFIER = "NTOSBOOT";
private static final String WINDOWS_PREFIX = "/WINDOWS";
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());
/**
* Sorts TopProgramsResults pushing highest run time count then most recent
* run and then the program name that comes earliest in the alphabet.
*/
private static final Comparator<TopProgramsResult> TOP_PROGRAMS_RESULT_COMPARE = (a, b) -> {
// first priority for sorting is the run times
// if non-0, this is the return value for the comparator
int runTimesCompare = nullableCompare(a.getRunTimes(), b.getRunTimes());
if (runTimesCompare != 0) {
return -runTimesCompare;
}
// second priority for sorting is the last run date
// if non-0, this is the return value for the comparator
int lastRunCompare = nullableCompare(
a.getLastRun() == null ? null : a.getLastRun().getTime(),
b.getLastRun() == null ? null : b.getLastRun().getTime());
if (lastRunCompare != 0) {
return -lastRunCompare;
}
// otherwise sort alphabetically
return (a.getProgramName() == null ? "" : a.getProgramName())
.compareToIgnoreCase((b.getProgramName() == null ? "" : b.getProgramName()));
};
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()
ARTIFACT_TYPE.TSK_WEB_HISTORY.getTypeID(),
ARTIFACT_TYPE.TSK_PROG_RUN.getTypeID()
));
private static final Set<String> DEVICE_EXCLUDE_LIST = new HashSet<>(Arrays.asList("ROOT_HUB", "ROOT_HUB20"));
@ -539,6 +605,189 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
.collect(Collectors.toList());
}
/**
* Determines a short folder name if any. Otherwise, returns empty string.
*
* @param strPath The string path.
* @param applicationName The application name.
*
* @return The short folder name or empty string if not found.
*/
public String getShortFolderName(String strPath, String applicationName) {
if (strPath == null) {
return "";
}
List<String> pathEls = new ArrayList<>(Arrays.asList(applicationName));
File file = new File(strPath);
while (file != null && org.apache.commons.lang.StringUtils.isNotBlank(file.getName())) {
pathEls.add(file.getName());
file = file.getParentFile();
}
Collections.reverse(pathEls);
for (Function<List<String>, String> matchEntry : SHORT_FOLDER_MATCHERS) {
String result = matchEntry.apply(pathEls);
if (org.apache.commons.lang.StringUtils.isNotBlank(result)) {
return result;
}
}
return "";
}
/**
* Creates a TopProgramsResult from a TSK_PROG_RUN blackboard artifact.
*
* @param artifact The TSK_PROG_RUN blackboard artifact.
*
* @return The generated TopProgramsResult.
*/
private TopProgramsResult getTopProgramsResult(BlackboardArtifact artifact) {
String programName = DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_PROG_NAME);
// ignore items with no name or a ntos boot identifier
if (StringUtils.isBlank(programName) || NTOS_BOOT_IDENTIFIER.equalsIgnoreCase(programName)) {
return null;
}
String path = DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_PATH);
// ignore windows directory
if (StringUtils.startsWithIgnoreCase(path, WINDOWS_PREFIX)) {
return null;
}
Integer count = DataSourceInfoUtilities.getIntOrNull(artifact, TYPE_COUNT);
Long longCount = (count == null) ? null : (long) count;
return new TopProgramsResult(
programName,
path,
longCount,
DataSourceInfoUtilities.getDateOrNull(artifact, TYPE_DATETIME)
);
}
/**
* Retrieves the maximum date given two (possibly null) dates.
*
* @param date1 First date.
* @param date2 Second date.
*
* @return The maximum non-null date or null if both items are null.
*/
private static Date getMax(Date date1, Date date2) {
if (date1 == null) {
return date2;
} else if (date2 == null) {
return date1;
} else {
return date1.compareTo(date2) > 0 ? date1 : date2;
}
}
/**
* Returns the compare value favoring the higher non-null number.
*
* @param long1 First possibly null long.
* @param long2 Second possibly null long.
*
* @return Returns the compare value: 1,0,-1 favoring the higher non-null
* value.
*/
private static int nullableCompare(Long long1, Long long2) {
if (long1 == null && long2 == null) {
return 0;
} else if (long1 != null && long2 == null) {
return 1;
} else if (long1 == null && long2 != null) {
return -1;
}
return Long.compare(long1, long2);
}
/**
* Returns true if number is non-null and higher than 0.
*
* @param longNum The number.
*
* @return True if non-null and higher than 0.
*/
private static boolean isPositiveNum(Long longNum) {
return longNum != null && longNum > 0;
}
/**
* Retrieves the top programs results for the given data source limited to
* the count provided as a parameter. The highest run times are at the top
* of the list. If that information isn't available the last run date is
* used. If both, the last run date and the number of run times are
* unavailable, the programs will be sorted alphabetically, the count will
* be ignored and all items will be returned.
*
* @param dataSource The datasource. If the datasource is null, an empty
* list will be returned.
* @param count The number of results to return. This value must be > 0
* or an IllegalArgumentException will be thrown.
*
* @return The sorted list and limited to the count if last run or run count
* information is available on any item.
*
* @throws SleuthkitCaseProviderException
* @throws TskCoreException
*/
public List<TopProgramsResult> getTopPrograms(DataSource dataSource, int count) throws SleuthkitCaseProviderException, TskCoreException {
assertValidCount(count);
if (dataSource == null) {
return Collections.emptyList();
}
// Get TopProgramsResults for each TSK_PROG_RUN artifact
Collection<TopProgramsResult> results = caseProvider.get().getBlackboard().getArtifacts(ARTIFACT_TYPE.TSK_PROG_RUN.getTypeID(), dataSource.getId())
.stream()
// convert to a TopProgramsResult object or null if missing critical information
.map((art) -> getTopProgramsResult(art))
// remove any null items
.filter((res) -> res != null)
// group by the program name and program path
// The value will be a TopProgramsResult with the max run times
// and most recent last run date for each program name / program path pair.
.collect(Collectors.toMap(
res -> Pair.of(res.getProgramName(), res.getProgramPath()),
res -> res,
(res1, res2) -> {
return new TopProgramsResult(
res1.getProgramName(),
res1.getProgramPath(),
getMax(res1.getRunTimes(), res2.getRunTimes()),
getMax(res1.getLastRun(), res2.getLastRun()));
})).values();
List<TopProgramsResult> orderedResults = results.stream()
.sorted(TOP_PROGRAMS_RESULT_COMPARE)
.collect(Collectors.toList());
// only limit the list to count if there is no last run date and no run times.
if (!orderedResults.isEmpty()) {
TopProgramsResult topResult = orderedResults.get(0);
// if run times / last run information is available, the first item should have some value,
// and then the items should be limited accordingly.
if (isPositiveNum(topResult.getRunTimes())
|| (topResult.getLastRun() != null && isPositiveNum(topResult.getLastRun().getTime()))) {
return orderedResults.stream().limit(count).collect(Collectors.toList());
}
}
// otherwise return the alphabetized list with no limit applied.
return orderedResults;
}
/**
* Object containing information about a web search artifact.
*/
@ -722,4 +971,57 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
return lastVisit;
}
}
/**
* 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

@ -42,3 +42,4 @@ 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.
TimelinePanel.activityRangeLabel.text=Activity Range

View File

@ -3,6 +3,7 @@ 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.
ContainerPanel_setFieldsForNonImageDataSource_na=N/A
CTL_DataSourceSummaryAction=Data Source Summary
DataSourceSummaryDialog.closeButton.text=Close
ContainerPanel.displayNameLabel.text=Display Name:
@ -47,6 +48,7 @@ DataSourceSummaryTabbedPane_detailsTab_title=Container
DataSourceSummaryTabbedPane_ingestHistoryTab_title=Ingest History
DataSourceSummaryTabbedPane_pastCasesTab_title=Past Cases
DataSourceSummaryTabbedPane_recentFileTab_title=Recent Files
DataSourceSummaryTabbedPane_timelineTab_title=Timeline
DataSourceSummaryTabbedPane_typesTab_title=Types
DataSourceSummaryTabbedPane_userActivityTab_title=User Activity
PastCasesPanel_caseColumn_title=Case
@ -64,6 +66,11 @@ SizeRepresentationUtil_units_kilobytes=\ kB
SizeRepresentationUtil_units_megabytes=\ MB
SizeRepresentationUtil_units_petabytes=\ PB
SizeRepresentationUtil_units_terabytes=\ TB
TimelinePanel_earliestLabel_title=Earliest
TimelinePanel_latestLabel_title=Latest
TimlinePanel_last30DaysChart_artifactEvts_title=Artifact Events
TimlinePanel_last30DaysChart_fileEvts_title=File Events
TimlinePanel_last30DaysChart_title=Last 30 Days
TypesPanel_artifactsTypesPieChart_title=Artifact Types
TypesPanel_fileMimeTypesChart_audio_title=Audio
TypesPanel_fileMimeTypesChart_documents_title=Documents
@ -95,6 +102,7 @@ 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.
TimelinePanel.activityRangeLabel.text=Activity Range
UserActivityPanel_noDataExists=No communication data exists
UserActivityPanel_tab_title=User Activity
UserActivityPanel_TopAccountTableModel_accountType_header=Account Type

View File

@ -26,6 +26,7 @@ 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.ContainerSummary;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult.ResultType;
@ -52,7 +53,7 @@ class ContainerPanel extends BaseDataSourceSummaryPanel {
/**
* Main constructor.
*
* @param dataSource The original datasource.
* @param dataSource The original datasource.
* @param unallocatedFilesSize The unallocated file size.
*/
ContainerPanelData(DataSource dataSource, Long unallocatedFilesSize) {
@ -165,8 +166,6 @@ class ContainerPanel extends BaseDataSourceSummaryPanel {
private void updateDetailsPanelData(DataSource selectedDataSource, Long unallocatedFilesSize) {
clearTableValues();
if (selectedDataSource != null) {
unallocatedSizeValue.setText(SizeRepresentationUtil.getSizeString(unallocatedFilesSize));
timeZoneValue.setText(selectedDataSource.getTimeZone());
displayNameValue.setText(selectedDataSource.getName());
originalNameValue.setText(selectedDataSource.getName());
deviceIdValue.setText(selectedDataSource.getDeviceId());
@ -178,24 +177,48 @@ class ContainerPanel extends BaseDataSourceSummaryPanel {
}
if (selectedDataSource instanceof Image) {
setFieldsForImage((Image) selectedDataSource);
setFieldsForImage((Image) selectedDataSource, unallocatedFilesSize);
} else {
setFieldsForNonImageDataSource();
}
}
updateFieldVisibility();
this.repaint();
}
@Messages({
"ContainerPanel_setFieldsForNonImageDataSource_na=N/A"
})
private void setFieldsForNonImageDataSource() {
String NA = Bundle.ContainerPanel_setFieldsForNonImageDataSource_na();
unallocatedSizeValue.setText(NA);
imageTypeValue.setText(NA);
sizeValue.setText(NA);
sectorSizeValue.setText(NA);
timeZoneValue.setText(NA);
((DefaultTableModel) filePathsTable.getModel()).addRow(new Object[]{NA});
md5HashValue.setText(NA);
sha1HashValue.setText(NA);
sha256HashValue.setText(NA);
}
/**
* Sets text fields for an image. This should be called after
* clearTableValues and before updateFieldVisibility to ensure the proper
* rendering.
*
* @param selectedImage The selected image.
* @param unallocatedFilesSize Unallocated file size in bytes.
*/
private void setFieldsForImage(Image selectedImage) {
private void setFieldsForImage(Image selectedImage, Long unallocatedFilesSize) {
unallocatedSizeValue.setText(SizeRepresentationUtil.getSizeString(unallocatedFilesSize));
imageTypeValue.setText(selectedImage.getType().getName());
sizeValue.setText(SizeRepresentationUtil.getSizeString(selectedImage.getSize()));
sectorSizeValue.setText(SizeRepresentationUtil.getSizeString(selectedImage.getSsize()));
timeZoneValue.setText(selectedImage.getTimeZone());
for (String path : selectedImage.getPaths()) {
((DefaultTableModel) filePathsTable.getModel()).addRow(new Object[]{path});
@ -233,41 +256,6 @@ class ContainerPanel extends BaseDataSourceSummaryPanel {
}
}
/**
* Update the visibility of all fields and their labels based on whether
* they have contents. Empty fields have them and their contents hidden.
*/
private void updateFieldVisibility() {
displayNameValue.setVisible(!displayNameValue.getText().isEmpty());
displayNameLabel.setVisible(!displayNameValue.getText().isEmpty());
originalNameValue.setVisible(!originalNameValue.getText().isEmpty());
originalNameLabel.setVisible(!originalNameValue.getText().isEmpty());
deviceIdValue.setVisible(!deviceIdValue.getText().isEmpty());
deviceIdLabel.setVisible(!deviceIdValue.getText().isEmpty());
timeZoneValue.setVisible(!timeZoneValue.getText().isEmpty());
timeZoneLabel.setVisible(!timeZoneValue.getText().isEmpty());
acquisitionDetailsTextArea.setVisible(!acquisitionDetailsTextArea.getText().isEmpty());
acquisitionDetailsLabel.setVisible(!acquisitionDetailsTextArea.getText().isEmpty());
acquisitionDetailsScrollPane.setVisible(!acquisitionDetailsTextArea.getText().isEmpty());
imageTypeValue.setVisible(!imageTypeValue.getText().isEmpty());
imageTypeLabel.setVisible(!imageTypeValue.getText().isEmpty());
sizeValue.setVisible(!sizeValue.getText().isEmpty());
sizeLabel.setVisible(!sizeValue.getText().isEmpty());
sectorSizeValue.setVisible(!sectorSizeValue.getText().isEmpty());
sectorSizeLabel.setVisible(!sectorSizeValue.getText().isEmpty());
md5HashValue.setVisible(!md5HashValue.getText().isEmpty());
md5HashLabel.setVisible(!md5HashValue.getText().isEmpty());
sha1HashValue.setVisible(!sha1HashValue.getText().isEmpty());
sha1HashLabel.setVisible(!sha1HashValue.getText().isEmpty());
sha256HashValue.setVisible(!sha256HashValue.getText().isEmpty());
sha256HashLabel.setVisible(!sha256HashValue.getText().isEmpty());
unallocatedSizeValue.setVisible(!unallocatedSizeValue.getText().isEmpty());
unallocatedSizeLabel.setVisible(!unallocatedSizeValue.getText().isEmpty());
filePathsTable.setVisible(filePathsTable.getRowCount() > 0);
filePathsLabel.setVisible(filePathsTable.getRowCount() > 0);
filePathsScrollPane.setVisible(filePathsTable.getRowCount() > 0);
}
/**
* Set the contents of all fields to be empty.
*/

View File

@ -38,7 +38,8 @@ import org.sleuthkit.datamodel.DataSource;
"DataSourceSummaryTabbedPane_ingestHistoryTab_title=Ingest History",
"DataSourceSummaryTabbedPane_recentFileTab_title=Recent Files",
"DataSourceSummaryTabbedPane_pastCasesTab_title=Past Cases",
"DataSourceSummaryTabbedPane_analysisTab_title=Analysis"
"DataSourceSummaryTabbedPane_analysisTab_title=Analysis",
"DataSourceSummaryTabbedPane_timelineTab_title=Timeline"
})
public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
@ -56,10 +57,10 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
/**
* Main constructor.
*
* @param tabTitle The title of the tab.
* @param component The component to be displayed.
* @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.
* @param onClose Called to cleanup resources when closing tabs.
*/
DataSourceTab(String tabTitle, Component component, Consumer<DataSource> onDataSource, Runnable onClose) {
this.tabTitle = tabTitle;
@ -72,7 +73,7 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
* Main constructor.
*
* @param tabTitle The title of the tab.
* @param panel The component to be displayed in the tab.
* @param panel The component to be displayed in the tab.
*/
DataSourceTab(String tabTitle, BaseDataSourceSummaryPanel panel) {
this.tabTitle = tabTitle;
@ -123,6 +124,7 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_analysisTab_title(), new AnalysisPanel()),
new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_recentFileTab_title(), new RecentFilesPanel()),
new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_pastCasesTab_title(), new PastCasesPanel()),
new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_timelineTab_title(), new TimelinePanel()),
// do nothing on closing
new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_ingestHistoryTab_title(), ingestHistoryPanel, ingestHistoryPanel::setDataSource, () -> {
}),

View File

@ -20,7 +20,6 @@ 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;
@ -28,7 +27,6 @@ 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;
@ -103,31 +101,8 @@ public class PastCasesPanel extends BaseDataSourceSummaryPanel {
* @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());
}
showResultWithModuleCheck(notableFileTable, DataFetchResult.getSubResult(result, (res) -> res.getTaggedNotable()), CR_FACTORY, CR_NAME);
showResultWithModuleCheck(sameIdTable, DataFetchResult.getSubResult(result, (res) -> res.getSameIdsResults()), CR_FACTORY, CR_NAME);
}
@Override

View File

@ -0,0 +1,214 @@
<?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="activityRangeLabel">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Segoe UI" size="12" style="1"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="TimelinePanel.activityRangeLabel.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="earliestLabelPanel">
<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="[100, 20]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[100, 20]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="earliestLabel"/>
<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="latestLabelPanel">
<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="[100, 20]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[100, 20]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="latestLabel"/>
<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>
<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="[600, 300]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[600, 300]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[600, 300]"/>
</Property>
<Property name="verifyInputWhenFocusTarget" type="boolean" value="false"/>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="last30DaysChart"/>
<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,263 @@
/*
* 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.Color;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import org.apache.commons.collections.CollectionUtils;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineSummary;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineSummary.DailyActivityAmount;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineSummary.TimelineSummaryData;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartPanel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartPanel.BarChartItem;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartPanel.BarChartSeries;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartPanel.OrderedKey;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult;
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.LoadableLabel;
import org.sleuthkit.datamodel.DataSource;
/**
* A tab shown in data source summary displaying information about a data
* source's timeline events.
*/
@Messages({
"TimelinePanel_earliestLabel_title=Earliest",
"TimelinePanel_latestLabel_title=Latest",
"TimlinePanel_last30DaysChart_title=Last 30 Days",
"TimlinePanel_last30DaysChart_fileEvts_title=File Events",
"TimlinePanel_last30DaysChart_artifactEvts_title=Artifact Events",})
public class TimelinePanel extends BaseDataSourceSummaryPanel {
private static final long serialVersionUID = 1L;
private static final DateFormat EARLIEST_LATEST_FORMAT = getUtcFormat("MMM d, yyyy");
private static final DateFormat CHART_FORMAT = getUtcFormat("MMM d");
private static final int MOST_RECENT_DAYS_COUNT = 30;
/**
* Creates a DateFormat formatter that uses UTC for time zone.
*
* @param formatString The date format string.
* @return The data format.
*/
private static DateFormat getUtcFormat(String formatString) {
return new SimpleDateFormat(formatString, Locale.getDefault());
}
// components displayed in the tab
private final IngestRunningLabel ingestRunningLabel = new IngestRunningLabel();
private final LoadableLabel earliestLabel = new LoadableLabel(Bundle.TimelinePanel_earliestLabel_title());
private final LoadableLabel latestLabel = new LoadableLabel(Bundle.TimelinePanel_latestLabel_title());
private final BarChartPanel last30DaysChart = new BarChartPanel(Bundle.TimlinePanel_last30DaysChart_title(), "", "");
// all loadable components on this tab
private final List<LoadableComponent<?>> loadableComponents = Arrays.asList(earliestLabel, latestLabel, last30DaysChart);
// actions to load data for this tab
private final List<DataFetchComponents<DataSource, ?>> dataFetchComponents;
public TimelinePanel() {
this(new TimelineSummary());
}
/**
* Creates new form PastCasesPanel
*/
public TimelinePanel(TimelineSummary timelineData) {
// set up data acquisition methods
dataFetchComponents = Arrays.asList(
new DataFetchWorker.DataFetchComponents<>(
(dataSource) -> timelineData.getData(dataSource, MOST_RECENT_DAYS_COUNT),
(result) -> handleResult(result))
);
initComponents();
}
/**
* Formats a date using a DateFormat. In the event that the date is null,
* returns a null string.
*
* @param date The date to format.
* @param formatter The DateFormat to use to format the date.
* @return The formatted string generated from the formatter or null if the
* date is null.
*/
private static String formatDate(Date date, DateFormat formatter) {
return date == null ? null : formatter.format(date);
}
private static final Color FILE_EVT_COLOR = new Color(228, 22, 28);
private static final Color ARTIFACT_EVT_COLOR = new Color(21, 227, 100);
/**
* Converts DailyActivityAmount data retrieved from TimelineSummary into
* data to be displayed as a bar chart.
*
* @param recentDaysActivity The data retrieved from TimelineSummary.
* @return The data to be displayed in the BarChart.
*/
private List<BarChartSeries> parseChartData(List<DailyActivityAmount> recentDaysActivity) {
// if no data, return null indicating no result.
if (CollectionUtils.isEmpty(recentDaysActivity)) {
return null;
}
// Create a bar chart item for each recent days activity item
List<BarChartItem> fileEvtCounts = new ArrayList<>();
List<BarChartItem> artifactEvtCounts = new ArrayList<>();
for (int i = 0; i < recentDaysActivity.size(); i++) {
DailyActivityAmount curItem = recentDaysActivity.get(i);
long fileAmt = curItem.getFileActivityCount();
long artifactAmt = curItem.getArtifactActivityCount() * 100;
String formattedDate = (i == 0 || i == recentDaysActivity.size() - 1)
? formatDate(curItem.getDay(), CHART_FORMAT) : "";
OrderedKey thisKey = new OrderedKey(formattedDate, i);
fileEvtCounts.add(new BarChartItem(thisKey, fileAmt));
artifactEvtCounts.add(new BarChartItem(thisKey, artifactAmt));
}
return Arrays.asList(
new BarChartSeries(Bundle.TimlinePanel_last30DaysChart_fileEvts_title(), FILE_EVT_COLOR, fileEvtCounts),
new BarChartSeries(Bundle.TimlinePanel_last30DaysChart_artifactEvts_title(), ARTIFACT_EVT_COLOR, artifactEvtCounts));
}
/**
* Handles displaying the result for each displayable item in the
* TimelinePanel by breaking the TimelineSummaryData result into its
* constituent parts and then sending each data item to the pertinent
* component.
*
* @param result The result to be displayed on this tab.
*/
private void handleResult(DataFetchResult<TimelineSummaryData> result) {
earliestLabel.showDataFetchResult(DataFetchResult.getSubResult(result, r -> formatDate(r.getMinDate(), EARLIEST_LATEST_FORMAT)));
latestLabel.showDataFetchResult(DataFetchResult.getSubResult(result, r -> formatDate(r.getMaxDate(), EARLIEST_LATEST_FORMAT)));
last30DaysChart.showDataFetchResult(DataFetchResult.getSubResult(result, r -> parseChartData(r.getMostRecentDaysActivity())));
}
@Override
protected void fetchInformation(DataSource dataSource) {
fetchInformation(dataFetchComponents, dataSource);
}
@Override
protected void onNewDataSource(DataSource dataSource) {
onNewDataSource(dataFetchComponents, loadableComponents, 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 activityRangeLabel = 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 earliestLabelPanel = earliestLabel;
javax.swing.JPanel latestLabelPanel = latestLabel;
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.JPanel sameIdPanel = last30DaysChart;
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);
activityRangeLabel.setFont(new java.awt.Font("Segoe UI", 1, 12)); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(activityRangeLabel, org.openide.util.NbBundle.getMessage(TimelinePanel.class, "TimelinePanel.activityRangeLabel.text")); // NOI18N
mainContentPanel.add(activityRangeLabel);
activityRangeLabel.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(TimelinePanel.class, "PastCasesPanel.notableFileLabel.text")); // NOI18N
filler1.setAlignmentX(0.0F);
mainContentPanel.add(filler1);
earliestLabelPanel.setAlignmentX(0.0F);
earliestLabelPanel.setMaximumSize(new java.awt.Dimension(32767, 20));
earliestLabelPanel.setMinimumSize(new java.awt.Dimension(100, 20));
earliestLabelPanel.setPreferredSize(new java.awt.Dimension(100, 20));
mainContentPanel.add(earliestLabelPanel);
latestLabelPanel.setAlignmentX(0.0F);
latestLabelPanel.setMaximumSize(new java.awt.Dimension(32767, 20));
latestLabelPanel.setMinimumSize(new java.awt.Dimension(100, 20));
latestLabelPanel.setPreferredSize(new java.awt.Dimension(100, 20));
mainContentPanel.add(latestLabelPanel);
filler2.setAlignmentX(0.0F);
mainContentPanel.add(filler2);
sameIdPanel.setAlignmentX(0.0F);
sameIdPanel.setMaximumSize(new java.awt.Dimension(600, 300));
sameIdPanel.setMinimumSize(new java.awt.Dimension(600, 300));
sameIdPanel.setPreferredSize(new java.awt.Dimension(600, 300));
sameIdPanel.setVerifyInputWhenFocusTarget(false);
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

@ -18,7 +18,6 @@
*/
package org.sleuthkit.autopsy.datasourcesummary.ui;
import java.awt.BorderLayout;
import java.awt.Color;
import java.sql.SQLException;
import java.text.DecimalFormat;
@ -30,7 +29,6 @@ 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;
@ -40,13 +38,13 @@ 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.LoadableLabel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.PieChartPanel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.PieChartPanel.PieChartItem;
import org.sleuthkit.autopsy.modules.filetypeid.FileTypeIdModuleFactory;
@ -78,46 +76,6 @@ import org.sleuthkit.datamodel.TskCoreException;
"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.
*/
@ -129,9 +87,9 @@ class TypesPanel extends BaseDataSourceSummaryPanel {
/**
* Main constructor.
*
* @param pieSlices The pie slices.
* @param pieSlices The pie slices.
* @param usefulContent True if this is useful content; false if there
* is 0 mime type information.
* is 0 mime type information.
*/
public TypesPieChartData(List<PieChartItem> pieSlices, boolean usefulContent) {
this.pieSlices = pieSlices;
@ -165,9 +123,9 @@ class TypesPanel extends BaseDataSourceSummaryPanel {
/**
* Main constructor.
*
* @param label The label for this slice.
* @param label The label for this slice.
* @param mimeTypes The mime types associated with this slice.
* @param color The color associated with this slice.
* @param color The color associated with this slice.
*/
TypesPieCategory(String label, Set<String> mimeTypes, Color color) {
this.label = label;
@ -178,9 +136,9 @@ class TypesPanel extends BaseDataSourceSummaryPanel {
/**
* Constructor that accepts FileTypeCategory.
*
* @param label The label for this slice.
* @param label The label for this slice.
* @param mimeTypes The mime types associated with this slice.
* @param color The color associated with this slice.
* @param color The color associated with this slice.
*/
TypesPieCategory(String label, FileTypeCategory fileCategory, Color color) {
this(label, fileCategory.getMediaTypes(), color);
@ -278,8 +236,8 @@ class TypesPanel extends BaseDataSourceSummaryPanel {
/**
* Creates a new TypesPanel.
*
* @param mimeTypeData The service for mime types.
* @param typeData The service for file types data.
* @param mimeTypeData The service for mime types.
* @param typeData The service for file types data.
* @param containerData The service for container information.
*/
public TypesPanel(
@ -358,7 +316,7 @@ class TypesPanel extends BaseDataSourceSummaryPanel {
* Gets all the data for the file type pie chart.
*
* @param mimeTypeData The means of acquiring data.
* @param dataSource The datasource.
* @param dataSource The datasource.
*
* @return The pie chart items.
*/

View File

@ -29,12 +29,11 @@ import org.apache.commons.lang.StringUtils;
import org.openide.util.NbBundle.Messages;
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.datamodel.UserActivitySummary.TopProgramsResult;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.DefaultCellModel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
@ -227,35 +226,30 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel {
private final IngestRunningLabel ingestRunningLabel = new IngestRunningLabel();
private final List<DataFetchComponents<DataSource, ?>> dataFetchComponents;
private final TopProgramsSummary topProgramsData;
private final UserActivitySummary userActivityData;
/**
* Creates a new UserActivityPanel.
*/
public UserActivityPanel() {
this(new TopProgramsSummary(), new UserActivitySummary());
this(new UserActivitySummary());
}
/**
* 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 UserActivityPanel(
TopProgramsSummary topProgramsData,
UserActivitySummary userActivityData) {
super(topProgramsData, userActivityData);
this.topProgramsData = topProgramsData;
public UserActivityPanel(UserActivitySummary userActivityData) {
super(userActivityData);
this.userActivityData = userActivityData;
// set up data acquisition methods
this.dataFetchComponents = Arrays.asList(
// top programs query
new DataFetchComponents<DataSource, List<TopProgramsResult>>(
(dataSource) -> topProgramsData.getTopPrograms(dataSource, TOP_PROGS_COUNT),
(dataSource) -> userActivityData.getTopPrograms(dataSource, TOP_PROGS_COUNT),
(result) -> {
showResultWithModuleCheck(topProgramsTable, result,
IngestModuleCheckUtil.RECENT_ACTIVITY_FACTORY,
@ -307,7 +301,7 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel {
* @return The underlying short folder name if one exists.
*/
private String getShortFolderName(String path, String appName) {
return this.topProgramsData.getShortFolderName(path, appName);
return this.userActivityData.getShortFolderName(path, appName);
}
@Override

View File

@ -0,0 +1,307 @@
/*
* 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.awt.Color;
import java.awt.Font;
import java.util.Collections;
import java.util.List;
import javax.swing.JLabel;
import org.apache.commons.collections4.CollectionUtils;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.renderer.category.BarRenderer;
import org.jfree.chart.renderer.category.StandardBarPainter;
import org.jfree.data.category.DefaultCategoryDataset;
/**
* A bar chart panel.
*/
public class BarChartPanel extends AbstractLoadableComponent<List<BarChartPanel.BarChartSeries>> {
/**
* Represents a series in a bar chart where all items pertain to one
* category.
*/
public static class BarChartSeries {
private final Comparable<?> key;
private final Color color;
private final List<BarChartItem> items;
/**
* Main constructor.
*
* @param color The color for this series.
* @param items The bars to be displayed for this series.
*/
public BarChartSeries(Comparable<?> key, Color color, List<BarChartItem> items) {
this.key = key;
this.color = color;
this.items = (items == null) ? Collections.emptyList() : Collections.unmodifiableList(items);
}
/**
* @return The color for this series.
*/
public Color getColor() {
return color;
}
/**
* @return The bars to be displayed for this series.
*/
public List<BarChartItem> getItems() {
return items;
}
/**
* @return The key for this item.
*/
public Comparable<?> getKey() {
return key;
}
}
/**
* An individual bar to be displayed in the bar chart.
*/
public static class BarChartItem {
private final Comparable<?> key;
private final double value;
/**
* Main constructor.
*
* @param label The key for this bar. Also serves as the label using
* toString().
* @param value The value for this item.
*/
public BarChartItem(Comparable<?> key, double value) {
this.key = key;
this.value = value;
}
/**
* @return The key for this item.
*/
public Comparable<?> getKey() {
return key;
}
/**
* @return The value for this item.
*/
public double getValue() {
return value;
}
}
/**
* JFreeChart bar charts don't preserve the order of bars provided to the
* chart, but instead uses the comparable nature to order items. This
* provides order using a provided index as well as the value for the axis.
*/
public static class OrderedKey implements Comparable<OrderedKey> {
private final Object keyValue;
private final int keyIndex;
/**
* Main constructor.
*
* @param keyValue The value for the key to be displayed in the domain
* axis.
* @param keyIndex The index at which it will be displayed.
*/
public OrderedKey(Object keyValue, int keyIndex) {
this.keyValue = keyValue;
this.keyIndex = keyIndex;
}
/**
* @return The value for the key to be displayed in the domain axis.
*/
Object getKeyValue() {
return keyValue;
}
/**
* @return The index at which it will be displayed.
*/
int getKeyIndex() {
return keyIndex;
}
@Override
public int compareTo(OrderedKey o) {
// this will have a higher value than null.
if (o == null) {
return 1;
}
// compare by index
return Integer.compare(this.getKeyIndex(), o.getKeyIndex());
}
@Override
public int hashCode() {
int hash = 3;
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final OrderedKey other = (OrderedKey) obj;
if (this.keyIndex != other.keyIndex) {
return false;
}
return true;
}
@Override
public String toString() {
// use toString on the key.
return this.getKeyValue() == null ? null : this.getKeyValue().toString();
}
}
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 ChartMessageOverlay overlay = new ChartMessageOverlay();
private final DefaultCategoryDataset dataset = new DefaultCategoryDataset();
private final JFreeChart chart;
private final CategoryPlot plot;
/**
* Main constructor assuming null values for all items.
*/
public BarChartPanel() {
this(null, null, null);
}
/**
* Main constructor for the pie chart.
*
* @param title The title for this pie chart.
* @param categoryLabel The x-axis label.
* @param valueLabel The y-axis label.
*/
public BarChartPanel(String title, String categoryLabel, String valueLabel) {
this.chart = ChartFactory.createStackedBarChart(
title,
categoryLabel,
valueLabel,
dataset,
PlotOrientation.VERTICAL,
true, false, false);
// set style to match autopsy components
chart.setBackgroundPaint(null);
chart.getTitle().setFont(DEFAULT_HEADER_FONT);
this.plot = ((CategoryPlot) chart.getPlot());
this.plot.getRenderer().setBaseItemLabelFont(DEFAULT_FONT);
plot.setBackgroundPaint(null);
plot.setOutlinePaint(null);
// hide y axis labels
ValueAxis range = plot.getRangeAxis();
range.setVisible(false);
// make sure x axis labels don't get cut off
plot.getDomainAxis().setMaximumCategoryLabelWidthRatio(10);
((BarRenderer) plot.getRenderer()).setBarPainter(new StandardBarPainter());
// 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 BarChartPanel 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<BarChartPanel.BarChartSeries> data) {
this.dataset.clear();
if (CollectionUtils.isNotEmpty(data)) {
for (int s = 0; s < data.size(); s++) {
BarChartPanel.BarChartSeries series = data.get(s);
if (series != null && CollectionUtils.isNotEmpty(series.getItems())) {
if (series.getColor() != null) {
this.plot.getRenderer().setSeriesPaint(s, series.getColor());
}
for (int i = 0; i < series.getItems().size(); i++) {
BarChartItem bar = series.getItems().get(i);
this.dataset.setValue(bar.getValue(), series.getKey(), bar.getKey());
}
}
}
}
}
}

View File

@ -0,0 +1,63 @@
/*
* 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.Graphics2D;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.panel.AbstractOverlay;
import org.jfree.chart.panel.Overlay;
/**
* A JFreeChart message overlay that can show a message for the purposes of the
* LoadableComponent.
*/
class ChartMessageOverlay 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);
}
}

View File

@ -18,6 +18,8 @@
*/
package org.sleuthkit.autopsy.datasourcesummary.uiutils;
import java.util.function.Function;
/**
* The result of a loading process.
*/
@ -30,6 +32,29 @@ public final class DataFetchResult<R> {
SUCCESS, ERROR
}
/**
* A utility method that, 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.
*/
public static <I, O> DataFetchResult<O> getSubResult(DataFetchResult<I> inputResult, Function<I, 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());
}
}
/**
* Creates a DataFetchResult of loaded data including the data.
*
@ -59,9 +84,8 @@ public final class DataFetchResult<R> {
/**
* Main constructor for the DataLoadingResult.
*
* @param state The state of the result.
* @param data If the result is SUCCESS, the data related to this
* result.
* @param state The state of the result.
* @param data If the result is SUCCESS, the data related to this result.
* @param exception If the result is ERROR, the related exception.
*/
private DataFetchResult(ResultType state, R data, Throwable exception) {

View File

@ -0,0 +1,63 @@
/*
* 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 javax.swing.JLabel;
import org.apache.commons.lang3.StringUtils;
/**
* A label that allows for displaying loading messages and can be used with a
* DataFetchResult. Text displays as "<key>:<value | message>".
*/
public 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.
*/
public 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);
}
}

View File

@ -21,7 +21,6 @@ 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;
@ -30,8 +29,6 @@ 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;
@ -59,7 +56,7 @@ public class PieChartPanel extends AbstractLoadableComponent<List<PieChartPanel.
* @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.
* auto-determined.
*/
public PieChartItem(String label, double value, Color color) {
this.label = label;
@ -89,46 +86,6 @@ public class PieChartPanel extends AbstractLoadableComponent<List<PieChartPanel.
}
}
/**
* 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();
@ -146,7 +103,7 @@ public class PieChartPanel extends AbstractLoadableComponent<List<PieChartPanel.
= new StandardPieSectionLabelGenerator(
"{0}: {1} ({2})", new DecimalFormat("#,###"), new DecimalFormat("0.0%"));
private final MessageOverlay overlay = new MessageOverlay();
private final ChartMessageOverlay overlay = new ChartMessageOverlay();
private final DefaultPieDataset dataset = new DefaultPieDataset();
private final JFreeChart chart;
private final PiePlot plot;
@ -242,7 +199,7 @@ public class PieChartPanel extends AbstractLoadableComponent<List<PieChartPanel.
/**
* Shows a message on top of data.
*
* @param data The data.
* @param data The data.
* @param message The message.
*/
public synchronized void showDataWithMessage(List<PieChartPanel.PieChartItem> data, String message) {

View File

@ -19,12 +19,14 @@
package org.sleuthkit.autopsy.discovery.search;
import com.google.common.eventbus.EventBus;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.sleuthkit.autopsy.discovery.search.DiscoveryKeyUtils.GroupKey;
import org.sleuthkit.autopsy.discovery.search.SearchData.Type;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact;
/**
* Class to handle event bus and events for discovery tool.
@ -88,13 +90,13 @@ public final class DiscoveryEventUtils {
//no arg constructor
}
}
/**
* Event to signal that any background tasks currently running should
* be cancelled.
* Event to signal that any background tasks currently running should be
* cancelled.
*/
public static final class CancelBackgroundTasksEvent {
public CancelBackgroundTasksEvent() {
//no-arg constructor
}
@ -124,6 +126,30 @@ public final class DiscoveryEventUtils {
}
}
/**
* Event to signal that the list should be populated.
*/
public static final class PopulateDomainTabsEvent {
private final String domain;
/**
* Construct a new PopulateDomainTabsEvent.
*/
public PopulateDomainTabsEvent(String domain) {
this.domain = domain;
}
/**
* Get the domain for the details area.
*
* @return The the domain for the details area.
*/
public String getDomain() {
return domain;
}
}
/**
* Event to signal the completion of a search being performed.
*/
@ -203,6 +229,47 @@ public final class DiscoveryEventUtils {
}
/**
* Event to signal the completion of a search being performed.
*/
public static final class ArtifactSearchResultEvent {
private final List<BlackboardArtifact> listOfArtifacts = new ArrayList<>();
private final BlackboardArtifact.ARTIFACT_TYPE artifactType;
/**
* Construct a new ArtifactSearchResultEvent with a list of specified
* artifacts and an artifact type.
*
* @param artifactType The type of artifacts in the list.
* @param listOfArtifacts The list of artifacts retrieved.
*/
public ArtifactSearchResultEvent(BlackboardArtifact.ARTIFACT_TYPE artifactType, List<BlackboardArtifact> listOfArtifacts) {
if (listOfArtifacts != null) {
this.listOfArtifacts.addAll(listOfArtifacts);
}
this.artifactType = artifactType;
}
/**
* Get the list of artifacts included in the event.
*
* @return The list of artifacts retrieved.
*/
public List<BlackboardArtifact> getListOfArtifacts() {
return Collections.unmodifiableList(listOfArtifacts);
}
/**
* Get the type of BlackboardArtifact type of which exist in the list.
*
* @return The BlackboardArtifact type of which exist in the list.
*/
public BlackboardArtifact.ARTIFACT_TYPE getArtifactType() {
return artifactType;
}
}
/**
* Event to signal the completion of page retrieval and include the page
* contents.

View File

@ -33,6 +33,7 @@ import org.sleuthkit.datamodel.TagName;
import org.sleuthkit.datamodel.TskCoreException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Locale;
@ -217,6 +218,15 @@ public class SearchFiltering {
this.types = types;
}
/**
* Get the list of artifact types specified by the filter.
*
* @return The list of artifact types specified by the filter.
*/
public List<ARTIFACT_TYPE> getTypes() {
return Collections.unmodifiableList(types);
}
@Override
public String getWhereClause() {
StringJoiner joiner = new StringJoiner(",");

View File

@ -0,0 +1,41 @@
/*
* 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.ui;
import javax.swing.JPanel;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.datamodel.BlackboardArtifact;
/**
* Class for ensuring all ArtifactDetailsPanels have a setArtifact method.
*
*/
public abstract class AbstractArtifactDetailsPanel extends JPanel {
private static final long serialVersionUID = 1L;
/**
* Called to display the contents of the given artifact.
*
* @param artifact the artifact to display.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
abstract public void setArtifact(BlackboardArtifact artifact);
}

View File

@ -24,6 +24,7 @@ import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.event.ListSelectionListener;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
/**
* Abstract class extending JPanel for filter controls.
@ -41,6 +42,7 @@ abstract class AbstractDiscoveryFilterPanel extends javax.swing.JPanel {
* selected, null to indicate leaving selected items
* unchanged or that there are no items to select.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
abstract void configurePanel(boolean selected, int[] indicesSelected);
/**
@ -48,6 +50,7 @@ abstract class AbstractDiscoveryFilterPanel extends javax.swing.JPanel {
*
* @return The JCheckBox which enables and disables this filter.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
abstract JCheckBox getCheckbox();
/**
@ -57,6 +60,7 @@ abstract class AbstractDiscoveryFilterPanel extends javax.swing.JPanel {
* @return The JList which contains the values available for selection for
* this filter.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
abstract JList<?> getList();
/**
@ -65,6 +69,7 @@ abstract class AbstractDiscoveryFilterPanel extends javax.swing.JPanel {
*
* @return The JLabel to display under the JCheckBox.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
abstract JLabel getAdditionalLabel();
/**
@ -73,6 +78,7 @@ abstract class AbstractDiscoveryFilterPanel extends javax.swing.JPanel {
* @return If the settings are invalid returns the error that has occurred,
* otherwise returns empty string.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
abstract String checkForError();
/**
@ -82,6 +88,7 @@ abstract class AbstractDiscoveryFilterPanel extends javax.swing.JPanel {
* @param actionlistener The listener for the checkbox selection events.
* @param listListener The listener for the list selection events.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void addListeners(ActionListener actionListener, ListSelectionListener listListener) {
if (getCheckbox() != null) {
getCheckbox().addActionListener(actionListener);
@ -97,11 +104,13 @@ abstract class AbstractDiscoveryFilterPanel extends javax.swing.JPanel {
* @return The AbstractFilter for the selected settings, null if the
* settings are not in use.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
abstract AbstractFilter getFilter();
/**
* Remove listeners from the checkbox and the list if they exist.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void removeListeners() {
if (getCheckbox() != null) {
for (ActionListener listener : getCheckbox().getActionListeners()) {

View File

@ -32,6 +32,7 @@ import javax.swing.JSplitPane;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import org.apache.commons.lang3.StringUtils;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.DiscoveryAttributes.GroupingAttributeType;
import org.sleuthkit.autopsy.discovery.search.Group;
import org.sleuthkit.autopsy.discovery.search.ResultsSorter.SortingMethod;
@ -65,6 +66,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
/**
* Setup necessary for implementations of this abstract class.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
AbstractFiltersPanel() {
firstColumnPanel.setLayout(new GridBagLayout());
secondColumnPanel.setLayout(new GridBagLayout());
@ -75,6 +77,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
*
* @return The type of results this panel filters.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
abstract SearchData.Type getType();
/**
@ -88,7 +91,8 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
* list, null if none are selected.
* @param column The column to add the DiscoveryFilterPanel to.
*/
final synchronized void addFilter(AbstractDiscoveryFilterPanel filterPanel, boolean isSelected, int[] indicesSelected, int column) {
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
final void addFilter(AbstractDiscoveryFilterPanel filterPanel, boolean isSelected, int[] indicesSelected, int column) {
if (!isInitialized) {
constraints.gridy = 0;
constraints.anchor = GridBagConstraints.FIRST_LINE_START;
@ -132,6 +136,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
*
* @param splitPane The JSplitPane which the columns are added to.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
final void addPanelsToScrollPane(JSplitPane splitPane) {
splitPane.setLeftComponent(firstColumnPanel);
splitPane.setRightComponent(secondColumnPanel);
@ -142,7 +147,8 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
/**
* Clear the filters from the panel
*/
final synchronized void clearFilters() {
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
final void clearFilters() {
for (AbstractDiscoveryFilterPanel filterPanel : filters) {
filterPanel.removeListeners();
}
@ -159,6 +165,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
* column.
* @param columnIndex The column to add the Component to.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void addToGridBagLayout(Component componentToAdd, Component additionalComponentToAdd, int columnIndex) {
addToColumn(componentToAdd, columnIndex);
if (additionalComponentToAdd != null) {
@ -174,6 +181,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
* @param component The Component to add.
* @param columnNumber The column to add the Component to.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void addToColumn(Component component, int columnNumber) {
if (columnNumber == 0) {
firstColumnPanel.add(component, constraints);
@ -186,7 +194,8 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
* Check if the fields are valid, and fire a property change event to
* indicate any errors.
*/
synchronized void validateFields() {
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void validateFields() {
String errorString = null;
for (AbstractDiscoveryFilterPanel filterPanel : filters) {
errorString = filterPanel.checkForError();
@ -197,6 +206,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
firePropertyChange("FilterError", null, errorString);
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
public void actionPerformed(ActionEvent e) {
validateFields();
@ -209,6 +219,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
*
* @return True if the ObjectsDetectedFilter is supported, false otherwise.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
boolean isObjectsFilterSupported() {
for (AbstractDiscoveryFilterPanel filter : filters) {
if (filter instanceof ObjectDetectedFilterPanel) {
@ -223,6 +234,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
*
* @return True if the HashSetFilter is supported, false otherwise.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
boolean isHashSetFilterSupported() {
for (AbstractDiscoveryFilterPanel filter : filters) {
if (filter instanceof HashSetFilterPanel) {
@ -237,6 +249,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
*
* @return True if the InterestingItemsFilter is supported, false otherwise.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
boolean isInterestingItemsFilterSupported() {
for (AbstractDiscoveryFilterPanel filter : filters) {
if (filter instanceof InterestingItemsFilterPanel) {
@ -251,8 +264,8 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
*
* @return The list of filters selected by the user.
*/
synchronized List<AbstractFilter> getFilters() {
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
List<AbstractFilter> getFilters() {
List<AbstractFilter> filtersToUse = new ArrayList<>();
if (getType() != SearchData.Type.DOMAIN) { //Domain type does not have a file type
filtersToUse.add(new SearchFiltering.FileTypeFilter(getType()));
@ -268,6 +281,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
return filtersToUse;
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
public void valueChanged(ListSelectionEvent evt) {
if (!evt.getValueIsAdjusting()) {
@ -282,6 +296,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
*
* @return The most recently used sorting method.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
SortingMethod getLastSortingMethod() {
return lastSortingMethod;
}
@ -291,6 +306,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
*
* @param lastSortingMethod The most recently used sorting method.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
final void setLastSortingMethod(SortingMethod lastSortingMethod) {
this.lastSortingMethod = lastSortingMethod;
}
@ -300,6 +316,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
*
* @return The most recently used grouping attribute.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
GroupingAttributeType getLastGroupingAttributeType() {
return lastGroupingAttributeType;
}
@ -310,6 +327,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
* @param lastGroupingAttributeType The most recently used grouping
* attribute.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
final void setLastGroupingAttributeType(GroupingAttributeType lastGroupingAttributeType) {
this.lastGroupingAttributeType = lastGroupingAttributeType;
}
@ -319,6 +337,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
*
* @return The most recently used group sorting algorithm.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
Group.GroupSortingAlgorithm getLastGroupSortingAlg() {
return lastGroupSortingAlg;
}
@ -329,6 +348,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
* @param lastGroupSortingAlg The most recently used group sorting
* algorithm.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
final void setLastGroupSortingAlg(Group.GroupSortingAlgorithm lastGroupSortingAlg) {
this.lastGroupSortingAlg = lastGroupSortingAlg;
}

View File

@ -26,6 +26,7 @@ import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JList;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.SearchData;
import org.sleuthkit.autopsy.discovery.search.SearchFiltering.ArtifactTypeFilter;
import org.sleuthkit.datamodel.BlackboardArtifact;
@ -40,6 +41,7 @@ class ArtifactTypeFilterPanel extends AbstractDiscoveryFilterPanel {
/**
* Creates new form ArtifactTypeFilterPanel
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
ArtifactTypeFilterPanel() {
initComponents();
setUpArtifactTypeFilter();
@ -49,6 +51,7 @@ class ArtifactTypeFilterPanel extends AbstractDiscoveryFilterPanel {
/**
* Initialize the data source filter.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void setUpArtifactTypeFilter() {
int count = 0;
DefaultListModel<ArtifactTypeItem> artifactTypeModel = (DefaultListModel<ArtifactTypeItem>) artifactList.getModel();
@ -104,6 +107,7 @@ class ArtifactTypeFilterPanel extends AbstractDiscoveryFilterPanel {
artifactList.setEnabled(artifactTypeCheckbox.isSelected());
}//GEN-LAST:event_artifactTypeCheckboxActionPerformed
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
void configurePanel(boolean selected, int[] indicesSelected) {
artifactTypeCheckbox.setSelected(selected);
@ -119,11 +123,13 @@ class ArtifactTypeFilterPanel extends AbstractDiscoveryFilterPanel {
}
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
JCheckBox getCheckbox() {
return artifactTypeCheckbox;
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
JList<?> getList() {
return artifactList;
@ -134,6 +140,7 @@ class ArtifactTypeFilterPanel extends AbstractDiscoveryFilterPanel {
return null;
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@NbBundle.Messages({"ArtifactTypeFilterPanel.selectionNeeded.text=At least one Result type must be selected."})
@Override
String checkForError() {
@ -143,6 +150,7 @@ class ArtifactTypeFilterPanel extends AbstractDiscoveryFilterPanel {
return "";
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
AbstractFilter getFilter() {
if (artifactTypeCheckbox.isSelected() && !artifactList.getSelectedValuesList().isEmpty()) {

View File

@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.6" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<Properties>
<Property name="opaque" type="boolean" value="false"/>
</Properties>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="jScrollPane1" pref="400" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="jScrollPane1" alignment="0" pref="607" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
</Layout>
<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>
<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="jTable1">
<Properties>
<Property name="autoCreateRowSorter" type="boolean" value="true"/>
<Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="tableModel" type="code"/>
</Property>
<Property name="selectionModel" type="javax.swing.ListSelectionModel" editor="org.netbeans.modules.form.editors2.JTableSelectionModelEditor">
<JTableSelectionModel selectionMode="0"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Form>

View File

@ -0,0 +1,346 @@
/*
* 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.ui;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import javax.swing.JPanel;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.AbstractTableModel;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.StringUtils;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Panel to display list of artifacts for selected domain.
*
*/
class ArtifactsListPanel extends JPanel {
private static final long serialVersionUID = 1L;
private final DomainArtifactTableModel tableModel;
private static final Logger logger = Logger.getLogger(ArtifactsListPanel.class.getName());
/**
* Creates new form ArtifactsListPanel.
*
* @param artifactType The type of artifact displayed in this table.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
ArtifactsListPanel(BlackboardArtifact.ARTIFACT_TYPE artifactType) {
tableModel = new DomainArtifactTableModel(artifactType);
initComponents();
jTable1.getRowSorter().toggleSortOrder(0);
jTable1.getRowSorter().toggleSortOrder(0);
}
/**
* Add a listener to the table of artifacts to perform actions when an
* artifact is selected.
*
* @param listener The listener to add to the table of artifacts.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void addSelectionListener(ListSelectionListener listener) {
jTable1.getSelectionModel().addListSelectionListener(listener);
}
/**
* Remove a listener from the table of artifacts.
*
* @param listener The listener to remove from the table of artifacts.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void removeListSelectionListener(ListSelectionListener listener) {
jTable1.getSelectionModel().removeListSelectionListener(listener);
}
/**
* The artifact which is currently selected, null if no artifact is
* selected.
*
* @return The currently selected BlackboardArtifact or null if none is
* selected.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
BlackboardArtifact getSelectedArtifact() {
int selectedIndex = jTable1.getSelectionModel().getLeadSelectionIndex();
if (selectedIndex < jTable1.getSelectionModel().getMinSelectionIndex() || jTable1.getSelectionModel().getMaxSelectionIndex() < 0 || selectedIndex > jTable1.getSelectionModel().getMaxSelectionIndex()) {
return null;
}
return tableModel.getArtifactByRow(jTable1.convertRowIndexToModel(selectedIndex));
}
/**
* Whether the list of artifacts is empty.
*
* @return true if the list of artifacts is empty, false if there are
* artifacts.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
boolean isEmpty() {
return tableModel.getRowCount() <= 0;
}
/**
* Select the first available artifact in the list if it is not empty to
* populate the panel to the right.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void selectFirst() {
if (!isEmpty()) {
jTable1.setRowSelectionInterval(0, 0);
} else {
jTable1.clearSelection();
}
}
/**
* Add the specified list of artifacts to the list of artifacts which should
* be displayed.
*
* @param artifactList The list of artifacts to display.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void addArtifacts(List<BlackboardArtifact> artifactList) {
tableModel.setContents(artifactList);
jTable1.validate();
jTable1.repaint();
tableModel.fireTableDataChanged();
}
/**
* Remove all artifacts from the list of artifacts displayed.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void clearArtifacts() {
tableModel.setContents(new ArrayList<>());
tableModel.fireTableDataChanged();
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
javax.swing.JScrollPane jScrollPane1 = new javax.swing.JScrollPane();
jTable1 = new javax.swing.JTable();
setOpaque(false);
jScrollPane1.setBorder(null);
jTable1.setAutoCreateRowSorter(true);
jTable1.setModel(tableModel);
jTable1.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
jScrollPane1.setViewportView(jTable1);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 607, Short.MAX_VALUE)
);
}// </editor-fold>//GEN-END:initComponents
/**
* Table model which allows the artifact table in this panel to mimic a list
* of artifacts.
*/
private class DomainArtifactTableModel extends AbstractTableModel {
private static final long serialVersionUID = 1L;
private final List<BlackboardArtifact> artifactList = new ArrayList<>();
private final BlackboardArtifact.ARTIFACT_TYPE artifactType;
/**
* Construct a new DomainArtifactTableModel.
*
* @param artifactType The type of artifact displayed in this table.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
DomainArtifactTableModel(BlackboardArtifact.ARTIFACT_TYPE artifactType) {
this.artifactType = artifactType;
}
/**
* Set the list of artifacts which should be represented by this table
* model.
*
* @param artifacts The list of BlackboardArtifacts to represent.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void setContents(List<BlackboardArtifact> artifacts) {
jTable1.clearSelection();
artifactList.clear();
artifactList.addAll(artifacts);
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
public int getRowCount() {
return artifactList.size();
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
public int getColumnCount() {
if (artifactType == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CACHE) {
return 3;
} else {
return 2;
}
}
/**
* Get the BlackboardArtifact at the specified row.
*
* @param rowIndex The row the artifact to return is at.
*
* @return The BlackboardArtifact at the specified row.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
BlackboardArtifact getArtifactByRow(int rowIndex) {
return artifactList.get(rowIndex);
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@NbBundle.Messages({"ArtifactsListPanel.value.noValue=No value available."})
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
if (columnIndex < 2 || artifactType == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CACHE) {
try {
for (BlackboardAttribute bba : getArtifactByRow(rowIndex).getAttributes()) {
if (!StringUtils.isBlank(bba.getDisplayString())) {
String stringFromAttribute = getStringForColumn(bba, columnIndex);
if (!StringUtils.isBlank(stringFromAttribute)) {
return stringFromAttribute;
}
}
}
return getFallbackValue(rowIndex, columnIndex);
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Error getting attributes for artifact " + getArtifactByRow(rowIndex).getArtifactID(), ex);
}
}
return Bundle.ArtifactsListPanel_value_noValue();
}
/**
* Get the appropriate String for the specified column from the
* BlackboardAttribute.
*
* @param bba The BlackboardAttribute which may contain a value.
* @param columnIndex The column the value will be displayed in.
*
* @return The value from the specified attribute which should be
* displayed in the specified column, null if the specified
* attribute does not contain a value for that column.
*
* @throws TskCoreException When unable to get abstract files based on
* the TSK_PATH_ID.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private String getStringForColumn(BlackboardAttribute bba, int columnIndex) throws TskCoreException {
if (columnIndex == 0 && bba.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED.getTypeID()) {
return bba.getDisplayString();
} else if (columnIndex == 1) {
if (artifactType == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD || artifactType == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CACHE) {
if (bba.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID.getTypeID()) {
return Case.getCurrentCase().getSleuthkitCase().getAbstractFileById(bba.getValueLong()).getName();
} else if (bba.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH.getTypeID()) {
return FilenameUtils.getName(bba.getDisplayString());
}
} else if (bba.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TITLE.getTypeID()) {
return bba.getDisplayString();
}
} else if (columnIndex == 2 && bba.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID.getTypeID()) {
return Case.getCurrentCase().getSleuthkitCase().getAbstractFileById(bba.getValueLong()).getMIMEType();
}
return null;
}
/**
* Private helper method to use when the value we want for either date
* or title is not available.
*
*
* @param rowIndex The row the artifact to return is at.
* @param columnIndex The column index the attribute will be displayed
* at.
*
* @return A string that can be used in place of the accessed date time
* attribute title when they are not available.
*
* @throws TskCoreException
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private String getFallbackValue(int rowIndex, int columnIndex) throws TskCoreException {
for (BlackboardAttribute bba : getArtifactByRow(rowIndex).getAttributes()) {
if (columnIndex == 0 && bba.getAttributeType().getTypeName().startsWith("TSK_DATETIME") && !StringUtils.isBlank(bba.getDisplayString())) {
return bba.getDisplayString();
} else if (columnIndex == 1 && bba.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_URL.getTypeID() && !StringUtils.isBlank(bba.getDisplayString())) {
return bba.getDisplayString();
}
}
return Bundle.ArtifactsListPanel_value_noValue();
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@NbBundle.Messages({"ArtifactsListPanel.titleColumn.name=Title",
"ArtifactsListPanel.fileNameColumn.name=Name",
"ArtifactsListPanel.dateColumn.name=Date/Time",
"ArtifactsListPanel.mimeTypeColumn.name=MIME Type"})
@Override
public String getColumnName(int column) {
switch (column) {
case 0:
return Bundle.ArtifactsListPanel_dateColumn_name();
case 1:
if (artifactType == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CACHE || artifactType == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD) {
return Bundle.ArtifactsListPanel_fileNameColumn_name();
} else {
return Bundle.ArtifactsListPanel_titleColumn_name();
}
case 2:
return Bundle.ArtifactsListPanel_mimeTypeColumn_name();
default:
return "";
}
}
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JTable jTable1;
// End of variables declaration//GEN-END:variables
}

View File

@ -0,0 +1,80 @@
/*
* 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.ui;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import javax.swing.SwingWorker;
import org.apache.commons.lang3.StringUtils;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.discovery.search.DiscoveryEventUtils;
import org.sleuthkit.autopsy.discovery.search.DomainSearch;
import org.sleuthkit.autopsy.discovery.search.DomainSearchArtifactsRequest;
import org.sleuthkit.datamodel.BlackboardArtifact;
/**
* SwingWorker to retrieve a list of artifacts for a specified type and domain.
*/
class ArtifactsWorker extends SwingWorker<List<BlackboardArtifact>, Void> {
private final BlackboardArtifact.ARTIFACT_TYPE artifactType;
private final static Logger logger = Logger.getLogger(ArtifactsWorker.class.getName());
private final String domain;
/**
* Construct a new ArtifactsWorker.
*
* @param artifactType The type of artifact being retrieved.
* @param domain The domain the artifacts should have as an attribute.
*/
ArtifactsWorker(BlackboardArtifact.ARTIFACT_TYPE artifactType, String domain) {
this.artifactType = artifactType;
this.domain = domain;
}
@Override
protected List<BlackboardArtifact> doInBackground() throws Exception {
if (artifactType != null && !StringUtils.isBlank(domain)) {
DomainSearch domainSearch = new DomainSearch();
return domainSearch.getArtifacts(new DomainSearchArtifactsRequest(Case.getCurrentCase().getSleuthkitCase(), domain, artifactType));
}
return new ArrayList<>();
}
@Override
protected void done() {
List<BlackboardArtifact> listOfArtifacts = new ArrayList<>();
if (!isCancelled()) {
try {
listOfArtifacts.addAll(get());
} catch (InterruptedException | ExecutionException ex) {
logger.log(Level.SEVERE, "Exception while trying to get list of artifacts for Domain details for artifact type: "
+ artifactType.getDisplayName() + " and domain: " + domain, ex);
} catch (CancellationException ignored) {
//Worker was cancelled after previously finishing its background work, exception ignored to cut down on non-helpful logging
}
}
DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.ArtifactSearchResultEvent(artifactType, listOfArtifacts));
}
}

View File

@ -51,10 +51,12 @@ HashSetFilterPanel.hashSetCheckbox.text=Hash Set:
PastOccurrencesFilterPanel.pastOccurrencesCheckbox.text=Past Occurrences:
DocumentFilterPanel.documentsFiltersSplitPane.border.title=Step 2: Filter which documents to show
ObjectDetectedFilterPanel.text=Object Detected:
DetailsPanel.instancesList.border.title=Instances
DateFilterPanel.mostRecentRadioButton.text=Only last:
DateFilterPanel.dateFilterCheckBox.text=Date Filter:
DomainSummaryPanel.activityLabel.text=
DomainSummaryPanel.pagesLabel.text=
DomainSummaryPanel.filesDownloadedLabel.text=
DomainSummaryPanel.totalVisitsLabel.text=
FileDetailsPanel.instancesList.border.title=Instances
CookieDetailsPanel.jLabel1.text=Artifact:
CookieDetailsPanel.jLabel2.text=

View File

@ -1,3 +1,8 @@
ArtifactsListPanel.dateColumn.name=Date/Time
ArtifactsListPanel.fileNameColumn.name=Name
ArtifactsListPanel.mimeTypeColumn.name=MIME Type
ArtifactsListPanel.titleColumn.name=Title
ArtifactsListPanel.value.noValue=No value available.
ArtifactTypeFilterPanel.selectionNeeded.text=At least one Result type must be selected.
CTL_OpenDiscoveryAction=Discovery
DataSourceFilterPanel.error.text=At least one data source must be selected.
@ -126,13 +131,15 @@ HashSetFilterPanel.hashSetCheckbox.text=Hash Set:
PastOccurrencesFilterPanel.pastOccurrencesCheckbox.text=Past Occurrences:
DocumentFilterPanel.documentsFiltersSplitPane.border.title=Step 2: Filter which documents to show
ObjectDetectedFilterPanel.text=Object Detected:
DetailsPanel.instancesList.border.title=Instances
DateFilterPanel.mostRecentRadioButton.text=Only last:
DateFilterPanel.dateFilterCheckBox.text=Date Filter:
DomainSummaryPanel.activityLabel.text=
DomainSummaryPanel.pagesLabel.text=
DomainSummaryPanel.filesDownloadedLabel.text=
DomainSummaryPanel.totalVisitsLabel.text=
FileDetailsPanel.instancesList.border.title=Instances
CookieDetailsPanel.jLabel1.text=Artifact:
CookieDetailsPanel.jLabel2.text=
VideoThumbnailPanel.bytes.text=bytes
VideoThumbnailPanel.deleted.text=All instances of file are deleted.
VideoThumbnailPanel.gigaBytes.text=GB
@ -144,3 +151,7 @@ VideoThumbnailPanel.nameLabel.more.text=\ and {0} more
# {1} - units
VideoThumbnailPanel.sizeLabel.text=Size: {0} {1}
VideoThumbnailPanel.terraBytes.text=TB
WebHistoryDetailsPanel.details.attrHeader=Attributes
WebHistoryDetailsPanel.details.dataSource=Data Source
WebHistoryDetailsPanel.details.file=File
WebHistoryDetailsPanel.details.sourceHeader=Source

View File

@ -0,0 +1,18 @@
<?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"/>
<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.DesignBorderLayout"/>
</Form>

View File

@ -0,0 +1,69 @@
/*
* 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.ui;
import org.openide.nodes.Node;
import org.sleuthkit.autopsy.corecomponents.DataContentPanel;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode;
import org.sleuthkit.datamodel.BlackboardArtifact;
/**
* Details panel for displaying the collection of content viewers.
*/
final class ContentViewerDetailsPanel extends AbstractArtifactDetailsPanel {
private static final long serialVersionUID = 1L;
private final DataContentPanel contentViewer = DataContentPanel.createInstance();
/**
* Creates new form ContentViewerDetailsPanel
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
ContentViewerDetailsPanel() {
initComponents();
add(contentViewer);
}
/**
* 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() {
setLayout(new java.awt.BorderLayout());
}// </editor-fold>//GEN-END:initComponents
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
public void setArtifact(BlackboardArtifact artifact) {
Node node = Node.EMPTY;
if (artifact != null) {
node = new BlackboardArtifactNode(artifact);
}
contentViewer.setNode(node);
}
// Variables declaration - do not modify//GEN-BEGIN:variables
// End of variables declaration//GEN-END:variables
}

View File

@ -30,6 +30,7 @@ import javax.swing.JList;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.SearchFiltering;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.TskCoreException;
@ -45,6 +46,7 @@ final class DataSourceFilterPanel extends AbstractDiscoveryFilterPanel {
/**
* Creates new form DataSourceFilterPanel.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
DataSourceFilterPanel() {
initComponents();
setUpDataSourceFilter();
@ -109,6 +111,7 @@ final class DataSourceFilterPanel extends AbstractDiscoveryFilterPanel {
private javax.swing.JScrollPane dataSourceScrollPane;
// End of variables declaration//GEN-END:variables
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
void configurePanel(boolean selected, int[] indicesSelected) {
dataSourceCheckbox.setSelected(selected);
@ -124,6 +127,7 @@ final class DataSourceFilterPanel extends AbstractDiscoveryFilterPanel {
}
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
JCheckBox getCheckbox() {
return dataSourceCheckbox;
@ -137,6 +141,7 @@ final class DataSourceFilterPanel extends AbstractDiscoveryFilterPanel {
/**
* Initialize the data source filter.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void setUpDataSourceFilter() {
int count = 0;
try {
@ -156,6 +161,7 @@ final class DataSourceFilterPanel extends AbstractDiscoveryFilterPanel {
}
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
JList<?> getList() {
return dataSourceList;
@ -193,6 +199,7 @@ final class DataSourceFilterPanel extends AbstractDiscoveryFilterPanel {
}
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@NbBundle.Messages({"DataSourceFilterPanel.error.text=At least one data source must be selected."})
@Override
String checkForError() {
@ -202,6 +209,7 @@ final class DataSourceFilterPanel extends AbstractDiscoveryFilterPanel {
return "";
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
AbstractFilter getFilter() {
if (dataSourceCheckbox.isSelected()) {

View File

@ -33,6 +33,7 @@ import javax.swing.JSpinner;
import javax.swing.event.ListSelectionListener;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.communications.Utils;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.SearchFiltering;
/**
@ -48,6 +49,7 @@ class DateFilterPanel extends AbstractDiscoveryFilterPanel {
*/
@NbBundle.Messages({"# {0} - timeZone",
"DateFilterPanel.dateRange.text=Date Range ({0}):"})
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
DateFilterPanel() {
initComponents();
rangeRadioButton.setText(Bundle.DateFilterPanel_dateRange_text(Utils.getUserPreferredZoneId().toString()));
@ -225,6 +227,7 @@ class DateFilterPanel extends AbstractDiscoveryFilterPanel {
endCheckBox.firePropertyChange("EndButtonChange", true, false);
}//GEN-LAST:event_rangeRadioButtonStateChanged
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
void configurePanel(boolean selected, int[] indicesSelected) {
dateFilterCheckBox.setSelected(selected);
@ -238,6 +241,7 @@ class DateFilterPanel extends AbstractDiscoveryFilterPanel {
}
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
JCheckBox getCheckbox() {
return dateFilterCheckBox;
@ -253,6 +257,7 @@ class DateFilterPanel extends AbstractDiscoveryFilterPanel {
return null;
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
void addListeners(ActionListener actionListener, ListSelectionListener listListener) {
dateFilterCheckBox.addActionListener(actionListener);
@ -274,6 +279,7 @@ class DateFilterPanel extends AbstractDiscoveryFilterPanel {
});
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
void removeListeners() {
for (ActionListener listener : dateFilterCheckBox.getActionListeners()) {
@ -302,6 +308,7 @@ class DateFilterPanel extends AbstractDiscoveryFilterPanel {
}
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@NbBundle.Messages({"DateFilterPanel.invalidRange.text=Range or Only Last must be selected.",
"DateFilterPanel.startOrEndNeeded.text=A start or end date must be specified to use the range filter.",
"DateFilterPanel.startAfterEnd.text=Start date should be before the end date when both are enabled."})
@ -320,6 +327,7 @@ class DateFilterPanel extends AbstractDiscoveryFilterPanel {
return "";
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
AbstractFilter getFilter() {
if (dateFilterCheckBox.isSelected()) {

View File

@ -40,6 +40,7 @@ import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.DiscoveryAttributes;
import org.sleuthkit.autopsy.discovery.search.DiscoveryEventUtils;
import org.sleuthkit.autopsy.discovery.search.Group;
@ -99,6 +100,7 @@ final class DiscoveryDialog extends javax.swing.JDialog {
/**
* Private constructor to construct a new DiscoveryDialog
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Messages("DiscoveryDialog.name.text=Discovery")
private DiscoveryDialog() {
super(WindowManager.getDefault().getMainWindow(), Bundle.DiscoveryDialog_name_text(), true);
@ -151,6 +153,7 @@ final class DiscoveryDialog extends javax.swing.JDialog {
/**
* Update the search settings to a default state.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void updateSearchSettings() {
removeAllPanels();
imageFilterPanel = null;
@ -176,6 +179,7 @@ final class DiscoveryDialog extends javax.swing.JDialog {
/**
* Set the type buttons to a default state where none are selected.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void unselectAllButtons() {
imagesButton.setSelected(false);
imagesButton.setEnabled(true);
@ -194,6 +198,7 @@ final class DiscoveryDialog extends javax.swing.JDialog {
/**
* Private helper method to perform update of comboboxes update.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void updateComboBoxes() {
// Set up the grouping attributes
List<GroupingAttributeType> groupingAttrs = new ArrayList<>();
@ -230,6 +235,7 @@ final class DiscoveryDialog extends javax.swing.JDialog {
*
* @return The panel that corresponds to the currently selected type.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private AbstractFiltersPanel getSelectedFilterPanel() {
switch (type) {
case IMAGE:
@ -251,6 +257,7 @@ final class DiscoveryDialog extends javax.swing.JDialog {
*
* @param type The Type of GroupingAttribute to add.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void addTypeToGroupByComboBox(GroupingAttributeType type) {
switch (type) {
case FREQUENCY:
@ -282,7 +289,8 @@ final class DiscoveryDialog extends javax.swing.JDialog {
/**
* Validate the filter settings for File type filters.
*/
synchronized void validateDialog() {
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void validateDialog() {
AbstractFiltersPanel panel = getSelectedFilterPanel();
if (panel != null) {
panel.validateFields();
@ -551,6 +559,7 @@ final class DiscoveryDialog extends javax.swing.JDialog {
/**
* Helper method to remove all filter panels and their listeners
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void removeAllPanels() {
if (imageFilterPanel != null) {
remove(imageFilterPanel);
@ -635,6 +644,7 @@ final class DiscoveryDialog extends javax.swing.JDialog {
repaint();
}//GEN-LAST:event_domainsButtonActionPerformed
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
public void dispose() {
setVisible(false);
@ -643,6 +653,7 @@ final class DiscoveryDialog extends javax.swing.JDialog {
/**
* Cancel the searchWorker if it exists.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void cancelSearch() {
if (searchWorker != null) {
searchWorker.cancel(true);
@ -656,6 +667,7 @@ final class DiscoveryDialog extends javax.swing.JDialog {
* @param error The error message to display, empty string if there is no
* error.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void setValid(String error) {
if (StringUtils.isBlank(error)) {
errorLabel.setText("");

View File

@ -27,6 +27,7 @@ import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.openide.nodes.Sheet;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode;
import org.sleuthkit.autopsy.datamodel.FileNode;
import org.sleuthkit.datamodel.AbstractFile;
@ -42,28 +43,27 @@ class DiscoveryThumbnailChildren extends Children.Keys<AbstractFile> {
/*
* Creates the list of thumbnails from the given list of AbstractFiles.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
DiscoveryThumbnailChildren(List<AbstractFile> files) {
super(false);
this.files = files;
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
protected Node[] createNodes(AbstractFile t) {
return new Node[]{new ThumbnailNode(t)};
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
protected void addNotify() {
super.addNotify();
Set<AbstractFile> thumbnails = new TreeSet<>((AbstractFile file1, AbstractFile file2) -> {
int result = Long.compare(file1.getSize(), file2.getSize());
if (result == 0) {
result = file1.getName().compareTo(file2.getName());
}
return result;
});
thumbnails.addAll(files);
@ -75,10 +75,12 @@ class DiscoveryThumbnailChildren extends Children.Keys<AbstractFile> {
*/
static class ThumbnailNode extends FileNode {
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
ThumbnailNode(AbstractFile file) {
super(file, false);
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
protected Sheet createSheet() {
Sheet sheet = super.createSheet();

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.4" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<Properties>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[199, 200]"/>

View File

@ -28,6 +28,7 @@ import java.beans.PropertyChangeListener;
import java.util.List;
import java.util.stream.Collectors;
import javax.swing.JSplitPane;
import javax.swing.SwingUtilities;
import javax.swing.plaf.basic.BasicSplitPaneDivider;
import javax.swing.plaf.basic.BasicSplitPaneUI;
import org.openide.util.NbBundle;
@ -40,6 +41,8 @@ import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.DiscoveryEventUtils;
import org.sleuthkit.autopsy.discovery.search.SearchData.Type;
import static org.sleuthkit.autopsy.discovery.search.SearchData.Type.DOMAIN;
import org.sleuthkit.autopsy.discovery.search.SearchFiltering.ArtifactTypeFilter;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
/**
* Create a dialog for displaying the Discovery results.
@ -55,8 +58,8 @@ public final class DiscoveryTopComponent extends TopComponent {
private static final int ANIMATION_INCREMENT = 30;
private volatile static int resultsAreaSize = 250;
private final GroupListPanel groupListPanel;
private final DetailsPanel detailsPanel;
private final ResultsPanel resultsPanel;
private String selectedDomainTabName;
private Type searchType;
private int dividerLocation = -1;
private SwingAnimator animator = null;
@ -70,10 +73,7 @@ public final class DiscoveryTopComponent extends TopComponent {
setName(Bundle.DiscoveryTopComponent_name());
groupListPanel = new GroupListPanel();
resultsPanel = new ResultsPanel();
detailsPanel = new DetailsPanel();
mainSplitPane.setLeftComponent(groupListPanel);
rightSplitPane.setTopComponent(resultsPanel);
rightSplitPane.setBottomComponent(detailsPanel);
//set color of divider
rightSplitPane.setUI(new BasicSplitPaneUI() {
@Override
@ -95,6 +95,7 @@ public final class DiscoveryTopComponent extends TopComponent {
}
}
});
rightSplitPane.setTopComponent(resultsPanel);
}
/**
@ -108,6 +109,7 @@ public final class DiscoveryTopComponent extends TopComponent {
* @param ui The component which contains the split pane this divider is
* in.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
BasicSplitPaneDividerImpl(BasicSplitPaneUI ui) {
super(ui);
this.setLayout(new BorderLayout());
@ -129,11 +131,13 @@ public final class DiscoveryTopComponent extends TopComponent {
/**
* Reset the top component so it isn't displaying any results.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
public void resetTopComponent() {
resultsPanel.resetResultViewer();
groupListPanel.resetGroupList();
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
public void componentOpened() {
super.componentOpened();
@ -141,9 +145,9 @@ public final class DiscoveryTopComponent extends TopComponent {
DiscoveryEventUtils.getDiscoveryEventBus().register(this);
DiscoveryEventUtils.getDiscoveryEventBus().register(resultsPanel);
DiscoveryEventUtils.getDiscoveryEventBus().register(groupListPanel);
DiscoveryEventUtils.getDiscoveryEventBus().register(detailsPanel);
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
protected void componentClosed() {
DiscoveryDialog.getDiscoveryDialogInstance().cancelSearch();
@ -152,7 +156,10 @@ public final class DiscoveryTopComponent extends TopComponent {
DiscoveryEventUtils.getDiscoveryEventBus().unregister(this);
DiscoveryEventUtils.getDiscoveryEventBus().unregister(groupListPanel);
DiscoveryEventUtils.getDiscoveryEventBus().unregister(resultsPanel);
DiscoveryEventUtils.getDiscoveryEventBus().unregister(detailsPanel);
DiscoveryEventUtils.getDiscoveryEventBus().unregister(rightSplitPane.getBottomComponent());
if (rightSplitPane.getBottomComponent() instanceof DomainDetailsPanel) {
selectedDomainTabName = ((DomainDetailsPanel) rightSplitPane.getBottomComponent()).getSelectedTabName();
}
super.componentClosed();
}
@ -262,7 +269,7 @@ public final class DiscoveryTopComponent extends TopComponent {
*/
@Subscribe
void handleDetailsVisibleEvent(DiscoveryEventUtils.DetailsVisibleEvent detailsVisibleEvent) {
if (resultsPanel.getActiveType() != DOMAIN) {
SwingUtilities.invokeLater(() -> {
if (animator != null && animator.isRunning()) {
animator.stop();
animator = null;
@ -274,7 +281,7 @@ public final class DiscoveryTopComponent extends TopComponent {
animator = new SwingAnimator(new HideDetailsAreaCallback());
}
animator.start();
}
});
}
/**
@ -289,12 +296,12 @@ public final class DiscoveryTopComponent extends TopComponent {
"DiscoveryTopComponent.searchError.text=Error no type specified for search."})
@Subscribe
void handleSearchStartedEvent(DiscoveryEventUtils.SearchStartedEvent searchStartedEvent) {
newSearchButton.setText(Bundle.DiscoveryTopComponent_cancelButton_text());
progressMessageTextArea.setForeground(Color.red);
searchType = searchStartedEvent.getType();
progressMessageTextArea.setText(Bundle.DiscoveryTopComponent_searchInProgress_text(searchType.name()));
rightSplitPane.getComponent(1).setVisible(searchStartedEvent.getType() != DOMAIN);
rightSplitPane.getComponent(2).setVisible(searchStartedEvent.getType() != DOMAIN);
SwingUtilities.invokeLater(() -> {
newSearchButton.setText(Bundle.DiscoveryTopComponent_cancelButton_text());
progressMessageTextArea.setForeground(Color.red);
searchType = searchStartedEvent.getType();
progressMessageTextArea.setText(Bundle.DiscoveryTopComponent_searchInProgress_text(searchType.name()));
});
}
/**
@ -310,19 +317,51 @@ public final class DiscoveryTopComponent extends TopComponent {
"DiscoveryTopComponent.domainSearch.text=Type: Domain",
"DiscoveryTopComponent.additionalFilters.text=; "})
void handleSearchCompleteEvent(DiscoveryEventUtils.SearchCompleteEvent searchCompleteEvent) {
newSearchButton.setText(Bundle.DiscoveryTopComponent_newSearch_text());
progressMessageTextArea.setForeground(Color.black);
String descriptionText = "";
if (searchType == DOMAIN) {
//domain does not have a file type filter to add the type information so it is manually added
descriptionText = Bundle.DiscoveryTopComponent_domainSearch_text();
if (!searchCompleteEvent.getFilters().isEmpty()) {
descriptionText += Bundle.DiscoveryTopComponent_additionalFilters_text();
SwingUtilities.invokeLater(() -> {
newSearchButton.setText(Bundle.DiscoveryTopComponent_newSearch_text());
progressMessageTextArea.setForeground(Color.black);
String descriptionText = "";
if (searchType == DOMAIN) {
//domain does not have a file type filter to add the type information so it is manually added
descriptionText = Bundle.DiscoveryTopComponent_domainSearch_text();
if (!searchCompleteEvent.getFilters().isEmpty()) {
descriptionText += Bundle.DiscoveryTopComponent_additionalFilters_text();
}
selectedDomainTabName = validateLastSelectedType(searchCompleteEvent);
rightSplitPane.setBottomComponent(new DomainDetailsPanel(selectedDomainTabName));
} else {
rightSplitPane.setBottomComponent(new FileDetailsPanel());
}
DiscoveryEventUtils.getDiscoveryEventBus().register(rightSplitPane.getBottomComponent());
descriptionText += searchCompleteEvent.getFilters().stream().map(AbstractFilter::getDesc).collect(Collectors.joining("; "));
progressMessageTextArea.setText(Bundle.DiscoveryTopComponent_searchComplete_text(descriptionText));
progressMessageTextArea.setCaretPosition(0);
});
}
/**
* Get the name of the tab which was last selected unless the tab last
* selected would not be included in the types currently being displayed or
* was not previously set.
*
* @return The name of the tab which should be selected in the new
* DomainDetailsPanel.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private String validateLastSelectedType(DiscoveryEventUtils.SearchCompleteEvent searchCompleteEvent) {
String typeFilteredOn = selectedDomainTabName;
for (AbstractFilter filter : searchCompleteEvent.getFilters()) {
if (filter instanceof ArtifactTypeFilter) {
for (ARTIFACT_TYPE type : ((ArtifactTypeFilter) filter).getTypes()) {
typeFilteredOn = type.getDisplayName();
if (selectedDomainTabName == null || typeFilteredOn.equalsIgnoreCase(selectedDomainTabName)) {
break;
}
}
break;
}
}
descriptionText += searchCompleteEvent.getFilters().stream().map(AbstractFilter::getDesc).collect(Collectors.joining("; "));
progressMessageTextArea.setText(Bundle.DiscoveryTopComponent_searchComplete_text(descriptionText));
progressMessageTextArea.setCaretPosition(0);
return typeFilteredOn;
}
/**
@ -334,9 +373,11 @@ public final class DiscoveryTopComponent extends TopComponent {
@Messages({"DiscoveryTopComponent.searchCancelled.text=Search has been cancelled."})
@Subscribe
void handleSearchCancelledEvent(DiscoveryEventUtils.SearchCancelledEvent searchCancelledEvent) {
newSearchButton.setText(Bundle.DiscoveryTopComponent_newSearch_text());
progressMessageTextArea.setForeground(Color.red);
progressMessageTextArea.setText(Bundle.DiscoveryTopComponent_searchCancelled_text());
SwingUtilities.invokeLater(() -> {
newSearchButton.setText(Bundle.DiscoveryTopComponent_newSearch_text());
progressMessageTextArea.setForeground(Color.red);
progressMessageTextArea.setText(Bundle.DiscoveryTopComponent_searchCancelled_text());
});
}
@ -345,12 +386,14 @@ public final class DiscoveryTopComponent extends TopComponent {
*/
private final class ShowDetailsAreaCallback implements SwingAnimatorCallback {
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
public void callback(Object caller) {
dividerLocation -= ANIMATION_INCREMENT;
repaint();
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
public boolean hasTerminated() {
if (dividerLocation != JSplitPane.UNDEFINED_CONDITION && dividerLocation < resultsAreaSize) {
@ -368,12 +411,14 @@ public final class DiscoveryTopComponent extends TopComponent {
*/
private final class HideDetailsAreaCallback implements SwingAnimatorCallback {
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
public void callback(Object caller) {
dividerLocation += ANIMATION_INCREMENT;
repaint();
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
public boolean hasTerminated() {
if (dividerLocation > rightSplitPane.getHeight() || dividerLocation == JSplitPane.UNDEFINED_CONDITION) {
@ -399,6 +444,7 @@ public final class DiscoveryTopComponent extends TopComponent {
private static final long serialVersionUID = 1L;
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
public void paintComponent(Graphics g) {
if (animator != null && animator.isRunning() && (dividerLocation == JSplitPane.UNDEFINED_CONDITION

View File

@ -51,6 +51,7 @@ import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.corelibs.ScalrWrapper;
import org.sleuthkit.autopsy.coreutils.ImageUtils;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import static org.sleuthkit.autopsy.coreutils.VideoUtils.getVideoFileInTempDir;
import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.autopsy.discovery.search.ResultFile;
@ -173,6 +174,7 @@ final class DiscoveryUiUtils {
*
* @return True if the point is over the icon, false otherwise.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
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;
}
@ -186,6 +188,7 @@ final class DiscoveryUiUtils {
* @param isDeletedLabel The label to set the icon and tooltip for.
*/
@NbBundle.Messages({"DiscoveryUiUtils.isDeleted.text=All instances of file are deleted."})
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
static void setDeletedIcon(boolean isDeleted, javax.swing.JLabel isDeletedLabel) {
if (isDeleted) {
isDeletedLabel.setIcon(DELETED_ICON);
@ -203,6 +206,7 @@ final class DiscoveryUiUtils {
* score of.
* @param scoreLabel The label to set the icon and tooltip for.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
static void setScoreIcon(ResultFile resultFile, javax.swing.JLabel scoreLabel) {
switch (resultFile.getScore()) {
case NOTABLE_SCORE:
@ -232,6 +236,7 @@ final class DiscoveryUiUtils {
* Helper method to display an error message when the results of the
* Discovery Top component may be incomplete.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@NbBundle.Messages({"DiscoveryUiUtils.resultsIncomplete.text=Discovery results may be incomplete"})
static void displayErrorMessage(DiscoveryDialog dialog) {
//check if modules run and assemble message

View File

@ -19,6 +19,7 @@
package org.sleuthkit.autopsy.discovery.ui;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.SearchData;
/**
@ -32,6 +33,7 @@ final class DocumentFilterPanel extends AbstractFiltersPanel {
/**
* Constructs a new DocumentFilterPanel.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
DocumentFilterPanel() {
super();
initComponents();

View File

@ -79,9 +79,6 @@
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/images/file-icon-deleted.png"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/discovery/ui/Bundle.properties" key="DocumentPanel.isDeletedLabel.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new Dimension(org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize(),org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize())" type="code"/>
</Property>
@ -91,6 +88,9 @@
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new Dimension(org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize(),org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize())" type="code"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/discovery/ui/Bundle.properties" key="DocumentPanel.isDeletedLabel.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="scoreLabel">
@ -98,7 +98,6 @@
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/images/red-circle-exclamation.png"/>
</Property>
<Property name="toolTipText" type="java.lang.String" value=""/>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new Dimension(org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize(),org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize())" type="code"/>
</Property>
@ -108,6 +107,7 @@
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new Dimension(org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize(),org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize())" type="code"/>
</Property>
<Property name="toolTipText" type="java.lang.String" value=""/>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="fileSizeLabel">

View File

@ -29,6 +29,7 @@ import javax.swing.JList;
import javax.swing.ListCellRenderer;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.corecomponents.AutoWrappingJTextPane;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.SearchData;
/**
@ -43,6 +44,7 @@ class DocumentPanel extends javax.swing.JPanel implements ListCellRenderer<Docum
/**
* Creates new form DocumentPanel.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
DocumentPanel() {
initComponents();
}
@ -150,7 +152,7 @@ class DocumentPanel extends javax.swing.JPanel implements ListCellRenderer<Docum
"DocumentPanel.numberOfImages.text=1 of {0} images",
"DocumentPanel.numberOfImages.noImages=No images",
"DocumentPanel.noImageExtraction.text=0 of ? images"})
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
public Component getListCellRendererComponent(JList<? extends DocumentWrapper> list, DocumentWrapper value, int index, boolean isSelected, boolean cellHasFocus) {
fileSizeLabel.setText(DiscoveryUiUtils.getFileSizeString(value.getResultFile().getFirstInstance().getSize()));
@ -180,6 +182,7 @@ class DocumentPanel extends javax.swing.JPanel implements ListCellRenderer<Docum
return this;
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
public String getToolTipText(MouseEvent event) {
if (event != null) {

View File

@ -22,6 +22,7 @@ import java.util.ArrayList;
import java.util.List;
import javax.swing.DefaultListModel;
import javax.swing.event.ListSelectionListener;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.datamodel.AbstractFile;
/**
@ -35,6 +36,7 @@ final class DocumentPreviewViewer extends javax.swing.JPanel {
/**
* Creates new form DocumentViewer.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
DocumentPreviewViewer() {
initComponents();
}
@ -75,11 +77,10 @@ final class DocumentPreviewViewer extends javax.swing.JPanel {
/**
* Clear the list of documents being displayed.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void clearViewer() {
synchronized (this) {
documentListModel.removeAllElements();
documentScrollPane.getVerticalScrollBar().setValue(0);
}
documentListModel.removeAllElements();
documentScrollPane.getVerticalScrollBar().setValue(0);
}
/**
@ -88,6 +89,7 @@ final class DocumentPreviewViewer extends javax.swing.JPanel {
*
* @param listener The ListSelectionListener to add to the selection model.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void addListSelectionListener(ListSelectionListener listener) {
documentList.getSelectionModel().addListSelectionListener(listener);
}
@ -99,13 +101,12 @@ final class DocumentPreviewViewer extends javax.swing.JPanel {
* @return The list of AbstractFiles which are represented by the selected
* document preview.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
List<AbstractFile> getInstancesForSelected() {
synchronized (this) {
if (documentList.getSelectedIndex() == -1) {
return new ArrayList<>();
} else {
return documentListModel.getElementAt(documentList.getSelectedIndex()).getResultFile().getAllInstances();
}
if (documentList.getSelectedIndex() == -1) {
return new ArrayList<>();
} else {
return documentListModel.getElementAt(documentList.getSelectedIndex()).getResultFile().getAllInstances();
}
}
@ -120,9 +121,8 @@ final class DocumentPreviewViewer extends javax.swing.JPanel {
* @param documentWrapper The object which contains the document preview
* which will be displayed.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void addDocument(DocumentWrapper documentWrapper) {
synchronized (this) {
documentListModel.addElement(documentWrapper);
}
documentListModel.addElement(documentWrapper);
}
}

View File

@ -0,0 +1,29 @@
<?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"/>
<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.DesignBorderLayout"/>
<SubComponents>
<Container class="javax.swing.JSplitPane" name="jSplitPane1">
<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.JSplitPaneSupportLayout"/>
</Container>
</SubComponents>
</Form>

View File

@ -0,0 +1,188 @@
/*
* 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.ui;
import com.google.common.eventbus.Subscribe;
import java.util.logging.Level;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import org.sleuthkit.autopsy.contentviewers.artifactviewers.DefaultArtifactContentViewer;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.DiscoveryEventUtils;
import org.sleuthkit.datamodel.BlackboardArtifact;
/**
* JPanel which should be used as a tab in the domain artifacts details area.
*/
final class DomainArtifactsTabPanel extends JPanel {
private static final long serialVersionUID = 1L;
private final static Logger logger = Logger.getLogger(DomainArtifactsTabPanel.class.getName());
private final ArtifactsListPanel listPanel;
private final BlackboardArtifact.ARTIFACT_TYPE artifactType;
private AbstractArtifactDetailsPanel rightPanel = null;
private volatile ArtifactRetrievalStatus status = ArtifactRetrievalStatus.UNPOPULATED;
private final ListSelectionListener listener = new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent event) {
if (!event.getValueIsAdjusting()) {
rightPanel.setArtifact(listPanel.getSelectedArtifact());
}
}
};
/**
* Creates new form CookiesPanel
*
* @param type The type of Artifact this tab is displaying information for.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
DomainArtifactsTabPanel(BlackboardArtifact.ARTIFACT_TYPE type) {
initComponents();
this.artifactType = type;
listPanel = new ArtifactsListPanel(artifactType);
jSplitPane1.setLeftComponent(listPanel);
setRightComponent();
listPanel.addSelectionListener(listener);
}
/**
* Set the right component of the tab panel, which will display the details
* for the artifact.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void setRightComponent() {
switch (artifactType) {
case TSK_WEB_HISTORY:
rightPanel = new WebHistoryDetailsPanel();
break;
case TSK_WEB_COOKIE:
case TSK_WEB_SEARCH_QUERY:
case TSK_WEB_BOOKMARK:
rightPanel = new DefaultArtifactContentViewer();
break;
case TSK_WEB_DOWNLOAD:
case TSK_WEB_CACHE:
rightPanel = new ContentViewerDetailsPanel();
break;
default:
rightPanel = new DefaultArtifactContentViewer();
break;
}
if (rightPanel != null) {
jSplitPane1.setRightComponent(new JScrollPane(rightPanel));
}
}
/**
* Get the status of the panel which indicates if it is populated.
*
* @return The ArtifactRetrievalStatuss of the panel.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
ArtifactRetrievalStatus getStatus() {
return status;
}
/**
* Manually set the status of the panel.
*
* @param status The ArtifactRetrievalStatus of the panel.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void setStatus(ArtifactRetrievalStatus status) {
this.status = status;
if (status == ArtifactRetrievalStatus.UNPOPULATED && rightPanel != null) {
rightPanel.setArtifact(null);
}
}
/**
* Handle the event which indicates the artifacts have been retrieved.
*
* @param artifactresultEvent The event which indicates the artifacts have
* been retrieved.
*/
@Subscribe
void handleArtifactSearchResultEvent(DiscoveryEventUtils.ArtifactSearchResultEvent artifactresultEvent) {
SwingUtilities.invokeLater(() -> {
if (artifactType == artifactresultEvent.getArtifactType()) {
listPanel.removeListSelectionListener(listener);
listPanel.addArtifacts(artifactresultEvent.getListOfArtifacts());
listPanel.addSelectionListener(listener);
listPanel.selectFirst();
try {
DiscoveryEventUtils.getDiscoveryEventBus().unregister(this);
} catch (IllegalArgumentException notRegistered) {
logger.log(Level.INFO, "Attempting to unregister tab which was not registered");
// attempting to remove a tab that was never registered
}
status = ArtifactRetrievalStatus.POPULATED;
setEnabled(!listPanel.isEmpty());
validate();
repaint();
}
});
}
/**
* Get the type of Artifact the panel exists for.
*
* @return The ARTIFACT_TYPE of the BlackboardArtifact being displayed.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
BlackboardArtifact.ARTIFACT_TYPE getArtifactType() {
return artifactType;
}
/**
* 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() {
jSplitPane1 = new javax.swing.JSplitPane();
setLayout(new java.awt.BorderLayout());
add(jSplitPane1, java.awt.BorderLayout.CENTER);
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JSplitPane jSplitPane1;
// End of variables declaration//GEN-END:variables
/**
* Enum to keep track of the populated state of this panel.
*/
enum ArtifactRetrievalStatus {
UNPOPULATED(),
POPULATING(),
POPULATED();
}
}

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<Properties>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
<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.DesignBorderLayout"/>
<SubComponents>
<Container class="javax.swing.JTabbedPane" name="jTabbedPane1">
<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.JTabbedPaneSupportLayout"/>
</Container>
</SubComponents>
</Form>

View File

@ -0,0 +1,182 @@
/*
* 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.ui;
import com.google.common.eventbus.Subscribe;
import java.awt.Component;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.apache.commons.lang.StringUtils;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.DiscoveryEventUtils;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.autopsy.discovery.search.SearchData;
/**
* Panel to display details area for domain discovery results.
*
*/
final class DomainDetailsPanel extends JPanel {
private static final long serialVersionUID = 1L;
private ArtifactsWorker detailsWorker;
private String domain;
private String selectedTabName;
/**
* Creates new form ArtifactDetailsPanel.
*
* @param selectedTabName The name of the tab to select initially.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
DomainDetailsPanel(String selectedTabName) {
initComponents();
addArtifactTabs(selectedTabName);
}
/**
* Add the tabs for each of the artifact types which we will be displaying.
*
* @param tabName The name of the tab to select initially.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void addArtifactTabs(String tabName) {
for (BlackboardArtifact.ARTIFACT_TYPE type : SearchData.Type.DOMAIN.getArtifactTypes()) {
jTabbedPane1.add(type.getDisplayName(), new DomainArtifactsTabPanel(type));
}
selectedTabName = tabName;
selectTab();
jTabbedPane1.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
if (jTabbedPane1.getSelectedIndex() >= 0) {
String newTabTitle = jTabbedPane1.getTitleAt(jTabbedPane1.getSelectedIndex());
if (selectedTabName == null || !selectedTabName.equals(newTabTitle)) {
selectedTabName = newTabTitle;
runDomainWorker();
}
}
}
});
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
/**
* Set the selected tab index to be the previously selected tab if a
* previously selected tab exists.
*/
private void selectTab() {
for (int i = 0; i < jTabbedPane1.getTabCount(); i++) {
if (!StringUtils.isBlank(selectedTabName) && selectedTabName.equals(jTabbedPane1.getTitleAt(i))) {
jTabbedPane1.setSelectedIndex(i);
return;
}
}
}
/**
* Run the worker which retrieves the list of artifacts for the domain to
* populate the details area.
*/
private void runDomainWorker() {
SwingUtilities.invokeLater(() -> {
Component selectedComponent = jTabbedPane1.getSelectedComponent();
if (selectedComponent instanceof DomainArtifactsTabPanel) {
if (detailsWorker != null && !detailsWorker.isDone()) {
detailsWorker.cancel(true);
}
DomainArtifactsTabPanel selectedTab = (DomainArtifactsTabPanel) selectedComponent;
if (selectedTab.getStatus() == DomainArtifactsTabPanel.ArtifactRetrievalStatus.UNPOPULATED) {
selectedTab.setStatus(DomainArtifactsTabPanel.ArtifactRetrievalStatus.POPULATING);
DiscoveryEventUtils.getDiscoveryEventBus().register(selectedTab);
detailsWorker = new ArtifactsWorker(selectedTab.getArtifactType(), domain);
detailsWorker.execute();
}
}
});
}
/**
* Populate the the details tabs.
*
* @param populateEvent The PopulateDomainTabsEvent which indicates which
* domain the details tabs should be populated for.
*/
@Subscribe
void handlePopulateDomainTabsEvent(DiscoveryEventUtils.PopulateDomainTabsEvent populateEvent) {
SwingUtilities.invokeLater(() -> {
domain = populateEvent.getDomain();
resetTabsStatus();
selectTab();
runDomainWorker();
if (StringUtils.isBlank(domain)) {
//send fade out event
DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.DetailsVisibleEvent(false));
} else {
//send fade in event
DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.DetailsVisibleEvent(true));
}
});
}
/**
* Private helper method to ensure tabs will re-populate after a new domain
* is selected.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void resetTabsStatus() {
for (Component comp : jTabbedPane1.getComponents()) {
if (comp instanceof DomainArtifactsTabPanel) {
((DomainArtifactsTabPanel) comp).setStatus(DomainArtifactsTabPanel.ArtifactRetrievalStatus.UNPOPULATED);
}
}
}
/**
* Get the name of the tab that was most recently selected.
*
* @return The name of the tab that was most recently selected.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
String getSelectedTabName() {
return selectedTabName;
}
/**
* 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() {
jTabbedPane1 = new javax.swing.JTabbedPane();
setEnabled(false);
setLayout(new java.awt.BorderLayout());
add(jTabbedPane1, java.awt.BorderLayout.CENTER);
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JTabbedPane jTabbedPane1;
// End of variables declaration//GEN-END:variables
}

View File

@ -19,6 +19,7 @@
package org.sleuthkit.autopsy.discovery.ui;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.DiscoveryAttributes;
import org.sleuthkit.autopsy.discovery.search.ResultsSorter;
import org.sleuthkit.autopsy.discovery.search.SearchData;
@ -34,6 +35,7 @@ public class DomainFilterPanel extends AbstractFiltersPanel {
/**
* Creates new form DomainFilterPanel.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
public DomainFilterPanel() {
super();
initComponents();

View File

@ -30,6 +30,7 @@ import javax.swing.JComponent;
import javax.swing.JList;
import javax.swing.ListCellRenderer;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
/**
* Class which displays a preview and details about a domain.
@ -43,6 +44,7 @@ class DomainSummaryPanel extends javax.swing.JPanel implements ListCellRenderer<
/**
* Creates new form DomainPanel.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
DomainSummaryPanel() {
initComponents();
domainNameLabel.setFont(domainNameLabel.getFont().deriveFont(domainNameLabel.getFont().getStyle(), domainNameLabel.getFont().getSize() + 6));
@ -136,6 +138,7 @@ class DomainSummaryPanel extends javax.swing.JPanel implements ListCellRenderer<
private javax.swing.JLabel totalVisitsLabel;
// End of variables declaration//GEN-END:variables
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@NbBundle.Messages({"# {0} - startDate",
"# {1} - endDate",
"DomainSummaryPanel.activity.text=Activity: {0} to {1}",
@ -163,6 +166,7 @@ class DomainSummaryPanel extends javax.swing.JPanel implements ListCellRenderer<
return this;
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
public String getToolTipText(MouseEvent event) {
if (event != null) {

View File

@ -39,8 +39,6 @@
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;DomainWrapper&gt;"/>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
</Component>
</SubComponents>

View File

@ -19,6 +19,9 @@
package org.sleuthkit.autopsy.discovery.ui;
import javax.swing.DefaultListModel;
import javax.swing.event.ListSelectionListener;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.DiscoveryEventUtils;
/**
* A JPanel to display domain summaries.
@ -30,20 +33,20 @@ public class DomainSummaryViewer extends javax.swing.JPanel {
private final DefaultListModel<DomainWrapper> domainListModel = new DefaultListModel<>();
/**
* Clear the list of documents being displayed.
* Creates new form DomainSummaryPanel
*/
void clearViewer() {
synchronized (this) {
domainListModel.removeAllElements();
domainScrollPane.getVerticalScrollBar().setValue(0);
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
public DomainSummaryViewer() {
initComponents();
}
/**
* Creates new form DomainSummaryPanel
* Clear the list of documents being displayed.
*/
public DomainSummaryViewer() {
initComponents();
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void clearViewer() {
domainListModel.removeAllElements();
domainScrollPane.getVerticalScrollBar().setValue(0);
}
/**
@ -56,7 +59,7 @@ public class DomainSummaryViewer extends javax.swing.JPanel {
private void initComponents() {
domainScrollPane = new javax.swing.JScrollPane();
javax.swing.JList<DomainWrapper> domainList = new javax.swing.JList<>();
domainList = new javax.swing.JList<>();
setLayout(new java.awt.BorderLayout());
@ -69,6 +72,7 @@ public class DomainSummaryViewer extends javax.swing.JPanel {
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JList<DomainWrapper> domainList;
private javax.swing.JScrollPane domainScrollPane;
// End of variables declaration//GEN-END:variables
@ -78,9 +82,39 @@ public class DomainSummaryViewer extends javax.swing.JPanel {
* @param domainWrapper The object which contains the domain summary which
* will be displayed.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void addDomain(DomainWrapper domainWrapper) {
synchronized (this) {
domainListModel.addElement(domainWrapper);
domainListModel.addElement(domainWrapper);
}
/**
* Send an event to perform the population of the domain details tabs to
* reflect the currently selected domain. Will populate the list with
* nothing when a domain is not used.
*
* @param useDomain If the currently selected domain should be used to
* retrieve a list.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void sendPopulateEvent(boolean useDomain) {
String domain = "";
if (useDomain) {
if (domainList.getSelectedIndex() != -1) {
domain = domainListModel.getElementAt(domainList.getSelectedIndex()).getResultDomain().getDomain();
}
}
//send populateMesage
DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.PopulateDomainTabsEvent(domain));
}
/**
* Add a selection listener to the list of document previews being
* displayed.
*
* @param listener The ListSelectionListener to add to the selection model.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void addListSelectionListener(ListSelectionListener listener) {
domainList.getSelectionModel().addListSelectionListener(listener);
}
}

View File

@ -105,8 +105,8 @@
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.TitledBorderInfo">
<TitledBorder title="Instances">
<ResourceString PropertyName="titleX" bundle="org/sleuthkit/autopsy/discovery/ui/Bundle.properties" key="DetailsPanel.instancesList.border.title" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<TitledBorder title="&lt;FileDetailsPanel.instancesList.border.title&gt;">
<ResourceString PropertyName="titleX" bundle="org/sleuthkit/autopsy/discovery/ui/Bundle.properties" key="FileDetailsPanel.instancesList.border.title" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</TitledBorder>
</Border>
</Property>

View File

@ -36,6 +36,7 @@ import org.sleuthkit.autopsy.actions.AddContentTagAction;
import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction;
import org.sleuthkit.autopsy.corecomponents.DataContentPanel;
import org.sleuthkit.autopsy.corecomponents.TableFilterNode;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.datamodel.FileNode;
import org.sleuthkit.autopsy.directorytree.ExternalViewerAction;
import org.sleuthkit.autopsy.directorytree.ViewContextAction;
@ -48,7 +49,7 @@ import org.sleuthkit.datamodel.TskCoreException;
/**
* Panel to display the details of the selected result.
*/
final class DetailsPanel extends javax.swing.JPanel {
final class FileDetailsPanel extends javax.swing.JPanel {
private static final long serialVersionUID = 1L;
@ -59,7 +60,8 @@ final class DetailsPanel extends javax.swing.JPanel {
/**
* Creates new form DetailsPanel.
*/
DetailsPanel() {
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
FileDetailsPanel() {
initComponents();
dataContentPanel = DataContentPanel.createInstance();
detailsSplitPane.setBottomComponent(dataContentPanel);
@ -112,7 +114,9 @@ final class DetailsPanel extends javax.swing.JPanel {
*/
@Subscribe
void handleClearSelectionListener(DiscoveryEventUtils.ClearInstanceSelectionEvent clearEvent) {
instancesList.clearSelection();
SwingUtilities.invokeLater(() -> {
instancesList.clearSelection();
});
}
/**
@ -122,7 +126,7 @@ final class DetailsPanel extends javax.swing.JPanel {
* instances list should be populated
*/
@Subscribe
synchronized void handlePopulateInstancesListEvent(DiscoveryEventUtils.PopulateInstancesListEvent populateEvent) {
void handlePopulateInstancesListEvent(DiscoveryEventUtils.PopulateInstancesListEvent populateEvent) {
SwingUtilities.invokeLater(() -> {
List<AbstractFile> files = populateEvent.getInstances();
if (files.isEmpty()) {
@ -154,7 +158,8 @@ final class DetailsPanel extends javax.swing.JPanel {
*
* @return The AbstractFile which is currently selected.
*/
synchronized AbstractFile getSelectedFile() {
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
AbstractFile getSelectedFile() {
if (instancesList.getSelectedIndex() == -1) {
return null;
} else {
@ -186,7 +191,7 @@ final class DetailsPanel extends javax.swing.JPanel {
instancesScrollPane.setPreferredSize(new java.awt.Dimension(775, 60));
instancesList.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(DetailsPanel.class, "DetailsPanel.instancesList.border.title"))); // NOI18N
instancesList.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(FileDetailsPanel.class, "FileDetailsPanel.instancesList.border.title"))); // NOI18N
instancesList.setModel(instancesListModel);
instancesList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
instancesList.setCellRenderer(new InstancesCellRenderer());
@ -241,6 +246,7 @@ final class DetailsPanel extends javax.swing.JPanel {
private static final long serialVersionUID = 1L;
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);

View File

@ -31,6 +31,7 @@ import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.DiscoveryAttributes;
import org.sleuthkit.autopsy.discovery.search.DiscoveryEventUtils;
import org.sleuthkit.autopsy.discovery.search.DiscoveryKeyUtils.GroupKey;
@ -56,6 +57,7 @@ final class GroupListPanel extends javax.swing.JPanel {
/**
* Creates new form GroupListPanel.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
GroupListPanel() {
initComponents();
}
@ -67,8 +69,10 @@ final class GroupListPanel extends javax.swing.JPanel {
*/
@Subscribe
void handleSearchStartedEvent(DiscoveryEventUtils.SearchStartedEvent searchStartedEvent) {
type = searchStartedEvent.getType();
groupKeyList.setListData(new GroupKey[0]);
SwingUtilities.invokeLater(() -> {
type = searchStartedEvent.getType();
groupKeyList.setListData(new GroupKey[0]);
});
}
@Messages({"GroupsListPanel.noFileResults.message.text=No files were found for the selected filters.\n\n"
@ -90,27 +94,29 @@ final class GroupListPanel extends javax.swing.JPanel {
*/
@Subscribe
void handleSearchCompleteEvent(DiscoveryEventUtils.SearchCompleteEvent searchCompleteEvent) {
groupMap = searchCompleteEvent.getGroupMap();
searchfilters = searchCompleteEvent.getFilters();
groupingAttribute = searchCompleteEvent.getGroupingAttr();
groupSort = searchCompleteEvent.getGroupSort();
resultSortMethod = searchCompleteEvent.getResultSort();
groupKeyList.setListData(groupMap.keySet().toArray(new GroupKey[groupMap.keySet().size()]));
SwingUtilities.invokeLater(() -> {
if (groupKeyList.getModel().getSize() > 0) {
groupKeyList.setSelectedIndex(0);
} else if (type == DOMAIN) {
JOptionPane.showMessageDialog(DiscoveryTopComponent.getTopComponent(),
Bundle.GroupsListPanel_noDomainResults_message_text(),
Bundle.GroupsListPanel_noResults_title_text(),
JOptionPane.PLAIN_MESSAGE);
} else {
JOptionPane.showMessageDialog(DiscoveryTopComponent.getTopComponent(),
Bundle.GroupsListPanel_noFileResults_message_text(),
Bundle.GroupsListPanel_noResults_title_text(),
JOptionPane.PLAIN_MESSAGE);
}
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
groupMap = searchCompleteEvent.getGroupMap();
searchfilters = searchCompleteEvent.getFilters();
groupingAttribute = searchCompleteEvent.getGroupingAttr();
groupSort = searchCompleteEvent.getGroupSort();
resultSortMethod = searchCompleteEvent.getResultSort();
groupKeyList.setListData(groupMap.keySet().toArray(new GroupKey[groupMap.keySet().size()]));
SwingUtilities.invokeLater(() -> {
if (groupKeyList.getModel().getSize() > 0) {
groupKeyList.setSelectedIndex(0);
} else if (type == DOMAIN) {
JOptionPane.showMessageDialog(DiscoveryTopComponent.getTopComponent(),
Bundle.GroupsListPanel_noDomainResults_message_text(),
Bundle.GroupsListPanel_noResults_title_text(),
JOptionPane.PLAIN_MESSAGE);
} else {
JOptionPane.showMessageDialog(DiscoveryTopComponent.getTopComponent(),
Bundle.GroupsListPanel_noFileResults_message_text(),
Bundle.GroupsListPanel_noResults_title_text(),
JOptionPane.PLAIN_MESSAGE);
}
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
});
});
}
@ -168,10 +174,9 @@ final class GroupListPanel extends javax.swing.JPanel {
/**
* Reset the group list to be empty.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void resetGroupList() {
SwingUtilities.invokeLater(() -> {
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
});
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
groupKeyList.setListData(new GroupKey[0]);
}
@ -211,6 +216,7 @@ final class GroupListPanel extends javax.swing.JPanel {
private static final long serialVersionUID = 1L;
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
public java.awt.Component getListCellRendererComponent(
JList<?> list,

View File

@ -27,6 +27,7 @@ import javax.swing.JLabel;
import javax.swing.JList;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.SearchFiltering;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
@ -43,6 +44,7 @@ final class HashSetFilterPanel extends AbstractDiscoveryFilterPanel {
/**
* Creates new form HashSetFilterPaenl.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
HashSetFilterPanel() {
initComponents();
setUpHashFilter();
@ -51,6 +53,7 @@ final class HashSetFilterPanel extends AbstractDiscoveryFilterPanel {
/**
* Initialize the hash filter.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void setUpHashFilter() {
int count = 0;
try {
@ -123,6 +126,7 @@ final class HashSetFilterPanel extends AbstractDiscoveryFilterPanel {
private javax.swing.JScrollPane hashSetScrollPane;
// End of variables declaration//GEN-END:variables
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
void configurePanel(boolean selected, int[] indicesSelected) {
boolean hasHashSets = hashSetList.getModel().getSize() > 0;
@ -140,6 +144,7 @@ final class HashSetFilterPanel extends AbstractDiscoveryFilterPanel {
}
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
JCheckBox getCheckbox() {
return hashSetCheckbox;
@ -150,6 +155,7 @@ final class HashSetFilterPanel extends AbstractDiscoveryFilterPanel {
return null;
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@NbBundle.Messages({"HashSetFilterPanel.error.text=At least one hash set name must be selected."})
@Override
String checkForError() {
@ -159,11 +165,13 @@ final class HashSetFilterPanel extends AbstractDiscoveryFilterPanel {
return "";
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
JList<?> getList() {
return hashSetList;
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
AbstractFilter getFilter() {
if (hashSetCheckbox.isSelected()) {

View File

@ -19,6 +19,7 @@
package org.sleuthkit.autopsy.discovery.ui;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.SearchData;
/**
@ -32,6 +33,7 @@ final class ImageFilterPanel extends AbstractFiltersPanel {
/**
* Creates new form ImageFilterPanel.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
ImageFilterPanel() {
super();
initComponents();

View File

@ -84,7 +84,6 @@
</Component>
<Component class="javax.swing.JLabel" name="nameLabel">
<Properties>
<Property name="toolTipText" type="java.lang.String" value=""/>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[159, 12]"/>
</Property>
@ -94,6 +93,7 @@
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[159, 12]"/>
</Property>
<Property name="toolTipText" type="java.lang.String" value=""/>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="isDeletedLabel">
@ -101,9 +101,6 @@
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/images/file-icon-deleted.png"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/discovery/ui/Bundle.properties" key="ImageThumbnailPanel.isDeletedLabel.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new Dimension(org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize(),org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize())" type="code"/>
</Property>
@ -113,6 +110,9 @@
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new Dimension(org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize(),org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize())" type="code"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/discovery/ui/Bundle.properties" key="ImageThumbnailPanel.isDeletedLabel.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="scoreLabel">
@ -120,7 +120,6 @@
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/images/red-circle-exclamation.png"/>
</Property>
<Property name="toolTipText" type="java.lang.String" value=""/>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new Dimension(org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize(),org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize())" type="code"/>
</Property>
@ -130,6 +129,7 @@
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new Dimension(org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize(),org.sleuthkit.autopsy.discovery.ui.DiscoveryUiUtils.getIconSize())" type="code"/>
</Property>
<Property name="toolTipText" type="java.lang.String" value=""/>
</Properties>
</Component>
</SubComponents>

View File

@ -28,6 +28,7 @@ import javax.swing.JComponent;
import javax.swing.JList;
import javax.swing.ListCellRenderer;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
/**
* Class which displays a thumbnail and information for an image file.
@ -41,6 +42,7 @@ final class ImageThumbnailPanel extends javax.swing.JPanel implements ListCellRe
/**
* Creates new form ImageThumbnailPanel
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
ImageThumbnailPanel() {
initComponents();
}
@ -129,6 +131,7 @@ final class ImageThumbnailPanel extends javax.swing.JPanel implements ListCellRe
private javax.swing.JLabel thumbnailLabel;
// End of variables declaration//GEN-END:variables
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@NbBundle.Messages({
"# {0} - otherInstanceCount",
"ImageThumbnailPanel.nameLabel.more.text= and {0} more",
@ -152,6 +155,7 @@ final class ImageThumbnailPanel extends javax.swing.JPanel implements ListCellRe
return this;
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
public String getToolTipText(MouseEvent event) {
if (event != null) {

View File

@ -22,6 +22,7 @@ import java.util.ArrayList;
import java.util.List;
import javax.swing.DefaultListModel;
import javax.swing.event.ListSelectionListener;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.datamodel.AbstractFile;
/**
@ -37,6 +38,7 @@ final class ImageThumbnailViewer extends javax.swing.JPanel {
/**
* Creates new form ImageThumbnailViewer.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
ImageThumbnailViewer() {
initComponents();
@ -77,6 +79,7 @@ final class ImageThumbnailViewer extends javax.swing.JPanel {
*
* @param listener The ListSelectionListener to add to the selection model.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void addListSelectionListener(ListSelectionListener listener) {
thumbnailList.getSelectionModel().addListSelectionListener(listener);
}
@ -88,24 +91,23 @@ final class ImageThumbnailViewer extends javax.swing.JPanel {
* @return The list of AbstractFiles which are represented by the selected
* image thumbnail.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
List<AbstractFile> getInstancesForSelected() {
synchronized (this) {
if (thumbnailList.getSelectedIndex() == -1) {
return new ArrayList<>();
} else {
return thumbnailListModel.getElementAt(thumbnailList.getSelectedIndex()).getResultFile().getAllInstances();
}
if (thumbnailList.getSelectedIndex() == -1) {
return new ArrayList<>();
} else {
return thumbnailListModel.getElementAt(thumbnailList.getSelectedIndex()).getResultFile().getAllInstances();
}
}
/**
* Clear the list of thumbnails being displayed.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void clearViewer() {
synchronized (this) {
thumbnailListModel.removeAllElements();
thumbnailListScrollPane.getVerticalScrollBar().setValue(0);
}
thumbnailListModel.removeAllElements();
thumbnailListScrollPane.getVerticalScrollBar().setValue(0);
}
/**
@ -114,9 +116,8 @@ final class ImageThumbnailViewer extends javax.swing.JPanel {
* @param thumbnailWrapper The object which contains the thumbnail which
* will be displayed.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void addImage(ImageThumbnailWrapper thumbnailWrapper) {
synchronized (this) {
thumbnailListModel.addElement(thumbnailWrapper);
}
thumbnailListModel.addElement(thumbnailWrapper);
}
}

View File

@ -27,6 +27,7 @@ import javax.swing.JLabel;
import javax.swing.JList;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.SearchFiltering;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
@ -43,6 +44,7 @@ final class InterestingItemsFilterPanel extends AbstractDiscoveryFilterPanel {
/**
* Creates new form InterestingItemsFilterPanel.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
InterestingItemsFilterPanel() {
initComponents();
setUpInterestingItemsFilter();
@ -51,6 +53,7 @@ final class InterestingItemsFilterPanel extends AbstractDiscoveryFilterPanel {
/**
* Initialize the interesting items filter.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void setUpInterestingItemsFilter() {
int count = 0;
try {
@ -118,6 +121,7 @@ final class InterestingItemsFilterPanel extends AbstractDiscoveryFilterPanel {
interestingItemsList.setEnabled(interestingItemsCheckbox.isSelected());
}//GEN-LAST:event_interestingItemsCheckboxActionPerformed
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
void configurePanel(boolean selected, int[] indicesSelected) {
boolean hasInterestingItems = interestingItemsList.getModel().getSize() > 0;
@ -135,6 +139,7 @@ final class InterestingItemsFilterPanel extends AbstractDiscoveryFilterPanel {
}
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
JCheckBox getCheckbox() {
return interestingItemsCheckbox;
@ -145,6 +150,7 @@ final class InterestingItemsFilterPanel extends AbstractDiscoveryFilterPanel {
return null;
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@NbBundle.Messages({"InterestingItemsFilterPanel.error.text=At least one interesting file set name must be selected."})
@Override
String checkForError() {
@ -161,11 +167,13 @@ final class InterestingItemsFilterPanel extends AbstractDiscoveryFilterPanel {
private javax.swing.JScrollPane interestingItemsScrollPane;
// End of variables declaration//GEN-END:variables
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
JList<?> getList() {
return interestingItemsList;
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
AbstractFilter getFilter() {
if (interestingItemsCheckbox.isSelected()) {

View File

@ -27,6 +27,7 @@ import javax.swing.JLabel;
import javax.swing.JList;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.SearchFiltering;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
@ -43,6 +44,7 @@ final class ObjectDetectedFilterPanel extends AbstractDiscoveryFilterPanel {
/**
* Creates new form ObjectDetectedFilter.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
ObjectDetectedFilterPanel() {
initComponents();
setUpObjectFilter();
@ -51,6 +53,7 @@ final class ObjectDetectedFilterPanel extends AbstractDiscoveryFilterPanel {
/**
* Initialize the object filter.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void setUpObjectFilter() {
int count = 0;
try {
@ -129,6 +132,7 @@ final class ObjectDetectedFilterPanel extends AbstractDiscoveryFilterPanel {
private javax.swing.JScrollPane objectsScrollPane;
// End of variables declaration//GEN-END:variables
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
void configurePanel(boolean selected, int[] indicesSelected) {
boolean hasObjects = objectsList.getModel().getSize() > 0;
@ -146,6 +150,7 @@ final class ObjectDetectedFilterPanel extends AbstractDiscoveryFilterPanel {
}
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
JCheckBox getCheckbox() {
return objectsCheckbox;
@ -155,6 +160,8 @@ final class ObjectDetectedFilterPanel extends AbstractDiscoveryFilterPanel {
JLabel getAdditionalLabel() {
return null;
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@NbBundle.Messages({"ObjectDetectedFilterPanel.error.text=At least one object type name must be selected."})
@Override
String checkForError() {
@ -164,11 +171,13 @@ final class ObjectDetectedFilterPanel extends AbstractDiscoveryFilterPanel {
return "";
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
JList<?> getList() {
return objectsList;
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
AbstractFilter getFilter() {
if (objectsCheckbox.isSelected()) {

View File

@ -31,6 +31,7 @@ import org.openide.util.NbBundle;
import org.openide.util.actions.CallableSystemAction;
import org.openide.util.actions.Presenter;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
/**
* Class to open the Discovery dialog. Allows the user to run searches and see
@ -76,6 +77,7 @@ public final class OpenDiscoveryAction extends CallableSystemAction implements P
*
* @return The toolbar button
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
public Component getToolbarPresenter() {
ImageIcon icon = new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/discovery-icon-24.png")); //NON-NLS

View File

@ -26,6 +26,7 @@ import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JList;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.SearchFiltering;
import org.sleuthkit.autopsy.discovery.search.SearchFiltering.ParentSearchTerm;
@ -41,6 +42,7 @@ final class ParentFolderFilterPanel extends AbstractDiscoveryFilterPanel {
/**
* Creates new form ParentFolderFilterPanel.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
ParentFolderFilterPanel() {
initComponents();
setUpParentPathFilter();
@ -49,6 +51,7 @@ final class ParentFolderFilterPanel extends AbstractDiscoveryFilterPanel {
/**
* Initialize the parent path filter.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void setUpParentPathFilter() {
fullRadioButton.setSelected(true);
includeRadioButton.setSelected(true);
@ -239,6 +242,7 @@ final class ParentFolderFilterPanel extends AbstractDiscoveryFilterPanel {
private javax.swing.JRadioButton substringRadioButton;
// End of variables declaration//GEN-END:variables
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
void configurePanel(boolean selected, int[] indicesSelected) {
parentCheckbox.setSelected(selected);
@ -270,16 +274,19 @@ final class ParentFolderFilterPanel extends AbstractDiscoveryFilterPanel {
}
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
JCheckBox getCheckbox() {
return parentCheckbox;
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
JLabel getAdditionalLabel() {
return parentLabel;
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@NbBundle.Messages({"ParentFolderFilterPanel.error.text=At least one parent path must be entered."})
@Override
String checkForError() {
@ -290,6 +297,7 @@ final class ParentFolderFilterPanel extends AbstractDiscoveryFilterPanel {
return "";
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
/**
* Utility method to get the parent path objects out of the JList.
*
@ -303,11 +311,13 @@ final class ParentFolderFilterPanel extends AbstractDiscoveryFilterPanel {
return results;
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
JList<?> getList() {
return parentList;
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
AbstractFilter getFilter() {
if (parentCheckbox.isSelected()) {

View File

@ -24,6 +24,7 @@ import javax.swing.JLabel;
import javax.swing.JList;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.AbstractFilter;
import org.sleuthkit.autopsy.discovery.search.SearchData;
import org.sleuthkit.autopsy.discovery.search.SearchData.Frequency;
@ -41,6 +42,7 @@ final class PastOccurrencesFilterPanel extends AbstractDiscoveryFilterPanel {
/**
* Creates new form PastOccurrencesFilterPanel.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
PastOccurrencesFilterPanel(Type type) {
initComponents();
this.type = type;
@ -101,6 +103,7 @@ final class PastOccurrencesFilterPanel extends AbstractDiscoveryFilterPanel {
/**
* Initialize the frequency filter.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void setUpFrequencyFilter() {
int count = 0;
DefaultListModel<SearchData.Frequency> frequencyListModel = (DefaultListModel<SearchData.Frequency>) crFrequencyList.getModel();
@ -126,6 +129,7 @@ final class PastOccurrencesFilterPanel extends AbstractDiscoveryFilterPanel {
private javax.swing.JCheckBox pastOccurrencesCheckbox;
// End of variables declaration//GEN-END:variables
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
void configurePanel(boolean selected, int[] indicesSelected) {
boolean canBeFilteredOn = type != Type.DOMAIN || CentralRepository.isEnabled();
@ -144,6 +148,7 @@ final class PastOccurrencesFilterPanel extends AbstractDiscoveryFilterPanel {
}
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
JCheckBox getCheckbox() {
return pastOccurrencesCheckbox;
@ -154,6 +159,7 @@ final class PastOccurrencesFilterPanel extends AbstractDiscoveryFilterPanel {
return null;
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@NbBundle.Messages({"PastOccurrencesFilterPanel.error.text=At least one value in the past occurrence filter must be selected."})
@Override
String checkForError() {
@ -163,11 +169,13 @@ final class PastOccurrencesFilterPanel extends AbstractDiscoveryFilterPanel {
return "";
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
JList<?> getList() {
return crFrequencyList;
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
AbstractFilter getFilter() {
if (pastOccurrencesCheckbox.isSelected()) {

View File

@ -40,6 +40,7 @@ import org.sleuthkit.autopsy.coreutils.ImageUtils;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.DiscoveryAttributes;
import org.sleuthkit.autopsy.discovery.search.DiscoveryEventUtils;
import org.sleuthkit.autopsy.discovery.search.DiscoveryKeyUtils.GroupKey;
@ -85,6 +86,7 @@ final class ResultsPanel extends javax.swing.JPanel {
*/
@Messages({"ResultsPanel.viewFileInDir.name=View File in Directory",
"ResultsPanel.openInExternalViewer.name=Open in External Viewer"})
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
ResultsPanel() {
initComponents();
imageThumbnailViewer = new ImageThumbnailViewer();
@ -125,9 +127,14 @@ final class ResultsPanel extends javax.swing.JPanel {
}
}
});
//JIRA-TODO 6307 Add listener for domainSummaryViewer when 6782, 6773, and the other details area related stories are done
domainSummaryViewer.addListSelectionListener((e) -> {
if (resultType == SearchData.Type.DOMAIN) {
domainSummaryViewer.sendPopulateEvent(!e.getValueIsAdjusting());
}
});
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
SearchData.Type getActiveType() {
return resultType;
}
@ -139,6 +146,7 @@ final class ResultsPanel extends javax.swing.JPanel {
* @return The list of AbstractFiles which are represented by the item
* selected in the results viewer area.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private List<AbstractFile> getInstancesForSelected() {
if (null != resultType) {
switch (resultType) {
@ -209,23 +217,25 @@ final class ResultsPanel extends javax.swing.JPanel {
}
);
}
@Subscribe
void handleCancelBackgroundTasksEvent(DiscoveryEventUtils.CancelBackgroundTasksEvent cancelEvent) {
for (SwingWorker<Void, Void> thumbWorker : resultContentWorkers) {
if (!thumbWorker.isDone()) {
thumbWorker.cancel(true);
SwingUtilities.invokeLater(() -> {
for (SwingWorker<Void, Void> thumbWorker : resultContentWorkers) {
if (!thumbWorker.isDone()) {
thumbWorker.cancel(true);
}
}
}
resultContentWorkers.clear();
resultContentWorkers.clear();
});
}
/**
* Reset the result viewer and any associate workers to a default empty
* state.
*/
synchronized void resetResultViewer() {
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void resetResultViewer() {
resultsViewerPanel.remove(imageThumbnailViewer);
resultsViewerPanel.remove(videoThumbnailViewer);
resultsViewerPanel.remove(documentPreviewViewer);
@ -250,7 +260,8 @@ final class ResultsPanel extends javax.swing.JPanel {
*
* @param results The list of ResultFiles to populate the video viewer with.
*/
synchronized void populateVideoViewer(List<Result> results) {
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void populateVideoViewer(List<Result> results) {
for (Result result : results) {
VideoThumbnailWorker thumbWorker = new VideoThumbnailWorker((ResultFile) result);
thumbWorker.execute();
@ -265,7 +276,8 @@ final class ResultsPanel extends javax.swing.JPanel {
*
* @param results The list of ResultFiles to populate the image viewer with.
*/
synchronized void populateImageViewer(List<Result> results) {
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void populateImageViewer(List<Result> results) {
for (Result result : results) {
ImageThumbnailWorker thumbWorker = new ImageThumbnailWorker((ResultFile) result);
thumbWorker.execute();
@ -281,7 +293,8 @@ final class ResultsPanel extends javax.swing.JPanel {
* @param results The list of ResultFiles to populate the document viewer
* with.
*/
synchronized void populateDocumentViewer(List<Result> results) {
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void populateDocumentViewer(List<Result> results) {
for (Result result : results) {
DocumentPreviewWorker documentWorker = new DocumentPreviewWorker((ResultFile) result);
documentWorker.execute();
@ -297,7 +310,8 @@ final class ResultsPanel extends javax.swing.JPanel {
* @param results The list of ResultDomains to populate the domain summary
* viewer with.
*/
synchronized void populateDomainViewer(List<Result> results) {
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void populateDomainViewer(List<Result> results) {
SleuthkitCase currentCase;
try {
currentCase = Case.getCurrentCaseThrows().getSleuthkitCase();
@ -374,37 +388,33 @@ final class ResultsPanel extends javax.swing.JPanel {
* @param startingEntry The index of the first file in the group to include
* in this page.
*/
@Subscribe
private synchronized void setPage(int startingEntry
) {
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void setPage(int startingEntry) {
int pageSize = pageSizeComboBox.getItemAt(pageSizeComboBox.getSelectedIndex());
synchronized (this) {
if (pageWorker != null && !pageWorker.isDone()) {
pageWorker.cancel(true);
}
CentralRepository centralRepo = null;
if (CentralRepository.isEnabled()) {
try {
centralRepo = CentralRepository.getInstance();
} catch (CentralRepoException ex) {
centralRepo = null;
logger.log(Level.SEVERE, "Error loading central repository database, no central repository options will be available for Discovery", ex);
}
}
if (groupSize != 0) {
pageWorker = new PageWorker(searchFilters, groupingAttribute, groupSort, fileSortMethod, selectedGroupKey, startingEntry, pageSize, resultType, centralRepo);
pageWorker.execute();
} else {
SwingUtilities.invokeLater(() -> {
pageSizeComboBox.setEnabled(true);
});
if (pageWorker != null && !pageWorker.isDone()) {
pageWorker.cancel(true);
}
CentralRepository centralRepo = null;
if (CentralRepository.isEnabled()) {
try {
centralRepo = CentralRepository.getInstance();
} catch (CentralRepoException ex) {
centralRepo = null;
logger.log(Level.SEVERE, "Error loading central repository database, no central repository options will be available for Discovery", ex);
}
}
if (groupSize != 0) {
pageWorker = new PageWorker(searchFilters, groupingAttribute, groupSort, fileSortMethod, selectedGroupKey, startingEntry, pageSize, resultType, centralRepo);
pageWorker.execute();
} else {
pageSizeComboBox.setEnabled(true);
}
}
/**
* Enable the paging controls based on what exists in the page.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Messages({"# {0} - currentPage",
"# {1} - totalPages",
"ResultsPanel.currentPage.displayValue=Page: {0} of {1}"})
@ -678,6 +688,7 @@ final class ResultsPanel extends javax.swing.JPanel {
/**
* Disable all the paging controls.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void disablePagingControls() {
nextPageButton.setEnabled(false);
previousPageButton.setEnabled(false);
@ -708,6 +719,7 @@ final class ResultsPanel extends javax.swing.JPanel {
* @param file The ResultFile which represents the video file thumbnails
* are being retrieved for.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
VideoThumbnailWorker(ResultFile file) {
thumbnailWrapper = new VideoThumbnailsWrapper(file);
videoThumbnailViewer.addVideo(thumbnailWrapper);
@ -746,6 +758,7 @@ final class ResultsPanel extends javax.swing.JPanel {
* @param file The ResultFile which represents the image file thumbnails
* are being retrieved for.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
ImageThumbnailWorker(ResultFile file) {
thumbnailWrapper = new ImageThumbnailWrapper(file);
imageThumbnailViewer.addImage(thumbnailWrapper);
@ -788,6 +801,7 @@ final class ResultsPanel extends javax.swing.JPanel {
* @param file The ResultFile which represents the document file a
* preview is being retrieved for.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
DocumentPreviewWorker(ResultFile file) {
documentWrapper = new DocumentWrapper(file);
documentPreviewViewer.addDocument(documentWrapper);
@ -836,6 +850,7 @@ final class ResultsPanel extends javax.swing.JPanel {
* @param file The ResultFile which represents the domain attribute the
* preview is being retrieved for.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
DomainThumbnailWorker(SleuthkitCase caseDb, ResultDomain domain) {
this.caseDb = caseDb;
domainWrapper = new DomainWrapper(domain);

View File

@ -19,6 +19,7 @@
package org.sleuthkit.autopsy.discovery.ui;
import java.awt.Cursor;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.DiscoveryEventUtils;
@ -32,6 +33,7 @@ final class ResultsSplitPaneDivider extends javax.swing.JPanel {
/**
* Creates new form LabeledSplitPaneDivider.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
ResultsSplitPaneDivider() {
initComponents();
}

View File

@ -26,6 +26,7 @@ import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JList;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.SearchData;
import org.sleuthkit.autopsy.discovery.search.SearchData.FileSize;
import org.sleuthkit.autopsy.discovery.search.SearchFiltering;
@ -42,6 +43,7 @@ final class SizeFilterPanel extends AbstractDiscoveryFilterPanel {
*
* @param type The type of result being searched for.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
SizeFilterPanel(SearchData.Type type) {
initComponents();
setUpSizeFilter(type);
@ -109,6 +111,7 @@ final class SizeFilterPanel extends AbstractDiscoveryFilterPanel {
private javax.swing.JScrollPane sizeScrollPane;
// End of variables declaration//GEN-END:variables
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
void configurePanel(boolean selected, int[] indicesSelected) {
sizeCheckbox.setSelected(selected);
@ -124,6 +127,7 @@ final class SizeFilterPanel extends AbstractDiscoveryFilterPanel {
}
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
JCheckBox getCheckbox() {
return sizeCheckbox;
@ -137,6 +141,7 @@ final class SizeFilterPanel extends AbstractDiscoveryFilterPanel {
/**
* Initialize the file size filter.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void setUpSizeFilter(SearchData.Type fileType) {
int count = 0;
DefaultListModel<FileSize> sizeListModel = (DefaultListModel<FileSize>) sizeList.getModel();
@ -169,6 +174,7 @@ final class SizeFilterPanel extends AbstractDiscoveryFilterPanel {
@NbBundle.Messages({"SizeFilterPanel.error.text=At least one size must be selected."})
@Override
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
String checkForError() {
if (sizeCheckbox.isSelected() && sizeList.getSelectedValuesList().isEmpty()) {
return Bundle.SizeFilterPanel_error_text();
@ -177,11 +183,13 @@ final class SizeFilterPanel extends AbstractDiscoveryFilterPanel {
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
JList<?> getList() {
return sizeList;
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
AbstractFilter getFilter() {
if (sizeCheckbox.isSelected()) {

View File

@ -22,6 +22,7 @@ import org.sleuthkit.autopsy.discovery.search.AbstractFilter;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JList;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.SearchFiltering;
/**
@ -34,6 +35,7 @@ final class UserCreatedFilterPanel extends AbstractDiscoveryFilterPanel {
/**
* Creates new form UserCreatedFilterPanel.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
UserCreatedFilterPanel() {
initComponents();
}
@ -69,11 +71,13 @@ final class UserCreatedFilterPanel extends AbstractDiscoveryFilterPanel {
);
}// </editor-fold>//GEN-END:initComponents
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
void configurePanel(boolean selected, int[] indicesSelected) {
userCreatedCheckbox.setSelected(selected);
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
JCheckBox getCheckbox() {
return userCreatedCheckbox;
@ -99,6 +103,7 @@ final class UserCreatedFilterPanel extends AbstractDiscoveryFilterPanel {
return null;
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
AbstractFilter getFilter() {
if (userCreatedCheckbox.isSelected()) {

View File

@ -19,6 +19,7 @@
package org.sleuthkit.autopsy.discovery.ui;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.SearchData;
/**
@ -32,6 +33,7 @@ final class VideoFilterPanel extends AbstractFiltersPanel {
/**
* Creates new form VideoFilterPanel.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
VideoFilterPanel() {
super();
initComponents();

View File

@ -32,6 +32,7 @@ import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.ListCellRenderer;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
/**
* Class which displays thumbnails and information for a video file.
@ -47,6 +48,7 @@ final class VideoThumbnailPanel extends javax.swing.JPanel implements ListCellRe
/**
* Creates new form VideoThumbnailPanel.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
VideoThumbnailPanel() {
initComponents();
this.setFocusable(true);
@ -58,6 +60,7 @@ final class VideoThumbnailPanel extends javax.swing.JPanel implements ListCellRe
* @param thumbnailWrapper The object which contains the video thumbnails to
* add.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void addThumbnails(VideoThumbnailsWrapper thumbnailWrapper) {
imagePanel.removeAll();
GridBagConstraints gridBagConstraints = new GridBagConstraints();
@ -164,6 +167,7 @@ final class VideoThumbnailPanel extends javax.swing.JPanel implements ListCellRe
private javax.swing.JLabel scoreLabel;
// End of variables declaration//GEN-END:variables
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Messages({
"# {0} - otherInstanceCount",
"VideoThumbnailPanel.nameLabel.more.text= and {0} more",
@ -231,6 +235,7 @@ final class VideoThumbnailPanel extends javax.swing.JPanel implements ListCellRe
return Bundle.VideoThumbnailPanel_sizeLabel_text(size, units);
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
public String getToolTipText(MouseEvent event) {
if (event != null) {

View File

@ -22,6 +22,7 @@ import java.util.ArrayList;
import java.util.List;
import javax.swing.DefaultListModel;
import javax.swing.event.ListSelectionListener;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.datamodel.AbstractFile;
/**
@ -36,6 +37,7 @@ final class VideoThumbnailViewer extends javax.swing.JPanel {
/**
* Creates new form VideoThumbnailViewer.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
VideoThumbnailViewer() {
initComponents();
}
@ -45,6 +47,7 @@ final class VideoThumbnailViewer extends javax.swing.JPanel {
*
* @param listener The ListSelectionListener to add to the selection model.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void addListSelectionListener(ListSelectionListener listener) {
thumbnailList.getSelectionModel().addListSelectionListener(listener);
}
@ -56,24 +59,22 @@ final class VideoThumbnailViewer extends javax.swing.JPanel {
* @return The list of AbstractFiles which are represented by the selected
* Video thumbnails.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
List<AbstractFile> getInstancesForSelected() {
synchronized (this) {
if (thumbnailList.getSelectedIndex() == -1) {
return new ArrayList<>();
} else {
return thumbnailListModel.getElementAt(thumbnailList.getSelectedIndex()).getResultFile().getAllInstances();
}
if (thumbnailList.getSelectedIndex() == -1) {
return new ArrayList<>();
} else {
return thumbnailListModel.getElementAt(thumbnailList.getSelectedIndex()).getResultFile().getAllInstances();
}
}
/**
* Clear the list of thumbnails being displayed.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void clearViewer() {
synchronized (this) {
thumbnailListModel.removeAllElements();
thumbnailListScrollPane.getVerticalScrollBar().setValue(0);
}
thumbnailListModel.removeAllElements();
thumbnailListScrollPane.getVerticalScrollBar().setValue(0);
}
/**
@ -82,10 +83,9 @@ final class VideoThumbnailViewer extends javax.swing.JPanel {
* @param thumbnailWrapper The object which contains the thumbnails which
* will be displayed.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void addVideo(VideoThumbnailsWrapper thumbnailWrapper) {
synchronized (this) {
thumbnailListModel.addElement(thumbnailWrapper);
}
thumbnailListModel.addElement(thumbnailWrapper);
}
/**

View File

@ -0,0 +1,28 @@
<?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">
<EmptySpace min="0" pref="400" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<EmptySpace min="0" pref="300" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
</Layout>
</Form>

View File

@ -0,0 +1,222 @@
/*
* 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.ui;
import java.awt.Component;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import javax.swing.JScrollPane;
import org.openide.util.NbBundle;
import org.openide.util.lookup.ServiceProvider;
import org.sleuthkit.autopsy.contentviewers.artifactviewers.ArtifactContentViewer;
import org.sleuthkit.autopsy.contentviewers.artifactviewers.CommunicationArtifactViewerHelper;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Panel to display the details for a Web History Artifact.
*/
@ServiceProvider(service = ArtifactContentViewer.class)
public class WebHistoryDetailsPanel extends AbstractArtifactDetailsPanel implements ArtifactContentViewer {
private static final long serialVersionUID = 1L;
private static final Logger logger = Logger.getLogger(WebHistoryDetailsPanel.class.getName());
private BlackboardArtifact webHistoryArtifact;
private final GridBagLayout gridBagLayout = new GridBagLayout();
private final List<BlackboardAttribute> urlList = new ArrayList<>();
private final List<BlackboardAttribute> dateAccessedList = new ArrayList<>();
private final List<BlackboardAttribute> referrerUrlList = new ArrayList<>();
private final List<BlackboardAttribute> titleList = new ArrayList<>();
private final List<BlackboardAttribute> programNameList = new ArrayList<>();
private final List<BlackboardAttribute> domainList = new ArrayList<>();
private final List<BlackboardAttribute> otherList = new ArrayList<>();
private final GridBagConstraints gridBagConstraints = new GridBagConstraints();
private String dataSourceName;
private String sourceFileName;
/**
* Creates new form WebHistoryDetailsPanel.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
public WebHistoryDetailsPanel() {
initComponents();
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
public void setArtifact(BlackboardArtifact artifact) {
resetComponent();
if (artifact != null) {
try {
extractArtifactData(artifact);
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Unable to get attributes for artifact " + artifact.getArtifactID(), ex);
}
updateView();
}
this.setLayout(this.gridBagLayout);
this.revalidate();
this.repaint();
}
/**
* Extracts data from the artifact to be displayed in the panel.
*
* @param artifact Artifact to show.
*
* @throws TskCoreException
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void extractArtifactData(BlackboardArtifact artifact) throws TskCoreException {
webHistoryArtifact = artifact;
// Get all the attributes and group them by the section panels they go in
for (BlackboardAttribute bba : webHistoryArtifact.getAttributes()) {
if (bba.getAttributeType().getTypeName().startsWith("TSK_URL")) {
urlList.add(bba);
} else if (bba.getAttributeType().getTypeName().startsWith("TSK_PROG_NAME")) {
programNameList.add(bba);
} else if (bba.getAttributeType().getTypeName().startsWith("TSK_DOMAIN")) {
domainList.add(bba);
} else if (bba.getAttributeType().getTypeName().startsWith("TSK_REFERRER")) {
referrerUrlList.add(bba);
} else if (bba.getAttributeType().getTypeName().startsWith("TSK_DATETIME_ACCESSED")) {
dateAccessedList.add(bba);
} else if (bba.getAttributeType().getTypeName().startsWith("TSK_TITLE")) {
titleList.add(bba);
} else {
otherList.add(bba);
}
}
dataSourceName = webHistoryArtifact.getDataSource().getName();
sourceFileName = webHistoryArtifact.getParent().getName();
}
/**
* Reset the panel so that it is empty.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void resetComponent() {
// clear the panel
this.removeAll();
gridBagConstraints.anchor = GridBagConstraints.FIRST_LINE_START;
gridBagConstraints.gridy = 0;
gridBagConstraints.gridx = 0;
gridBagConstraints.weighty = 0.0;
gridBagConstraints.weightx = 0.0; // keep components fixed horizontally.
gridBagConstraints.insets = new java.awt.Insets(0, 12, 0, 0);
gridBagConstraints.fill = GridBagConstraints.NONE;
webHistoryArtifact = null;
dataSourceName = null;
sourceFileName = null;
urlList.clear();
dateAccessedList.clear();
referrerUrlList.clear();
titleList.clear();
programNameList.clear();
domainList.clear();
otherList.clear();
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
public Component getComponent() {
// Slap a vertical scrollbar on the panel.
return new JScrollPane(this, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
public boolean isSupported(BlackboardArtifact artifact) {
return (artifact != null)
&& (artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY.getTypeID());
}
@NbBundle.Messages({"WebHistoryDetailsPanel.details.attrHeader=Attributes",
"WebHistoryDetailsPanel.details.sourceHeader=Source",
"WebHistoryDetailsPanel.details.dataSource=Data Source",
"WebHistoryDetailsPanel.details.file=File"})
/**
* 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.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 400, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 300, Short.MAX_VALUE)
);
}// </editor-fold>//GEN-END:initComponents
/**
* Update the view to reflect the current artifact's details.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private void updateView() {
CommunicationArtifactViewerHelper.addHeader(this, gridBagLayout, gridBagConstraints, Bundle.WebHistoryDetailsPanel_details_attrHeader());
for (BlackboardAttribute bba : this.titleList) {
CommunicationArtifactViewerHelper.addNameValueRow(this, gridBagLayout, gridBagConstraints, bba.getAttributeType().getDisplayName(), bba.getDisplayString());
}
for (BlackboardAttribute bba : dateAccessedList) {
CommunicationArtifactViewerHelper.addNameValueRow(this, gridBagLayout, gridBagConstraints, bba.getAttributeType().getDisplayName(), bba.getDisplayString());
}
for (BlackboardAttribute bba : domainList) {
CommunicationArtifactViewerHelper.addNameValueRow(this, gridBagLayout, gridBagConstraints, bba.getAttributeType().getDisplayName(), bba.getDisplayString());
}
for (BlackboardAttribute bba : urlList) {
CommunicationArtifactViewerHelper.addNameValueRow(this, gridBagLayout, gridBagConstraints, bba.getAttributeType().getDisplayName(), bba.getDisplayString());
}
for (BlackboardAttribute bba : referrerUrlList) {
CommunicationArtifactViewerHelper.addNameValueRow(this, gridBagLayout, gridBagConstraints, bba.getAttributeType().getDisplayName(), bba.getDisplayString());
}
for (BlackboardAttribute bba : programNameList) {
CommunicationArtifactViewerHelper.addNameValueRow(this, gridBagLayout, gridBagConstraints, bba.getAttributeType().getDisplayName(), bba.getDisplayString());
}
for (BlackboardAttribute bba : otherList) {
CommunicationArtifactViewerHelper.addNameValueRow(this, gridBagLayout, gridBagConstraints, bba.getAttributeType().getDisplayName(), bba.getDisplayString());
}
CommunicationArtifactViewerHelper.addHeader(this, gridBagLayout, gridBagConstraints, Bundle.WebHistoryDetailsPanel_details_sourceHeader());
CommunicationArtifactViewerHelper.addNameValueRow(this, gridBagLayout, gridBagConstraints, Bundle.WebHistoryDetailsPanel_details_dataSource(), dataSourceName);
CommunicationArtifactViewerHelper.addNameValueRow(this, gridBagLayout, gridBagConstraints, Bundle.WebHistoryDetailsPanel_details_file(), sourceFileName);
// add veritcal glue at the end
CommunicationArtifactViewerHelper.addPageEndGlue(this, gridBagLayout, this.gridBagConstraints);
}
// Variables declaration - do not modify//GEN-BEGIN:variables
// End of variables declaration//GEN-END:variables
}

View File

@ -49,6 +49,7 @@ SizeSearchPanel.sizeCompareComboBox.lessThan=less than
FileSearchPanel.searchButton.text=Search
MimeTypePanel.mimeTypeCheckBox.text=MIME Type:
HashSearchPanel.md5CheckBox.text=MD5:
Sha256HashSearchPanel.sha256CheckBox.text=SHA-256:
HashSearchPanel.emptyHashMsg.text=Must enter something for hash search.
FileSearchPanel.errorLabel.text=\
DataSourcePanel.dataSourceCheckBox.label=Data Source:
@ -58,3 +59,5 @@ DataSourcePanel.dataSourceNoteLabel.text=*Note: Multiple data sources can be sel
DateSearchPanel.noLimitLabel.text=*Empty fields mean "No Limit"
DateSearchPanel.dateFormatLabel.text=*The date format is mm/dd/yyyy
MimeTypePanel.noteLabel.text=*Note: Multiple MIME types can be selected
HashSearchPanel.sha256CheckBox.text=SHA-256:
HashSearchPanel.sha256TextField.text=

View File

@ -5,7 +5,9 @@ FileSearchPanel.emptyNode.display.text=No results found.
HashSearchFilter.errorMessage.emptyHash=Hash data is empty.
HashSearchFilter.errorMessage.wrongCharacter=MD5 contains invalid hex characters.
# {0} - hash data length
HashSearchFilter.errorMessage.wrongLength=Input length({0}), doesn''t match the MD5 length(32).
HashSearchFilter.errorMessage.wrongLengthMd5=Input length({0}), doesn''t match the MD5 length(32).
# {0} - hash data length
HashSearchFilter.errorMessage.wrongLengthSha256=Input length({0}), doesn''t match the SHA-256 length(64).
KnownStatusSearchFilter.errorMessage.noKnownStatusCheckboxSelected=At least one known status checkbox must be selected.
MimeTypeFilter.errorMessage.emptyMimeType=At least one MIME type must be selected.
NameSearchFilter.errorMessage.emtpyName=Please input a name to search.
@ -63,6 +65,7 @@ SizeSearchPanel.sizeCompareComboBox.lessThan=less than
FileSearchPanel.searchButton.text=Search
MimeTypePanel.mimeTypeCheckBox.text=MIME Type:
HashSearchPanel.md5CheckBox.text=MD5:
Sha256HashSearchPanel.sha256CheckBox.text=SHA-256:
HashSearchPanel.emptyHashMsg.text=Must enter something for hash search.
FileSearchPanel.errorLabel.text=\
DataSourcePanel.dataSourceCheckBox.label=Data Source:
@ -72,3 +75,5 @@ DataSourcePanel.dataSourceNoteLabel.text=*Note: Multiple data sources can be sel
DateSearchPanel.noLimitLabel.text=*Empty fields mean "No Limit"
DateSearchPanel.dateFormatLabel.text=*The date format is mm/dd/yyyy
MimeTypePanel.noteLabel.text=*Note: Multiple MIME types can be selected
HashSearchPanel.sha256CheckBox.text=SHA-256:
HashSearchPanel.sha256TextField.text=

View File

@ -41,18 +41,36 @@ class HashSearchFilter extends AbstractFileSearchFilter<HashSearchPanel> {
@Override
public boolean isEnabled() {
return this.getComponent().getHashCheckBox().isSelected();
return (this.getComponent().getMd5HashCheckBox().isSelected()
|| this.getComponent().getSha256HashCheckBox().isSelected());
}
@Override
public String getPredicate() throws FilterValidationException {
String md5Hash = this.getComponent().getSearchTextField().getText();
String predicate = "";
if (this.getComponent().getMd5HashCheckBox().isSelected()) {
String md5Hash = this.getComponent().getMd5TextField().getText();
if (md5Hash.isEmpty()) {
throw new FilterValidationException(EMPTY_HASH_MESSAGE);
if (md5Hash.isEmpty()) {
throw new FilterValidationException(EMPTY_HASH_MESSAGE);
}
predicate = "md5 = '" + md5Hash.toLowerCase() + "'"; //NON-NLS
}
if (this.getComponent().getSha256HashCheckBox().isSelected()) {
String sha256Hash = this.getComponent().getSha256TextField().getText();
if (sha256Hash.isEmpty()) {
throw new FilterValidationException(EMPTY_HASH_MESSAGE);
}
if (predicate.isEmpty()) {
predicate = "sha256 = '" + sha256Hash.toLowerCase() + "'"; //NON-NLS
} else {
predicate = "( " + predicate + " AND sha256 = '" + sha256Hash.toLowerCase() + "')"; //NON-NLS
}
}
return "md5 = '" + md5Hash.toLowerCase() + "'"; //NON-NLS
return predicate;
}
@Override
@ -63,23 +81,45 @@ class HashSearchFilter extends AbstractFileSearchFilter<HashSearchPanel> {
@Override
@Messages({
"HashSearchFilter.errorMessage.emptyHash=Hash data is empty.",
"# {0} - hash data length", "HashSearchFilter.errorMessage.wrongLength=Input length({0}), doesn''t match the MD5 length(32).",
"# {0} - hash data length",
"HashSearchFilter.errorMessage.wrongLengthMd5=Input length({0}), doesn''t match the MD5 length(32).",
"# {0} - hash data length",
"HashSearchFilter.errorMessage.wrongLengthSha256=Input length({0}), doesn''t match the SHA-256 length(64).",
"HashSearchFilter.errorMessage.wrongCharacter=MD5 contains invalid hex characters."
})
public boolean isValid() {
String inputHashData = this.getComponent().getSearchTextField().getText();
if (inputHashData.isEmpty()) {
setLastError(Bundle.HashSearchFilter_errorMessage_emptyHash());
return false;
if (this.getComponent().getMd5HashCheckBox().isSelected()) {
String inputHashData = this.getComponent().getMd5TextField().getText();
if (inputHashData.isEmpty()) {
setLastError(Bundle.HashSearchFilter_errorMessage_emptyHash());
return false;
}
if (inputHashData.length() != 32) {
setLastError(Bundle.HashSearchFilter_errorMessage_wrongLengthMd5(inputHashData.length()));
return false;
}
if (!inputHashData.matches("[0-9a-fA-F]+")) {
setLastError(Bundle.HashSearchFilter_errorMessage_wrongCharacter());
return false;
}
}
if (inputHashData.length() != 32) {
setLastError(Bundle.HashSearchFilter_errorMessage_wrongLength(inputHashData.length()));
return false;
}
if (!inputHashData.matches("[0-9a-fA-F]+")) {
setLastError(Bundle.HashSearchFilter_errorMessage_wrongCharacter());
return false;
if (this.getComponent().getSha256HashCheckBox().isSelected()) {
String inputHashData = this.getComponent().getSha256TextField().getText();
if (inputHashData.isEmpty()) {
setLastError(Bundle.HashSearchFilter_errorMessage_emptyHash());
return false;
}
if (inputHashData.length() != 64) {
setLastError(Bundle.HashSearchFilter_errorMessage_wrongLengthSha256(inputHashData.length()));
return false;
}
if (!inputHashData.matches("[0-9a-fA-F]+")) {
setLastError(Bundle.HashSearchFilter_errorMessage_wrongCharacter());
return false;
}
}
return true;
}
}

View File

@ -56,34 +56,67 @@
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
<Component id="hashCheckBox" min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Component id="sha256CheckBox" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="sha256TextField" pref="254" max="32767" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<Component id="md5CheckBox" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="md5TextField" max="32767" attributes="0"/>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Component id="searchTextField" min="-2" pref="247" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="103" groupAlignment="3" attributes="0">
<Component id="hashCheckBox" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="searchTextField" alignment="3" min="-2" max="-2" attributes="0"/>
<Group type="102" attributes="0">
<Group type="103" groupAlignment="3" attributes="0">
<Component id="md5CheckBox" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="md5TextField" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="sha256CheckBox" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="sha256TextField" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JCheckBox" name="hashCheckBox">
<Component class="javax.swing.JCheckBox" name="md5CheckBox">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/filesearch/Bundle.properties" key="HashSearchPanel.md5CheckBox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="hashCheckBoxActionPerformed"/>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="md5CheckBoxActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JTextField" name="searchTextField">
<Component class="javax.swing.JTextField" name="md5TextField">
</Component>
<Component class="javax.swing.JCheckBox" name="sha256CheckBox">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/filesearch/Bundle.properties" key="HashSearchPanel.sha256CheckBox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="sha256CheckBoxActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JTextField" name="sha256TextField">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/filesearch/Bundle.properties" key="HashSearchPanel.sha256TextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Form>

View File

@ -45,19 +45,19 @@ class HashSearchPanel extends javax.swing.JPanel {
private void customizeComponents() {
searchTextField.setComponentPopupMenu(rightClickMenu);
md5TextField.setComponentPopupMenu(rightClickMenu);
ActionListener actList = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
JMenuItem jmi = (JMenuItem) e.getSource();
if (jmi.equals(cutMenuItem)) {
searchTextField.cut();
md5TextField.cut();
} else if (jmi.equals(copyMenuItem)) {
searchTextField.copy();
md5TextField.copy();
} else if (jmi.equals(pasteMenuItem)) {
searchTextField.paste();
md5TextField.paste();
} else if (jmi.equals(selectAllMenuItem)) {
searchTextField.selectAll();
md5TextField.selectAll();
}
}
};
@ -65,7 +65,24 @@ class HashSearchPanel extends javax.swing.JPanel {
copyMenuItem.addActionListener(actList);
pasteMenuItem.addActionListener(actList);
selectAllMenuItem.addActionListener(actList);
this.searchTextField.getDocument().addDocumentListener(new DocumentListener() {
this.md5TextField.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void insertUpdate(DocumentEvent e) {
firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null);
}
@Override
public void removeUpdate(DocumentEvent e) {
firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null);
}
@Override
public void changedUpdate(DocumentEvent e) {
firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null);
}
});
this.sha256TextField.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void insertUpdate(DocumentEvent e) {
firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null);
@ -84,17 +101,27 @@ class HashSearchPanel extends javax.swing.JPanel {
}
JCheckBox getHashCheckBox() {
return hashCheckBox;
JCheckBox getMd5HashCheckBox() {
return md5CheckBox;
}
JTextField getSearchTextField() {
return searchTextField;
JTextField getMd5TextField() {
return md5TextField;
}
JCheckBox getSha256HashCheckBox() {
return sha256CheckBox;
}
JTextField getSha256TextField() {
return sha256TextField;
}
void setComponentsEnabled() {
boolean enabled = hashCheckBox.isSelected();
this.searchTextField.setEnabled(enabled);
boolean md5Enabled = md5CheckBox.isSelected();
this.md5TextField.setEnabled(md5Enabled);
boolean sha256Enabled = sha256CheckBox.isSelected();
this.sha256TextField.setEnabled(sha256Enabled);
}
/**
@ -111,8 +138,10 @@ class HashSearchPanel extends javax.swing.JPanel {
copyMenuItem = new javax.swing.JMenuItem();
pasteMenuItem = new javax.swing.JMenuItem();
selectAllMenuItem = new javax.swing.JMenuItem();
hashCheckBox = new javax.swing.JCheckBox();
searchTextField = new javax.swing.JTextField();
md5CheckBox = new javax.swing.JCheckBox();
md5TextField = new javax.swing.JTextField();
sha256CheckBox = new javax.swing.JCheckBox();
sha256TextField = new javax.swing.JTextField();
cutMenuItem.setText(org.openide.util.NbBundle.getMessage(HashSearchPanel.class, "NameSearchPanel.cutMenuItem.text")); // NOI18N
rightClickMenu.add(cutMenuItem);
@ -126,48 +155,75 @@ class HashSearchPanel extends javax.swing.JPanel {
selectAllMenuItem.setText(org.openide.util.NbBundle.getMessage(HashSearchPanel.class, "NameSearchPanel.selectAllMenuItem.text")); // NOI18N
rightClickMenu.add(selectAllMenuItem);
hashCheckBox.setText(org.openide.util.NbBundle.getMessage(HashSearchPanel.class, "HashSearchPanel.md5CheckBox.text")); // NOI18N
hashCheckBox.addActionListener(new java.awt.event.ActionListener() {
md5CheckBox.setText(org.openide.util.NbBundle.getMessage(HashSearchPanel.class, "HashSearchPanel.md5CheckBox.text")); // NOI18N
md5CheckBox.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
hashCheckBoxActionPerformed(evt);
md5CheckBoxActionPerformed(evt);
}
});
sha256CheckBox.setText(org.openide.util.NbBundle.getMessage(HashSearchPanel.class, "HashSearchPanel.sha256CheckBox.text")); // NOI18N
sha256CheckBox.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
sha256CheckBoxActionPerformed(evt);
}
});
sha256TextField.setText(org.openide.util.NbBundle.getMessage(HashSearchPanel.class, "HashSearchPanel.sha256TextField.text")); // NOI18N
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(0, 0, 0)
.addComponent(hashCheckBox)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(searchTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 247, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(0, 0, 0))
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(sha256CheckBox)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(sha256TextField, javax.swing.GroupLayout.DEFAULT_SIZE, 254, Short.MAX_VALUE))
.addGroup(layout.createSequentialGroup()
.addComponent(md5CheckBox)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(md5TextField)))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(hashCheckBox)
.addComponent(searchTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(md5CheckBox)
.addComponent(md5TextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(sha256CheckBox)
.addComponent(sha256TextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
);
}// </editor-fold>//GEN-END:initComponents
private void hashCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_hashCheckBoxActionPerformed
private void md5CheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_md5CheckBoxActionPerformed
setComponentsEnabled();
firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null);
}//GEN-LAST:event_hashCheckBoxActionPerformed
}//GEN-LAST:event_md5CheckBoxActionPerformed
private void sha256CheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_sha256CheckBoxActionPerformed
setComponentsEnabled();
firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null);
}//GEN-LAST:event_sha256CheckBoxActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JMenuItem copyMenuItem;
private javax.swing.JMenuItem cutMenuItem;
private javax.swing.JCheckBox hashCheckBox;
private javax.swing.JCheckBox md5CheckBox;
private javax.swing.JTextField md5TextField;
private javax.swing.JMenuItem pasteMenuItem;
private javax.swing.JPopupMenu rightClickMenu;
private javax.swing.JTextField searchTextField;
private javax.swing.JMenuItem selectAllMenuItem;
private javax.swing.JCheckBox sha256CheckBox;
private javax.swing.JTextField sha256TextField;
// End of variables declaration//GEN-END:variables
void addActionListener(ActionListener l) {
searchTextField.addActionListener(l);
md5TextField.addActionListener(l);
}
}

View File

@ -1274,6 +1274,9 @@ final class IngestJobPipeline {
}
}
}
// If a data source had no tasks in progress it may now be complete.
checkForStageCompleted();
}
/**

View File

@ -491,7 +491,7 @@ class DocumentEmbeddedContentExtractor {
try {
Path outputDirectory = Paths.get(getOutputFolderPath(parentFileName));
//Get map of attachment name -> location disk.
Map<String, Path> extractedAttachments = pdfExtractor.extract(
Map<String, PDFAttachmentExtractor.NewResourceData> extractedAttachments = pdfExtractor.extract(
new ReadContentInputStream(abstractFile), abstractFile.getId(),
outputDirectory);
@ -499,10 +499,11 @@ class DocumentEmbeddedContentExtractor {
List<ExtractedFile> extractedFiles = new ArrayList<>();
extractedAttachments.entrySet().forEach((pathEntry) -> {
String fileName = pathEntry.getKey();
Path writeLocation = pathEntry.getValue();
Path writeLocation = pathEntry.getValue().getPath();
int fileSize = pathEntry.getValue().getLength();
extractedFiles.add(new ExtractedFile(fileName,
getFileRelativePath(writeLocation.getFileName().toString()),
writeLocation.toFile().length()));
fileSize));
});
return extractedFiles;

View File

@ -117,6 +117,12 @@ public final class EmbeddedFileExtractorIngestModule extends FileIngestModuleAda
* while processing archive files.
*/
mapOfDepthTrees.put(jobId, new ConcurrentHashMap<>());
/**
* Initialize Java's Image I/O API so that image reading and writing
* (needed for image extraction) happens consistently through the
* same providers. See JIRA-6951 for more details.
*/
initializeImageIO();
}
/*
* Construct an embedded content extractor for processing Microsoft
@ -127,14 +133,6 @@ public final class EmbeddedFileExtractorIngestModule extends FileIngestModuleAda
} catch (NoCurrentCaseException ex) {
throw new IngestModuleException(Bundle.EmbeddedFileExtractorIngestModule_UnableToGetMSOfficeExtractor_errMsg(), ex);
}
/**
* Initialize Java's Image I/O API so that image reading and writing
* (needed for image extraction) happens consistently through the
* same providers. See JIRA-6951 for more details.
*/
initializeImageIO();
}
/**

View File

@ -73,7 +73,7 @@ final class PDFAttachmentExtractor {
* @throws SAXException
* @throws TikaException
*/
public Map<String, Path> extract(InputStream input, long parentID, Path outputDir) throws IOException, SAXException, TikaException {
public Map<String, NewResourceData> extract(InputStream input, long parentID, Path outputDir) throws IOException, SAXException, TikaException {
ExtractionPreconditions.checkArgument(Files.exists(outputDir),
String.format("Output directory: %s, does not exist.", outputDir.toString())); //NON-NLS
@ -139,8 +139,8 @@ final class PDFAttachmentExtractor {
try (EncodedFileOutputStream outputStream = new EncodedFileOutputStream(
new FileOutputStream(outputFile.toFile()), TskData.EncodingType.XOR1)){
IOUtils.copy(in, outputStream);
watcher.notify(name, outputFile);
int bytesCopied = IOUtils.copy(in, outputStream);
watcher.notify(name, outputFile, bytesCopied);
} catch (IOException ex) {
logger.log(Level.WARNING, String.format("Could not extract attachment %s into directory %s", //NON-NLS
uniqueExtractedName, outputFile), ex);
@ -148,6 +148,29 @@ final class PDFAttachmentExtractor {
}
}
/**
* Utility class to hold an extracted file's path and length.
* Note that we can not use the length of the file on disk because
* the XOR header has been added to it.
*/
static class NewResourceData {
private final Path path;
private final int length;
NewResourceData(Path path, int length) {
this.path = path;
this.length = length;
}
Path getPath() {
return path;
}
int getLength() {
return length;
}
}
/**
* Convenient wrapper for keeping track of new resource paths and the display
* name for each of these resources.
@ -157,17 +180,17 @@ final class PDFAttachmentExtractor {
*/
static class NewResourceWatcher {
private final Map<String, Path> newResourcePaths;
private final Map<String, NewResourceData> newResourcePaths;
public NewResourceWatcher() {
newResourcePaths = new HashMap<>();
}
public void notify(String name, Path newResource) {
newResourcePaths.put(name, newResource);
public void notify(String name, Path localPath, int length) {
newResourcePaths.put(name, new NewResourceData(localPath, length));
}
public Map<String, Path> getSnapshot() {
public Map<String, NewResourceData> getSnapshot() {
return newResourcePaths;
}
}

View File

@ -18,8 +18,8 @@
*/
package org.sleuthkit.autopsy.modules.hashdatabase;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
@ -208,10 +208,17 @@ public class HashDbIngestModule implements FileIngestModule {
// Safely get a reference to the totalsForIngestJobs object
IngestJobTotals totals = getTotalsForIngestJobs(jobId);
// calc hash value
String md5Hash = getHash(file, totals);
if (md5Hash == null) {
return ProcessResult.ERROR;
// calc hash values
try {
calculateHashes(file, totals);
} catch (TskCoreException ex) {
logger.log(Level.WARNING, String.format("Error calculating hash of file '%s' (id=%d).", file.getName(), file.getId()), ex); //NON-NLS
services.postMessage(IngestMessage.createErrorMessage(
HashLookupModuleFactory.getModuleName(),
NbBundle.getMessage(this.getClass(), "HashDbIngestModule.fileReadErrorMsg", file.getName()),
NbBundle.getMessage(this.getClass(), "HashDbIngestModule.calcHashValueErr",
file.getParentPath() + file.getName(),
file.isMetaFlagSet(TskData.TSK_FS_META_FLAG_ENUM.ALLOC) ? "Allocated File" : "Deleted File")));
}
// the processing result of handling this file
@ -451,50 +458,46 @@ public class HashDbIngestModule implements FileIngestModule {
}
/**
* Retrieves the md5 hash for a file or generates one if no one exists on
* the file.
* Generates hashes for the given file if they haven't already been set.
* Hashes are saved to the AbstractFile object.
*
* @param file The file in order to determine the hash.
* @param totals The timing metrics for this process.
*
* @return The found or determined md5 hash or null if none could be
* determined.
*/
private String getHash(AbstractFile file, IngestJobTotals totals) {
private void calculateHashes(AbstractFile file, IngestJobTotals totals) throws TskCoreException {
// First check if we've already calculated the hashes.
String md5Hash = file.getMd5Hash();
if (md5Hash != null && md5Hash.isEmpty()) {
return md5Hash;
String sha256Hash = file.getSha256Hash();
if ((md5Hash != null && ! md5Hash.isEmpty())
&& (sha256Hash != null && ! sha256Hash.isEmpty())) {
return;
}
try {
TimingMetric metric = HealthMonitor.getTimingMetric("Disk Reads: Hash calculation");
long calcstart = System.currentTimeMillis();
md5Hash = HashUtility.calculateMd5Hash(file);
if (file.getSize() > 0) {
// Surprisingly, the hash calculation does not seem to be correlated that
// strongly with file size until the files get large.
// Only normalize if the file size is greater than ~1MB.
if (file.getSize() < 1000000) {
HealthMonitor.submitTimingMetric(metric);
} else {
// In testing, this normalization gave reasonable resuls
HealthMonitor.submitNormalizedTimingMetric(metric, file.getSize() / 500000);
}
TimingMetric metric = HealthMonitor.getTimingMetric("Disk Reads: Hash calculation");
long calcstart = System.currentTimeMillis();
List<HashUtility.HashResult> newHashResults =
HashUtility.calculateHashes(file, Arrays.asList(HashUtility.HashType.MD5,HashUtility.HashType.SHA256 ));
if (file.getSize() > 0) {
// Surprisingly, the hash calculation does not seem to be correlated that
// strongly with file size until the files get large.
// Only normalize if the file size is greater than ~1MB.
if (file.getSize() < 1000000) {
HealthMonitor.submitTimingMetric(metric);
} else {
// In testing, this normalization gave reasonable resuls
HealthMonitor.submitNormalizedTimingMetric(metric, file.getSize() / 500000);
}
file.setMd5Hash(md5Hash);
long delta = (System.currentTimeMillis() - calcstart);
totals.totalCalctime.addAndGet(delta);
return md5Hash;
} catch (IOException ex) {
logger.log(Level.WARNING, String.format("Error calculating hash of file '%s' (id=%d).", file.getName(), file.getId()), ex); //NON-NLS
services.postMessage(IngestMessage.createErrorMessage(
HashLookupModuleFactory.getModuleName(),
NbBundle.getMessage(this.getClass(), "HashDbIngestModule.fileReadErrorMsg", file.getName()),
NbBundle.getMessage(this.getClass(), "HashDbIngestModule.calcHashValueErr",
file.getParentPath() + file.getName(),
file.isMetaFlagSet(TskData.TSK_FS_META_FLAG_ENUM.ALLOC) ? "Allocated File" : "Deleted File")));
return null;
}
for (HashUtility.HashResult hash : newHashResults) {
if (hash.getType().equals(HashUtility.HashType.MD5)) {
file.setMd5Hash(hash.getValue());
} else if (hash.getType().equals(HashUtility.HashType.SHA256)) {
file.setSha256Hash(hash.getValue());
}
}
long delta = (System.currentTimeMillis() - calcstart);
totals.totalCalctime.addAndGet(delta);
}
/**

View File

@ -198,7 +198,6 @@ final class VMExtractorIngestModule extends DataSourceIngestModuleAdapter {
// for extracted virtual machines there is no manifest XML file to read data source ID from so use parent data source ID.
// ingest the data sources
ingestVirtualMachineImage(Paths.get(folder, file));
logger.log(Level.INFO, "Ingest complete for virtual machine file {0} in folder {1}", new Object[]{file, folder}); //NON-NLS
} catch (InterruptedException ex) {
logger.log(Level.INFO, "Interrupted while ingesting virtual machine file " + file + " in folder " + folder, ex); //NON-NLS
} catch (IOException ex) {
@ -287,8 +286,8 @@ final class VMExtractorIngestModule extends DataSourceIngestModuleAdapter {
}
/*
* If the image was added, analyze it with the ingest modules for this
* ingest context.
* If the image was added, start analysis on it with the ingest modules for this
* ingest context. Note that this does not wait for ingest to complete.
*/
if (!dspCallback.vmDataSources.isEmpty()) {
Case.getCurrentCaseThrows().notifyDataSourceAdded(dspCallback.vmDataSources.get(0), taskId);
@ -300,7 +299,7 @@ final class VMExtractorIngestModule extends DataSourceIngestModuleAdapter {
IngestServices.getInstance().postMessage(IngestMessage.createMessage(IngestMessage.MessageType.INFO,
VMExtractorIngestModuleFactory.getModuleName(),
NbBundle.getMessage(this.getClass(), "VMExtractorIngestModule.addedVirtualMachineImage.message", vmFile.toString())));
IngestManager.getInstance().queueIngestJob(dataSourceContent, ingestJobSettings);
IngestManager.getInstance().beginIngestJob(dataSourceContent, ingestJobSettings);
} else {
Case.getCurrentCaseThrows().notifyFailedAddingDataSource(taskId);
}

View File

@ -1117,7 +1117,7 @@ public class PortableCaseReportModule implements ReportModule {
newContent = portableSkCase.addLocalFile(abstractFile.getName(), relativePath, abstractFile.getSize(),
abstractFile.getCtime(), abstractFile.getCrtime(), abstractFile.getAtime(), abstractFile.getMtime(),
abstractFile.getMd5Hash(), abstractFile.getKnown(), abstractFile.getMIMEType(),
abstractFile.getMd5Hash(), abstractFile.getSha256Hash(), abstractFile.getKnown(), abstractFile.getMIMEType(),
true, TskData.EncodingType.NONE,
newParent, trans);
} catch (IOException ex) {

View File

@ -228,12 +228,19 @@ public class STIXReportModule implements GeneralReportModule {
*/
private STIXPackage loadSTIXFile(String stixFileName) throws JAXBException {
// Create STIXPackage object from xml.
File file = new File(stixFileName);
JAXBContext jaxbContext = JAXBContext.newInstance("org.mitre.stix.stix_1:org.mitre.stix.common_1:org.mitre.stix.indicator_2:" //NON-NLS
+ "org.mitre.cybox.objects:org.mitre.cybox.cybox_2:org.mitre.cybox.common_2"); //NON-NLS
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
STIXPackage stix = (STIXPackage) jaxbUnmarshaller.unmarshal(file);
return stix;
// See JIRA-6958 for details about class loading and jaxb.
ClassLoader original = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(STIXReportModule.class.getClassLoader());
File file = new File(stixFileName);
JAXBContext jaxbContext = JAXBContext.newInstance("org.mitre.stix.stix_1:org.mitre.stix.common_1:org.mitre.stix.indicator_2:" //NON-NLS
+ "org.mitre.cybox.objects:org.mitre.cybox.cybox_2:org.mitre.cybox.common_2"); //NON-NLS
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
STIXPackage stix = (STIXPackage) jaxbUnmarshaller.unmarshal(file);
return stix;
} finally {
Thread.currentThread().setContextClassLoader(original);
}
}
/**

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