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

This commit is contained in:
esaunders 2020-11-16 10:21:31 -05:00
commit 3ae06e65b3
79 changed files with 3438 additions and 424 deletions

View File

@ -197,7 +197,7 @@
</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" />
<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
@ -238,4 +238,67 @@
</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

@ -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",
@ -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 {
@ -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);
}
}
@ -406,7 +405,7 @@ public class DefaultArtifactContentViewer extends javax.swing.JPanel implements
* 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.
@ -417,24 +416,24 @@ public class DefaultArtifactContentViewer extends javax.swing.JPanel implements
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()) {
@ -468,6 +467,7 @@ public class DefaultArtifactContentViewer extends javax.swing.JPanel implements
* 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) {
@ -490,13 +490,12 @@ public class DefaultArtifactContentViewer extends javax.swing.JPanel implements
*/
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

@ -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

@ -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

@ -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();
}
}
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});

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

@ -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.
@ -90,8 +92,8 @@ public final class DiscoveryEventUtils {
}
/**
* 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 {
@ -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) {
@ -212,20 +220,22 @@ 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

@ -187,7 +187,6 @@ class Chunker implements Iterator<Chunk>, Iterable<Chunk> {
private static StringBuilder sanitize(String s) {
String normStr = Normalizer.normalize(s, Normalizer.Form.NFKC);
return sanitizeToUTF8(replaceInvalidUTF16(normStr));
}
@Override
@ -336,8 +335,9 @@ class Chunker implements Iterator<Chunk>, Iterable<Chunk> {
String chunkSegment;
if (Character.isHighSurrogate(ch)) {
//read another char into the buffer.
charsRead = reader.read(tempChunkBuf, 1, 1);
if (charsRead == -1) {
int surrogateCharsRead = reader.read(tempChunkBuf, 1, 1);
charsRead += surrogateCharsRead;
if (surrogateCharsRead == -1) {
//this is the last chunk, so just drop the unpaired surrogate
endOfReaderReached = true;
return;
@ -352,17 +352,32 @@ class Chunker implements Iterator<Chunk>, Iterable<Chunk> {
//cleanup any invalid utf-16 sequences
StringBuilder sanitizedChunkSegment = sanitize(chunkSegment);
//check for whitespace.
whitespaceFound = Character.isWhitespace(sanitizedChunkSegment.codePointAt(0));
//add read chars to the chunk and update the length.
currentChunk.append(sanitizedChunkSegment);
chunkSizeBytes += sanitizedChunkSegment.toString().getBytes(UTF_8).length;
//get the length in utf8 bytes of the read chars
int segmentSize = chunkSegment.getBytes(UTF_8).length;
// lower case the string and get it's size. NOTE: lower casing can
// change the size of the string.
String lowerCasedSegment = sanitizedChunkSegment.toString().toLowerCase();
lowerCasedChunk.append(lowerCasedSegment);
lowerCasedChunkSizeBytes += lowerCasedSegment.getBytes(UTF_8).length;
int lowerCasedSegmentSize = lowerCasedSegment.getBytes(UTF_8).length;
//if it will not put us past maxBytes
if ((chunkSizeBytes + segmentSize < maxBytes - MAX_CHAR_SIZE_INCREASE_IN_BYTES)
&& (lowerCasedChunkSizeBytes + lowerCasedSegmentSize < maxBytes - MAX_CHAR_SIZE_INCREASE_IN_BYTES)) {
//add read chars to the chunk and update the length.
currentChunk.append(sanitizedChunkSegment);
chunkSizeBytes += segmentSize;
lowerCasedChunk.append(lowerCasedSegment);
lowerCasedChunkSizeBytes += lowerCasedSegmentSize;
//check for whitespace.
whitespaceFound = Character.isWhitespace(sanitizedChunkSegment.codePointAt(0));
} else {
//unread it, and break out of read loop.
reader.unread(tempChunkBuf, 0, charsRead);
return;
}
}
}
}

View File

@ -6,15 +6,14 @@ cache:
- C:\ProgramData\chocolatey\lib
- '%APPVEYOR_BUILD_FOLDER%\Core\test\qa-functional\data'
image: Visual Studio 2015
platform: x64
environment:
global:
TSK_HOME: "C:\\sleuthkit"
JDK_HOME: C:\Program Files\BellSoft\LibericaJDK-11-Full
JAVA_HOME: C:\Program Files\BellSoft\LibericaJDK-11-Full
PYTHON: "C:\\Python36-x64"
global:
TSK_HOME: "C:\\sleuthkit"
JDK_HOME: C:\Program Files\BellSoft\LibericaJDK-11-Full
JAVA_HOME: C:\Program Files\BellSoft\LibericaJDK-11-Full
PYTHON: "C:\\Python36-x64"
install:
- ps: choco install nuget.commandline
@ -44,4 +43,6 @@ build_script:
- cd %APPVEYOR_BUILD_FOLDER%
- cmd: ant -q build
test: off
test_script:
- cd %APPVEYOR_BUILD_FOLDER%
- cmd: ant -q test-no-regression

View File

@ -21,7 +21,7 @@
<!-- Verify that the java version running is . -->
<fail message="Unsupported Java version: ${ant.java.version}.
Make sure that the Java version is 11 or higher."
unless="supported-java-versions"/>
unless="supported-java-versions"/>
<!-- Determine platform and include specific file -->
<condition property="os.family" value="unix">
@ -65,22 +65,54 @@
</target>
<target name="clean" depends="suite.clean">
<delete includeEmptyDirs="true" failonerror="false">
<delete includeEmptyDirs="true" failonerror="false">
<fileset dir="docs\doxygen-user\user-docs" includes="**/*"/>
</delete>
</delete>
<delete includeEmptyDirs="true" failonerror="false">
<fileset dir="docs\doxygen\doxygen_docs\api-docs" includes="**/*"/>
</delete>
</delete>
<delete includeemptydirs="true" failonerror="false">
<fileset dir="${basedir}/docs/doxygen-dev/build-docs" includes="**/*"/>
</delete>
</target>
<!-- This target is similar to the regular test target that calls test on all nbm's,
but this target excludes the Testing nbm which runs the regression tests -->
<target name="test-no-regression" depends="build" description="Runs tests for all modules in the suite excluding the regression tests of the Testing NBM.">
<!--taken from https://stackoverflow.com/a/10859103; remove "Testing" from the modules and provide 'modulesNoTesting' as result. -->
<property name="modulesBeforeChange" value="${modules}"/>
<script language="javascript">
<![CDATA[
var before = project.getProperty("modulesBeforeChange");
var separator = ":";
var testingNbm = "Testing";
var beforeSplit = before.split(separator);
var items = [];
for (var i = 0; i < beforeSplit.length; i++) {
if (beforeSplit[i].toUpperCase() !== testingNbm.toUpperCase()) {
items.push(beforeSplit[i]);
}
}
var itemsJoined = items.join(separator);
project.setNewProperty("modulesNoTesting", itemsJoined);
]]>
</script>
<sortsuitemodules unsortedmodules="${modulesNoTesting}" sortedmodulesproperty="modules.test.sorted" sorttests="true"/>
<!-- continue on fail -->
<property name="continue.after.failing.tests" value="true"/>
<subant target="test" buildpath="${modules.test.sorted}" inheritrefs="false" inheritall="false">
<property name="cluster.path.evaluated" value="${cluster.path.evaluated}"/> <!-- Just for speed of pre-7.0 projects -->
<property name="harness.taskdefs.done" value="${harness.taskdefs.done}"/> <!-- optimization -->
<property name="continue.after.failing.tests" value="${continue.after.failing.tests}"/>
</subant>
</target>
<!-- This target will create a custom ZIP file for us. It first uses the general
ZIP target and then opens it up and adds in any files that we want. This is where we customize the
version number. -->
ZIP target and then opens it up and adds in any files that we want. This is where we customize the
version number. -->
<target name="build-zip" depends="doxygen, suite.build-zip"> <!--,findJRE" -->
<property name="release.dir" value="${nbdist.dir}/${app.name}"/>
@ -125,15 +157,15 @@
<!-- for Japanese localized version add option: -Duser.language=ja -->
<if>
<equals arg1="${os.family}" arg2="mac"/>
<then>
<property name="jvm.options" value="&quot;${jvm-value} -J-Xdock:name=${app.title}&quot;"/>
</then>
<else>
<property name="jvm.options" value="&quot;${jvm-value}&quot;"/>
</else>
</if>
<if>
<equals arg1="${os.family}" arg2="mac"/>
<then>
<property name="jvm.options" value="&quot;${jvm-value} -J-Xdock:name=${app.title}&quot;"/>
</then>
<else>
<property name="jvm.options" value="&quot;${jvm-value}&quot;"/>
</else>
</if>
<propertyfile file="${app.property.file}">
<!-- Note: can be higher on 64 bit systems, should be in sync with project.properties -->
@ -143,9 +175,9 @@
<replace file="${app.property.file}" token="@JVM_OPTIONS" value="${jvm.options}" />
<!-- We want to remove the dlls in autopsy/modules/lib because they will
shadow the files in the autopsy/modules/lib/ARCHITECTURE folder in the JAR.
These files are legacy from when we used to copy the dlls to this location.
This check should do away in the future. Added Sept '13-->
shadow the files in the autopsy/modules/lib/ARCHITECTURE folder in the JAR.
These files are legacy from when we used to copy the dlls to this location.
This check should do away in the future. Added Sept '13-->
<delete failonerror="false">
<fileset dir="${zip-tmp}/${app.name}/autopsy/modules/lib">
<include name="libtsk_jni.dll" />
@ -185,9 +217,9 @@
<target name="input-build-type" unless="build.type">
<input addProperty="build.type"
message="Enter the desired build type:"
validargs="DEVELOPMENT,RELEASE"
defaultvalue="DEVELOPMENT"/>
message="Enter the desired build type:"
validargs="DEVELOPMENT,RELEASE"
defaultvalue="DEVELOPMENT"/>
</target>
<target name="input-version" unless="app.version">
@ -225,21 +257,21 @@
<echo>${app.name} branding</echo>
<propertyfile
file="${branding.dir}/core/core.jar/org/netbeans/core/startup/Bundle.properties"
comment="Updated by build script">
file="${branding.dir}/core/core.jar/org/netbeans/core/startup/Bundle.properties"
comment="Updated by build script">
<entry key="currentVersion" value="${app.title} ${app.version}" />
</propertyfile>
<propertyfile
file="${branding.dir}/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties"
comment="Updated by build script">
file="${branding.dir}/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties"
comment="Updated by build script">
<entry key="CTL_MainWindow_Title" value="${app.title} ${app.version}" />
<entry key="CTL_MainWindow_Title_No_Project" value="${app.title} ${app.version}" />
</propertyfile>
<propertyfile
file="${basedir}/Core/src/org/sleuthkit/autopsy/coreutils/Version.properties"
comment="Updated by build script">
file="${basedir}/Core/src/org/sleuthkit/autopsy/coreutils/Version.properties"
comment="Updated by build script">
<entry key="app.name" value="${app.title}" />
<entry key="app.version" value="${app.version}" />
<entry key="build.type" value="${build.type}" />
@ -326,7 +358,7 @@
<antcall target="build-installer-${os.family}" />
</target>
<target name="chmod_executables" >
<target name="chmod_executables" >
<chmod perm="a+x">
<fileset dir="${cluster}/markmckinnon" casesensitive="no" id="mm">
<include name="*_linux"/>

