mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-06 21:00:22 +00:00
Merge pull request #6140 from gdicristofaro/6631-DataSourceSummarizer
6631 data source summarizer
This commit is contained in:
commit
e95c7f796b
@ -31,7 +31,9 @@ import javax.swing.JOptionPane;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import static org.sleuthkit.autopsy.casemodule.Case.Events.CURRENT_CASE;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.events.AutopsyEvent;
|
||||
import org.sleuthkit.autopsy.ingest.IngestManager;
|
||||
import org.sleuthkit.datamodel.IngestJobInfo;
|
||||
import org.sleuthkit.datamodel.IngestModuleInfo;
|
||||
@ -47,6 +49,8 @@ public final class IngestJobInfoPanel extends javax.swing.JPanel {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(IngestJobInfoPanel.class.getName());
|
||||
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.STARTED, IngestManager.IngestJobEvent.CANCELLED, IngestManager.IngestJobEvent.COMPLETED);
|
||||
private static final Set<Case.Events> CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.CURRENT_CASE);
|
||||
|
||||
private List<IngestJobInfo> ingestJobs;
|
||||
private final List<IngestJobInfo> ingestJobsForSelectedDataSource = new ArrayList<>();
|
||||
private IngestJobTableModel ingestJobTableModel = new IngestJobTableModel();
|
||||
@ -79,6 +83,16 @@ public final class IngestJobInfoPanel extends javax.swing.JPanel {
|
||||
refresh();
|
||||
}
|
||||
});
|
||||
|
||||
Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, (PropertyChangeEvent evt) -> {
|
||||
if (!(evt instanceof AutopsyEvent) || (((AutopsyEvent) evt).getSourceType() != AutopsyEvent.SourceType.LOCAL)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (CURRENT_CASE == Case.Events.valueOf(evt.getPropertyName())) {
|
||||
refresh();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -110,9 +124,15 @@ public final class IngestJobInfoPanel extends javax.swing.JPanel {
|
||||
*/
|
||||
private void refresh() {
|
||||
try {
|
||||
SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
|
||||
this.ingestJobs = skCase.getIngestJobs();
|
||||
setDataSource(selectedDataSource);
|
||||
if (Case.isCaseOpen()) {
|
||||
SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
|
||||
this.ingestJobs = skCase.getIngestJobs();
|
||||
setDataSource(selectedDataSource);
|
||||
} else {
|
||||
this.ingestJobs = new ArrayList<>();
|
||||
setDataSource(null);
|
||||
}
|
||||
|
||||
} catch (TskCoreException | NoCurrentCaseException ex) {
|
||||
logger.log(Level.SEVERE, "Failed to load ingest jobs.", ex);
|
||||
JOptionPane.showMessageDialog(this, Bundle.IngestJobInfoPanel_loadIngestJob_error_text(), Bundle.IngestJobInfoPanel_loadIngestJob_error_title(), JOptionPane.ERROR_MESSAGE);
|
||||
|
@ -1,4 +1,6 @@
|
||||
CTL_DataSourceSummaryAction=Data Source Summary
|
||||
DataSourceSummaryCountsPanel.ArtifactCountsTableModel.count.header=Count
|
||||
DataSourceSummaryCountsPanel.ArtifactCountsTableModel.type.header=Result Type
|
||||
DataSourceSummaryCountsPanel.FilesByCategoryTableModel.all.row=All
|
||||
DataSourceSummaryCountsPanel.FilesByCategoryTableModel.allocated.row=Allocated
|
||||
DataSourceSummaryCountsPanel.FilesByCategoryTableModel.count.header=Count
|
||||
|
@ -18,7 +18,8 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.casemodule.datasourcesummary;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
@ -43,6 +44,297 @@ final class DataSourceInfoUtilities {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(DataSourceInfoUtilities.class.getName());
|
||||
|
||||
/**
|
||||
* Gets a count of files for a particular datasource where it is not a
|
||||
* virtual directory and has a name.
|
||||
*
|
||||
* @param currentDataSource The datasource.
|
||||
* @param additionalWhere Additional sql where clauses.
|
||||
* @param onError The message to log on error.
|
||||
*
|
||||
* @return The count of files or null on error.
|
||||
*/
|
||||
private static Long getCountOfFiles(DataSource currentDataSource, String additionalWhere, String onError) {
|
||||
if (currentDataSource != null) {
|
||||
try {
|
||||
SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
|
||||
return skCase.countFilesWhere(
|
||||
"dir_type<>" + TskData.TSK_FS_NAME_TYPE_ENUM.VIRT_DIR.getValue()
|
||||
+ " AND name<>''"
|
||||
+ " AND data_source_obj_id=" + currentDataSource.getId()
|
||||
+ " AND " + additionalWhere);
|
||||
} catch (TskCoreException | NoCurrentCaseException ex) {
|
||||
logger.log(Level.WARNING, onError, ex);
|
||||
//unable to get count of files for the specified types cell will be displayed as empty
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get count of files in a data source.
|
||||
*
|
||||
* @param currentDataSource The data source.
|
||||
*
|
||||
* @return The count.
|
||||
*/
|
||||
static Long getCountOfFiles(DataSource currentDataSource) {
|
||||
return getCountOfFiles(currentDataSource,
|
||||
"type<>" + TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType(),
|
||||
"Unable to get count of files, providing empty results");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get count of unallocated files in a data source.
|
||||
*
|
||||
* @param currentDataSource The data source.
|
||||
*
|
||||
* @return The count.
|
||||
*/
|
||||
static Long getCountOfUnallocatedFiles(DataSource currentDataSource) {
|
||||
return getCountOfFiles(currentDataSource,
|
||||
"type<>" + TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType()
|
||||
+ " AND dir_flags=" + TskData.TSK_FS_NAME_FLAG_ENUM.UNALLOC.getValue(),
|
||||
"Unable to get counts of unallocated files for datasource, providing empty results");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get count of directories in a data source.
|
||||
*
|
||||
* @param currentDataSource The data source.
|
||||
*
|
||||
* @return The count.
|
||||
*/
|
||||
static Long getCountOfDirectories(DataSource currentDataSource) {
|
||||
return getCountOfFiles(currentDataSource,
|
||||
"type<>" + TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType()
|
||||
+ " AND meta_type=" + TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_DIR.getValue(),
|
||||
"Unable to get count of directories for datasource, providing empty results");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get count of slack files in a data source.
|
||||
*
|
||||
* @param currentDataSource The data source.
|
||||
*
|
||||
* @return The count.
|
||||
*/
|
||||
static Long getCountOfSlackFiles(DataSource currentDataSource) {
|
||||
return getCountOfFiles(currentDataSource,
|
||||
"type=" + TskData.TSK_DB_FILES_TYPE_ENUM.SLACK.getFileType(),
|
||||
"Unable to get count of slack files for datasources, providing empty results");
|
||||
}
|
||||
|
||||
/**
|
||||
* An interface for handling a result set and returning a value.
|
||||
*/
|
||||
private interface ResultSetHandler<T> {
|
||||
|
||||
T process(ResultSet resultset) throws SQLException;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a result based on the provided query.
|
||||
*
|
||||
* @param query The query.
|
||||
* @param processor The result set handler.
|
||||
* @param errorMessage The error message to display if there is an error
|
||||
* retrieving the resultset.
|
||||
*
|
||||
* @return The ResultSetHandler value or null if no ResultSet could be
|
||||
* obtained.
|
||||
*/
|
||||
private static <T> T getBaseQueryResult(String query, ResultSetHandler<T> processor, String errorMessage) {
|
||||
try (SleuthkitCase.CaseDbQuery dbQuery = Case.getCurrentCaseThrows().getSleuthkitCase().executeQuery(query)) {
|
||||
ResultSet resultSet = dbQuery.getResultSet();
|
||||
try {
|
||||
return processor.process(resultSet);
|
||||
} catch (SQLException ex) {
|
||||
logger.log(Level.WARNING, errorMessage, ex);
|
||||
}
|
||||
} catch (TskCoreException | NoCurrentCaseException ex) {
|
||||
logger.log(Level.WARNING, errorMessage, ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the size of unallocated files in a particular datasource.
|
||||
*
|
||||
* @param currentDataSource The data source.
|
||||
*
|
||||
* @return The size or null if the query could not be executed.
|
||||
*/
|
||||
static Long getSizeOfUnallocatedFiles(DataSource currentDataSource) {
|
||||
if (currentDataSource == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final String valueParam = "value";
|
||||
final String countParam = "count";
|
||||
String query = "SELECT SUM(size) AS " + valueParam + ", COUNT(*) AS " + countParam
|
||||
+ " FROM tsk_files WHERE type<>" + TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType()
|
||||
+ " AND dir_type<>" + TskData.TSK_FS_NAME_TYPE_ENUM.VIRT_DIR.getValue()
|
||||
+ " AND dir_flags=" + TskData.TSK_FS_NAME_FLAG_ENUM.UNALLOC.getValue()
|
||||
+ " AND name<>''"
|
||||
+ " AND data_source_obj_id=" + currentDataSource.getId();
|
||||
|
||||
ResultSetHandler<Long> handler = (resultSet) -> {
|
||||
if (resultSet.next()) {
|
||||
// ensure that there is an unallocated count result that is attached to this data source
|
||||
long resultCount = resultSet.getLong(valueParam);
|
||||
return (resultCount > 0) ? resultSet.getLong(valueParam) : null;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
String errorMessage = "Unable to get size of unallocated files; returning null.";
|
||||
|
||||
return getBaseQueryResult(query, handler, errorMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves counts for each artifact type in a data source.
|
||||
*
|
||||
* @param selectedDataSource The data source.
|
||||
*
|
||||
* @return A mapping of artifact type name to the counts or null if there
|
||||
* was an error executing the query.
|
||||
*/
|
||||
static Map<String, Long> getCountsOfArtifactsByType(DataSource selectedDataSource) {
|
||||
if (selectedDataSource == null) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
final String nameParam = "name";
|
||||
final String valueParam = "value";
|
||||
String query
|
||||
= "SELECT bbt.display_name AS " + nameParam + ", COUNT(*) AS " + valueParam
|
||||
+ " FROM blackboard_artifacts bba "
|
||||
+ " INNER JOIN blackboard_artifact_types bbt ON bba.artifact_type_id = bbt.artifact_type_id"
|
||||
+ " WHERE bba.data_source_obj_id =" + selectedDataSource.getId()
|
||||
+ " GROUP BY bbt.display_name";
|
||||
|
||||
ResultSetHandler<Map<String, Long>> handler = (resultSet) -> {
|
||||
Map<String, Long> toRet = new HashMap<>();
|
||||
while (resultSet.next()) {
|
||||
try {
|
||||
toRet.put(resultSet.getString(nameParam), resultSet.getLong(valueParam));
|
||||
} catch (SQLException ex) {
|
||||
logger.log(Level.WARNING, "Failed to get a result pair from the result set.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
return toRet;
|
||||
};
|
||||
|
||||
String errorMessage = "Unable to get artifact type counts; returning null.";
|
||||
|
||||
return getBaseQueryResult(query, handler, errorMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a string which is a concatenation of the value received from
|
||||
* the result set.
|
||||
*
|
||||
* @param query The query.
|
||||
* @param valueParam The parameter for the value in the result set.
|
||||
* @param separator The string separator used in concatenation.
|
||||
* @param errorMessage The error message if the result set could not
|
||||
* be received.
|
||||
* @param singleErrorMessage The error message if a single result could not
|
||||
* be obtained.
|
||||
*
|
||||
* @return The concatenated string or null if the query could not be
|
||||
* executed.
|
||||
*/
|
||||
private static String getConcattedStringsResult(String query, String valueParam, String separator, String errorMessage, String singleErrorMessage) {
|
||||
ResultSetHandler<String> handler = (resultSet) -> {
|
||||
String toRet = "";
|
||||
boolean first = true;
|
||||
while (resultSet.next()) {
|
||||
try {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
toRet += separator;
|
||||
}
|
||||
toRet += resultSet.getString(valueParam);
|
||||
} catch (SQLException ex) {
|
||||
logger.log(Level.WARNING, singleErrorMessage, ex);
|
||||
}
|
||||
}
|
||||
|
||||
return toRet;
|
||||
};
|
||||
|
||||
return getBaseQueryResult(query, handler, errorMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a concatenated string value based on the text value of a
|
||||
* particular attribute in a particular artifact for a specific data source.
|
||||
*
|
||||
* @param dataSourceId The data source id.
|
||||
* @param artifactTypeId The artifact type id.
|
||||
* @param attributeTypeId The attribute type id.
|
||||
*
|
||||
* @return The concatenated value or null if the query could not be
|
||||
* executed.
|
||||
*/
|
||||
private static String getConcattedAttrValue(long dataSourceId, int artifactTypeId, int attributeTypeId) {
|
||||
final String valueParam = "concatted_attribute_value";
|
||||
String query = "SELECT attr.value_text AS " + valueParam
|
||||
+ " FROM blackboard_artifacts bba "
|
||||
+ " INNER JOIN blackboard_attributes attr ON bba.artifact_id = attr.artifact_id "
|
||||
+ " WHERE bba.data_source_obj_id = " + dataSourceId
|
||||
+ " AND bba.artifact_type_id = " + artifactTypeId
|
||||
+ " AND attr.attribute_type_id = " + attributeTypeId;
|
||||
|
||||
String errorMessage = "Unable to execute query to retrieve concatted attribute values.";
|
||||
String singleErrorMessage = "There was an error retrieving one of the results. That result will be omitted from concatted value.";
|
||||
String separator = ", ";
|
||||
return getConcattedStringsResult(query, valueParam, separator, errorMessage, singleErrorMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the concatenation of operating system attributes for a
|
||||
* particular data source.
|
||||
*
|
||||
* @param dataSource The data source.
|
||||
*
|
||||
* @return The concatenated value or null if the query could not be
|
||||
* executed.
|
||||
*/
|
||||
static String getOperatingSystems(DataSource dataSource) {
|
||||
if (dataSource == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return getConcattedAttrValue(dataSource.getId(),
|
||||
BlackboardArtifact.ARTIFACT_TYPE.TSK_OS_INFO.getTypeID(),
|
||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME.getTypeID());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the concatenation of data source usage for a particular data
|
||||
* source.
|
||||
*
|
||||
* @param dataSource The data source.
|
||||
*
|
||||
* @return The concatenated value or null if the query could not be
|
||||
* executed.
|
||||
*/
|
||||
static String getDataSourceType(DataSource dataSource) {
|
||||
if (dataSource == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return getConcattedAttrValue(dataSource.getId(),
|
||||
BlackboardArtifact.ARTIFACT_TYPE.TSK_DATA_SOURCE_USAGE.getTypeID(),
|
||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION.getTypeID());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a map containing the TSK_DATA_SOURCE_USAGE description attributes
|
||||
* associated with each data source in the current case.
|
||||
@ -147,39 +439,6 @@ final class DataSourceInfoUtilities {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a map containing the names of operating systems joined in a comma
|
||||
* seperated list to the Data Source they exist on in the current case. No
|
||||
* item will exist in the map for data sources which do not contain
|
||||
* TS_OS_INFO artifacts which have a program name.
|
||||
*
|
||||
* @return Collection which maps datasource id to a String which is a comma
|
||||
* seperated list of Operating system names found on the data
|
||||
* source.
|
||||
*/
|
||||
static Map<Long, String> getOperatingSystems() {
|
||||
Map<Long, String> osDetailMap = new HashMap<>();
|
||||
try {
|
||||
SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
|
||||
ArrayList<BlackboardArtifact> osInfoArtifacts = skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_OS_INFO);
|
||||
for (BlackboardArtifact osInfo : osInfoArtifacts) {
|
||||
BlackboardAttribute programName = osInfo.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME));
|
||||
if (programName != null) {
|
||||
String currentOsString = osDetailMap.get(osInfo.getDataSource().getId());
|
||||
if (currentOsString == null || currentOsString.isEmpty()) {
|
||||
currentOsString = programName.getValueString();
|
||||
} else {
|
||||
currentOsString = currentOsString + ", " + programName.getValueString();
|
||||
}
|
||||
osDetailMap.put(osInfo.getDataSource().getId(), currentOsString);
|
||||
}
|
||||
}
|
||||
} catch (TskCoreException | NoCurrentCaseException ex) {
|
||||
logger.log(Level.SEVERE, "Failed to load OS info artifacts.", ex);
|
||||
}
|
||||
return osDetailMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of files in the case database for the current data source
|
||||
* which have the specified mimetypes.
|
||||
@ -212,134 +471,6 @@ final class DataSourceInfoUtilities {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a map containing the number of unallocated files in each data source
|
||||
* in the current case.
|
||||
*
|
||||
* @return Collection which maps datasource id to a count for the number of
|
||||
* unallocated files in the datasource, will only contain entries
|
||||
* for datasources which have at least 1 unallocated file
|
||||
*/
|
||||
static Map<Long, Long> getCountsOfUnallocatedFiles() {
|
||||
try {
|
||||
final String countUnallocatedFilesQuery = "data_source_obj_id, COUNT(*) AS value"
|
||||
+ " FROM tsk_files WHERE type<>" + TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType()
|
||||
+ " AND dir_type<>" + TskData.TSK_FS_NAME_TYPE_ENUM.VIRT_DIR.getValue()
|
||||
+ " AND dir_flags=" + TskData.TSK_FS_NAME_FLAG_ENUM.UNALLOC.getValue()
|
||||
+ " AND name<>'' GROUP BY data_source_obj_id"; //NON-NLS
|
||||
return getValuesMap(countUnallocatedFilesQuery);
|
||||
} catch (TskCoreException | NoCurrentCaseException ex) {
|
||||
logger.log(Level.WARNING, "Unable to get counts of unallocated files for all datasources, providing empty results", ex);
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a map containing the total size of unallocated files in each data
|
||||
* source in the current case.
|
||||
*
|
||||
* @return Collection which maps datasource id to a total size in bytes of
|
||||
* unallocated files in the datasource, will only contain entries
|
||||
* for datasources which have at least 1 unallocated file
|
||||
*/
|
||||
static Map<Long, Long> getSizeOfUnallocatedFiles() {
|
||||
try {
|
||||
final String countUnallocatedFilesQuery = "data_source_obj_id, sum(size) AS value"
|
||||
+ " FROM tsk_files WHERE type<>" + TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType()
|
||||
+ " AND dir_type<>" + TskData.TSK_FS_NAME_TYPE_ENUM.VIRT_DIR.getValue()
|
||||
+ " AND dir_flags=" + TskData.TSK_FS_NAME_FLAG_ENUM.UNALLOC.getValue()
|
||||
+ " AND name<>'' GROUP BY data_source_obj_id"; //NON-NLS
|
||||
return getValuesMap(countUnallocatedFilesQuery);
|
||||
} catch (TskCoreException | NoCurrentCaseException ex) {
|
||||
logger.log(Level.WARNING, "Unable to get size of unallocated files for all datasources, providing empty results", ex);
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a map containing the number of directories in each data source in the
|
||||
* current case.
|
||||
*
|
||||
* @return Collection which maps datasource id to a count for the number of
|
||||
* directories in the datasource, will only contain entries for
|
||||
* datasources which have at least 1 directory
|
||||
*/
|
||||
static Map<Long, Long> getCountsOfDirectories() {
|
||||
try {
|
||||
final String countDirectoriesQuery = "data_source_obj_id, COUNT(*) AS value"
|
||||
+ " FROM tsk_files WHERE type<>" + TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType()
|
||||
+ " AND dir_type<>" + TskData.TSK_FS_NAME_TYPE_ENUM.VIRT_DIR.getValue()
|
||||
+ " AND meta_type=" + TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_DIR.getValue()
|
||||
+ " AND name<>'' GROUP BY data_source_obj_id"; //NON-NLS
|
||||
return getValuesMap(countDirectoriesQuery);
|
||||
} catch (TskCoreException | NoCurrentCaseException ex) {
|
||||
logger.log(Level.WARNING, "Unable to get counts of directories for all datasources, providing empty results", ex);
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a map containing the number of slack files in each data source in the
|
||||
* current case.
|
||||
*
|
||||
* @return Collection which maps datasource id to a count for the number of
|
||||
* slack files in the datasource, will only contain entries for
|
||||
* datasources which have at least 1 slack file
|
||||
*/
|
||||
static Map<Long, Long> getCountsOfSlackFiles() {
|
||||
try {
|
||||
final String countSlackFilesQuery = "data_source_obj_id, COUNT(*) AS value"
|
||||
+ " FROM tsk_files WHERE type=" + TskData.TSK_DB_FILES_TYPE_ENUM.SLACK.getFileType()
|
||||
+ " AND dir_type<>" + TskData.TSK_FS_NAME_TYPE_ENUM.VIRT_DIR.getValue()
|
||||
+ " AND name<>'' GROUP BY data_source_obj_id"; //NON-NLS
|
||||
return getValuesMap(countSlackFilesQuery);
|
||||
} catch (TskCoreException | NoCurrentCaseException ex) {
|
||||
logger.log(Level.WARNING, "Unable to get counts of slack files for all datasources, providing empty results", ex);
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a map containing maps which map artifact type to the number of times
|
||||
* it exosts in each data source in the current case.
|
||||
*
|
||||
* @return Collection which maps datasource id to maps of artifact display
|
||||
* name to number of occurences in the datasource, will only contain
|
||||
* entries for artifacts which have at least one occurence in the
|
||||
* data source.
|
||||
*/
|
||||
static Map<Long, Map<String, Long>> getCountsOfArtifactsByType() {
|
||||
try {
|
||||
final String countArtifactsQuery = "blackboard_artifacts.data_source_obj_id, blackboard_artifact_types.display_name AS label, COUNT(*) AS value"
|
||||
+ " FROM blackboard_artifacts, blackboard_artifact_types"
|
||||
+ " WHERE blackboard_artifacts.artifact_type_id = blackboard_artifact_types.artifact_type_id"
|
||||
+ " GROUP BY blackboard_artifacts.data_source_obj_id, blackboard_artifact_types.display_name";
|
||||
return getLabeledValuesMap(countArtifactsQuery);
|
||||
} catch (TskCoreException | NoCurrentCaseException ex) {
|
||||
logger.log(Level.WARNING, "Unable to get counts of all artifact types for all datasources, providing empty results", ex);
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to execute a select query with a
|
||||
* DataSourceLabeledValueCallback.
|
||||
*
|
||||
* @param query the portion of the query which should follow the SELECT
|
||||
*
|
||||
* @return a map of datasource object IDs to maps of String labels to the
|
||||
* values associated with them.
|
||||
*
|
||||
* @throws TskCoreException
|
||||
* @throws NoCurrentCaseException
|
||||
*/
|
||||
private static Map<Long, Map<String, Long>> getLabeledValuesMap(String query) throws TskCoreException, NoCurrentCaseException {
|
||||
SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
|
||||
DataSourceLabeledValueCallback callback = new DataSourceLabeledValueCallback();
|
||||
skCase.getCaseDbAccessManager().select(query, callback);
|
||||
return callback.getMapOfLabeledValues();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to execute a select query with a
|
||||
* DataSourceSingleValueCallback.
|
||||
|
@ -70,18 +70,13 @@
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JTable" name="fileCountsByMimeTypeTable">
|
||||
<Properties>
|
||||
<Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
||||
<Connection code="filesByMimeTypeTableModel" type="code"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Component class="javax.swing.JLabel" name="byMimeTypeLabel">
|
||||
<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="DataSourceSummaryCountsPanel.byMimeTypeLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties" key="DataSourceSummaryCountsPanel.byMimeTypeLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
@ -93,25 +88,20 @@
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JTable" name="fileCountsByCategoryTable">
|
||||
<Properties>
|
||||
<Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
||||
<Connection code="filesByCategoryTableModel" type="code"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Component class="javax.swing.JLabel" name="byCategoryLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties" key="DataSourceSummaryCountsPanel.byCategoryLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties" key="DataSourceSummaryCountsPanel.byCategoryLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="jLabel1">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties" key="DataSourceSummaryCountsPanel.jLabel1.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties" key="DataSourceSummaryCountsPanel.jLabel1.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
@ -123,15 +113,6 @@
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JTable" name="artifactCountsTable">
|
||||
<Properties>
|
||||
<Property name="autoCreateRowSorter" type="boolean" value="true"/>
|
||||
<Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.editors2.TableModelEditor">
|
||||
<Table columnCount="2" rowCount="0">
|
||||
<Column editable="false" title="Result Type" type="java.lang.Object"/>
|
||||
<Column editable="false" title="Count" type="java.lang.Object"/>
|
||||
</Table>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
|
@ -18,15 +18,12 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.casemodule.datasourcesummary;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
import javax.swing.table.DefaultTableCellRenderer;
|
||||
import javax.swing.table.DefaultTableModel;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.coreutils.FileTypeUtils;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
|
||||
@ -34,69 +31,224 @@ import org.sleuthkit.datamodel.DataSource;
|
||||
* Panel for displaying summary information on the known files present in the
|
||||
* specified DataSource
|
||||
*/
|
||||
@Messages({"DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.type.header=File Type",
|
||||
"DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.count.header=Count",
|
||||
"DataSourceSummaryCountsPanel.ArtifactCountsTableModel.type.header=Result Type",
|
||||
"DataSourceSummaryCountsPanel.ArtifactCountsTableModel.count.header=Count",
|
||||
"DataSourceSummaryCountsPanel.FilesByCategoryTableModel.type.header=File Type",
|
||||
"DataSourceSummaryCountsPanel.FilesByCategoryTableModel.count.header=Count"
|
||||
})
|
||||
class DataSourceSummaryCountsPanel extends javax.swing.JPanel {
|
||||
|
||||
// Result returned for a data model if no data found.
|
||||
private static final Object[][] EMPTY_PAIRS = new Object[][]{};
|
||||
|
||||
// column headers for mime type table
|
||||
private static final Object[] MIME_TYPE_COLUMN_HEADERS = new Object[]{
|
||||
Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_type_header(),
|
||||
Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_count_header()
|
||||
};
|
||||
|
||||
// column headers for file by category table
|
||||
private static final Object[] FILE_BY_CATEGORY_COLUMN_HEADERS = new Object[]{
|
||||
Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_type_header(),
|
||||
Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_count_header()
|
||||
};
|
||||
|
||||
// column headers for artifact counts table
|
||||
private static final Object[] ARTIFACT_COUNTS_COLUMN_HEADERS = new Object[]{
|
||||
Bundle.DataSourceSummaryCountsPanel_ArtifactCountsTableModel_type_header(),
|
||||
Bundle.DataSourceSummaryCountsPanel_ArtifactCountsTableModel_count_header()
|
||||
};
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private FilesByMimeTypeTableModel filesByMimeTypeTableModel = new FilesByMimeTypeTableModel(null);
|
||||
private FilesByCategoryTableModel filesByCategoryTableModel = new FilesByCategoryTableModel(null);
|
||||
private static final Logger logger = Logger.getLogger(DataSourceSummaryCountsPanel.class.getName());
|
||||
private final Map<Long, Long> allFilesCountsMap;
|
||||
private final Map<Long, Long> slackFilesCountsMap;
|
||||
private final Map<Long, Long> directoriesCountsMap;
|
||||
private final Map<Long, Long> unallocatedFilesCountsMap;
|
||||
private final Map<Long, Map<String, Long>> artifactsByTypeCountsMap;
|
||||
private final DefaultTableCellRenderer rightAlignedRenderer = new DefaultTableCellRenderer();
|
||||
|
||||
private DataSource dataSource;
|
||||
|
||||
/**
|
||||
* Creates new form DataSourceSummaryCountsPanel
|
||||
*/
|
||||
DataSourceSummaryCountsPanel(Map<Long, Long> fileCountsMap) {
|
||||
this.allFilesCountsMap = fileCountsMap;
|
||||
this.slackFilesCountsMap = DataSourceInfoUtilities.getCountsOfSlackFiles();
|
||||
this.directoriesCountsMap = DataSourceInfoUtilities.getCountsOfDirectories();
|
||||
this.unallocatedFilesCountsMap = DataSourceInfoUtilities.getCountsOfUnallocatedFiles();
|
||||
this.artifactsByTypeCountsMap = DataSourceInfoUtilities.getCountsOfArtifactsByType();
|
||||
DataSourceSummaryCountsPanel() {
|
||||
rightAlignedRenderer.setHorizontalAlignment(JLabel.RIGHT);
|
||||
initComponents();
|
||||
fileCountsByMimeTypeTable.getTableHeader().setReorderingAllowed(false);
|
||||
fileCountsByCategoryTable.getTableHeader().setReorderingAllowed(false);
|
||||
artifactCountsTable.getTableHeader().setReorderingAllowed(false);
|
||||
setDataSource(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the DataSource to display file information for
|
||||
* The datasource currently used as the model in this panel.
|
||||
*
|
||||
* @param selectedDataSource the DataSource to display file information for
|
||||
* @return The datasource currently being used as the model in this panel.
|
||||
*/
|
||||
void updateCountsTableData(DataSource selectedDataSource) {
|
||||
filesByMimeTypeTableModel = new FilesByMimeTypeTableModel(selectedDataSource);
|
||||
fileCountsByMimeTypeTable.setModel(filesByMimeTypeTableModel);
|
||||
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()) {
|
||||
updateCountsTableData(EMPTY_PAIRS,
|
||||
EMPTY_PAIRS,
|
||||
EMPTY_PAIRS);
|
||||
} else {
|
||||
updateCountsTableData(getMimeTypeModel(dataSource),
|
||||
getFileCategoryModel(dataSource),
|
||||
getArtifactCountsModel(dataSource));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the DataSource to display file information for.
|
||||
*
|
||||
* @param mimeTypeDataModel The mime type data model.
|
||||
* @param fileCategoryDataModel The file category data model.
|
||||
* @param artifactDataModel The artifact type data model.
|
||||
*/
|
||||
private void updateCountsTableData(Object[][] mimeTypeDataModel, Object[][] fileCategoryDataModel, Object[][] artifactDataModel) {
|
||||
fileCountsByMimeTypeTable.setModel(new NonEditableTableModel(mimeTypeDataModel, MIME_TYPE_COLUMN_HEADERS));
|
||||
fileCountsByMimeTypeTable.getColumnModel().getColumn(1).setCellRenderer(rightAlignedRenderer);
|
||||
fileCountsByMimeTypeTable.getColumnModel().getColumn(0).setPreferredWidth(130);
|
||||
filesByCategoryTableModel = new FilesByCategoryTableModel(selectedDataSource);
|
||||
fileCountsByCategoryTable.setModel(filesByCategoryTableModel);
|
||||
|
||||
fileCountsByCategoryTable.setModel(new NonEditableTableModel(fileCategoryDataModel, FILE_BY_CATEGORY_COLUMN_HEADERS));
|
||||
fileCountsByCategoryTable.getColumnModel().getColumn(1).setCellRenderer(rightAlignedRenderer);
|
||||
fileCountsByCategoryTable.getColumnModel().getColumn(0).setPreferredWidth(130);
|
||||
updateArtifactCounts(selectedDataSource);
|
||||
|
||||
artifactCountsTable.setModel(new NonEditableTableModel(artifactDataModel, ARTIFACT_COUNTS_COLUMN_HEADERS));
|
||||
artifactCountsTable.getColumnModel().getColumn(0).setPreferredWidth(230);
|
||||
artifactCountsTable.getColumnModel().getColumn(1).setCellRenderer(rightAlignedRenderer);
|
||||
|
||||
this.repaint();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to update the artifact specific counts by clearing the
|
||||
* table and adding counts for the artifacts which exist in the selected
|
||||
* data source.
|
||||
* Determines the JTable data model for datasource mime types.
|
||||
*
|
||||
* @param selectedDataSource the data source to display artifact counts for
|
||||
* @param dataSource The DataSource.
|
||||
*
|
||||
* @return The model to be used with a JTable.
|
||||
*/
|
||||
private void updateArtifactCounts(DataSource selectedDataSource) {
|
||||
((DefaultTableModel) artifactCountsTable.getModel()).setRowCount(0);
|
||||
if (selectedDataSource != null && artifactsByTypeCountsMap.get(selectedDataSource.getId()) != null) {
|
||||
Map<String, Long> artifactCounts = artifactsByTypeCountsMap.get(selectedDataSource.getId());
|
||||
for (String key : artifactCounts.keySet()) {
|
||||
((DefaultTableModel) artifactCountsTable.getModel()).addRow(new Object[]{key, artifactCounts.get(key)});
|
||||
}
|
||||
@Messages({
|
||||
"DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.images.row=Images",
|
||||
"DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.videos.row=Videos",
|
||||
"DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.audio.row=Audio",
|
||||
"DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.documents.row=Documents",
|
||||
"DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.executables.row=Executables"
|
||||
})
|
||||
private static Object[][] getMimeTypeModel(DataSource dataSource) {
|
||||
return new Object[][]{
|
||||
new Object[]{Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_images_row(),
|
||||
getCount(dataSource, FileTypeUtils.FileTypeCategory.IMAGE)},
|
||||
new Object[]{Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_videos_row(),
|
||||
getCount(dataSource, FileTypeUtils.FileTypeCategory.VIDEO)},
|
||||
new Object[]{Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_audio_row(),
|
||||
getCount(dataSource, FileTypeUtils.FileTypeCategory.AUDIO)},
|
||||
new Object[]{Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_documents_row(),
|
||||
getCount(dataSource, FileTypeUtils.FileTypeCategory.DOCUMENTS)},
|
||||
new Object[]{Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_executables_row(),
|
||||
getCount(dataSource, FileTypeUtils.FileTypeCategory.EXECUTABLE)}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the counts of files of a particular mime type for a particular
|
||||
* DataSource.
|
||||
*
|
||||
* @param dataSource The DataSource.
|
||||
* @param category The mime type category.
|
||||
*
|
||||
* @return The count.
|
||||
*/
|
||||
private static Long getCount(DataSource dataSource, FileTypeUtils.FileTypeCategory category) {
|
||||
return DataSourceInfoUtilities.getCountOfFilesForMimeTypes(dataSource, category.getMediaTypes());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the JTable data model for datasource file categories.
|
||||
*
|
||||
* @param dataSource The DataSource.
|
||||
*
|
||||
* @return The model to be used with a JTable.
|
||||
*/
|
||||
@Messages({
|
||||
"DataSourceSummaryCountsPanel.FilesByCategoryTableModel.all.row=All",
|
||||
"DataSourceSummaryCountsPanel.FilesByCategoryTableModel.allocated.row=Allocated",
|
||||
"DataSourceSummaryCountsPanel.FilesByCategoryTableModel.unallocated.row=Unallocated",
|
||||
"DataSourceSummaryCountsPanel.FilesByCategoryTableModel.slack.row=Slack",
|
||||
"DataSourceSummaryCountsPanel.FilesByCategoryTableModel.directory.row=Directory"
|
||||
})
|
||||
private static Object[][] getFileCategoryModel(DataSource selectedDataSource) {
|
||||
Long fileCount = zeroIfNull(DataSourceInfoUtilities.getCountOfFiles(selectedDataSource));
|
||||
Long unallocatedFiles = zeroIfNull(DataSourceInfoUtilities.getCountOfUnallocatedFiles(selectedDataSource));
|
||||
Long allocatedFiles = zeroIfNull(getAllocatedCount(fileCount, unallocatedFiles));
|
||||
Long slackFiles = zeroIfNull(DataSourceInfoUtilities.getCountOfSlackFiles(selectedDataSource));
|
||||
Long directories = zeroIfNull(DataSourceInfoUtilities.getCountOfDirectories(selectedDataSource));
|
||||
|
||||
return new Object[][]{
|
||||
new Object[]{Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_all_row(), fileCount},
|
||||
new Object[]{Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_allocated_row(), allocatedFiles},
|
||||
new Object[]{Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_unallocated_row(), unallocatedFiles},
|
||||
new Object[]{Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_slack_row(), slackFiles},
|
||||
new Object[]{Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_directory_row(), directories}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 0 if value is null.
|
||||
*
|
||||
* @param origValue The original value.
|
||||
*
|
||||
* @return The value or 0 if null.
|
||||
*/
|
||||
private static Long zeroIfNull(Long origValue) {
|
||||
return origValue == null ? 0 : origValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely gets the allocated files count.
|
||||
*
|
||||
* @param allFilesCount The count of all files.
|
||||
* @param unallocatedFilesCount The count of unallocated files.
|
||||
*
|
||||
* @return The count of allocated files.
|
||||
*/
|
||||
private static long getAllocatedCount(Long allFilesCount, Long unallocatedFilesCount) {
|
||||
if (allFilesCount == null) {
|
||||
return 0;
|
||||
} else if (unallocatedFilesCount == null) {
|
||||
return allFilesCount;
|
||||
} else {
|
||||
return allFilesCount - unallocatedFilesCount;
|
||||
}
|
||||
artifactCountsTable.getColumnModel().getColumn(0).setPreferredWidth(230);
|
||||
artifactCountsTable.getColumnModel().getColumn(1).setCellRenderer(rightAlignedRenderer);
|
||||
}
|
||||
|
||||
/**
|
||||
* The counts of different artifact types found in a DataSource.
|
||||
*
|
||||
* @param selectedDataSource The DataSource.
|
||||
*
|
||||
* @return The JTable data model of counts of artifact types.
|
||||
*/
|
||||
private static Object[][] getArtifactCountsModel(DataSource selectedDataSource) {
|
||||
Map<String, Long> artifactMapping = DataSourceInfoUtilities.getCountsOfArtifactsByType(selectedDataSource);
|
||||
if (artifactMapping == null) {
|
||||
return EMPTY_PAIRS;
|
||||
}
|
||||
|
||||
return artifactMapping.entrySet().stream()
|
||||
.filter((entrySet) -> entrySet != null && entrySet.getKey() != null)
|
||||
.sorted((a, b) -> a.getKey().compareTo(b.getKey()))
|
||||
.map((entrySet) -> new Object[]{entrySet.getKey(), entrySet.getValue()})
|
||||
.toArray(Object[][]::new);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -118,35 +270,16 @@ class DataSourceSummaryCountsPanel extends javax.swing.JPanel {
|
||||
artifactCountsScrollPane = new javax.swing.JScrollPane();
|
||||
artifactCountsTable = new javax.swing.JTable();
|
||||
|
||||
fileCountsByMimeTypeTable.setModel(filesByMimeTypeTableModel);
|
||||
fileCountsByMimeTypeScrollPane.setViewportView(fileCountsByMimeTypeTable);
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(byMimeTypeLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryCountsPanel.class, "DataSourceSummaryCountsPanel.byMimeTypeLabel.text")); // NOI18N
|
||||
|
||||
fileCountsByCategoryTable.setModel(filesByCategoryTableModel);
|
||||
fileCountsByCategoryScrollPane.setViewportView(fileCountsByCategoryTable);
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(byCategoryLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryCountsPanel.class, "DataSourceSummaryCountsPanel.byCategoryLabel.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(DataSourceSummaryCountsPanel.class, "DataSourceSummaryCountsPanel.jLabel1.text")); // NOI18N
|
||||
|
||||
artifactCountsTable.setAutoCreateRowSorter(true);
|
||||
artifactCountsTable.setModel(new javax.swing.table.DefaultTableModel(
|
||||
new Object [][] {
|
||||
|
||||
},
|
||||
new String [] {
|
||||
"Result Type", "Count"
|
||||
}
|
||||
) {
|
||||
boolean[] canEdit = new boolean [] {
|
||||
false, false
|
||||
};
|
||||
|
||||
public boolean isCellEditable(int rowIndex, int columnIndex) {
|
||||
return canEdit [columnIndex];
|
||||
}
|
||||
});
|
||||
artifactCountsScrollPane.setViewportView(artifactCountsTable);
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
@ -206,190 +339,4 @@ class DataSourceSummaryCountsPanel extends javax.swing.JPanel {
|
||||
private javax.swing.JTable fileCountsByMimeTypeTable;
|
||||
private javax.swing.JLabel jLabel1;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
/**
|
||||
* Table model for the files table model to display counts of specific file
|
||||
* types by mime type found in the currently selected data source.
|
||||
*/
|
||||
@Messages({"DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.type.header=File Type",
|
||||
"DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.count.header=Count"})
|
||||
private class FilesByMimeTypeTableModel extends AbstractTableModel {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final DataSource currentDataSource;
|
||||
private final List<String> columnHeaders = new ArrayList<>();
|
||||
private static final int IMAGES_ROW_INDEX = 0;
|
||||
private static final int VIDEOS_ROW_INDEX = 1;
|
||||
private static final int AUDIO_ROW_INDEX = 2;
|
||||
private static final int DOCUMENTS_ROW_INDEX = 3;
|
||||
private static final int EXECUTABLES_ROW_INDEX = 4;
|
||||
|
||||
/**
|
||||
* Create a FilesByMimeTypeTableModel for the speicified datasource.
|
||||
*
|
||||
* @param selectedDataSource the datasource which this
|
||||
* FilesByMimeTypeTablemodel will represent
|
||||
*/
|
||||
FilesByMimeTypeTableModel(DataSource selectedDataSource) {
|
||||
columnHeaders.add(Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_type_header());
|
||||
columnHeaders.add(Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_count_header());
|
||||
currentDataSource = selectedDataSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRowCount() {
|
||||
//should be kept equal to the number of types we are displaying in the tables
|
||||
return 5;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnCount() {
|
||||
return columnHeaders.size();
|
||||
}
|
||||
|
||||
@Messages({
|
||||
"DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.images.row=Images",
|
||||
"DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.videos.row=Videos",
|
||||
"DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.audio.row=Audio",
|
||||
"DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.documents.row=Documents",
|
||||
"DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.executables.row=Executables"
|
||||
})
|
||||
@Override
|
||||
public Object getValueAt(int rowIndex, int columnIndex) {
|
||||
if (columnIndex == 0) {
|
||||
switch (rowIndex) {
|
||||
case IMAGES_ROW_INDEX:
|
||||
return Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_images_row();
|
||||
case VIDEOS_ROW_INDEX:
|
||||
return Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_videos_row();
|
||||
case AUDIO_ROW_INDEX:
|
||||
return Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_audio_row();
|
||||
case DOCUMENTS_ROW_INDEX:
|
||||
return Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_documents_row();
|
||||
case EXECUTABLES_ROW_INDEX:
|
||||
return Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_executables_row();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if (columnIndex == 1) {
|
||||
switch (rowIndex) {
|
||||
case 0:
|
||||
return DataSourceInfoUtilities.getCountOfFilesForMimeTypes(currentDataSource, FileTypeUtils.FileTypeCategory.IMAGE.getMediaTypes());
|
||||
case 1:
|
||||
return DataSourceInfoUtilities.getCountOfFilesForMimeTypes(currentDataSource, FileTypeUtils.FileTypeCategory.VIDEO.getMediaTypes());
|
||||
case 2:
|
||||
return DataSourceInfoUtilities.getCountOfFilesForMimeTypes(currentDataSource, FileTypeUtils.FileTypeCategory.AUDIO.getMediaTypes());
|
||||
case 3:
|
||||
return DataSourceInfoUtilities.getCountOfFilesForMimeTypes(currentDataSource, FileTypeUtils.FileTypeCategory.DOCUMENTS.getMediaTypes());
|
||||
case 4:
|
||||
return DataSourceInfoUtilities.getCountOfFilesForMimeTypes(currentDataSource, FileTypeUtils.FileTypeCategory.EXECUTABLE.getMediaTypes());
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getColumnName(int column) {
|
||||
return columnHeaders.get(column);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Table model for the files table model to display counts of specific file
|
||||
* types by category found in the currently selected data source.
|
||||
*/
|
||||
@Messages({"DataSourceSummaryCountsPanel.FilesByCategoryTableModel.type.header=File Type",
|
||||
"DataSourceSummaryCountsPanel.FilesByCategoryTableModel.count.header=Count"})
|
||||
private class FilesByCategoryTableModel extends AbstractTableModel {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final DataSource currentDataSource;
|
||||
private final List<String> columnHeaders = new ArrayList<>();
|
||||
private static final int ALL_FILES_ROW_INDEX = 0;
|
||||
private static final int ALLOCATED_FILES_ROW_INDEX = 1;
|
||||
private static final int UNALLOCATED_FILES_ROW_INDEX = 2;
|
||||
private static final int SLACK_FILES_ROW_INDEX = 3;
|
||||
private static final int DIRECTORIES_ROW_INDEX = 4;
|
||||
/**
|
||||
* Create a FilesByCategoryTableModel for the speicified datasource.
|
||||
*
|
||||
* @param selectedDataSource the datasource which this
|
||||
* FilesByCategoryTablemodel will represent
|
||||
*/
|
||||
FilesByCategoryTableModel(DataSource selectedDataSource) {
|
||||
columnHeaders.add(Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_type_header());
|
||||
columnHeaders.add(Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_count_header());
|
||||
currentDataSource = selectedDataSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRowCount() {
|
||||
//should be kept equal to the number of types we are displaying in the tables
|
||||
return 5;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnCount() {
|
||||
return columnHeaders.size();
|
||||
}
|
||||
|
||||
@Messages({
|
||||
"DataSourceSummaryCountsPanel.FilesByCategoryTableModel.all.row=All",
|
||||
"DataSourceSummaryCountsPanel.FilesByCategoryTableModel.allocated.row=Allocated",
|
||||
"DataSourceSummaryCountsPanel.FilesByCategoryTableModel.unallocated.row=Unallocated",
|
||||
"DataSourceSummaryCountsPanel.FilesByCategoryTableModel.slack.row=Slack",
|
||||
"DataSourceSummaryCountsPanel.FilesByCategoryTableModel.directory.row=Directory"
|
||||
})
|
||||
@Override
|
||||
public Object getValueAt(int rowIndex, int columnIndex) {
|
||||
if (columnIndex == 0) {
|
||||
switch (rowIndex) {
|
||||
case ALL_FILES_ROW_INDEX:
|
||||
return Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_all_row();
|
||||
case ALLOCATED_FILES_ROW_INDEX:
|
||||
return Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_allocated_row();
|
||||
case UNALLOCATED_FILES_ROW_INDEX:
|
||||
return Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_unallocated_row();
|
||||
case SLACK_FILES_ROW_INDEX:
|
||||
return Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_slack_row();
|
||||
case DIRECTORIES_ROW_INDEX:
|
||||
return Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_directory_row();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if (columnIndex == 1 && currentDataSource != null) {
|
||||
switch (rowIndex) {
|
||||
case 0:
|
||||
return allFilesCountsMap.get(currentDataSource.getId()) == null ? 0 : allFilesCountsMap.get(currentDataSource.getId());
|
||||
case 1:
|
||||
//All files should be either allocated or unallocated as dir_flags only has two values so any file that isn't unallocated is allocated
|
||||
Long unallocatedFilesCount = unallocatedFilesCountsMap.get(currentDataSource.getId());
|
||||
Long allFilesCount = allFilesCountsMap.get(currentDataSource.getId());
|
||||
if (allFilesCount == null) {
|
||||
return 0;
|
||||
} else if (unallocatedFilesCount == null) {
|
||||
return allFilesCount;
|
||||
} else {
|
||||
return allFilesCount - unallocatedFilesCount;
|
||||
}
|
||||
case 2:
|
||||
return unallocatedFilesCountsMap.get(currentDataSource.getId()) == null ? 0 : unallocatedFilesCountsMap.get(currentDataSource.getId());
|
||||
case 3:
|
||||
return slackFilesCountsMap.get(currentDataSource.getId()) == null ? 0 : slackFilesCountsMap.get(currentDataSource.getId());
|
||||
case 4:
|
||||
return directoriesCountsMap.get(currentDataSource.getId()) == null ? 0 : directoriesCountsMap.get(currentDataSource.getId());
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getColumnName(int column) {
|
||||
return columnHeaders.get(column);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,12 +19,12 @@
|
||||
package org.sleuthkit.autopsy.casemodule.datasourcesummary;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import java.util.logging.Level;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import javax.swing.table.DefaultTableModel;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
import org.sleuthkit.datamodel.Image;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
@ -36,23 +36,47 @@ class DataSourceSummaryDetailsPanel extends javax.swing.JPanel {
|
||||
|
||||
//Because this panel was made using the gridbaglayout and netbean's Customize Layout tool it will be best to continue to modify it through that
|
||||
private static final long serialVersionUID = 1L;
|
||||
private Map<Long, String> osDetailMap = new HashMap<>();
|
||||
private static final Integer SIZE_COVERSION_CONSTANT = 1000;
|
||||
private static final DecimalFormat APPROXIMATE_SIZE_FORMAT = new DecimalFormat("#.##");
|
||||
private final Map<Long, Long> unallocatedFilesSizeMap;
|
||||
private final Map<Long, String> usageMap;
|
||||
private static final Logger logger = Logger.getLogger(DataSourceSummaryDetailsPanel.class.getName());
|
||||
|
||||
private DataSource dataSource;
|
||||
|
||||
/**
|
||||
* Creates new form DataSourceSummaryDetailsPanel
|
||||
*/
|
||||
@Messages({"DataSourceSummaryDetailsPanel.getDataSources.error.text=Failed to get the list of datasources for the current case.",
|
||||
"DataSourceSummaryDetailsPanel.getDataSources.error.title=Load Failure"})
|
||||
DataSourceSummaryDetailsPanel(Map<Long, String> usageMap) {
|
||||
DataSourceSummaryDetailsPanel() {
|
||||
initComponents();
|
||||
this.usageMap = usageMap;
|
||||
this.unallocatedFilesSizeMap = DataSourceInfoUtilities.getSizeOfUnallocatedFiles();
|
||||
osDetailMap = DataSourceInfoUtilities.getOperatingSystems();
|
||||
setDataSource(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()) {
|
||||
updateDetailsPanelData(null, null, null, null);
|
||||
} else {
|
||||
updateDetailsPanelData(dataSource,
|
||||
DataSourceInfoUtilities.getSizeOfUnallocatedFiles(dataSource),
|
||||
DataSourceInfoUtilities.getOperatingSystems(dataSource),
|
||||
DataSourceInfoUtilities.getDataSourceType(dataSource));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -60,77 +84,80 @@ class DataSourceSummaryDetailsPanel extends javax.swing.JPanel {
|
||||
*
|
||||
* @param selectedDataSource the DataSource to display details about.
|
||||
*/
|
||||
void updateDetailsPanelData(DataSource selectedDataSource) {
|
||||
private void updateDetailsPanelData(DataSource selectedDataSource, Long unallocatedFilesSize, String osDetails, String usage) {
|
||||
clearTableValues();
|
||||
if (selectedDataSource != null) {
|
||||
String sizeString = "";
|
||||
String sectorSizeString = "";
|
||||
String md5String = "";
|
||||
String sha1String = "";
|
||||
String sha256String = "";
|
||||
String acquisitionDetailsString = "";
|
||||
String imageTypeString = "";
|
||||
String[] filePaths = new String[0];
|
||||
String osDetailString = osDetailMap.get(selectedDataSource.getId()) == null ? "" : osDetailMap.get(selectedDataSource.getId());
|
||||
String dataSourceTypeString = usageMap.get(selectedDataSource.getId()) == null ? "" : usageMap.get(selectedDataSource.getId());
|
||||
try {
|
||||
acquisitionDetailsString = selectedDataSource.getAcquisitionDetails();
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.WARNING, "Unable to get aquisition details for selected data source", ex);
|
||||
}
|
||||
if (selectedDataSource instanceof Image) {
|
||||
imageTypeString = ((Image) selectedDataSource).getType().getName();
|
||||
filePaths = ((Image) selectedDataSource).getPaths();
|
||||
sizeString = getSizeString(selectedDataSource.getSize());
|
||||
sectorSizeString = getSizeString(((Image) selectedDataSource).getSsize());
|
||||
try {
|
||||
//older databases may have null as the hash values
|
||||
md5String = ((Image) selectedDataSource).getMd5();
|
||||
if (md5String == null) {
|
||||
md5String = "";
|
||||
}
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.WARNING, "Unable to get MD5 for selected data source", ex);
|
||||
}
|
||||
try {
|
||||
sha1String = ((Image) selectedDataSource).getSha1();
|
||||
if (sha1String == null) {
|
||||
sha1String = "";
|
||||
}
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.WARNING, "Unable to get SHA1 for selected data source", ex);
|
||||
}
|
||||
try {
|
||||
sha256String = ((Image) selectedDataSource).getSha256();
|
||||
if (sha256String == null) {
|
||||
sha256String = "";
|
||||
}
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.WARNING, "Unable to get SHA256 for selected data source", ex);
|
||||
}
|
||||
}
|
||||
unallocatedSizeValue.setText(getSizeString(unallocatedFilesSize));
|
||||
operatingSystemValue.setText(StringUtils.isBlank(osDetails) ? "" : osDetails);
|
||||
dataSourceUsageValue.setText(StringUtils.isBlank(usage) ? "" : usage);
|
||||
|
||||
timeZoneValue.setText(selectedDataSource.getTimeZone());
|
||||
displayNameValue.setText(selectedDataSource.getName());
|
||||
originalNameValue.setText(selectedDataSource.getName());
|
||||
deviceIdValue.setText(selectedDataSource.getDeviceId());
|
||||
dataSourceUsageValue.setText(dataSourceTypeString);
|
||||
operatingSystemValue.setText(osDetailString);
|
||||
timeZoneValue.setText(selectedDataSource.getTimeZone());
|
||||
acquisitionDetailsTextArea.setText(acquisitionDetailsString);
|
||||
imageTypeValue.setText(imageTypeString);
|
||||
sizeValue.setText(sizeString);
|
||||
unallocatedSizeValue.setText(getSizeString(unallocatedFilesSizeMap.get(selectedDataSource.getId())));
|
||||
sectorSizeValue.setText(sectorSizeString);
|
||||
md5HashValue.setText(md5String);
|
||||
sha1HashValue.setText(sha1String);
|
||||
sha256HashValue.setText(sha256String);
|
||||
for (String path : filePaths) {
|
||||
((DefaultTableModel) filePathsTable.getModel()).addRow(new Object[]{path});
|
||||
|
||||
try {
|
||||
acquisitionDetailsTextArea.setText(selectedDataSource.getAcquisitionDetails());
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.WARNING, "Unable to get aquisition details for selected data source", ex);
|
||||
}
|
||||
|
||||
if (selectedDataSource instanceof Image) {
|
||||
setFieldsForImage((Image) selectedDataSource);
|
||||
}
|
||||
}
|
||||
updateFieldVisibility();
|
||||
this.repaint();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets text fields for an image. This should be called after
|
||||
* clearTableValues and before updateFieldVisibility to ensure the proper
|
||||
* rendering.
|
||||
*
|
||||
* @param selectedImage The selected image.
|
||||
*/
|
||||
private void setFieldsForImage(Image selectedImage) {
|
||||
imageTypeValue.setText(selectedImage.getType().getName());
|
||||
sizeValue.setText(getSizeString(selectedImage.getSize()));
|
||||
sectorSizeValue.setText(getSizeString(selectedImage.getSsize()));
|
||||
|
||||
for (String path : selectedImage.getPaths()) {
|
||||
((DefaultTableModel) filePathsTable.getModel()).addRow(new Object[]{path});
|
||||
}
|
||||
|
||||
try {
|
||||
//older databases may have null as the hash values
|
||||
String md5String = selectedImage.getMd5();
|
||||
if (md5String == null) {
|
||||
md5String = "";
|
||||
}
|
||||
md5HashValue.setText(md5String);
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.WARNING, "Unable to get MD5 for selected data source", ex);
|
||||
}
|
||||
|
||||
try {
|
||||
String sha1String = selectedImage.getSha1();
|
||||
if (sha1String == null) {
|
||||
sha1String = "";
|
||||
}
|
||||
sha1HashValue.setText(sha1String);
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.WARNING, "Unable to get SHA1 for selected data source", ex);
|
||||
}
|
||||
|
||||
try {
|
||||
String sha256String = selectedImage.getSha256();
|
||||
if (sha256String == null) {
|
||||
sha256String = "";
|
||||
}
|
||||
sha256HashValue.setText(sha256String);
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.WARNING, "Unable to get SHA256 for selected data source", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a long size in bytes as a string formated to be read by users.
|
||||
*
|
||||
@ -147,7 +174,7 @@ class DataSourceSummaryDetailsPanel extends javax.swing.JPanel {
|
||||
"DataSourceSummaryDetailsPanel.units.terabytes= TB",
|
||||
"DataSourceSummaryDetailsPanel.units.petabytes= PB"
|
||||
})
|
||||
private String getSizeString(Long size) {
|
||||
private static String getSizeString(Long size) {
|
||||
if (size == null) {
|
||||
return "";
|
||||
}
|
||||
|
@ -68,6 +68,11 @@
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JTabbedPane" name="dataSourceTabbedPane">
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="dataSourceSummaryTabbedPane"/>
|
||||
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||
</AuxValues>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
|
||||
<JSplitPaneConstraints position="right"/>
|
||||
|
@ -27,7 +27,6 @@ import java.util.Observer;
|
||||
import java.util.Set;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.casemodule.IngestJobInfoPanel;
|
||||
import org.sleuthkit.autopsy.ingest.IngestManager;
|
||||
import org.sleuthkit.autopsy.ingest.events.DataSourceAnalysisCompletedEvent;
|
||||
import org.sleuthkit.autopsy.ingest.events.DataSourceAnalysisCompletedEvent.Reason;
|
||||
@ -41,10 +40,8 @@ final class DataSourceSummaryDialog extends javax.swing.JDialog implements Obser
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.DATA_SOURCE_ANALYSIS_COMPLETED);
|
||||
private final DataSourceSummaryCountsPanel countsPanel;
|
||||
private final DataSourceSummaryDetailsPanel detailsPanel;
|
||||
private final DataSourceBrowser dataSourcesPanel;
|
||||
private final IngestJobInfoPanel ingestHistoryPanel;
|
||||
private final DataSourceSummaryTabbedPane dataSourceSummaryTabbedPane;
|
||||
|
||||
/**
|
||||
* Creates new form DataSourceSummaryDialog for displaying a summary of the
|
||||
@ -61,21 +58,14 @@ final class DataSourceSummaryDialog extends javax.swing.JDialog implements Obser
|
||||
super(owner, Bundle.DataSourceSummaryDialog_window_title(), true);
|
||||
Map<Long, String> usageMap = DataSourceInfoUtilities.getDataSourceTypes();
|
||||
Map<Long, Long> fileCountsMap = DataSourceInfoUtilities.getCountsOfFiles();
|
||||
countsPanel = new DataSourceSummaryCountsPanel(fileCountsMap);
|
||||
detailsPanel = new DataSourceSummaryDetailsPanel(usageMap);
|
||||
dataSourcesPanel = new DataSourceBrowser(usageMap, fileCountsMap);
|
||||
ingestHistoryPanel = new IngestJobInfoPanel();
|
||||
dataSourceSummaryTabbedPane = new DataSourceSummaryTabbedPane();
|
||||
initComponents();
|
||||
dataSourceSummarySplitPane.setLeftComponent(dataSourcesPanel);
|
||||
dataSourceTabbedPane.addTab(Bundle.DataSourceSummaryDialog_detailsTab_title(), detailsPanel);
|
||||
dataSourceTabbedPane.addTab(Bundle.DataSourceSummaryDialog_countsTab_title(), countsPanel);
|
||||
dataSourceTabbedPane.addTab(Bundle.DataSourceSummaryDialog_ingestHistoryTab_title(), ingestHistoryPanel);
|
||||
dataSourcesPanel.addListSelectionListener((ListSelectionEvent e) -> {
|
||||
if (!e.getValueIsAdjusting()) {
|
||||
DataSource selectedDataSource = dataSourcesPanel.getSelectedDataSource();
|
||||
countsPanel.updateCountsTableData(selectedDataSource);
|
||||
detailsPanel.updateDetailsPanelData(selectedDataSource);
|
||||
ingestHistoryPanel.setDataSource(selectedDataSource);
|
||||
dataSourceSummaryTabbedPane.setDataSource(selectedDataSource);
|
||||
this.repaint();
|
||||
}
|
||||
});
|
||||
@ -116,7 +106,7 @@ final class DataSourceSummaryDialog extends javax.swing.JDialog implements Obser
|
||||
|
||||
closeButton = new javax.swing.JButton();
|
||||
dataSourceSummarySplitPane = new javax.swing.JSplitPane();
|
||||
dataSourceTabbedPane = new javax.swing.JTabbedPane();
|
||||
javax.swing.JTabbedPane dataSourceTabbedPane = dataSourceSummaryTabbedPane;
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(closeButton, org.openide.util.NbBundle.getMessage(DataSourceSummaryDialog.class, "DataSourceSummaryDialog.closeButton.text")); // NOI18N
|
||||
closeButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
@ -173,6 +163,5 @@ final class DataSourceSummaryDialog extends javax.swing.JDialog implements Obser
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JButton closeButton;
|
||||
private javax.swing.JSplitPane dataSourceSummarySplitPane;
|
||||
private javax.swing.JTabbedPane dataSourceTabbedPane;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
}
|
||||
|
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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 javax.swing.JTabbedPane;
|
||||
import org.sleuthkit.autopsy.casemodule.IngestJobInfoPanel;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
|
||||
/**
|
||||
* A tabbed pane showing the summary of a data source including tabs of:
|
||||
* DataSourceSummaryCountsPanel, DataSourceSummaryDetailsPanel, and
|
||||
* IngestJobInfoPanel.
|
||||
*/
|
||||
public class DataSourceSummaryTabbedPane extends JTabbedPane {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final DataSourceSummaryCountsPanel countsPanel;
|
||||
private final DataSourceSummaryDetailsPanel detailsPanel;
|
||||
private final IngestJobInfoPanel ingestHistoryPanel;
|
||||
|
||||
private DataSource dataSource = null;
|
||||
|
||||
/**
|
||||
* Constructs a tabbed pane showing the summary of a data source.
|
||||
*/
|
||||
public DataSourceSummaryTabbedPane() {
|
||||
countsPanel = new DataSourceSummaryCountsPanel();
|
||||
detailsPanel = new DataSourceSummaryDetailsPanel();
|
||||
ingestHistoryPanel = new IngestJobInfoPanel();
|
||||
|
||||
addTab(Bundle.DataSourceSummaryDialog_detailsTab_title(), detailsPanel);
|
||||
addTab(Bundle.DataSourceSummaryDialog_countsTab_title(), countsPanel);
|
||||
addTab(Bundle.DataSourceSummaryDialog_ingestHistoryTab_title(), ingestHistoryPanel);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 tabbed panel.
|
||||
*
|
||||
* @param dataSource The datasource to use in this panel.
|
||||
*/
|
||||
public void setDataSource(DataSource dataSource) {
|
||||
this.dataSource = dataSource;
|
||||
|
||||
detailsPanel.setDataSource(dataSource);
|
||||
countsPanel.setDataSource(dataSource);
|
||||
ingestHistoryPanel.setDataSource(dataSource);
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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 javax.swing.table.DefaultTableModel;
|
||||
|
||||
/**
|
||||
* A Table model where cells are not editable.
|
||||
*/
|
||||
class NonEditableTableModel extends DefaultTableModel {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
NonEditableTableModel(Object[][] data, Object[] columnNames) {
|
||||
super(data, columnNames);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCellEditable(int row, int column) {
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
DataSourceSummaryResultViewer_title=Summary
|
@ -0,0 +1,137 @@
|
||||
/*
|
||||
* 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.resultviewers.summary;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Cursor;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.SwingUtilities;
|
||||
import org.openide.explorer.ExplorerManager;
|
||||
import org.openide.nodes.Node;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.openide.util.lookup.ServiceProvider;
|
||||
import org.sleuthkit.autopsy.casemodule.datasourcesummary.DataSourceSummaryTabbedPane;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer;
|
||||
import org.sleuthkit.autopsy.corecomponents.AbstractDataResultViewer;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
||||
|
||||
/**
|
||||
* A tabular result viewer that displays a summary of the selected Data Source.
|
||||
*/
|
||||
@ServiceProvider(service = DataResultViewer.class)
|
||||
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
||||
public class DataSourceSummaryResultViewer extends AbstractDataResultViewer {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Logger LOGGER = Logger.getLogger(DataSourceSummaryResultViewer.class.getName());
|
||||
|
||||
private final String title;
|
||||
|
||||
/**
|
||||
* Constructs a tabular result viewer that displays a summary of the
|
||||
* selected Data Source.
|
||||
*/
|
||||
public DataSourceSummaryResultViewer() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a tabular result viewer that displays a summary of the
|
||||
* selected Data Source.
|
||||
*
|
||||
* @param explorerManager The explorer manager of the ancestor top
|
||||
* component.
|
||||
*
|
||||
*/
|
||||
@Messages({
|
||||
"DataSourceSummaryResultViewer_title=Summary"
|
||||
})
|
||||
public DataSourceSummaryResultViewer(ExplorerManager explorerManager) {
|
||||
this(explorerManager, Bundle.DataSourceSummaryResultViewer_title());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a tabular result viewer that displays a summary of the
|
||||
* selected Data Source.
|
||||
*
|
||||
* @param explorerManager The explorer manager of the ancestor top
|
||||
* component.
|
||||
* @param title The title.
|
||||
*
|
||||
*/
|
||||
public DataSourceSummaryResultViewer(ExplorerManager explorerManager, String title) {
|
||||
super(explorerManager);
|
||||
this.title = title;
|
||||
initComponents();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataResultViewer createInstance() {
|
||||
return new DataSourceSummaryResultViewer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSupported(Node node) {
|
||||
return getDataSource(node) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the datasource attached to the node or null if none can be found.
|
||||
*
|
||||
* @param node The node to search.
|
||||
*
|
||||
* @return The datasource or null if not found.
|
||||
*/
|
||||
private DataSource getDataSource(Node node) {
|
||||
return node == null ? null : node.getLookup().lookup(DataSource.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
public void setNode(Node node) {
|
||||
if (!SwingUtilities.isEventDispatchThread()) {
|
||||
LOGGER.log(Level.SEVERE, "Attempting to run setNode() from non-EDT thread.");
|
||||
return;
|
||||
}
|
||||
|
||||
DataSource dataSource = getDataSource(node);
|
||||
|
||||
this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
|
||||
try {
|
||||
summaryPanel.setDataSource(dataSource);
|
||||
} finally {
|
||||
this.setCursor(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
private void initComponents() {
|
||||
summaryPanel = new DataSourceSummaryTabbedPane();
|
||||
setLayout(new BorderLayout());
|
||||
add(summaryPanel, BorderLayout.CENTER);
|
||||
}
|
||||
|
||||
private DataSourceSummaryTabbedPane summaryPanel;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user