mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-17 02:07:42 +00:00
Merge branch 'develop' into data-src-summaries
This commit is contained in:
commit
ec3cb76de0
@ -36,3 +36,4 @@ DataSourceSummaryDetailsPanel.unallocatedSizeValue.text=
|
|||||||
DataSourceSummaryCountsPanel.byMimeTypeLabel.text=Files by MIME Type
|
DataSourceSummaryCountsPanel.byMimeTypeLabel.text=Files by MIME Type
|
||||||
DataSourceSummaryCountsPanel.byCategoryLabel.text=Files by Category
|
DataSourceSummaryCountsPanel.byCategoryLabel.text=Files by Category
|
||||||
DataSourceSummaryCountsPanel.jLabel1.text=Results by Type
|
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.byMimeTypeLabel.text=Files by MIME Type
|
||||||
DataSourceSummaryCountsPanel.byCategoryLabel.text=Files by Category
|
DataSourceSummaryCountsPanel.byCategoryLabel.text=Files by Category
|
||||||
DataSourceSummaryCountsPanel.jLabel1.text=Results by Type
|
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
|
DataSourceSummaryDialog.window.title=Data Sources Summary
|
||||||
DataSourceSummaryNode.column.dataSourceName.header=Data Source Name
|
DataSourceSummaryNode.column.dataSourceName.header=Data Source Name
|
||||||
DataSourceSummaryNode.column.files.header=Files
|
DataSourceSummaryNode.column.files.header=Files
|
||||||
@ -72,4 +69,14 @@ DataSourceSummaryNode.column.status.header=Ingest Status
|
|||||||
DataSourceSummaryNode.column.tags.header=Tags
|
DataSourceSummaryNode.column.tags.header=Tags
|
||||||
DataSourceSummaryNode.column.type.header=Type
|
DataSourceSummaryNode.column.type.header=Type
|
||||||
DataSourceSummaryNode.viewDataSourceAction.text=Go to Data Source
|
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
|
ViewSummaryInformationAction.name.text=View Summary Information
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2019 Basis Technology Corp.
|
* Copyright 2019 - 2020 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -18,8 +18,11 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.casemodule.datasourcesummary;
|
package org.sleuthkit.autopsy.casemodule.datasourcesummary;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
@ -27,13 +30,20 @@ import org.sleuthkit.autopsy.coreutils.Logger;
|
|||||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Set;
|
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.Case;
|
||||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||||
|
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
|
||||||
import org.sleuthkit.datamodel.TskData;
|
import org.sleuthkit.datamodel.TskData;
|
||||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||||
|
import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
|
||||||
import org.sleuthkit.datamodel.DataSource;
|
import org.sleuthkit.datamodel.DataSource;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -193,6 +203,31 @@ final class DataSourceInfoUtilities {
|
|||||||
return getBaseQueryResult(query, handler, errorMessage);
|
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.
|
* 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()
|
+ " WHERE bba.data_source_obj_id =" + selectedDataSource.getId()
|
||||||
+ " GROUP BY bbt.display_name";
|
+ " GROUP BY bbt.display_name";
|
||||||
|
|
||||||
ResultSetHandler<Map<String, Long>> handler = (resultSet) -> {
|
String errorMessage = "Unable to get artifact type counts; returning null.";
|
||||||
Map<String, Long> toRet = new HashMap<>();
|
return getBaseQueryResult(query, getStringLongResultSetHandler(nameParam, valueParam), errorMessage);
|
||||||
while (resultSet.next()) {
|
}
|
||||||
try {
|
|
||||||
toRet.put(resultSet.getString(nameParam), resultSet.getLong(valueParam));
|
/**
|
||||||
} catch (SQLException ex) {
|
* Describes a result of a program run on a datasource.
|
||||||
logger.log(Level.WARNING, "Failed to get a result pair from the result set.", ex);
|
*/
|
||||||
|
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);
|
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
|
* Generates a string which is a concatenation of the value received from
|
||||||
* the result set.
|
* the result set.
|
||||||
|
@ -49,10 +49,7 @@ final class DataSourceSummaryDialog extends javax.swing.JDialog implements Obser
|
|||||||
* datasource.
|
* datasource.
|
||||||
*/
|
*/
|
||||||
@Messages({
|
@Messages({
|
||||||
"DataSourceSummaryDialog.window.title=Data Sources Summary",
|
"DataSourceSummaryDialog.window.title=Data Sources Summary"
|
||||||
"DataSourceSummaryDialog.countsTab.title=Counts",
|
|
||||||
"DataSourceSummaryDialog.detailsTab.title=Details",
|
|
||||||
"DataSourceSummaryDialog.ingestHistoryTab.title=Ingest History"
|
|
||||||
})
|
})
|
||||||
DataSourceSummaryDialog(Frame owner) {
|
DataSourceSummaryDialog(Frame owner) {
|
||||||
super(owner, Bundle.DataSourceSummaryDialog_window_title(), true);
|
super(owner, Bundle.DataSourceSummaryDialog_window_title(), true);
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
package org.sleuthkit.autopsy.casemodule.datasourcesummary;
|
package org.sleuthkit.autopsy.casemodule.datasourcesummary;
|
||||||
|
|
||||||
import javax.swing.JTabbedPane;
|
import javax.swing.JTabbedPane;
|
||||||
|
import org.openide.util.NbBundle.Messages;
|
||||||
import org.sleuthkit.autopsy.casemodule.IngestJobInfoPanel;
|
import org.sleuthkit.autopsy.casemodule.IngestJobInfoPanel;
|
||||||
import org.sleuthkit.datamodel.DataSource;
|
import org.sleuthkit.datamodel.DataSource;
|
||||||
|
|
||||||
@ -27,13 +28,20 @@ import org.sleuthkit.datamodel.DataSource;
|
|||||||
* DataSourceSummaryCountsPanel, DataSourceSummaryDetailsPanel, and
|
* DataSourceSummaryCountsPanel, DataSourceSummaryDetailsPanel, and
|
||||||
* IngestJobInfoPanel.
|
* 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 {
|
public class DataSourceSummaryTabbedPane extends JTabbedPane {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
private final DataSourceSummaryCountsPanel countsPanel;
|
private final DataSourceSummaryCountsPanel countsPanel = new DataSourceSummaryCountsPanel();
|
||||||
private final DataSourceSummaryDetailsPanel detailsPanel;
|
private final DataSourceSummaryDetailsPanel detailsPanel = new DataSourceSummaryDetailsPanel();
|
||||||
private final IngestJobInfoPanel ingestHistoryPanel;
|
private final DataSourceSummaryUserActivityPanel userActivityPanel = new DataSourceSummaryUserActivityPanel();
|
||||||
|
private final IngestJobInfoPanel ingestHistoryPanel = new IngestJobInfoPanel();
|
||||||
|
|
||||||
private DataSource dataSource = null;
|
private DataSource dataSource = null;
|
||||||
|
|
||||||
@ -41,13 +49,11 @@ public class DataSourceSummaryTabbedPane extends JTabbedPane {
|
|||||||
* Constructs a tabbed pane showing the summary of a data source.
|
* Constructs a tabbed pane showing the summary of a data source.
|
||||||
*/
|
*/
|
||||||
public DataSourceSummaryTabbedPane() {
|
public DataSourceSummaryTabbedPane() {
|
||||||
countsPanel = new DataSourceSummaryCountsPanel();
|
|
||||||
detailsPanel = new DataSourceSummaryDetailsPanel();
|
|
||||||
ingestHistoryPanel = new IngestJobInfoPanel();
|
|
||||||
|
|
||||||
addTab(Bundle.DataSourceSummaryDialog_detailsTab_title(), detailsPanel);
|
addTab(Bundle.DataSourceSummaryTabbedPane_detailsTab_title(), detailsPanel);
|
||||||
addTab(Bundle.DataSourceSummaryDialog_countsTab_title(), countsPanel);
|
addTab(Bundle.DataSourceSummaryTabbedPane_countsTab_title(), countsPanel);
|
||||||
addTab(Bundle.DataSourceSummaryDialog_ingestHistoryTab_title(), ingestHistoryPanel);
|
addTab(Bundle.DataSourceSummaryTabbedPane_userActivityTab_title(), userActivityPanel);
|
||||||
|
addTab(Bundle.DataSourceSummaryTabbedPane_ingestHistoryTab_title(), ingestHistoryPanel);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -69,6 +75,7 @@ public class DataSourceSummaryTabbedPane extends JTabbedPane {
|
|||||||
|
|
||||||
detailsPanel.setDataSource(dataSource);
|
detailsPanel.setDataSource(dataSource);
|
||||||
countsPanel.setDataSource(dataSource);
|
countsPanel.setDataSource(dataSource);
|
||||||
|
userActivityPanel.setDataSource(dataSource);
|
||||||
ingestHistoryPanel.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
|
||||||
|
}
|
@ -35,8 +35,12 @@ import org.sleuthkit.autopsy.coreutils.Logger;
|
|||||||
import org.sleuthkit.autopsy.datamodel.NodeProperty;
|
import org.sleuthkit.autopsy.datamodel.NodeProperty;
|
||||||
import org.sleuthkit.datamodel.Account;
|
import org.sleuthkit.datamodel.Account;
|
||||||
import org.sleuthkit.datamodel.AccountDeviceInstance;
|
import org.sleuthkit.datamodel.AccountDeviceInstance;
|
||||||
|
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||||
|
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||||
|
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME;
|
||||||
import org.sleuthkit.datamodel.CommunicationsFilter;
|
import org.sleuthkit.datamodel.CommunicationsFilter;
|
||||||
import org.sleuthkit.datamodel.CommunicationsManager;
|
import org.sleuthkit.datamodel.CommunicationsManager;
|
||||||
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Node to represent an Account Device Instance in the CVT
|
* Node to represent an Account Device Instance in the CVT
|
||||||
@ -49,6 +53,8 @@ final class AccountDeviceInstanceNode extends AbstractNode {
|
|||||||
private final CommunicationsManager commsManager;
|
private final CommunicationsManager commsManager;
|
||||||
private final Account account;
|
private final Account account;
|
||||||
|
|
||||||
|
private static final BlackboardAttribute.Type NAME_ATTRIBUTE = new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.fromID(TSK_NAME.getTypeID()));
|
||||||
|
|
||||||
AccountDeviceInstanceNode(AccountDeviceInstanceKey accountDeviceInstanceKey, CommunicationsManager commsManager) {
|
AccountDeviceInstanceNode(AccountDeviceInstanceKey accountDeviceInstanceKey, CommunicationsManager commsManager) {
|
||||||
super(Children.LEAF, Lookups.fixed(accountDeviceInstanceKey, commsManager));
|
super(Children.LEAF, Lookups.fixed(accountDeviceInstanceKey, commsManager));
|
||||||
this.accountDeviceInstanceKey = accountDeviceInstanceKey;
|
this.accountDeviceInstanceKey = accountDeviceInstanceKey;
|
||||||
@ -122,15 +128,17 @@ final class AccountDeviceInstanceNode extends AbstractNode {
|
|||||||
@Override
|
@Override
|
||||||
public String getShortDescription() {
|
public String getShortDescription() {
|
||||||
List<PersonaAccount> personaList;
|
List<PersonaAccount> personaList;
|
||||||
|
List<BlackboardArtifact> contactArtifactList;
|
||||||
try {
|
try {
|
||||||
personaList = CVTPersonaCache.getPersonaAccounts(account);
|
personaList = CVTPersonaCache.getPersonaAccounts(account);
|
||||||
|
contactArtifactList = ContactCache.getContacts(account);
|
||||||
} catch (ExecutionException ex) {
|
} catch (ExecutionException ex) {
|
||||||
logger.log(Level.WARNING, "Failed to retrieve Persona details for node.", ex);
|
logger.log(Level.WARNING, "Failed to retrieve Persona details for node.", ex);
|
||||||
return getDisplayName();
|
return getDisplayName();
|
||||||
}
|
}
|
||||||
|
|
||||||
String personaName;
|
String personaName;
|
||||||
if (!personaList.isEmpty()) {
|
if (personaList != null && !personaList.isEmpty()) {
|
||||||
personaName = personaList.get(0).getPersona().getName();
|
personaName = personaList.get(0).getPersona().getName();
|
||||||
if (personaList.size() > 1) {
|
if (personaList.size() > 1) {
|
||||||
personaName += Bundle.AccountInstanceNode_Tooltip_suffix(Integer.toString(personaList.size()));
|
personaName += Bundle.AccountInstanceNode_Tooltip_suffix(Integer.toString(personaList.size()));
|
||||||
@ -139,6 +147,19 @@ final class AccountDeviceInstanceNode extends AbstractNode {
|
|||||||
personaName = "None";
|
personaName = "None";
|
||||||
}
|
}
|
||||||
|
|
||||||
return Bundle.AccountInstanceNode_Tooltip_Template(getDisplayName(), personaName);
|
String contactName = getDisplayName();
|
||||||
|
if (contactArtifactList != null && !contactArtifactList.isEmpty()) {
|
||||||
|
try {
|
||||||
|
BlackboardArtifact contactArtifact = contactArtifactList.get(0);
|
||||||
|
BlackboardAttribute attribute = contactArtifact.getAttribute(NAME_ATTRIBUTE);
|
||||||
|
if (attribute != null) {
|
||||||
|
contactName = attribute.getValueString();
|
||||||
|
}
|
||||||
|
} catch (TskCoreException ex) {
|
||||||
|
logger.log(Level.WARNING, "Failed to retrive name attribute from contact artifact.", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Bundle.AccountInstanceNode_Tooltip_Template(contactName, personaName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
166
Core/src/org/sleuthkit/autopsy/communications/ContactCache.java
Executable file
166
Core/src/org/sleuthkit/autopsy/communications/ContactCache.java
Executable file
@ -0,0 +1,166 @@
|
|||||||
|
/*
|
||||||
|
* 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.communications;
|
||||||
|
|
||||||
|
import com.google.common.cache.CacheBuilder;
|
||||||
|
import com.google.common.cache.CacheLoader;
|
||||||
|
import com.google.common.cache.LoadingCache;
|
||||||
|
import java.beans.PropertyChangeEvent;
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
|
import org.sleuthkit.autopsy.ingest.IngestManager;
|
||||||
|
import static org.sleuthkit.autopsy.ingest.IngestManager.IngestModuleEvent.DATA_ADDED;
|
||||||
|
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
|
||||||
|
import org.sleuthkit.datamodel.Account;
|
||||||
|
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||||
|
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||||
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A singleton cache of the Contact artifacts for accounts. The map of account
|
||||||
|
* unique ids to list of contact artifacts is stored in a LoadingCache which
|
||||||
|
* expires after 10 of non-use.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
final class ContactCache {
|
||||||
|
|
||||||
|
private static final Logger logger = Logger.getLogger(ContactCache.class.getName());
|
||||||
|
|
||||||
|
private static ContactCache instance;
|
||||||
|
|
||||||
|
private final LoadingCache<String, Map<String, List<BlackboardArtifact>>> accountMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the list of Contacts for the given Account.
|
||||||
|
*
|
||||||
|
* @param account Account instance.
|
||||||
|
*
|
||||||
|
* @return List of TSK_CONTACT artifacts that references the given Account.
|
||||||
|
* An empty list is returned if no contacts are found.
|
||||||
|
*
|
||||||
|
* @throws ExecutionException
|
||||||
|
*/
|
||||||
|
static synchronized List<BlackboardArtifact> getContacts(Account account) throws ExecutionException {
|
||||||
|
return getInstance().accountMap.get("realMap").get(account.getTypeSpecificID());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Force the cache to invalidate all entries.
|
||||||
|
*/
|
||||||
|
static synchronized void invalidateCache() {
|
||||||
|
getInstance().accountMap.invalidateAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new instance.
|
||||||
|
*/
|
||||||
|
private ContactCache() {
|
||||||
|
|
||||||
|
accountMap = CacheBuilder.newBuilder().expireAfterAccess(10, TimeUnit.MINUTES).build(
|
||||||
|
new CacheLoader<String, Map<String, List<BlackboardArtifact>>>() {
|
||||||
|
@Override
|
||||||
|
public Map<String, List<BlackboardArtifact>> load(String key) {
|
||||||
|
try {
|
||||||
|
return buildMap();
|
||||||
|
} catch (SQLException | TskCoreException ex) {
|
||||||
|
logger.log(Level.WARNING, "Failed to build account to contact map", ex);
|
||||||
|
}
|
||||||
|
return new HashMap<>(); // Return an empty map if there is an exception to avoid NPE and continual trying.
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
PropertyChangeListener ingestListener = pce -> {
|
||||||
|
String eventType = pce.getPropertyName();
|
||||||
|
if (eventType.equals(DATA_ADDED.toString())) {
|
||||||
|
ModuleDataEvent eventData = (ModuleDataEvent) pce.getOldValue();
|
||||||
|
if (eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT.getTypeID()) {
|
||||||
|
invalidateCache();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), (PropertyChangeEvent event) -> {
|
||||||
|
if (event.getNewValue() == null) {
|
||||||
|
invalidateCache();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
IngestManager.getInstance().addIngestModuleEventListener(EnumSet.of(DATA_ADDED), ingestListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the singleton instance of the cache object.
|
||||||
|
*
|
||||||
|
* @return AccountCache instance.
|
||||||
|
*/
|
||||||
|
private static synchronized ContactCache getInstance() {
|
||||||
|
if (instance == null) {
|
||||||
|
instance = new ContactCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the map of account IDs to contacts that reference them.
|
||||||
|
*
|
||||||
|
* @return A map of account IDs to contact artifacts.
|
||||||
|
*
|
||||||
|
* @throws TskCoreException
|
||||||
|
* @throws SQLException
|
||||||
|
*/
|
||||||
|
private Map<String, List<BlackboardArtifact>> buildMap() throws TskCoreException, SQLException {
|
||||||
|
Map<String, List<BlackboardArtifact>> acctMap = new HashMap<>();
|
||||||
|
List<BlackboardArtifact> contactList = Case.getCurrentCase().getSleuthkitCase().getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT);
|
||||||
|
|
||||||
|
for (BlackboardArtifact contactArtifact : contactList) {
|
||||||
|
List<BlackboardAttribute> contactAttributes = contactArtifact.getAttributes();
|
||||||
|
for (BlackboardAttribute attribute : contactAttributes) {
|
||||||
|
String typeName = attribute.getAttributeType().getTypeName();
|
||||||
|
|
||||||
|
if (typeName.startsWith("TSK_EMAIL")
|
||||||
|
|| typeName.startsWith("TSK_PHONE")
|
||||||
|
|| typeName.startsWith("TSK_NAME")
|
||||||
|
|| typeName.startsWith("TSK_ID")) {
|
||||||
|
String accountID = attribute.getValueString();
|
||||||
|
List<BlackboardArtifact> artifactList = acctMap.getOrDefault(accountID, new ArrayList<>());
|
||||||
|
|
||||||
|
acctMap.put(accountID, artifactList);
|
||||||
|
|
||||||
|
if (!artifactList.contains(contactArtifact)) {
|
||||||
|
artifactList.add(contactArtifact);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return acctMap;
|
||||||
|
}
|
||||||
|
}
|
@ -26,6 +26,9 @@ CallLogArtifactViewer_label_from=From
|
|||||||
CallLogArtifactViewer_label_to=To
|
CallLogArtifactViewer_label_to=To
|
||||||
CallLogArtifactViewer_suffix_local=(Local)
|
CallLogArtifactViewer_suffix_local=(Local)
|
||||||
CallLogArtifactViewer_value_unknown=Unknown
|
CallLogArtifactViewer_value_unknown=Unknown
|
||||||
|
#{0} - contact name
|
||||||
|
CommunicationArtifactViewerHelper_contact_label=Contact: {0}
|
||||||
|
CommunicationArtifactViewerHelper_contact_label_unknown=Unknown
|
||||||
CommunicationArtifactViewerHelper_menuitem_copy=Copy
|
CommunicationArtifactViewerHelper_menuitem_copy=Copy
|
||||||
CommunicationArtifactViewerHelper_persona_button_create=Create
|
CommunicationArtifactViewerHelper_persona_button_create=Create
|
||||||
CommunicationArtifactViewerHelper_persona_button_view=View
|
CommunicationArtifactViewerHelper_persona_button_view=View
|
||||||
@ -61,6 +64,7 @@ DefaultArtifactContentViewer.copyMenuItem.text=Copy
|
|||||||
DefaultArtifactContentViewer.selectAllMenuItem.text=Select All
|
DefaultArtifactContentViewer.selectAllMenuItem.text=Select All
|
||||||
MessageAccountPanel_button_create_label=Create
|
MessageAccountPanel_button_create_label=Create
|
||||||
MessageAccountPanel_button_view_label=View
|
MessageAccountPanel_button_view_label=View
|
||||||
|
MessageAccountPanel_contact_label=Contact:
|
||||||
MessageAccountPanel_no_matches=No matches found.
|
MessageAccountPanel_no_matches=No matches found.
|
||||||
MessageAccountPanel_persona_label=Persona:
|
MessageAccountPanel_persona_label=Persona:
|
||||||
MessageAccountPanel_unknown_label=Unknown
|
MessageAccountPanel_unknown_label=Unknown
|
||||||
|
@ -36,6 +36,7 @@ import org.openide.util.NbBundle;
|
|||||||
import org.openide.util.lookup.ServiceProvider;
|
import org.openide.util.lookup.ServiceProvider;
|
||||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
|
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
|
import org.sleuthkit.autopsy.guiutils.ContactCache;
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||||
import org.sleuthkit.datamodel.Content;
|
import org.sleuthkit.datamodel.Content;
|
||||||
@ -143,6 +144,8 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac
|
|||||||
String fromAccountIdentifier = null;
|
String fromAccountIdentifier = null;
|
||||||
String toAccountIdentifier = null;
|
String toAccountIdentifier = null;
|
||||||
List<String> otherParties = null;
|
List<String> otherParties = null;
|
||||||
|
List<String> toContactNames = null;
|
||||||
|
List<String> fromContactNames = null;
|
||||||
|
|
||||||
Content dataSource = artifact.getDataSource();
|
Content dataSource = artifact.getDataSource();
|
||||||
String deviceId = ((DataSource) dataSource).getDeviceId();
|
String deviceId = ((DataSource) dataSource).getDeviceId();
|
||||||
@ -186,6 +189,7 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac
|
|||||||
String fromAccountAttrValue = fromAccountAttr.getValueString();
|
String fromAccountAttrValue = fromAccountAttr.getValueString();
|
||||||
if (fromAccountAttrValue.equalsIgnoreCase(deviceId) == false) {
|
if (fromAccountAttrValue.equalsIgnoreCase(deviceId) == false) {
|
||||||
fromAccountIdentifier = fromAccountAttrValue;
|
fromAccountIdentifier = fromAccountAttrValue;
|
||||||
|
fromContactNames = ContactCache.getContactNameList(fromAccountIdentifier);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,6 +199,7 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac
|
|||||||
String toAccountAttrValue = StringUtils.trim(numbers[0]);
|
String toAccountAttrValue = StringUtils.trim(numbers[0]);
|
||||||
if (toAccountAttrValue.equalsIgnoreCase(deviceId) == false) {
|
if (toAccountAttrValue.equalsIgnoreCase(deviceId) == false) {
|
||||||
toAccountIdentifier = toAccountAttrValue;
|
toAccountIdentifier = toAccountAttrValue;
|
||||||
|
toContactNames = ContactCache.getContactNameList(toAccountIdentifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if more than one To address, then stick the rest of them in the
|
// if more than one To address, then stick the rest of them in the
|
||||||
@ -228,6 +233,9 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac
|
|||||||
}
|
}
|
||||||
|
|
||||||
callLogViewData.setOtherAttributes(extractOtherAttributes(artifact));
|
callLogViewData.setOtherAttributes(extractOtherAttributes(artifact));
|
||||||
|
|
||||||
|
callLogViewData.setFromContactNameList(fromContactNames);
|
||||||
|
callLogViewData.setToContactNameList(toContactNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
return callLogViewData;
|
return callLogViewData;
|
||||||
@ -312,6 +320,11 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac
|
|||||||
String accountDisplayString = getAccountDisplayString(callLogViewData.getFromAccount(), callLogViewData);
|
String accountDisplayString = getAccountDisplayString(callLogViewData.getFromAccount(), callLogViewData);
|
||||||
CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, accountDisplayString);
|
CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, accountDisplayString);
|
||||||
|
|
||||||
|
List<String> contactNames = callLogViewData.getFromContactNameList();
|
||||||
|
for (String name : contactNames) {
|
||||||
|
CommunicationArtifactViewerHelper.addContactRow(this, m_gridBagLayout, m_constraints, name);
|
||||||
|
}
|
||||||
|
|
||||||
// show persona
|
// show persona
|
||||||
dataList.addAll(CommunicationArtifactViewerHelper.addPersonaRow(this, m_gridBagLayout, this.m_constraints, callLogViewData.getFromAccount()));
|
dataList.addAll(CommunicationArtifactViewerHelper.addPersonaRow(this, m_gridBagLayout, this.m_constraints, callLogViewData.getFromAccount()));
|
||||||
} else {
|
} else {
|
||||||
@ -324,6 +337,11 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac
|
|||||||
String accountDisplayString = getAccountDisplayString(callLogViewData.getToAccount(), callLogViewData);
|
String accountDisplayString = getAccountDisplayString(callLogViewData.getToAccount(), callLogViewData);
|
||||||
CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, accountDisplayString);
|
CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, accountDisplayString);
|
||||||
|
|
||||||
|
List<String> contactNames = callLogViewData.getToContactNameList();
|
||||||
|
for (String name : contactNames) {
|
||||||
|
CommunicationArtifactViewerHelper.addContactRow(this, m_gridBagLayout, m_constraints, name);
|
||||||
|
}
|
||||||
|
|
||||||
dataList.addAll(CommunicationArtifactViewerHelper.addPersonaRow(this, m_gridBagLayout, this.m_constraints, callLogViewData.getToAccount()));
|
dataList.addAll(CommunicationArtifactViewerHelper.addPersonaRow(this, m_gridBagLayout, this.m_constraints, callLogViewData.getToAccount()));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -22,6 +22,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -47,6 +48,9 @@ final class CallLogViewData {
|
|||||||
|
|
||||||
private String dataSourceName = null;
|
private String dataSourceName = null;
|
||||||
|
|
||||||
|
private List<String> toContactNameList = null;
|
||||||
|
private List<String> fromContactNameList = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
*
|
||||||
@ -146,4 +150,28 @@ final class CallLogViewData {
|
|||||||
this.localAccountId = localAccountId;
|
this.localAccountId = localAccountId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setToContactNameList(List<String> contactNameList) {
|
||||||
|
if (contactNameList != null) {
|
||||||
|
this.toContactNameList = new ArrayList<>(contactNameList);
|
||||||
|
} else {
|
||||||
|
this.toContactNameList = new ArrayList<>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getToContactNameList() {
|
||||||
|
return Collections.unmodifiableList(this.toContactNameList);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFromContactNameList(List<String> contactNameList) {
|
||||||
|
if (contactNameList != null) {
|
||||||
|
this.fromContactNameList = new ArrayList<>(contactNameList);
|
||||||
|
} else {
|
||||||
|
this.fromContactNameList = new ArrayList<>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getFromContactNameList() {
|
||||||
|
return Collections.unmodifiableList(this.fromContactNameList);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -429,6 +429,35 @@ final class CommunicationArtifactViewerHelper {
|
|||||||
return dataList;
|
return dataList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a contact row to the panel.
|
||||||
|
*
|
||||||
|
* @param panel Panel to update.
|
||||||
|
* @param gridbagLayout Layout to use.
|
||||||
|
* @param constraints Constrains to use.
|
||||||
|
* @param contactId Contact name to display.
|
||||||
|
*
|
||||||
|
* @return A JLabel with the contact information.
|
||||||
|
*/
|
||||||
|
@NbBundle.Messages({
|
||||||
|
"#{0} - contact name",
|
||||||
|
"CommunicationArtifactViewerHelper_contact_label=Contact: {0}",
|
||||||
|
"CommunicationArtifactViewerHelper_contact_label_unknown=Unknown"
|
||||||
|
})
|
||||||
|
static JLabel addContactRow(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints, String contactId) {
|
||||||
|
// Increase the y value because we are not calling the addKey
|
||||||
|
constraints.gridy++;
|
||||||
|
//Don't change the origian constraints, just make a copy to modify
|
||||||
|
GridBagConstraints indentedConstraints = (GridBagConstraints) constraints.clone();
|
||||||
|
|
||||||
|
// Add an indent to match persona labels
|
||||||
|
indentedConstraints.insets = new java.awt.Insets(0, 2 * LEFT_INSET, 0, 0);
|
||||||
|
|
||||||
|
String contactInfo = Bundle.CommunicationArtifactViewerHelper_contact_label(contactId != null && !contactId.isEmpty() ? contactId : Bundle.CommunicationArtifactViewerHelper_contact_label_unknown());
|
||||||
|
|
||||||
|
return addValueAtCol(panel, gridbagLayout, indentedConstraints, contactInfo, 1);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Event handler for mouse click event. Attaches a 'Copy' menu item to right
|
* Event handler for mouse click event. Attaches a 'Copy' menu item to right
|
||||||
* click.
|
* click.
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.sleuthkit.autopsy.contentviewers.artifactviewers;
|
package org.sleuthkit.autopsy.contentviewers.artifactviewers;
|
||||||
|
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
@ -39,6 +38,7 @@ import javax.swing.SwingUtilities;
|
|||||||
import javax.swing.SwingWorker;
|
import javax.swing.SwingWorker;
|
||||||
import org.openide.util.NbBundle.Messages;
|
import org.openide.util.NbBundle.Messages;
|
||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
|
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
|
||||||
import org.sleuthkit.autopsy.centralrepository.datamodel.Persona;
|
import org.sleuthkit.autopsy.centralrepository.datamodel.Persona;
|
||||||
import org.sleuthkit.autopsy.centralrepository.datamodel.PersonaAccount;
|
import org.sleuthkit.autopsy.centralrepository.datamodel.PersonaAccount;
|
||||||
import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsDialog;
|
import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsDialog;
|
||||||
@ -46,8 +46,10 @@ import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsDialogCallb
|
|||||||
import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsMode;
|
import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsMode;
|
||||||
import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsPanel;
|
import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsPanel;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
|
import org.sleuthkit.autopsy.guiutils.ContactCache;
|
||||||
import org.sleuthkit.datamodel.Account;
|
import org.sleuthkit.datamodel.Account;
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||||
|
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||||
import org.sleuthkit.datamodel.CommunicationsManager;
|
import org.sleuthkit.datamodel.CommunicationsManager;
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
|
||||||
@ -113,13 +115,24 @@ final class MessageAccountPanel extends JPanel {
|
|||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<BlackboardArtifact> contactList = ContactCache.getContacts(account);
|
||||||
|
BlackboardArtifact contact = null;
|
||||||
|
|
||||||
|
if (contactList != null && !contactList.isEmpty()) {
|
||||||
|
contact = contactList.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CentralRepository.isEnabled()) {
|
||||||
Collection<PersonaAccount> personAccounts = PersonaAccount.getPersonaAccountsForAccount(account);
|
Collection<PersonaAccount> personAccounts = PersonaAccount.getPersonaAccountsForAccount(account);
|
||||||
if (personAccounts != null && !personAccounts.isEmpty()) {
|
if (personAccounts != null && !personAccounts.isEmpty()) {
|
||||||
for (PersonaAccount personaAccount : PersonaAccount.getPersonaAccountsForAccount(account)) {
|
for (PersonaAccount personaAccount : PersonaAccount.getPersonaAccountsForAccount(account)) {
|
||||||
dataList.add(new AccountContainer(account, personaAccount));
|
dataList.add(new AccountContainer(account, personaAccount, contact));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
dataList.add(new AccountContainer(account, null));
|
dataList.add(new AccountContainer(account, null, contact));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dataList.add(new AccountContainer(account, null, contact));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,8 +140,7 @@ final class MessageAccountPanel extends JPanel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Messages({
|
@Messages({
|
||||||
"MessageAccountPanel_no_matches=No matches found.",
|
"MessageAccountPanel_no_matches=No matches found.",})
|
||||||
})
|
|
||||||
@Override
|
@Override
|
||||||
protected void done() {
|
protected void done() {
|
||||||
try {
|
try {
|
||||||
@ -199,6 +211,7 @@ final class MessageAccountPanel extends JPanel {
|
|||||||
for (AccountContainer o : data) {
|
for (AccountContainer o : data) {
|
||||||
group.addGap(5)
|
group.addGap(5)
|
||||||
.addComponent(o.getAccountLabel())
|
.addComponent(o.getAccountLabel())
|
||||||
|
.addGroup(o.getContactLineVerticalGroup(layout))
|
||||||
.addGroup(o.getPersonLineVerticalGroup(layout));
|
.addGroup(o.getPersonLineVerticalGroup(layout));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -234,6 +247,7 @@ final class MessageAccountPanel extends JPanel {
|
|||||||
group.addGap(10);
|
group.addGap(10);
|
||||||
for (AccountContainer o : data) {
|
for (AccountContainer o : data) {
|
||||||
pgroup.addGroup(o.getPersonaSequentialGroup(layout));
|
pgroup.addGroup(o.getPersonaSequentialGroup(layout));
|
||||||
|
pgroup.addGroup(o.getContactSequentialGroup(layout));
|
||||||
}
|
}
|
||||||
group.addGap(10)
|
group.addGap(10)
|
||||||
.addGroup(pgroup)
|
.addGroup(pgroup)
|
||||||
@ -253,10 +267,13 @@ final class MessageAccountPanel extends JPanel {
|
|||||||
|
|
||||||
private final Account account;
|
private final Account account;
|
||||||
private Persona persona = null;
|
private Persona persona = null;
|
||||||
|
private final String contactName;
|
||||||
|
|
||||||
private JLabel accountLabel;
|
private JLabel accountLabel;
|
||||||
private JLabel personaHeader;
|
private JLabel personaHeader;
|
||||||
private JLabel personaDisplayName;
|
private JLabel personaDisplayName;
|
||||||
|
private JLabel contactHeader;
|
||||||
|
private JLabel contactDisplayName;
|
||||||
private JButton button;
|
private JButton button;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -265,16 +282,22 @@ final class MessageAccountPanel extends JPanel {
|
|||||||
* @param account
|
* @param account
|
||||||
* @param personaAccount
|
* @param personaAccount
|
||||||
*/
|
*/
|
||||||
AccountContainer(Account account, PersonaAccount personaAccount) {
|
AccountContainer(Account account, PersonaAccount personaAccount, BlackboardArtifact contactArtifact) throws TskCoreException {
|
||||||
|
if (contactArtifact != null && contactArtifact.getArtifactTypeID() != BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT.getTypeID()) {
|
||||||
|
throw new IllegalArgumentException("Failed to create AccountContainer object, passed in artifact was not a TSK_CONTACT");
|
||||||
|
}
|
||||||
|
|
||||||
this.account = account;
|
this.account = account;
|
||||||
this.persona = personaAccount != null ? personaAccount.getPersona() : null;
|
this.persona = personaAccount != null ? personaAccount.getPersona() : null;
|
||||||
|
this.contactName = getNameFromContactArtifact(contactArtifact);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Messages({
|
@Messages({
|
||||||
"MessageAccountPanel_persona_label=Persona:",
|
"MessageAccountPanel_persona_label=Persona:",
|
||||||
"MessageAccountPanel_unknown_label=Unknown",
|
"MessageAccountPanel_unknown_label=Unknown",
|
||||||
"MessageAccountPanel_button_view_label=View",
|
"MessageAccountPanel_button_view_label=View",
|
||||||
"MessageAccountPanel_button_create_label=Create"
|
"MessageAccountPanel_button_create_label=Create",
|
||||||
|
"MessageAccountPanel_contact_label=Contact:"
|
||||||
})
|
})
|
||||||
/**
|
/**
|
||||||
* Swing components will not be initialized until this method is called.
|
* Swing components will not be initialized until this method is called.
|
||||||
@ -282,16 +305,29 @@ final class MessageAccountPanel extends JPanel {
|
|||||||
private void initalizeSwingControls() {
|
private void initalizeSwingControls() {
|
||||||
accountLabel = new JLabel();
|
accountLabel = new JLabel();
|
||||||
personaHeader = new JLabel(Bundle.MessageAccountPanel_persona_label());
|
personaHeader = new JLabel(Bundle.MessageAccountPanel_persona_label());
|
||||||
|
contactHeader = new JLabel(Bundle.MessageAccountPanel_contact_label());
|
||||||
personaDisplayName = new JLabel();
|
personaDisplayName = new JLabel();
|
||||||
|
contactDisplayName = new JLabel();
|
||||||
button = new JButton();
|
button = new JButton();
|
||||||
button.addActionListener(new PersonaButtonListener(this));
|
button.addActionListener(new PersonaButtonListener(this));
|
||||||
|
|
||||||
accountLabel.setText(account.getTypeSpecificID());
|
accountLabel.setText(account.getTypeSpecificID());
|
||||||
|
contactDisplayName.setText(contactName);
|
||||||
personaDisplayName.setText(persona != null ? persona.getName() : Bundle.MessageAccountPanel_unknown_label());
|
personaDisplayName.setText(persona != null ? persona.getName() : Bundle.MessageAccountPanel_unknown_label());
|
||||||
button.setText(persona != null ? Bundle.MessageAccountPanel_button_view_label() : Bundle.MessageAccountPanel_button_create_label());
|
button.setText(persona != null ? Bundle.MessageAccountPanel_button_view_label() : Bundle.MessageAccountPanel_button_create_label());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getNameFromContactArtifact(BlackboardArtifact contactArtifact) throws TskCoreException {
|
||||||
|
if (contactArtifact != null) {
|
||||||
|
BlackboardAttribute attribute = contactArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME));
|
||||||
|
if (attribute != null) {
|
||||||
|
return attribute.getValueString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Bundle.MessageAccountPanel_unknown_label();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets a new persona for this object and update the controls.
|
* Sets a new persona for this object and update the controls.
|
||||||
*
|
*
|
||||||
@ -366,6 +402,17 @@ final class MessageAccountPanel extends JPanel {
|
|||||||
return group;
|
return group;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private SequentialGroup getContactSequentialGroup(GroupLayout layout) {
|
||||||
|
SequentialGroup group = layout.createSequentialGroup();
|
||||||
|
|
||||||
|
group
|
||||||
|
.addComponent(contactHeader)
|
||||||
|
.addPreferredGap(ComponentPlacement.RELATED)
|
||||||
|
.addComponent(contactDisplayName);
|
||||||
|
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates the vertical layout code for the persona line.
|
* Generates the vertical layout code for the persona line.
|
||||||
*
|
*
|
||||||
@ -379,6 +426,12 @@ final class MessageAccountPanel extends JPanel {
|
|||||||
.addComponent(personaDisplayName)
|
.addComponent(personaDisplayName)
|
||||||
.addComponent(button);
|
.addComponent(button);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ParallelGroup getContactLineVerticalGroup(GroupLayout layout) {
|
||||||
|
return layout.createParallelGroup(Alignment.BASELINE)
|
||||||
|
.addComponent(contactHeader)
|
||||||
|
.addComponent(contactDisplayName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -447,11 +447,9 @@ public class MessageArtifactViewer extends javax.swing.JPanel implements Artifac
|
|||||||
resetComponent();
|
resetComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
msgbodyTabbedPane.setEnabledAt(ACCT_TAB_INDEX, CentralRepository.isEnabled());
|
msgbodyTabbedPane.setEnabledAt(ACCT_TAB_INDEX, true);
|
||||||
if(CentralRepository.isEnabled()) {
|
|
||||||
accountsPanel.setArtifact(artifact);
|
accountsPanel.setArtifact(artifact);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the artifact associated with the given artifact, if there is one.
|
* Get the artifact associated with the given artifact, if there is one.
|
||||||
|
192
Core/src/org/sleuthkit/autopsy/guiutils/ContactCache.java
Executable file
192
Core/src/org/sleuthkit/autopsy/guiutils/ContactCache.java
Executable file
@ -0,0 +1,192 @@
|
|||||||
|
/*
|
||||||
|
* 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.guiutils;
|
||||||
|
|
||||||
|
import com.google.common.cache.CacheBuilder;
|
||||||
|
import com.google.common.cache.CacheLoader;
|
||||||
|
import com.google.common.cache.LoadingCache;
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
|
import org.sleuthkit.autopsy.ingest.IngestManager;
|
||||||
|
import static org.sleuthkit.autopsy.ingest.IngestManager.IngestModuleEvent.DATA_ADDED;
|
||||||
|
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
|
||||||
|
import org.sleuthkit.datamodel.Account;
|
||||||
|
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||||
|
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||||
|
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME;
|
||||||
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A singleton cache of the Contact artifacts for accounts. The map of account
|
||||||
|
* unique ids to list of contact artifacts is stored in a LoadingCache which
|
||||||
|
* expires after 10 of non-use.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public final class ContactCache {
|
||||||
|
|
||||||
|
private static final Logger logger = Logger.getLogger(ContactCache.class.getName());
|
||||||
|
|
||||||
|
private static ContactCache instance;
|
||||||
|
|
||||||
|
private final LoadingCache<String, Map<String, List<BlackboardArtifact>>> accountMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the list of Contacts for the given Account.
|
||||||
|
*
|
||||||
|
* @param account Account instance.
|
||||||
|
*
|
||||||
|
* @return List of TSK_CONTACT artifacts that references the given Account.
|
||||||
|
* An empty list is returned if no contacts are found.
|
||||||
|
*
|
||||||
|
* @throws ExecutionException
|
||||||
|
*/
|
||||||
|
public static synchronized List<BlackboardArtifact> getContacts(Account account) throws ExecutionException {
|
||||||
|
return getInstance().accountMap.get("realMap").get(account.getTypeSpecificID());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of Contact TSK_NAME values for the given account type
|
||||||
|
* specific id.
|
||||||
|
*
|
||||||
|
* @param accountTypeSpecificID Account type specific id
|
||||||
|
*
|
||||||
|
* @return List of contact string names or empty list if none were found.
|
||||||
|
*
|
||||||
|
* @throws TskCoreException
|
||||||
|
*/
|
||||||
|
public static synchronized List<String> getContactNameList(String accountTypeSpecificID) throws TskCoreException {
|
||||||
|
List<BlackboardArtifact> contactList;
|
||||||
|
try {
|
||||||
|
contactList = getInstance().accountMap.get("realMap").get(accountTypeSpecificID);
|
||||||
|
} catch (ExecutionException ex) {
|
||||||
|
throw new TskCoreException("Unable to get contact list from cache", ex);
|
||||||
|
}
|
||||||
|
List<String> contactNameList = new ArrayList<>();
|
||||||
|
|
||||||
|
if (contactList != null) {
|
||||||
|
for (BlackboardArtifact artifact : contactList) {
|
||||||
|
BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.fromID(TSK_NAME.getTypeID())));
|
||||||
|
if (attribute != null && !contactNameList.contains(attribute.getValueString())) {
|
||||||
|
contactNameList.add(attribute.getValueString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return contactNameList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Force the cache to invalidate all entries.
|
||||||
|
*/
|
||||||
|
static synchronized void invalidateCache() {
|
||||||
|
getInstance().accountMap.invalidateAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new instance.
|
||||||
|
*/
|
||||||
|
private ContactCache() {
|
||||||
|
|
||||||
|
accountMap = CacheBuilder.newBuilder().expireAfterAccess(10, TimeUnit.MINUTES).build(
|
||||||
|
new CacheLoader<String, Map<String, List<BlackboardArtifact>>>() {
|
||||||
|
@Override
|
||||||
|
public Map<String, List<BlackboardArtifact>> load(String key) {
|
||||||
|
try {
|
||||||
|
return buildMap();
|
||||||
|
} catch (SQLException | TskCoreException ex) {
|
||||||
|
logger.log(Level.WARNING, "Failed to build account to contact map", ex);
|
||||||
|
}
|
||||||
|
return new HashMap<>(); // Return an empty map if there is an exception to avoid NPE and continual trying.
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
PropertyChangeListener ingestListener = pce -> {
|
||||||
|
String eventType = pce.getPropertyName();
|
||||||
|
if (eventType.equals(DATA_ADDED.toString())) {
|
||||||
|
ModuleDataEvent eventData = (ModuleDataEvent) pce.getOldValue();
|
||||||
|
if (eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT.getTypeID()) {
|
||||||
|
invalidateCache();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
IngestManager.getInstance().addIngestModuleEventListener(EnumSet.of(DATA_ADDED), ingestListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the singleton instance of the cache object.
|
||||||
|
*
|
||||||
|
* @return AccountCache instance.
|
||||||
|
*/
|
||||||
|
private static synchronized ContactCache getInstance() {
|
||||||
|
if (instance == null) {
|
||||||
|
instance = new ContactCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the map of account IDs to contacts that reference them.
|
||||||
|
*
|
||||||
|
* @return A map of account IDs to contact artifacts.
|
||||||
|
*
|
||||||
|
* @throws TskCoreException
|
||||||
|
* @throws SQLException
|
||||||
|
*/
|
||||||
|
private Map<String, List<BlackboardArtifact>> buildMap() throws TskCoreException, SQLException {
|
||||||
|
Map<String, List<BlackboardArtifact>> acctMap = new HashMap<>();
|
||||||
|
List<BlackboardArtifact> contactList = Case.getCurrentCase().getSleuthkitCase().getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT);
|
||||||
|
|
||||||
|
for (BlackboardArtifact contactArtifact : contactList) {
|
||||||
|
List<BlackboardAttribute> contactAttributes = contactArtifact.getAttributes();
|
||||||
|
for (BlackboardAttribute attribute : contactAttributes) {
|
||||||
|
String typeName = attribute.getAttributeType().getTypeName();
|
||||||
|
|
||||||
|
if (typeName.startsWith("TSK_EMAIL")
|
||||||
|
|| typeName.startsWith("TSK_PHONE")
|
||||||
|
|| typeName.startsWith("TSK_NAME")
|
||||||
|
|| typeName.startsWith("TSK_ID")) {
|
||||||
|
String accountID = attribute.getValueString();
|
||||||
|
List<BlackboardArtifact> artifactList = acctMap.get(accountID);
|
||||||
|
if (artifactList == null) {
|
||||||
|
artifactList = new ArrayList<>();
|
||||||
|
acctMap.put(accountID, artifactList);
|
||||||
|
}
|
||||||
|
if (!artifactList.contains(contactArtifact)) {
|
||||||
|
artifactList.add(contactArtifact);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return acctMap;
|
||||||
|
}
|
||||||
|
}
|
@ -2,7 +2,14 @@ OpenIDE-Module-Display-Category=Ingest Module
|
|||||||
OpenIDE-Module-Long-Description=Recent Activity ingest module.\n\n\The module extracts useful information about the recent user activity on the disk image being ingested, such as:\n\n- Recently open documents,\n- Web activity (sites visited, stored cookies, book marked sites, search engine queries, file downloads),\n- Recently attached devices,\n- Installed programs.\n\nThe module currently supports Windows only disk images.\nThe plugin is also fully functional when deployed on Windows version of Autopsy.
|
OpenIDE-Module-Long-Description=Recent Activity ingest module.\n\n\The module extracts useful information about the recent user activity on the disk image being ingested, such as:\n\n- Recently open documents,\n- Web activity (sites visited, stored cookies, book marked sites, search engine queries, file downloads),\n- Recently attached devices,\n- Installed programs.\n\nThe module currently supports Windows only disk images.\nThe plugin is also fully functional when deployed on Windows version of Autopsy.
|
||||||
OpenIDE-Module-Name=RecentActivity
|
OpenIDE-Module-Name=RecentActivity
|
||||||
OpenIDE-Module-Short-Description=Recent Activity finder ingest module
|
OpenIDE-Module-Short-Description=Recent Activity finder ingest module
|
||||||
Chrome.moduleName=Chrome
|
Browser.name.Microsoft.Edge=Microsoft Edge
|
||||||
|
Browser.name.Yandex=Yandex
|
||||||
|
Browser.name.Opera=Opera
|
||||||
|
Browser.name.SalamWeb=SalamWeb
|
||||||
|
Browser.name.UC.Browser=UC Browser
|
||||||
|
Browser.name.Brave=Brave
|
||||||
|
Browser.name.Google.Chrome=Google Chrome
|
||||||
|
Chrome.moduleName=Chromium
|
||||||
Chrome.getHistory.errMsg.errGettingFiles=Error when trying to get Chrome history files.
|
Chrome.getHistory.errMsg.errGettingFiles=Error when trying to get Chrome history files.
|
||||||
Chrome.getHistory.errMsg.couldntFindAnyFiles=Could not find any allocated Chrome history files.
|
Chrome.getHistory.errMsg.couldntFindAnyFiles=Could not find any allocated Chrome history files.
|
||||||
Chrome.getHistory.errMsg.errAnalyzingFile={0}: Error while trying to analyze file:{1}
|
Chrome.getHistory.errMsg.errAnalyzingFile={0}: Error while trying to analyze file:{1}
|
||||||
|
@ -5,15 +5,10 @@ ChromeCacheExtract_adding_artifacts_msg=Chrome Cache: Adding %d artifacts for an
|
|||||||
ChromeCacheExtract_adding_extracted_files_msg=Chrome Cache: Adding %d extracted files for analysis.
|
ChromeCacheExtract_adding_extracted_files_msg=Chrome Cache: Adding %d extracted files for analysis.
|
||||||
ChromeCacheExtract_loading_files_msg=Chrome Cache: Loading files from %s.
|
ChromeCacheExtract_loading_files_msg=Chrome Cache: Loading files from %s.
|
||||||
ChromeCacheExtractor.moduleName=ChromeCacheExtractor
|
ChromeCacheExtractor.moduleName=ChromeCacheExtractor
|
||||||
# {0} - module name
|
|
||||||
# {1} - row number
|
|
||||||
# {2} - table length
|
|
||||||
# {3} - cache path
|
|
||||||
ChromeCacheExtractor.progressMsg={0}: Extracting cache entry {1} of {2} entries from {3}
|
ChromeCacheExtractor.progressMsg={0}: Extracting cache entry {1} of {2} entries from {3}
|
||||||
DataSourceUsage_AndroidMedia=Android Media Card
|
DataSourceUsage_AndroidMedia=Android Media Card
|
||||||
DataSourceUsage_DJU_Drone_DAT=DJI Internal SD Card
|
DataSourceUsage_DJU_Drone_DAT=DJI Internal SD Card
|
||||||
DataSourceUsage_FlashDrive=Flash Drive
|
DataSourceUsage_FlashDrive=Flash Drive
|
||||||
# {0} - OS name
|
|
||||||
DataSourceUsageAnalyzer.customVolume.label=OS Drive ({0})
|
DataSourceUsageAnalyzer.customVolume.label=OS Drive ({0})
|
||||||
DataSourceUsageAnalyzer.parentModuleName=Recent Activity
|
DataSourceUsageAnalyzer.parentModuleName=Recent Activity
|
||||||
Extract.indexError.message=Failed to index artifact for keyword search.
|
Extract.indexError.message=Failed to index artifact for keyword search.
|
||||||
@ -77,10 +72,17 @@ ExtractZone_progress_Msg=Extracting :Zone.Identifer files
|
|||||||
ExtractZone_Restricted=Restricted Sites Zone
|
ExtractZone_Restricted=Restricted Sites Zone
|
||||||
ExtractZone_Trusted=Trusted Sites Zone
|
ExtractZone_Trusted=Trusted Sites Zone
|
||||||
OpenIDE-Module-Display-Category=Ingest Module
|
OpenIDE-Module-Display-Category=Ingest Module
|
||||||
OpenIDE-Module-Long-Description=Recent Activity ingest module.\n\n\The module extracts useful information about the recent user activity on the disk image being ingested, such as:\n\n- Recently open documents,\n- Web activity (sites visited, stored cookies, book marked sites, search engine queries, file downloads),\n- Recently attached devices,\n- Installed programs.\n\nThe module currently supports Windows only disk images.\nThe plugin is also fully functional when deployed on Windows version of Autopsy.
|
OpenIDE-Module-Long-Description=Recent Activity ingest module.\n\nThe module extracts useful information about the recent user activity on the disk image being ingested, such as:\n\n- Recently open documents,\n- Web activity (sites visited, stored cookies, book marked sites, search engine queries, file downloads),\n- Recently attached devices,\n- Installed programs.\n\nThe module currently supports Windows only disk images.\nThe plugin is also fully functional when deployed on Windows version of Autopsy.
|
||||||
OpenIDE-Module-Name=RecentActivity
|
OpenIDE-Module-Name=RecentActivity
|
||||||
OpenIDE-Module-Short-Description=Recent Activity finder ingest module
|
OpenIDE-Module-Short-Description=Recent Activity finder ingest module
|
||||||
Chrome.moduleName=Chrome
|
Browser.name.Microsoft.Edge=Microsoft Edge
|
||||||
|
Browser.name.Yandex=Yandex
|
||||||
|
Browser.name.Opera=Opera
|
||||||
|
Browser.name.SalamWeb=SalamWeb
|
||||||
|
Browser.name.UC.Browser=UC Browser
|
||||||
|
Browser.name.Brave=Brave
|
||||||
|
Browser.name.Google.Chrome=Google Chrome
|
||||||
|
Chrome.moduleName=Chromium
|
||||||
Chrome.getHistory.errMsg.errGettingFiles=Error when trying to get Chrome history files.
|
Chrome.getHistory.errMsg.errGettingFiles=Error when trying to get Chrome history files.
|
||||||
Chrome.getHistory.errMsg.couldntFindAnyFiles=Could not find any allocated Chrome history files.
|
Chrome.getHistory.errMsg.couldntFindAnyFiles=Could not find any allocated Chrome history files.
|
||||||
Chrome.getHistory.errMsg.errAnalyzingFile={0}: Error while trying to analyze file:{1}
|
Chrome.getHistory.errMsg.errAnalyzingFile={0}: Error while trying to analyze file:{1}
|
||||||
@ -148,14 +150,14 @@ Firefox.getDlV24.errMsg.errAnalyzeFile={0}: Error while trying to analyze file:{
|
|||||||
Firefox.getDlV24.errMsg.errParsingArtifacts={0}: Error parsing {1} Firefox web download artifacts.
|
Firefox.getDlV24.errMsg.errParsingArtifacts={0}: Error parsing {1} Firefox web download artifacts.
|
||||||
Progress_Message_Analyze_Registry=Analyzing Registry Files
|
Progress_Message_Analyze_Registry=Analyzing Registry Files
|
||||||
Progress_Message_Analyze_Usage=Data Sources Usage Analysis
|
Progress_Message_Analyze_Usage=Data Sources Usage Analysis
|
||||||
Progress_Message_Chrome_AutoFill=Chrome Auto Fill
|
Progress_Message_Chrome_AutoFill=Chrome Auto Fill Browser {0}
|
||||||
Progress_Message_Chrome_Bookmarks=Chrome Bookmarks
|
Progress_Message_Chrome_Bookmarks=Chrome Bookmarks Browser {0}
|
||||||
Progress_Message_Chrome_Cache=Chrome Cache
|
Progress_Message_Chrome_Cache=Chrome Cache
|
||||||
Progress_Message_Chrome_Cookies=Chrome Cookies
|
Progress_Message_Chrome_Cookies=Chrome Cookies Browser {0}
|
||||||
Progress_Message_Chrome_Downloads=Chrome Downloads
|
Progress_Message_Chrome_Downloads=Chrome Downloads Browser {0}
|
||||||
Progress_Message_Chrome_FormHistory=Chrome Form History
|
Progress_Message_Chrome_FormHistory=Chrome Form History
|
||||||
Progress_Message_Chrome_History=Chrome History
|
Progress_Message_Chrome_History=Chrome History Browser {0}
|
||||||
Progress_Message_Chrome_Logins=Chrome Logins
|
Progress_Message_Chrome_Logins=Chrome Logins Browser {0}
|
||||||
Progress_Message_Edge_Bookmarks=Microsoft Edge Bookmarks
|
Progress_Message_Edge_Bookmarks=Microsoft Edge Bookmarks
|
||||||
Progress_Message_Edge_Cookies=Microsoft Edge Cookies
|
Progress_Message_Edge_Cookies=Microsoft Edge Cookies
|
||||||
Progress_Message_Edge_History=Microsoft Edge History
|
Progress_Message_Edge_History=Microsoft Edge History
|
||||||
@ -209,7 +211,6 @@ Recently_Used_Artifacts_Winrar=Recently opened according to WinRAR MRU
|
|||||||
Registry_System_Bam=Recently Executed according to Background Activity Moderator (BAM)
|
Registry_System_Bam=Recently Executed according to Background Activity Moderator (BAM)
|
||||||
RegRipperFullNotFound=Full version RegRipper executable not found.
|
RegRipperFullNotFound=Full version RegRipper executable not found.
|
||||||
RegRipperNotFound=Autopsy RegRipper executable not found.
|
RegRipperNotFound=Autopsy RegRipper executable not found.
|
||||||
# {0} - file name
|
|
||||||
SearchEngineURLQueryAnalyzer.init.exception.msg=Unable to find {0}.
|
SearchEngineURLQueryAnalyzer.init.exception.msg=Unable to find {0}.
|
||||||
SearchEngineURLQueryAnalyzer.moduleName.text=Search Engine
|
SearchEngineURLQueryAnalyzer.moduleName.text=Search Engine
|
||||||
SearchEngineURLQueryAnalyzer.engineName.none=NONE
|
SearchEngineURLQueryAnalyzer.engineName.none=NONE
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.recentactivity;
|
package org.sleuthkit.autopsy.recentactivity;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.gson.JsonArray;
|
import com.google.gson.JsonArray;
|
||||||
import com.google.gson.JsonElement;
|
import com.google.gson.JsonElement;
|
||||||
import com.google.gson.JsonIOException;
|
import com.google.gson.JsonIOException;
|
||||||
@ -58,9 +59,9 @@ import org.sleuthkit.datamodel.TskData;
|
|||||||
import org.sleuthkit.datamodel.blackboardutils.WebBrowserArtifactsHelper;
|
import org.sleuthkit.datamodel.blackboardutils.WebBrowserArtifactsHelper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Chrome recent activity extraction
|
* Chromium recent activity extraction
|
||||||
*/
|
*/
|
||||||
class Chrome extends Extract {
|
class Chromium extends Extract {
|
||||||
|
|
||||||
private static final String HISTORY_QUERY = "SELECT urls.url, urls.title, urls.visit_count, urls.typed_count, " //NON-NLS
|
private static final String HISTORY_QUERY = "SELECT urls.url, urls.title, urls.visit_count, urls.typed_count, " //NON-NLS
|
||||||
+ "last_visit_time, urls.hidden, visits.visit_time, (SELECT urls.url FROM urls WHERE urls.id=visits.url) AS from_visit, visits.transition FROM urls, visits WHERE urls.id = visits.url"; //NON-NLS
|
+ "last_visit_time, urls.hidden, visits.visit_time, (SELECT urls.url FROM urls WHERE urls.id=visits.url) AS from_visit, visits.transition FROM urls, visits WHERE urls.id = visits.url"; //NON-NLS
|
||||||
@ -84,19 +85,30 @@ class Chrome extends Extract {
|
|||||||
private Content dataSource;
|
private Content dataSource;
|
||||||
private IngestJobContext context;
|
private IngestJobContext context;
|
||||||
|
|
||||||
|
private static final Map<String, String> BROWSERS_MAP = ImmutableMap.<String, String>builder()
|
||||||
|
.put(NbBundle.getMessage(Chromium.class, "Browser.name.Microsoft.Edge"), "Microsoft/Edge")
|
||||||
|
.put(NbBundle.getMessage(Chromium.class, "Browser.name.Yandex"), "YandexBrowser")
|
||||||
|
.put(NbBundle.getMessage(Chromium.class, "Browser.name.Opera"), "Opera Software")
|
||||||
|
.put(NbBundle.getMessage(Chromium.class, "Browser.name.SalamWeb"), "SalamWeb")
|
||||||
|
.put(NbBundle.getMessage(Chromium.class, "Browser.name.UC.Browser"), "UCBrowser")
|
||||||
|
.put(NbBundle.getMessage(Chromium.class, "Browser.name.Brave"), "BraveSoftware")
|
||||||
|
.put(NbBundle.getMessage(Chromium.class, "Browser.name.Google.Chrome"), "Chrome")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
|
||||||
@Messages({
|
@Messages({
|
||||||
"Progress_Message_Chrome_History=Chrome History",
|
"Progress_Message_Chrome_History=Chrome History Browser {0}",
|
||||||
"Progress_Message_Chrome_Bookmarks=Chrome Bookmarks",
|
"Progress_Message_Chrome_Bookmarks=Chrome Bookmarks Browser {0}",
|
||||||
"Progress_Message_Chrome_Cookies=Chrome Cookies",
|
"Progress_Message_Chrome_Cookies=Chrome Cookies Browser {0}",
|
||||||
"Progress_Message_Chrome_Downloads=Chrome Downloads",
|
"Progress_Message_Chrome_Downloads=Chrome Downloads Browser {0}",
|
||||||
"Progress_Message_Chrome_FormHistory=Chrome Form History",
|
"Progress_Message_Chrome_FormHistory=Chrome Form History",
|
||||||
"Progress_Message_Chrome_AutoFill=Chrome Auto Fill",
|
"Progress_Message_Chrome_AutoFill=Chrome Auto Fill Browser {0}",
|
||||||
"Progress_Message_Chrome_Logins=Chrome Logins",
|
"Progress_Message_Chrome_Logins=Chrome Logins Browser {0}",
|
||||||
"Progress_Message_Chrome_Cache=Chrome Cache",
|
"Progress_Message_Chrome_Cache=Chrome Cache",
|
||||||
})
|
})
|
||||||
|
|
||||||
Chrome() {
|
Chromium() {
|
||||||
moduleName = NbBundle.getMessage(Chrome.class, "Chrome.moduleName");
|
moduleName = NbBundle.getMessage(Chromium.class, "Chrome.moduleName");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -105,55 +117,60 @@ class Chrome extends Extract {
|
|||||||
this.context = context;
|
this.context = context;
|
||||||
dataFound = false;
|
dataFound = false;
|
||||||
|
|
||||||
progressBar.progress(Bundle.Progress_Message_Chrome_History());
|
for (Map.Entry<String, String> browser : BROWSERS_MAP.entrySet()) {
|
||||||
this.getHistory();
|
String browserName = browser.getKey();
|
||||||
|
String browserLocation = browser.getValue();
|
||||||
|
progressBar.progress(NbBundle.getMessage(this.getClass(), "Progress_Message_Chrome_History", browserName));
|
||||||
|
this.getHistory(browser.getKey(), browser.getValue());
|
||||||
if (context.dataSourceIngestIsCancelled()) {
|
if (context.dataSourceIngestIsCancelled()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
progressBar.progress(Bundle.Progress_Message_Chrome_Bookmarks());
|
progressBar.progress(NbBundle.getMessage(this.getClass(), "Progress_Message_Chrome_Bookmarks", browserName));
|
||||||
this.getBookmark();
|
this.getBookmark(browser.getKey(), browser.getValue());
|
||||||
if (context.dataSourceIngestIsCancelled()) {
|
if (context.dataSourceIngestIsCancelled()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
progressBar.progress(Bundle.Progress_Message_Chrome_Cookies());
|
progressBar.progress(NbBundle.getMessage(this.getClass(), "Progress_Message_Chrome_Cookies", browserName));
|
||||||
this.getCookie();
|
this.getCookie(browser.getKey(), browser.getValue());
|
||||||
if (context.dataSourceIngestIsCancelled()) {
|
if (context.dataSourceIngestIsCancelled()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
progressBar.progress(Bundle.Progress_Message_Chrome_Logins());
|
progressBar.progress(NbBundle.getMessage(this.getClass(), "Progress_Message_Chrome_Logins", browserName));
|
||||||
this.getLogins();
|
this.getLogins(browser.getKey(), browser.getValue());
|
||||||
if (context.dataSourceIngestIsCancelled()) {
|
if (context.dataSourceIngestIsCancelled()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
progressBar.progress(Bundle.Progress_Message_Chrome_AutoFill());
|
progressBar.progress(NbBundle.getMessage(this.getClass(), "Progress_Message_Chrome_AutoFill", browserName));
|
||||||
this.getAutofill();
|
this.getAutofill(browser.getKey(), browser.getValue());
|
||||||
if (context.dataSourceIngestIsCancelled()) {
|
if (context.dataSourceIngestIsCancelled()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
progressBar.progress(Bundle.Progress_Message_Chrome_Downloads());
|
progressBar.progress(NbBundle.getMessage(this.getClass(), "Progress_Message_Chrome_Downloads", browserName));
|
||||||
this.getDownload();
|
this.getDownload(browser.getKey(), browser.getValue());
|
||||||
if (context.dataSourceIngestIsCancelled()) {
|
if (context.dataSourceIngestIsCancelled()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
progressBar.progress(Bundle.Progress_Message_Chrome_Cache());
|
progressBar.progress(Bundle.Progress_Message_Chrome_Cache());
|
||||||
ChromeCacheExtractor chromeCacheExtractor = new ChromeCacheExtractor(dataSource, context, progressBar);
|
ChromeCacheExtractor chromeCacheExtractor = new ChromeCacheExtractor(dataSource, context, progressBar);
|
||||||
chromeCacheExtractor.processCaches();
|
chromeCacheExtractor.processCaches();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Query for history databases and add artifacts
|
* Query for history databases and add artifacts
|
||||||
*/
|
*/
|
||||||
private void getHistory() {
|
private void getHistory(String browser, String browserLocation) {
|
||||||
FileManager fileManager = currentCase.getServices().getFileManager();
|
FileManager fileManager = currentCase.getServices().getFileManager();
|
||||||
List<AbstractFile> historyFiles;
|
List<AbstractFile> historyFiles;
|
||||||
try {
|
try {
|
||||||
historyFiles = fileManager.findFiles(dataSource, "History", "Chrome"); //NON-NLS
|
historyFiles = fileManager.findFiles(dataSource, "%History%", browserLocation); //NON-NLS
|
||||||
} catch (TskCoreException ex) {
|
} catch (TskCoreException ex) {
|
||||||
String msg = NbBundle.getMessage(this.getClass(), "Chrome.getHistory.errMsg.errGettingFiles");
|
String msg = NbBundle.getMessage(this.getClass(), "Chrome.getHistory.errMsg.errGettingFiles");
|
||||||
logger.log(Level.SEVERE, msg, ex);
|
logger.log(Level.SEVERE, msg, ex);
|
||||||
@ -179,10 +196,11 @@ class Chrome extends Extract {
|
|||||||
dataFound = true;
|
dataFound = true;
|
||||||
Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
|
Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
|
||||||
int j = 0;
|
int j = 0;
|
||||||
while (j < historyFiles.size()) {
|
while (j < allocatedHistoryFiles.size()) {
|
||||||
String temps = RAImageIngestModule.getRATempPath(currentCase, "chrome") + File.separator + historyFiles.get(j).getName() + j + ".db"; //NON-NLS
|
String temps = RAImageIngestModule.getRATempPath(currentCase, browser) + File.separator + allocatedHistoryFiles.get(j).getName() + j + ".db"; //NON-NLS
|
||||||
final AbstractFile historyFile = historyFiles.get(j++);
|
final AbstractFile historyFile = allocatedHistoryFiles.get(j++);
|
||||||
if (historyFile.getSize() == 0) {
|
if ((historyFile.getSize() == 0) || (historyFile.getName().toLowerCase().contains("-slack"))
|
||||||
|
|| (historyFile.getName().toLowerCase().contains("cache")) || (historyFile.getName().toLowerCase().contains("media"))) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@ -223,8 +241,7 @@ class Chrome extends Extract {
|
|||||||
RecentActivityExtracterModuleFactory.getModuleName(),
|
RecentActivityExtracterModuleFactory.getModuleName(),
|
||||||
((result.get("title").toString() != null) ? result.get("title").toString() : ""))); //NON-NLS
|
((result.get("title").toString() != null) ? result.get("title").toString() : ""))); //NON-NLS
|
||||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
|
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
|
||||||
RecentActivityExtracterModuleFactory.getModuleName(),
|
RecentActivityExtracterModuleFactory.getModuleName(), browser));
|
||||||
NbBundle.getMessage(this.getClass(), "Chrome.moduleName")));
|
|
||||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
|
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
|
||||||
RecentActivityExtracterModuleFactory.getModuleName(),
|
RecentActivityExtracterModuleFactory.getModuleName(),
|
||||||
(NetworkUtils.extractDomain((result.get("url").toString() != null) ? result.get("url").toString() : "")))); //NON-NLS
|
(NetworkUtils.extractDomain((result.get("url").toString() != null) ? result.get("url").toString() : "")))); //NON-NLS
|
||||||
@ -245,11 +262,11 @@ class Chrome extends Extract {
|
|||||||
/**
|
/**
|
||||||
* Search for bookmark files and make artifacts.
|
* Search for bookmark files and make artifacts.
|
||||||
*/
|
*/
|
||||||
private void getBookmark() {
|
private void getBookmark(String browser, String browserLocation) {
|
||||||
FileManager fileManager = currentCase.getServices().getFileManager();
|
FileManager fileManager = currentCase.getServices().getFileManager();
|
||||||
List<AbstractFile> bookmarkFiles;
|
List<AbstractFile> bookmarkFiles;
|
||||||
try {
|
try {
|
||||||
bookmarkFiles = fileManager.findFiles(dataSource, "Bookmarks", "Chrome"); //NON-NLS
|
bookmarkFiles = fileManager.findFiles(dataSource, "%Bookmarks%", browserLocation); //NON-NLS
|
||||||
} catch (TskCoreException ex) {
|
} catch (TskCoreException ex) {
|
||||||
String msg = NbBundle.getMessage(this.getClass(), "Chrome.getBookmark.errMsg.errGettingFiles");
|
String msg = NbBundle.getMessage(this.getClass(), "Chrome.getBookmark.errMsg.errGettingFiles");
|
||||||
logger.log(Level.SEVERE, msg, ex);
|
logger.log(Level.SEVERE, msg, ex);
|
||||||
@ -268,10 +285,12 @@ class Chrome extends Extract {
|
|||||||
|
|
||||||
while (j < bookmarkFiles.size()) {
|
while (j < bookmarkFiles.size()) {
|
||||||
AbstractFile bookmarkFile = bookmarkFiles.get(j++);
|
AbstractFile bookmarkFile = bookmarkFiles.get(j++);
|
||||||
if (bookmarkFile.getSize() == 0) {
|
if ((bookmarkFile.getSize() == 0) || (bookmarkFile.getName().toLowerCase().contains("-slack"))
|
||||||
|
|| (bookmarkFile.getName().toLowerCase().contains("extras")) || (bookmarkFile.getName().toLowerCase().contains("log"))
|
||||||
|
|| (bookmarkFile.getName().toLowerCase().contains("backup")) || (bookmarkFile.getName().toLowerCase().contains("visualized"))) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
String temps = RAImageIngestModule.getRATempPath(currentCase, "chrome") + File.separator + bookmarkFile.getName() + j + ".db"; //NON-NLS
|
String temps = RAImageIngestModule.getRATempPath(currentCase, browser) + File.separator + bookmarkFile.getName() + j + ".db"; //NON-NLS
|
||||||
try {
|
try {
|
||||||
ContentUtils.writeToFile(bookmarkFile, new File(temps), context::dataSourceIngestIsCancelled);
|
ContentUtils.writeToFile(bookmarkFile, new File(temps), context::dataSourceIngestIsCancelled);
|
||||||
} catch (ReadContentInputStreamException ex) {
|
} catch (ReadContentInputStreamException ex) {
|
||||||
@ -359,8 +378,7 @@ class Chrome extends Extract {
|
|||||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
|
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
|
||||||
RecentActivityExtracterModuleFactory.getModuleName(), (date / 1000000) - Long.valueOf("11644473600")));
|
RecentActivityExtracterModuleFactory.getModuleName(), (date / 1000000) - Long.valueOf("11644473600")));
|
||||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
|
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
|
||||||
RecentActivityExtracterModuleFactory.getModuleName(),
|
RecentActivityExtracterModuleFactory.getModuleName(), browser));
|
||||||
NbBundle.getMessage(this.getClass(), "Chrome.moduleName")));
|
|
||||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
|
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
|
||||||
RecentActivityExtracterModuleFactory.getModuleName(), domain));
|
RecentActivityExtracterModuleFactory.getModuleName(), domain));
|
||||||
bbart.addAttributes(bbattributes);
|
bbart.addAttributes(bbattributes);
|
||||||
@ -381,12 +399,12 @@ class Chrome extends Extract {
|
|||||||
/**
|
/**
|
||||||
* Queries for cookie files and adds artifacts
|
* Queries for cookie files and adds artifacts
|
||||||
*/
|
*/
|
||||||
private void getCookie() {
|
private void getCookie(String browser, String browserLocation) {
|
||||||
|
|
||||||
FileManager fileManager = currentCase.getServices().getFileManager();
|
FileManager fileManager = currentCase.getServices().getFileManager();
|
||||||
List<AbstractFile> cookiesFiles;
|
List<AbstractFile> cookiesFiles;
|
||||||
try {
|
try {
|
||||||
cookiesFiles = fileManager.findFiles(dataSource, "Cookies", "Chrome"); //NON-NLS
|
cookiesFiles = fileManager.findFiles(dataSource, "%Cookies%", browserLocation); //NON-NLS
|
||||||
} catch (TskCoreException ex) {
|
} catch (TskCoreException ex) {
|
||||||
String msg = NbBundle.getMessage(this.getClass(), "Chrome.getCookie.errMsg.errGettingFiles");
|
String msg = NbBundle.getMessage(this.getClass(), "Chrome.getCookie.errMsg.errGettingFiles");
|
||||||
logger.log(Level.SEVERE, msg, ex);
|
logger.log(Level.SEVERE, msg, ex);
|
||||||
@ -404,10 +422,10 @@ class Chrome extends Extract {
|
|||||||
int j = 0;
|
int j = 0;
|
||||||
while (j < cookiesFiles.size()) {
|
while (j < cookiesFiles.size()) {
|
||||||
AbstractFile cookiesFile = cookiesFiles.get(j++);
|
AbstractFile cookiesFile = cookiesFiles.get(j++);
|
||||||
if (cookiesFile.getSize() == 0) {
|
if ((cookiesFile.getSize() == 0) || (cookiesFile.getName().toLowerCase().contains("-slack"))) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
String temps = RAImageIngestModule.getRATempPath(currentCase, "chrome") + File.separator + cookiesFile.getName() + j + ".db"; //NON-NLS
|
String temps = RAImageIngestModule.getRATempPath(currentCase, browser) + File.separator + cookiesFile.getName() + j + ".db"; //NON-NLS
|
||||||
try {
|
try {
|
||||||
ContentUtils.writeToFile(cookiesFile, new File(temps), context::dataSourceIngestIsCancelled);
|
ContentUtils.writeToFile(cookiesFile, new File(temps), context::dataSourceIngestIsCancelled);
|
||||||
} catch (ReadContentInputStreamException ex) {
|
} catch (ReadContentInputStreamException ex) {
|
||||||
@ -447,8 +465,7 @@ class Chrome extends Extract {
|
|||||||
RecentActivityExtracterModuleFactory.getModuleName(),
|
RecentActivityExtracterModuleFactory.getModuleName(),
|
||||||
((result.get("value").toString() != null) ? result.get("value").toString() : ""))); //NON-NLS
|
((result.get("value").toString() != null) ? result.get("value").toString() : ""))); //NON-NLS
|
||||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
|
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
|
||||||
RecentActivityExtracterModuleFactory.getModuleName(),
|
RecentActivityExtracterModuleFactory.getModuleName(), browser));
|
||||||
NbBundle.getMessage(this.getClass(), "Chrome.moduleName")));
|
|
||||||
String domain = result.get("host_key").toString(); //NON-NLS
|
String domain = result.get("host_key").toString(); //NON-NLS
|
||||||
domain = domain.replaceFirst("^\\.+(?!$)", "");
|
domain = domain.replaceFirst("^\\.+(?!$)", "");
|
||||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
|
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
|
||||||
@ -471,11 +488,11 @@ class Chrome extends Extract {
|
|||||||
/**
|
/**
|
||||||
* Queries for download files and adds artifacts
|
* Queries for download files and adds artifacts
|
||||||
*/
|
*/
|
||||||
private void getDownload() {
|
private void getDownload(String browser, String browserLocation) {
|
||||||
FileManager fileManager = currentCase.getServices().getFileManager();
|
FileManager fileManager = currentCase.getServices().getFileManager();
|
||||||
List<AbstractFile> downloadFiles;
|
List<AbstractFile> downloadFiles;
|
||||||
try {
|
try {
|
||||||
downloadFiles = fileManager.findFiles(dataSource, "History", "Chrome"); //NON-NLS
|
downloadFiles = fileManager.findFiles(dataSource, "%History%", browserLocation); //NON-NLS
|
||||||
} catch (TskCoreException ex) {
|
} catch (TskCoreException ex) {
|
||||||
String msg = NbBundle.getMessage(this.getClass(), "Chrome.getDownload.errMsg.errGettingFiles");
|
String msg = NbBundle.getMessage(this.getClass(), "Chrome.getDownload.errMsg.errGettingFiles");
|
||||||
logger.log(Level.SEVERE, msg, ex);
|
logger.log(Level.SEVERE, msg, ex);
|
||||||
@ -493,10 +510,12 @@ class Chrome extends Extract {
|
|||||||
int j = 0;
|
int j = 0;
|
||||||
while (j < downloadFiles.size()) {
|
while (j < downloadFiles.size()) {
|
||||||
AbstractFile downloadFile = downloadFiles.get(j++);
|
AbstractFile downloadFile = downloadFiles.get(j++);
|
||||||
if (downloadFile.getSize() == 0) {
|
if ((downloadFile.getSize() == 0) || (downloadFile.getName().toLowerCase().contains("-slack"))
|
||||||
|
|| (downloadFile.getName().toLowerCase().contains("cache"))) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
String temps = RAImageIngestModule.getRATempPath(currentCase, "chrome") + File.separator + downloadFile.getName() + j + ".db"; //NON-NLS
|
|
||||||
|
String temps = RAImageIngestModule.getRATempPath(currentCase, browser) + File.separator + downloadFile.getName() + j + ".db"; //NON-NLS
|
||||||
try {
|
try {
|
||||||
ContentUtils.writeToFile(downloadFile, new File(temps), context::dataSourceIngestIsCancelled);
|
ContentUtils.writeToFile(downloadFile, new File(temps), context::dataSourceIngestIsCancelled);
|
||||||
} catch (ReadContentInputStreamException ex) {
|
} catch (ReadContentInputStreamException ex) {
|
||||||
@ -552,8 +571,7 @@ class Chrome extends Extract {
|
|||||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
|
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
|
||||||
RecentActivityExtracterModuleFactory.getModuleName(), domain));
|
RecentActivityExtracterModuleFactory.getModuleName(), domain));
|
||||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
|
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
|
||||||
RecentActivityExtracterModuleFactory.getModuleName(),
|
RecentActivityExtracterModuleFactory.getModuleName(), browser));
|
||||||
NbBundle.getMessage(this.getClass(), "Chrome.moduleName")));
|
|
||||||
|
|
||||||
BlackboardArtifact webDownloadArtifact = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, downloadFile, bbattributes);
|
BlackboardArtifact webDownloadArtifact = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, downloadFile, bbattributes);
|
||||||
if (webDownloadArtifact != null) {
|
if (webDownloadArtifact != null) {
|
||||||
@ -588,12 +606,12 @@ class Chrome extends Extract {
|
|||||||
/**
|
/**
|
||||||
* Gets user logins from Login Data sqlite database
|
* Gets user logins from Login Data sqlite database
|
||||||
*/
|
*/
|
||||||
private void getLogins() {
|
private void getLogins(String browser, String browserLocation) {
|
||||||
|
|
||||||
FileManager fileManager = currentCase.getServices().getFileManager();
|
FileManager fileManager = currentCase.getServices().getFileManager();
|
||||||
List<AbstractFile> loginDataFiles;
|
List<AbstractFile> loginDataFiles;
|
||||||
try {
|
try {
|
||||||
loginDataFiles = fileManager.findFiles(dataSource, "Login Data", "Chrome"); //NON-NLS
|
loginDataFiles = fileManager.findFiles(dataSource, "%Login Data%", browserLocation); //NON-NLS
|
||||||
} catch (TskCoreException ex) {
|
} catch (TskCoreException ex) {
|
||||||
String msg = NbBundle.getMessage(this.getClass(), "Chrome.getLogin.errMsg.errGettingFiles");
|
String msg = NbBundle.getMessage(this.getClass(), "Chrome.getLogin.errMsg.errGettingFiles");
|
||||||
logger.log(Level.SEVERE, msg, ex);
|
logger.log(Level.SEVERE, msg, ex);
|
||||||
@ -611,10 +629,10 @@ class Chrome extends Extract {
|
|||||||
int j = 0;
|
int j = 0;
|
||||||
while (j < loginDataFiles.size()) {
|
while (j < loginDataFiles.size()) {
|
||||||
AbstractFile loginDataFile = loginDataFiles.get(j++);
|
AbstractFile loginDataFile = loginDataFiles.get(j++);
|
||||||
if (loginDataFile.getSize() == 0) {
|
if ((loginDataFile.getSize() == 0) || (loginDataFile.getName().toLowerCase().contains("-slack"))) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
String temps = RAImageIngestModule.getRATempPath(currentCase, "chrome") + File.separator + loginDataFile.getName() + j + ".db"; //NON-NLS
|
String temps = RAImageIngestModule.getRATempPath(currentCase, browser) + File.separator + loginDataFile.getName() + j + ".db"; //NON-NLS
|
||||||
try {
|
try {
|
||||||
ContentUtils.writeToFile(loginDataFile, new File(temps), context::dataSourceIngestIsCancelled);
|
ContentUtils.writeToFile(loginDataFile, new File(temps), context::dataSourceIngestIsCancelled);
|
||||||
} catch (ReadContentInputStreamException ex) {
|
} catch (ReadContentInputStreamException ex) {
|
||||||
@ -661,6 +679,9 @@ class Chrome extends Extract {
|
|||||||
RecentActivityExtracterModuleFactory.getModuleName(),
|
RecentActivityExtracterModuleFactory.getModuleName(),
|
||||||
((result.get("signon_realm").toString() != null) ? result.get("signon_realm").toString() : ""))); //NON-NLS
|
((result.get("signon_realm").toString() != null) ? result.get("signon_realm").toString() : ""))); //NON-NLS
|
||||||
|
|
||||||
|
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
|
||||||
|
RecentActivityExtracterModuleFactory.getModuleName(), browser));
|
||||||
|
|
||||||
BlackboardArtifact bbart = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_SERVICE_ACCOUNT, loginDataFile, bbattributes);
|
BlackboardArtifact bbart = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_SERVICE_ACCOUNT, loginDataFile, bbattributes);
|
||||||
if (bbart != null) {
|
if (bbart != null) {
|
||||||
bbartifacts.add(bbart);
|
bbartifacts.add(bbart);
|
||||||
@ -679,12 +700,12 @@ class Chrome extends Extract {
|
|||||||
* Gets and parses Autofill data from 'Web Data' database,
|
* Gets and parses Autofill data from 'Web Data' database,
|
||||||
* and creates TSK_WEB_FORM_AUTOFILL, TSK_WEB_FORM_ADDRESS artifacts
|
* and creates TSK_WEB_FORM_AUTOFILL, TSK_WEB_FORM_ADDRESS artifacts
|
||||||
*/
|
*/
|
||||||
private void getAutofill() {
|
private void getAutofill(String browser, String browserLocation) {
|
||||||
|
|
||||||
FileManager fileManager = currentCase.getServices().getFileManager();
|
FileManager fileManager = currentCase.getServices().getFileManager();
|
||||||
List<AbstractFile> webDataFiles;
|
List<AbstractFile> webDataFiles;
|
||||||
try {
|
try {
|
||||||
webDataFiles = fileManager.findFiles(dataSource, "Web Data", "Chrome"); //NON-NLS
|
webDataFiles = fileManager.findFiles(dataSource, "%Web Data%", browserLocation); //NON-NLS
|
||||||
} catch (TskCoreException ex) {
|
} catch (TskCoreException ex) {
|
||||||
String msg = NbBundle.getMessage(this.getClass(), "Chrome.getAutofills.errMsg.errGettingFiles");
|
String msg = NbBundle.getMessage(this.getClass(), "Chrome.getAutofills.errMsg.errGettingFiles");
|
||||||
logger.log(Level.SEVERE, msg, ex);
|
logger.log(Level.SEVERE, msg, ex);
|
||||||
@ -702,10 +723,10 @@ class Chrome extends Extract {
|
|||||||
int j = 0;
|
int j = 0;
|
||||||
while (j < webDataFiles.size()) {
|
while (j < webDataFiles.size()) {
|
||||||
AbstractFile webDataFile = webDataFiles.get(j++);
|
AbstractFile webDataFile = webDataFiles.get(j++);
|
||||||
if (webDataFile.getSize() == 0) {
|
if ((webDataFile.getSize() == 0) || (webDataFile.getName().toLowerCase().contains("-slack"))) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
String tempFilePath = RAImageIngestModule.getRATempPath(currentCase, "chrome") + File.separator + webDataFile.getName() + j + ".db"; //NON-NLS
|
String tempFilePath = RAImageIngestModule.getRATempPath(currentCase, browser) + File.separator + webDataFile.getName() + j + ".db"; //NON-NLS
|
||||||
try {
|
try {
|
||||||
ContentUtils.writeToFile(webDataFile, new File(tempFilePath), context::dataSourceIngestIsCancelled);
|
ContentUtils.writeToFile(webDataFile, new File(tempFilePath), context::dataSourceIngestIsCancelled);
|
||||||
} catch (ReadContentInputStreamException ex) {
|
} catch (ReadContentInputStreamException ex) {
|
||||||
@ -731,7 +752,7 @@ class Chrome extends Extract {
|
|||||||
boolean isSchemaV8X = Util.checkColumn("date_created", "autofill", tempFilePath);
|
boolean isSchemaV8X = Util.checkColumn("date_created", "autofill", tempFilePath);
|
||||||
|
|
||||||
// get form autofill artifacts
|
// get form autofill artifacts
|
||||||
bbartifacts.addAll(getFormAutofillArtifacts(webDataFile, tempFilePath, isSchemaV8X));
|
bbartifacts.addAll(getFormAutofillArtifacts(webDataFile, tempFilePath, isSchemaV8X, browser));
|
||||||
try {
|
try {
|
||||||
// get form address atifacts
|
// get form address atifacts
|
||||||
getFormAddressArtifacts(webDataFile, tempFilePath, isSchemaV8X);
|
getFormAddressArtifacts(webDataFile, tempFilePath, isSchemaV8X);
|
||||||
@ -757,7 +778,7 @@ class Chrome extends Extract {
|
|||||||
*
|
*
|
||||||
* @return collection of TSK_WEB_FORM_AUTOFILL artifacts
|
* @return collection of TSK_WEB_FORM_AUTOFILL artifacts
|
||||||
*/
|
*/
|
||||||
private Collection<BlackboardArtifact> getFormAutofillArtifacts (AbstractFile webDataFile, String dbFilePath , boolean isSchemaV8X ) {
|
private Collection<BlackboardArtifact> getFormAutofillArtifacts (AbstractFile webDataFile, String dbFilePath , boolean isSchemaV8X, String browser ) {
|
||||||
|
|
||||||
Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
|
Collection<BlackboardArtifact> bbartifacts = new ArrayList<>();
|
||||||
|
|
||||||
@ -794,6 +815,9 @@ class Chrome extends Extract {
|
|||||||
Long.valueOf(result.get("date_last_used").toString()))); //NON-NLS
|
Long.valueOf(result.get("date_last_used").toString()))); //NON-NLS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
|
||||||
|
RecentActivityExtracterModuleFactory.getModuleName(), browser));
|
||||||
|
|
||||||
// Add an artifact
|
// Add an artifact
|
||||||
BlackboardArtifact bbart = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_FORM_AUTOFILL, webDataFile, bbattributes);
|
BlackboardArtifact bbart = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_FORM_AUTOFILL, webDataFile, bbattributes);
|
||||||
if (bbart != null) {
|
if (bbart != null) {
|
@ -83,9 +83,12 @@ import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_OS_AC
|
|||||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||||
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT;
|
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT;
|
||||||
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT;
|
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT;
|
||||||
|
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME;
|
||||||
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED;
|
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED;
|
||||||
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED;
|
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED;
|
||||||
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_MODIFIED;
|
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_MODIFIED;
|
||||||
|
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DEVICE_ID;
|
||||||
|
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME;
|
||||||
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH;
|
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH;
|
||||||
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_USER_ID;
|
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_USER_ID;
|
||||||
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_USER_NAME;
|
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_USER_NAME;
|
||||||
@ -353,12 +356,10 @@ class ExtractRegistry extends Extract {
|
|||||||
} catch (IOException | TskCoreException ex) {
|
} catch (IOException | TskCoreException ex) {
|
||||||
logger.log(Level.WARNING, String.format("Unable to get shell bags from file %s", regOutputFiles.fullPlugins), ex);
|
logger.log(Level.WARNING, String.format("Unable to get shell bags from file %s", regOutputFiles.fullPlugins), ex);
|
||||||
}
|
}
|
||||||
} else if (regFileNameLocal.toLowerCase().contains("system")) {
|
} else if (regFileNameLocal.toLowerCase().contains("system") && parseSystemPluginOutput(regOutputFiles.fullPlugins, regFile) == false) {
|
||||||
try {
|
this.addErrorMessage(
|
||||||
createSystemArtifacts(regOutputFiles.fullPlugins, regFile);
|
NbBundle.getMessage(this.getClass(), "ExtractRegistry.analyzeRegFiles.failedParsingResults",
|
||||||
} catch (IOException ex) {
|
this.getName(), regFileName));
|
||||||
logger.log(Level.WARNING, String.format("Unable to get artifacts from file %s", regOutputFiles.fullPlugins), ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
Report report = currentCase.addReport(regOutputFiles.fullPlugins,
|
Report report = currentCase.addReport(regOutputFiles.fullPlugins,
|
||||||
@ -952,6 +953,119 @@ class ExtractRegistry extends Extract {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private boolean parseSystemPluginOutput(String regfilePath, AbstractFile regAbstractFile) {
|
||||||
|
File regfile = new File(regfilePath);
|
||||||
|
try (BufferedReader reader = new BufferedReader(new FileReader(regfile))) {
|
||||||
|
String line = reader.readLine();
|
||||||
|
while (line != null) {
|
||||||
|
line = line.trim();
|
||||||
|
|
||||||
|
if (line.toLowerCase().matches("^bam v.*")) {
|
||||||
|
parseBamKey(regAbstractFile, reader, Bundle.Registry_System_Bam());
|
||||||
|
} else if (line.toLowerCase().matches("^bthport v..*")) {
|
||||||
|
parseBlueToothDevices(regAbstractFile, reader);
|
||||||
|
}
|
||||||
|
line = reader.readLine();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} catch (FileNotFoundException ex) {
|
||||||
|
logger.log(Level.WARNING, "Error finding the registry file.", ex); //NON-NLS
|
||||||
|
} catch (IOException ex) {
|
||||||
|
logger.log(Level.WARNING, "Error reading the system hive: {0}", ex); //NON-NLS
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create recently used artifacts to parse the regripper plugin output, this
|
||||||
|
* format is used in several diffent plugins
|
||||||
|
*
|
||||||
|
* @param regFile registry file the artifact is associated with
|
||||||
|
*
|
||||||
|
* @param reader buffered reader to parse adobemru records
|
||||||
|
*
|
||||||
|
* @param comment string that will populate attribute TSK_COMMENT
|
||||||
|
*
|
||||||
|
* @throws FileNotFound and IOException
|
||||||
|
*/
|
||||||
|
private void parseBlueToothDevices(AbstractFile regFile, BufferedReader reader) throws FileNotFoundException, IOException {
|
||||||
|
List<BlackboardArtifact> bbartifacts = new ArrayList<>();
|
||||||
|
String line = reader.readLine();
|
||||||
|
while ((line != null) && (!line.contains(SECTION_DIVIDER))) {
|
||||||
|
line = reader.readLine();
|
||||||
|
|
||||||
|
if (line != null) {
|
||||||
|
line = line.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((line != null) && (line.toLowerCase().contains("device unique id"))) {
|
||||||
|
// Columns are seperated by colons :
|
||||||
|
// Data : Values
|
||||||
|
// Record is 4 lines in length (Device Unique Id, Name, Last Seen, LastConnected
|
||||||
|
while (line != null && !line.contains(SECTION_DIVIDER) && !line.isEmpty() && !line.toLowerCase().contains("radio support not found")) {
|
||||||
|
Collection<BlackboardAttribute> attributes = new ArrayList<>();
|
||||||
|
addBlueToothAttribute(line, attributes, TSK_DEVICE_ID);
|
||||||
|
line = reader.readLine();
|
||||||
|
// Name may not exist, check for it to make sure.
|
||||||
|
if ((line != null) && (line.toLowerCase().contains("name"))) {
|
||||||
|
addBlueToothAttribute(line, attributes, TSK_NAME);
|
||||||
|
line = reader.readLine();
|
||||||
|
}
|
||||||
|
addBlueToothAttribute(line, attributes, TSK_DATETIME);
|
||||||
|
line = reader.readLine();
|
||||||
|
addBlueToothAttribute(line, attributes, TSK_DATETIME_ACCESSED);
|
||||||
|
BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_BLUETOOTH_PAIRING, regFile, attributes);
|
||||||
|
if(bba != null) {
|
||||||
|
bbartifacts.add(bba);
|
||||||
|
}
|
||||||
|
// Read blank line between records then next read line is start of next block
|
||||||
|
reader.readLine();
|
||||||
|
line = reader.readLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line != null) {
|
||||||
|
line = line.trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!bbartifacts.isEmpty()) {
|
||||||
|
postArtifacts(bbartifacts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void addBlueToothAttribute(String line, Collection<BlackboardAttribute> attributes, ATTRIBUTE_TYPE attributeType) {
|
||||||
|
if (line == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String tokens[] = line.split(": ");
|
||||||
|
if (tokens.length > 1 && !tokens[1].isEmpty()) {
|
||||||
|
String tokenString = tokens[1];
|
||||||
|
if (attributeType.getDisplayName().toLowerCase().contains("date")) {
|
||||||
|
String dateString = tokenString.toLowerCase().replace(" z", "");
|
||||||
|
// date format for plugin Tue Jun 23 10:27:54 2020 Z
|
||||||
|
SimpleDateFormat dateFormat = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy", US);
|
||||||
|
Long dateLong = Long.valueOf(0);
|
||||||
|
try {
|
||||||
|
Date newDate = dateFormat.parse(dateString);
|
||||||
|
dateLong = newDate.getTime()/1000;
|
||||||
|
} catch (ParseException ex) {
|
||||||
|
// catching error and displaying date that could not be parsed
|
||||||
|
// we set the timestamp to 0 and continue on processing
|
||||||
|
logger.log(Level.WARNING, String.format("Failed to parse date/time %s for Bluetooth Last Seen attribute.", dateString), ex); //NON-NLS
|
||||||
|
}
|
||||||
|
attributes.add(new BlackboardAttribute(attributeType, getName(), dateLong));
|
||||||
|
} else {
|
||||||
|
attributes.add(new BlackboardAttribute(attributeType, getName(), tokenString));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse the output of the SAM regripper plugin to get additional Account
|
* Parse the output of the SAM regripper plugin to get additional Account
|
||||||
* information
|
* information
|
||||||
@ -1280,30 +1394,6 @@ class ExtractRegistry extends Extract {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Create artifacts from the System registry Hive
|
|
||||||
*
|
|
||||||
* @param regFileName name of the regripper output file
|
|
||||||
*
|
|
||||||
* @param regFile registry file the artifact is associated with
|
|
||||||
*
|
|
||||||
* @throws FileNotFound and IOException
|
|
||||||
*/
|
|
||||||
private void createSystemArtifacts(String regFileName, AbstractFile regFile) throws FileNotFoundException, IOException {
|
|
||||||
File regfile = new File(regFileName);
|
|
||||||
try (BufferedReader reader = new BufferedReader(new FileReader(regfile))) {
|
|
||||||
String line = reader.readLine();
|
|
||||||
while (line != null) {
|
|
||||||
line = line.trim();
|
|
||||||
|
|
||||||
if (line.matches("^bam v.*")) {
|
|
||||||
parseBamKey(regFile, reader, Bundle.Registry_System_Bam());
|
|
||||||
}
|
|
||||||
line = reader.readLine();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create artifacts from BAM Regripper Plugin records
|
* Create artifacts from BAM Regripper Plugin records
|
||||||
*
|
*
|
||||||
|
@ -70,7 +70,7 @@ public final class RAImageIngestModule implements DataSourceIngestModule {
|
|||||||
|
|
||||||
Extract registry = new ExtractRegistry();
|
Extract registry = new ExtractRegistry();
|
||||||
Extract recentDocuments = new RecentDocumentsByLnk();
|
Extract recentDocuments = new RecentDocumentsByLnk();
|
||||||
Extract chrome = new Chrome();
|
Extract chrome = new Chromium();
|
||||||
Extract firefox = new Firefox();
|
Extract firefox = new Firefox();
|
||||||
Extract SEUQA = new SearchEngineURLQueryAnalyzer();
|
Extract SEUQA = new SearchEngineURLQueryAnalyzer();
|
||||||
Extract osExtract = new ExtractOs();
|
Extract osExtract = new ExtractOs();
|
||||||
|
1
thirdparty/rr-full/plugins/system
vendored
1
thirdparty/rr-full/plugins/system
vendored
@ -5,6 +5,7 @@ auditfail
|
|||||||
backuprestore
|
backuprestore
|
||||||
bam
|
bam
|
||||||
bam_tln
|
bam_tln
|
||||||
|
btconfig
|
||||||
bthport
|
bthport
|
||||||
comfoo
|
comfoo
|
||||||
compname
|
compname
|
||||||
|
Loading…
x
Reference in New Issue
Block a user