View File

@ -464,6 +464,16 @@ def normalize_db_entry(line, files_table, vs_parts_table, vs_info_table, fs_info
# remove object ID
if files_index:
# Ignore TIFF size and hash if extracted from PDFs.
# See JIRA-6951 for more details.
# index -1 = last element in the list, which is extension
# index -3 = 3rd from the end, which is the parent path.
if fields_list[-1] == "'tif'" and fields_list[-3].endswith(".pdf/'"):
fields_list[15] = "'SIZE_IGNORED'"
fields_list[23] = "'MD5_IGNORED'"
fields_list[24] = "'SHA256_IGNORED'"
newLine = ('INSERT INTO "tsk_files" VALUES(' + ', '.join(fields_list[1:]) + ');')
# Remove object ID from Unalloc file name
newLine = re.sub('Unalloc_[0-9]+_', 'Unalloc_', newLine)

View File

@ -70,6 +70,7 @@ sub processShellActivities {
::rptMsg("");
while ($offset < ($sz - 10)) {
# Code to locate the appropriate identifier
$tag = 1;
while ($tag) {
@ -78,9 +79,15 @@ sub processShellActivities {
}
else {
$offset++;
# Check if at end of file and exit loop if it is
last if ($offset >= $sz );
}
}
# Check if at end of file and exit loop if it is
last if ($offset >= $sz );
$offset += 2;
$l = unpack("C",substr($data,$offset,1));
# ::rptMsg("String Length: ".sprintf "0x%x",$l);

View File

@ -8,5 +8,19 @@ pushd bindings/java && ant -q dist && popd
echo "Building Autopsy..." && echo -en 'travis_fold:start:script.build\\r'
cd $TRAVIS_BUILD_DIR/
ant build
ant -q build
echo -en 'travis_fold:end:script.build\\r'
echo "Testing Autopsy..." && echo -en 'travis_fold:start:script.tests\\r'
echo "Free Space:"
echo `df -h .`
if [ "${TRAVIS_OS_NAME}" = "osx" ]; then
# if os x, just run it
ant -q test-no-regression
elif [ "${TRAVIS_OS_NAME}" = "linux" ]; then
# if linux use xvfb
xvfb-run ant -q test-no-regression
fi
echo -en 'travis_fold:end:script.tests\\r'