mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-11 23:46:15 +00:00
Merge pull request #6167 from gdicristofaro/6655-TopProgs
6655 top progs
This commit is contained in:
commit
dafb2a48e4
@ -36,3 +36,4 @@ DataSourceSummaryDetailsPanel.unallocatedSizeValue.text=
|
||||
DataSourceSummaryCountsPanel.byMimeTypeLabel.text=Files by MIME Type
|
||||
DataSourceSummaryCountsPanel.byCategoryLabel.text=Files by Category
|
||||
DataSourceSummaryCountsPanel.jLabel1.text=Results by Type
|
||||
DataSourceSummaryUserActivityPanel.programsRunLabel.text=Top Programs Run
|
||||
|
@ -61,9 +61,6 @@ DataSourceSummaryDetailsPanel.unallocatedSizeValue.text=
|
||||
DataSourceSummaryCountsPanel.byMimeTypeLabel.text=Files by MIME Type
|
||||
DataSourceSummaryCountsPanel.byCategoryLabel.text=Files by Category
|
||||
DataSourceSummaryCountsPanel.jLabel1.text=Results by Type
|
||||
DataSourceSummaryDialog.countsTab.title=Counts
|
||||
DataSourceSummaryDialog.detailsTab.title=Details
|
||||
DataSourceSummaryDialog.ingestHistoryTab.title=Ingest History
|
||||
DataSourceSummaryDialog.window.title=Data Sources Summary
|
||||
DataSourceSummaryNode.column.dataSourceName.header=Data Source Name
|
||||
DataSourceSummaryNode.column.files.header=Files
|
||||
@ -72,4 +69,14 @@ DataSourceSummaryNode.column.status.header=Ingest Status
|
||||
DataSourceSummaryNode.column.tags.header=Tags
|
||||
DataSourceSummaryNode.column.type.header=Type
|
||||
DataSourceSummaryNode.viewDataSourceAction.text=Go to Data Source
|
||||
DataSourceSummaryTabbedPane_countsTab_title=Counts
|
||||
DataSourceSummaryTabbedPane_detailsTab_title=Details
|
||||
DataSourceSummaryTabbedPane_ingestHistoryTab_title=Ingest History
|
||||
DataSourceSummaryTabbedPane_userActivityTab_title=User Activity
|
||||
DataSourceSummaryUserActivityPanel.programsRunLabel.text=Top Programs Run
|
||||
DataSourceSummaryUserActivityPanel_tab_title=User Activity
|
||||
DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header=Run Times
|
||||
DataSourceSummaryUserActivityPanel_TopProgramsTableModel_folder_header=Folder
|
||||
DataSourceSummaryUserActivityPanel_TopProgramsTableModel_lastrun_header=Last Run
|
||||
DataSourceSummaryUserActivityPanel_TopProgramsTableModel_name_header=Program
|
||||
ViewSummaryInformationAction.name.text=View Summary Information
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2019 Basis Technology Corp.
|
||||
* Copyright 2019 - 2020 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -18,8 +18,11 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.casemodule.datasourcesummary;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
@ -27,13 +30,20 @@ import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
|
||||
import org.sleuthkit.datamodel.TskData;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
|
||||
/**
|
||||
@ -193,6 +203,31 @@ final class DataSourceInfoUtilities {
|
||||
return getBaseQueryResult(query, handler, errorMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a result set handler that will return a map of string to long.
|
||||
*
|
||||
* @param keyParam The named parameter in the result set representing the
|
||||
* key.
|
||||
* @param valueParam The named parameter in the result set representing the
|
||||
* value.
|
||||
*
|
||||
* @return The result set handler to generate the map of string to long.
|
||||
*/
|
||||
private static ResultSetHandler<LinkedHashMap<String, Long>> getStringLongResultSetHandler(String keyParam, String valueParam) {
|
||||
return (resultSet) -> {
|
||||
LinkedHashMap<String, Long> toRet = new LinkedHashMap<>();
|
||||
while (resultSet.next()) {
|
||||
try {
|
||||
toRet.put(resultSet.getString(keyParam), resultSet.getLong(valueParam));
|
||||
} catch (SQLException ex) {
|
||||
logger.log(Level.WARNING, "Failed to get a result pair from the result set.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
return toRet;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves counts for each artifact type in a data source.
|
||||
*
|
||||
@ -215,24 +250,311 @@ final class DataSourceInfoUtilities {
|
||||
+ " WHERE bba.data_source_obj_id =" + selectedDataSource.getId()
|
||||
+ " GROUP BY bbt.display_name";
|
||||
|
||||
ResultSetHandler<Map<String, Long>> handler = (resultSet) -> {
|
||||
Map<String, Long> toRet = new HashMap<>();
|
||||
while (resultSet.next()) {
|
||||
try {
|
||||
toRet.put(resultSet.getString(nameParam), resultSet.getLong(valueParam));
|
||||
} catch (SQLException ex) {
|
||||
logger.log(Level.WARNING, "Failed to get a result pair from the result set.", ex);
|
||||
String errorMessage = "Unable to get artifact type counts; returning null.";
|
||||
return getBaseQueryResult(query, getStringLongResultSetHandler(nameParam, valueParam), errorMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes a result of a program run on a datasource.
|
||||
*/
|
||||
static class TopProgramsResult {
|
||||
|
||||
private final String programName;
|
||||
private final String programPath;
|
||||
private final Long runTimes;
|
||||
private final Date lastRun;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param programName The name of the program.
|
||||
* @param programPath The path of the program.
|
||||
* @param runTimes The number of runs.
|
||||
*/
|
||||
TopProgramsResult(String programName, String programPath, Long runTimes, Date lastRun) {
|
||||
this.programName = programName;
|
||||
this.programPath = programPath;
|
||||
this.runTimes = runTimes;
|
||||
this.lastRun = lastRun;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The name of the program
|
||||
*/
|
||||
String getProgramName() {
|
||||
return programName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The path of the program.
|
||||
*/
|
||||
String getProgramPath() {
|
||||
return programPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The number of run times or null if not present.
|
||||
*/
|
||||
Long getRunTimes() {
|
||||
return runTimes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The last time the program was run or null if not present.
|
||||
*/
|
||||
public Date getLastRun() {
|
||||
return lastRun;
|
||||
}
|
||||
}
|
||||
|
||||
return toRet;
|
||||
/**
|
||||
* A SQL join type.
|
||||
*/
|
||||
private enum JoinType {
|
||||
LEFT,
|
||||
RIGHT,
|
||||
INNER,
|
||||
OUTER
|
||||
}
|
||||
|
||||
/**
|
||||
* A blackboard attribute value column.
|
||||
*/
|
||||
private enum AttributeColumn {
|
||||
value_text,
|
||||
value_int32,
|
||||
value_int64
|
||||
}
|
||||
|
||||
/**
|
||||
* The suffix joined to a key name for use as an identifier of a query.
|
||||
*/
|
||||
private static final String QUERY_SUFFIX = "_query";
|
||||
|
||||
/**
|
||||
* Creates a sql statement querying the blackboard attributes table for a
|
||||
* particular attribute type and returning a specified value. That query
|
||||
* also joins with the blackboard artifact table.
|
||||
*
|
||||
* @param joinType The type of join statement to create.
|
||||
* @param attributeColumn The blackboard attribute column that should be
|
||||
* returned.
|
||||
* @param attrType The attribute type to query for.
|
||||
* @param keyName The aliased name of the attribute to return. This
|
||||
* is also used to calculate the alias of the query
|
||||
* same as getFullKey.
|
||||
* @param bbaName The blackboard artifact table alias.
|
||||
*
|
||||
* @return The generated sql statement.
|
||||
*/
|
||||
private static String getAttributeJoin(JoinType joinType, AttributeColumn attributeColumn, ATTRIBUTE_TYPE attrType, String keyName, String bbaName) {
|
||||
String queryName = keyName + QUERY_SUFFIX;
|
||||
String innerQueryName = "inner_attribute_" + queryName;
|
||||
|
||||
return "\n" + joinType + " JOIN (\n"
|
||||
+ " SELECT \n"
|
||||
+ " " + innerQueryName + ".artifact_id,\n"
|
||||
+ " " + innerQueryName + "." + attributeColumn + " AS " + keyName + "\n"
|
||||
+ " FROM blackboard_attributes " + innerQueryName + "\n"
|
||||
+ " WHERE " + innerQueryName + ".attribute_type_id = " + attrType.getTypeID() + " -- " + attrType.name() + "\n"
|
||||
+ ") " + queryName + " ON " + queryName + ".artifact_id = " + bbaName + ".artifact_id\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a column key, creates the full name for the column key.
|
||||
*
|
||||
* @param key The column key.
|
||||
*
|
||||
* @return The full identifier for the column key.
|
||||
*/
|
||||
private static String getFullKey(String key) {
|
||||
return key + QUERY_SUFFIX + "." + key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a SQL 'where' statement from a list of clauses and puts
|
||||
* parenthesis around each clause.
|
||||
*
|
||||
* @param clauses The clauses
|
||||
*
|
||||
* @return The generated 'where' statement.
|
||||
*/
|
||||
private static String getWhereString(List<String> clauses) {
|
||||
if (clauses.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
List<String> parenthesized = clauses.stream()
|
||||
.map(c -> "(" + c + ")")
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return "\nWHERE " + String.join("\n AND ", parenthesized) + "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a [column] LIKE sql clause.
|
||||
*
|
||||
* @param column The column identifier.
|
||||
* @param likeString The string that will be used as column comparison.
|
||||
* @param isLike if false, the statement becomes NOT LIKE.
|
||||
*
|
||||
* @return The generated statement.
|
||||
*/
|
||||
private static String getLikeClause(String column, String likeString, boolean isLike) {
|
||||
return column + (isLike ? "" : " NOT") + " LIKE '" + likeString + "'";
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a list of the top programs used on the data source. Currently
|
||||
* determines this based off of which prefetch results return the highest
|
||||
* count.
|
||||
*
|
||||
* @param dataSource The data source.
|
||||
* @param count The number of programs to return.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
static List<TopProgramsResult> getTopPrograms(DataSource dataSource, int count) {
|
||||
if (dataSource == null || count <= 0) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
// ntosboot should be ignored
|
||||
final String ntosBootIdentifier = "NTOSBOOT";
|
||||
// programs in windows directory to be ignored
|
||||
final String windowsDir = "/WINDOWS%";
|
||||
|
||||
final String nameParam = "name";
|
||||
final String pathParam = "path";
|
||||
final String runCountParam = "run_count";
|
||||
final String lastRunParam = "last_run";
|
||||
|
||||
String bbaQuery = "bba";
|
||||
|
||||
final String query = "SELECT\n"
|
||||
+ " " + getFullKey(nameParam) + " AS " + nameParam + ",\n"
|
||||
+ " " + getFullKey(pathParam) + " AS " + pathParam + ",\n"
|
||||
+ " MAX(" + getFullKey(runCountParam) + ") AS " + runCountParam + ",\n"
|
||||
+ " MAX(" + getFullKey(lastRunParam) + ") AS " + lastRunParam + "\n"
|
||||
+ "FROM blackboard_artifacts " + bbaQuery + "\n"
|
||||
+ getAttributeJoin(JoinType.INNER, AttributeColumn.value_text, ATTRIBUTE_TYPE.TSK_PROG_NAME, nameParam, bbaQuery)
|
||||
+ getAttributeJoin(JoinType.LEFT, AttributeColumn.value_text, ATTRIBUTE_TYPE.TSK_PATH, pathParam, bbaQuery)
|
||||
+ getAttributeJoin(JoinType.LEFT, AttributeColumn.value_int32, ATTRIBUTE_TYPE.TSK_COUNT, runCountParam, bbaQuery)
|
||||
+ getAttributeJoin(JoinType.LEFT, AttributeColumn.value_int64, ATTRIBUTE_TYPE.TSK_DATETIME, lastRunParam, bbaQuery)
|
||||
+ getWhereString(Arrays.asList(
|
||||
bbaQuery + ".artifact_type_id = " + ARTIFACT_TYPE.TSK_PROG_RUN.getTypeID(),
|
||||
bbaQuery + ".data_source_obj_id = " + dataSource.getId(),
|
||||
// exclude ntosBootIdentifier from results
|
||||
getLikeClause(getFullKey(nameParam), ntosBootIdentifier, false),
|
||||
// exclude windows directory items from results
|
||||
getFullKey(pathParam) + " IS NULL OR " + getLikeClause(getFullKey(pathParam), windowsDir, false)
|
||||
))
|
||||
+ "GROUP BY " + getFullKey(nameParam) + ", " + getFullKey(pathParam) + "\n"
|
||||
+ "ORDER BY \n"
|
||||
+ " MAX(" + getFullKey(runCountParam) + ") DESC,\n"
|
||||
+ " MAX(" + getFullKey(lastRunParam) + ") DESC,\n"
|
||||
+ " " + getFullKey(nameParam) + " ASC";
|
||||
|
||||
final String errorMessage = "Unable to get top program results; returning null.";
|
||||
|
||||
ResultSetHandler<List<TopProgramsResult>> handler = (resultSet) -> {
|
||||
List<TopProgramsResult> progResults = new ArrayList<>();
|
||||
|
||||
boolean quitAtCount = false;
|
||||
|
||||
while (resultSet.next() && (!quitAtCount || progResults.size() < count)) {
|
||||
try {
|
||||
long lastRunEpoch = resultSet.getLong(lastRunParam);
|
||||
Date lastRun = (resultSet.wasNull()) ? null : new Date(lastRunEpoch * 1000);
|
||||
|
||||
Long runCount = resultSet.getLong(runCountParam);
|
||||
if (resultSet.wasNull()) {
|
||||
runCount = null;
|
||||
}
|
||||
|
||||
if (lastRun != null || runCount != null) {
|
||||
quitAtCount = true;
|
||||
}
|
||||
|
||||
progResults.add(new TopProgramsResult(
|
||||
resultSet.getString(nameParam),
|
||||
resultSet.getString(pathParam),
|
||||
runCount,
|
||||
lastRun));
|
||||
|
||||
} catch (SQLException ex) {
|
||||
logger.log(Level.WARNING, "Failed to get a top program result from the result set.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
return progResults;
|
||||
};
|
||||
|
||||
String errorMessage = "Unable to get artifact type counts; returning null.";
|
||||
|
||||
return getBaseQueryResult(query, handler, errorMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Functions that determine the folder name of a list of path elements. If
|
||||
* not matched, function returns null.
|
||||
*/
|
||||
private static final List<Function<List<String>, String>> SHORT_FOLDER_MATCHERS = Arrays.asList(
|
||||
// handle Program Files and Program Files (x86) - if true, return the next folder
|
||||
(pathList) -> {
|
||||
if (pathList.size() < 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String rootParent = pathList.get(0).toUpperCase();
|
||||
if ("PROGRAM FILES".equals(rootParent) || "PROGRAM FILES (X86)".equals(rootParent)) {
|
||||
return pathList.get(1);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
// if there is a folder named "APPLICATION DATA" or "APPDATA"
|
||||
(pathList) -> {
|
||||
for (String pathEl : pathList) {
|
||||
String uppered = pathEl.toUpperCase();
|
||||
if ("APPLICATION DATA".equals(uppered) || "APPDATA".equals(uppered)) {
|
||||
return "AppData";
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Determines a short folder name if any. Otherwise, returns empty string.
|
||||
*
|
||||
* @param strPath The string path.
|
||||
*
|
||||
* @return The short folder name or empty string if not found.
|
||||
*/
|
||||
static String getShortFolderName(String strPath, String applicationName) {
|
||||
if (strPath == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
List<String> pathEls = new ArrayList<>(Arrays.asList(applicationName));
|
||||
|
||||
File file = new File(strPath);
|
||||
while (file != null && StringUtils.isNotBlank(file.getName())) {
|
||||
pathEls.add(file.getName());
|
||||
file = file.getParentFile();
|
||||
}
|
||||
|
||||
Collections.reverse(pathEls);
|
||||
|
||||
for (Function<List<String>, String> matchEntry : SHORT_FOLDER_MATCHERS) {
|
||||
String result = matchEntry.apply(pathEls);
|
||||
if (StringUtils.isNotBlank(result)) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a string which is a concatenation of the value received from
|
||||
* the result set.
|
||||
|
@ -49,10 +49,7 @@ final class DataSourceSummaryDialog extends javax.swing.JDialog implements Obser
|
||||
* datasource.
|
||||
*/
|
||||
@Messages({
|
||||
"DataSourceSummaryDialog.window.title=Data Sources Summary",
|
||||
"DataSourceSummaryDialog.countsTab.title=Counts",
|
||||
"DataSourceSummaryDialog.detailsTab.title=Details",
|
||||
"DataSourceSummaryDialog.ingestHistoryTab.title=Ingest History"
|
||||
"DataSourceSummaryDialog.window.title=Data Sources Summary"
|
||||
})
|
||||
DataSourceSummaryDialog(Frame owner) {
|
||||
super(owner, Bundle.DataSourceSummaryDialog_window_title(), true);
|
||||
|
@ -19,6 +19,7 @@
|
||||
package org.sleuthkit.autopsy.casemodule.datasourcesummary;
|
||||
|
||||
import javax.swing.JTabbedPane;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.casemodule.IngestJobInfoPanel;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
|
||||
@ -27,13 +28,20 @@ import org.sleuthkit.datamodel.DataSource;
|
||||
* DataSourceSummaryCountsPanel, DataSourceSummaryDetailsPanel, and
|
||||
* IngestJobInfoPanel.
|
||||
*/
|
||||
@Messages({
|
||||
"DataSourceSummaryTabbedPane_countsTab_title=Counts",
|
||||
"DataSourceSummaryTabbedPane_detailsTab_title=Details",
|
||||
"DataSourceSummaryTabbedPane_userActivityTab_title=User Activity",
|
||||
"DataSourceSummaryTabbedPane_ingestHistoryTab_title=Ingest History"
|
||||
})
|
||||
public class DataSourceSummaryTabbedPane extends JTabbedPane {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final DataSourceSummaryCountsPanel countsPanel;
|
||||
private final DataSourceSummaryDetailsPanel detailsPanel;
|
||||
private final IngestJobInfoPanel ingestHistoryPanel;
|
||||
private final DataSourceSummaryCountsPanel countsPanel = new DataSourceSummaryCountsPanel();
|
||||
private final DataSourceSummaryDetailsPanel detailsPanel = new DataSourceSummaryDetailsPanel();
|
||||
private final DataSourceSummaryUserActivityPanel userActivityPanel = new DataSourceSummaryUserActivityPanel();
|
||||
private final IngestJobInfoPanel ingestHistoryPanel = new IngestJobInfoPanel();
|
||||
|
||||
private DataSource dataSource = null;
|
||||
|
||||
@ -41,13 +49,11 @@ public class DataSourceSummaryTabbedPane extends JTabbedPane {
|
||||
* Constructs a tabbed pane showing the summary of a data source.
|
||||
*/
|
||||
public DataSourceSummaryTabbedPane() {
|
||||
countsPanel = new DataSourceSummaryCountsPanel();
|
||||
detailsPanel = new DataSourceSummaryDetailsPanel();
|
||||
ingestHistoryPanel = new IngestJobInfoPanel();
|
||||
|
||||
addTab(Bundle.DataSourceSummaryDialog_detailsTab_title(), detailsPanel);
|
||||
addTab(Bundle.DataSourceSummaryDialog_countsTab_title(), countsPanel);
|
||||
addTab(Bundle.DataSourceSummaryDialog_ingestHistoryTab_title(), ingestHistoryPanel);
|
||||
addTab(Bundle.DataSourceSummaryTabbedPane_detailsTab_title(), detailsPanel);
|
||||
addTab(Bundle.DataSourceSummaryTabbedPane_countsTab_title(), countsPanel);
|
||||
addTab(Bundle.DataSourceSummaryTabbedPane_userActivityTab_title(), userActivityPanel);
|
||||
addTab(Bundle.DataSourceSummaryTabbedPane_ingestHistoryTab_title(), ingestHistoryPanel);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -69,6 +75,7 @@ public class DataSourceSummaryTabbedPane extends JTabbedPane {
|
||||
|
||||
detailsPanel.setDataSource(dataSource);
|
||||
countsPanel.setDataSource(dataSource);
|
||||
userActivityPanel.setDataSource(dataSource);
|
||||
ingestHistoryPanel.setDataSource(dataSource);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,70 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
|
||||
<AuxValues>
|
||||
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
|
||||
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
|
||||
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="programsRunLabel" min="-2" pref="155" max="-2" attributes="0"/>
|
||||
<Component id="topProgramsScrollPane" min="-2" pref="460" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace pref="128" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="programsRunLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="topProgramsScrollPane" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JLabel" name="programsRunLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties" key="DataSourceSummaryUserActivityPanel.programsRunLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||
</AuxValues>
|
||||
</Component>
|
||||
<Container class="javax.swing.JScrollPane" name="topProgramsScrollPane">
|
||||
<Properties>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[750, 187]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JTable" name="topProgramsTable">
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Form>
|
@ -0,0 +1,291 @@
|
||||
/*
|
||||
* 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.casemodule.datasourcesummary;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
import javax.swing.table.DefaultTableCellRenderer;
|
||||
import javax.swing.table.TableCellRenderer;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
|
||||
/**
|
||||
* A panel to display user activity.
|
||||
*/
|
||||
@Messages({
|
||||
"DataSourceSummaryUserActivityPanel_tab_title=User Activity",
|
||||
"DataSourceSummaryUserActivityPanel_TopProgramsTableModel_name_header=Program",
|
||||
"DataSourceSummaryUserActivityPanel_TopProgramsTableModel_folder_header=Folder",
|
||||
"DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header=Run Times",
|
||||
"DataSourceSummaryUserActivityPanel_TopProgramsTableModel_lastrun_header=Last Run"
|
||||
})
|
||||
public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final DateFormat DATETIME_FORMAT = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.getDefault());
|
||||
private static final int TOP_PROGS_COUNT = 10;
|
||||
private static final DefaultTableCellRenderer RIGHT_ALIGNED_RENDERER = new DefaultTableCellRenderer();
|
||||
|
||||
static {
|
||||
RIGHT_ALIGNED_RENDERER.setHorizontalAlignment(JLabel.RIGHT);
|
||||
}
|
||||
|
||||
private DataSource dataSource;
|
||||
|
||||
/**
|
||||
* Creates new form DataSourceUserActivityPanel
|
||||
*/
|
||||
public DataSourceSummaryUserActivityPanel() {
|
||||
initComponents();
|
||||
topProgramsTable.getTableHeader().setReorderingAllowed(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* The datasource currently used as the model in this panel.
|
||||
*
|
||||
* @return The datasource currently being used as the model in this panel.
|
||||
*/
|
||||
public DataSource getDataSource() {
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets datasource to visualize in the panel.
|
||||
*
|
||||
* @param dataSource The datasource to use in this panel.
|
||||
*/
|
||||
public void setDataSource(DataSource dataSource) {
|
||||
this.dataSource = dataSource;
|
||||
if (dataSource == null || !Case.isCaseOpen()) {
|
||||
updateTopPrograms(new TopProgramsModel(null));
|
||||
} else {
|
||||
updateTopPrograms(getTopProgramsModel(dataSource));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the Top Programs Table in the gui.
|
||||
*
|
||||
* @param data The data in Object[][] form to be used by the
|
||||
* DefaultTableModel.
|
||||
*/
|
||||
private void updateTopPrograms(TopProgramsModel model) {
|
||||
topProgramsTable.setModel(model);
|
||||
topProgramsTable.getColumnModel().getColumn(0).setPreferredWidth(250);
|
||||
topProgramsTable.getColumnModel().getColumn(0).setCellRenderer(PATH_CELL_RENDERER);
|
||||
topProgramsTable.getColumnModel().getColumn(1).setPreferredWidth(150);
|
||||
topProgramsTable.getColumnModel().getColumn(2).setCellRenderer(RIGHT_ALIGNED_RENDERER);
|
||||
topProgramsTable.getColumnModel().getColumn(2).setPreferredWidth(80);
|
||||
topProgramsTable.getColumnModel().getColumn(3).setPreferredWidth(150);
|
||||
topProgramsScrollPane.getVerticalScrollBar().setValue(0);
|
||||
this.repaint();
|
||||
}
|
||||
|
||||
/**
|
||||
* The counts of top programs run.
|
||||
*
|
||||
* @param selectedDataSource The DataSource.
|
||||
*
|
||||
* @return The JTable data model of counts of program runs.
|
||||
*/
|
||||
private static TopProgramsModel getTopProgramsModel(DataSource selectedDataSource) {
|
||||
List<DataSourceInfoUtilities.TopProgramsResult> topProgramList
|
||||
= DataSourceInfoUtilities.getTopPrograms(selectedDataSource, TOP_PROGS_COUNT);
|
||||
|
||||
if (topProgramList == null) {
|
||||
return new TopProgramsModel(null);
|
||||
} else {
|
||||
return new TopProgramsModel(topProgramList);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A POJO defining the values present in the name cell. Defines the name as
|
||||
* well as the path for the tooltip.
|
||||
*/
|
||||
private static class ProgramNameCellValue {
|
||||
|
||||
private final String programName;
|
||||
private final String programPath;
|
||||
|
||||
ProgramNameCellValue(String programName, String programPath) {
|
||||
this.programName = programName;
|
||||
this.programPath = programPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
// override so that the value in the cell reads as programName
|
||||
return programName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The program name.
|
||||
*/
|
||||
String getProgramName() {
|
||||
return programName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The path of the program.
|
||||
*/
|
||||
String getProgramPath() {
|
||||
return programPath;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a cell renderer for the first cell rendering the name as the text
|
||||
* and path as the tooltip.
|
||||
*/
|
||||
private static TableCellRenderer PATH_CELL_RENDERER = new DefaultTableCellRenderer() {
|
||||
|
||||
public Component getTableCellRendererComponent(
|
||||
JTable table, Object value,
|
||||
boolean isSelected, boolean hasFocus,
|
||||
int row, int column) {
|
||||
JLabel c = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
|
||||
if (value instanceof ProgramNameCellValue) {
|
||||
ProgramNameCellValue cellValue = (ProgramNameCellValue) value;
|
||||
c.setToolTipText(cellValue.getProgramPath());
|
||||
}
|
||||
return c;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Defines the table model for a JTable of the programs. Accepts a list of
|
||||
* TopProgramsResult objects as rows data source.
|
||||
*/
|
||||
private static class TopProgramsModel extends AbstractTableModel {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
// column headers for artifact counts table
|
||||
private static final String[] TOP_PROGS_COLUMN_HEADERS = new String[]{
|
||||
Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_name_header(),
|
||||
Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_folder_header(),
|
||||
Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header(),
|
||||
Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_lastrun_header()
|
||||
};
|
||||
|
||||
private final List<DataSourceInfoUtilities.TopProgramsResult> programResults;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param programResults The results to display.
|
||||
*/
|
||||
TopProgramsModel(List<DataSourceInfoUtilities.TopProgramsResult> programResults) {
|
||||
this.programResults = programResults == null ? new ArrayList<>() : Collections.unmodifiableList(programResults);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getColumnName(int column) {
|
||||
return column < 0 || column >= TOP_PROGS_COLUMN_HEADERS.length ? null : TOP_PROGS_COLUMN_HEADERS[column];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRowCount() {
|
||||
return programResults.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnCount() {
|
||||
return TOP_PROGS_COLUMN_HEADERS.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValueAt(int rowIndex, int columnIndex) {
|
||||
if (rowIndex < 0 || rowIndex >= programResults.size()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
DataSourceInfoUtilities.TopProgramsResult result = programResults.get(rowIndex);
|
||||
switch (columnIndex) {
|
||||
case 0:
|
||||
return new ProgramNameCellValue(result.getProgramName(), result.getProgramPath());
|
||||
case 1:
|
||||
return DataSourceInfoUtilities.getShortFolderName(result.getProgramPath(), result.getProgramName());
|
||||
case 2:
|
||||
return result.getRunTimes();
|
||||
case 3:
|
||||
return result.getLastRun() == null ? null : DATETIME_FORMAT.format(result.getLastRun());
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.JLabel programsRunLabel = new javax.swing.JLabel();
|
||||
topProgramsScrollPane = new javax.swing.JScrollPane();
|
||||
topProgramsTable = new javax.swing.JTable();
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(programsRunLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryUserActivityPanel.class, "DataSourceSummaryUserActivityPanel.programsRunLabel.text")); // NOI18N
|
||||
|
||||
topProgramsScrollPane.setPreferredSize(new java.awt.Dimension(750, 187));
|
||||
topProgramsScrollPane.setViewportView(topProgramsTable);
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(programsRunLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 155, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(topProgramsScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 460, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addContainerGap(128, Short.MAX_VALUE))
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addComponent(programsRunLabel)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(topProgramsScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JScrollPane topProgramsScrollPane;
|
||||
private javax.swing.JTable topProgramsTable;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user