diff --git a/Core/ivy.xml b/Core/ivy.xml index 4c4bef4f9a..63fdd9ed92 100644 --- a/Core/ivy.xml +++ b/Core/ivy.xml @@ -50,9 +50,6 @@ - - - diff --git a/Core/nbproject/project.xml b/Core/nbproject/project.xml index f82ab0c7af..61e6a86b04 100644 --- a/Core/nbproject/project.xml +++ b/Core/nbproject/project.xml @@ -479,14 +479,6 @@ ext/jxmapviewer2-2.4.jar release/modules/ext/jxmapviewer2-2.4.jar - - ext/jfreechart-1.0.19.jar - release/modules/ext/jfreechart-1.0.19.jar - - - ext/jcommon-1.0.23.jar - release/modules/ext/jcommon-1.0.23.jar - ext/jdom-2.0.5-contrib.jar release/modules/ext/jdom-2.0.5-contrib.jar diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index 1e05403dd8..59b345501b 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -70,7 +70,7 @@ import org.sleuthkit.autopsy.actions.OpenOutputFolderAction; import org.sleuthkit.autopsy.appservices.AutopsyService; import org.sleuthkit.autopsy.appservices.AutopsyService.CaseContext; import org.sleuthkit.autopsy.casemodule.CaseMetadata.CaseMetadataException; -import org.sleuthkit.autopsy.datasourcesummary.ui.DataSourceSummaryAction; +import org.sleuthkit.autopsy.casemodule.datasourcesummary.DataSourceSummaryAction; import org.sleuthkit.autopsy.casemodule.events.AddingDataSourceEvent; import org.sleuthkit.autopsy.casemodule.events.AddingDataSourceFailedEvent; import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagAddedEvent; diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties similarity index 94% rename from Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties rename to Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties index 462d910dde..24a312f733 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties @@ -33,6 +33,7 @@ DataSourceSummaryDetailsPanel.acquisitionDetailsTextArea.text= DataSourceSummaryDetailsPanel.acquisitionDetailsLabel.text=Acquisition Details: DataSourceSummaryDetailsPanel.unallocatedSizeLabel.text=Unallocated Space: DataSourceSummaryDetailsPanel.unallocatedSizeValue.text= +DataSourceSummaryCountsPanel.byMimeTypeLabel.text=Files by MIME Type DataSourceSummaryCountsPanel.byCategoryLabel.text=Files by Category -DataSourceSummaryCountsPanel.resultsByTypeLabel.text=Results by Type +DataSourceSummaryCountsPanel.jLabel1.text=Results by Type DataSourceSummaryUserActivityPanel.programsRunLabel.text=Top Programs Run diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties-MERGED similarity index 95% rename from Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED rename to Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties-MERGED index 811040a3ca..ba0d3510c7 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties-MERGED @@ -1,7 +1,6 @@ CTL_DataSourceSummaryAction=Data Source Summary DataSourceSummaryCountsPanel.ArtifactCountsTableModel.count.header=Count DataSourceSummaryCountsPanel.ArtifactCountsTableModel.type.header=Result Type -DataSourceSummaryCountsPanel.byMimeTypeLabel.text=Files by MIME Type DataSourceSummaryCountsPanel.FilesByCategoryTableModel.all.row=All DataSourceSummaryCountsPanel.FilesByCategoryTableModel.allocated.row=Allocated DataSourceSummaryCountsPanel.FilesByCategoryTableModel.count.header=Count @@ -16,8 +15,6 @@ DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.executables.row=Executabl DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.images.row=Images DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.type.header=File Type DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.videos.row=Videos -DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_notAnalyzed_label=Not Analyzed -DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_other_label=Other DataSourceSummaryDetailsPanel.getDataSources.error.text=Failed to get the list of datasources for the current case. DataSourceSummaryDetailsPanel.getDataSources.error.title=Load Failure DataSourceSummaryDetailsPanel.units.bytes=\ bytes @@ -61,8 +58,9 @@ DataSourceSummaryDetailsPanel.acquisitionDetailsTextArea.text= DataSourceSummaryDetailsPanel.acquisitionDetailsLabel.text=Acquisition Details: DataSourceSummaryDetailsPanel.unallocatedSizeLabel.text=Unallocated Space: DataSourceSummaryDetailsPanel.unallocatedSizeValue.text= +DataSourceSummaryCountsPanel.byMimeTypeLabel.text=Files by MIME Type DataSourceSummaryCountsPanel.byCategoryLabel.text=Files by Category -DataSourceSummaryCountsPanel.resultsByTypeLabel.text=Results by Type +DataSourceSummaryCountsPanel.jLabel1.text=Results by Type DataSourceSummaryDialog.window.title=Data Sources Summary DataSourceSummaryNode.column.dataSourceName.header=Data Source Name DataSourceSummaryNode.column.files.header=Files diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle_ja.properties similarity index 100% rename from Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle_ja.properties rename to Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle_ja.properties diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceBrowser.form b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceBrowser.form similarity index 100% rename from Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceBrowser.form rename to Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceBrowser.form diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceBrowser.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceBrowser.java similarity index 96% rename from Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceBrowser.java rename to Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceBrowser.java index a31bbb03f7..92967738b0 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceBrowser.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceBrowser.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.datasourcesummary.ui; +package org.sleuthkit.autopsy.casemodule.datasourcesummary; import java.awt.EventQueue; import java.beans.PropertyVetoException; @@ -35,11 +35,10 @@ import javax.swing.event.ListSelectionListener; import org.openide.nodes.Node; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; -import org.sleuthkit.autopsy.datasourcesummary.ui.DataSourceSummaryNode.DataSourceSummaryEntryNode; +import org.sleuthkit.autopsy.casemodule.datasourcesummary.DataSourceSummaryNode.DataSourceSummaryEntryNode; import static javax.swing.SwingConstants.RIGHT; import javax.swing.SwingUtilities; import javax.swing.table.TableColumn; -import org.sleuthkit.autopsy.datasourcesummary.datamodel.CaseDataSourcesSummary; import org.sleuthkit.datamodel.DataSource; import org.sleuthkit.datamodel.IngestJobInfo; import org.sleuthkit.datamodel.SleuthkitCase; @@ -153,8 +152,8 @@ final class DataSourceBrowser extends javax.swing.JPanel implements ExplorerMana private List getDataSourceSummaryList(Map usageMap, Map fileCountsMap) { List summaryList = new ArrayList<>(); - final Map artifactCountsMap = CaseDataSourcesSummary.getCountsOfArtifacts(); - final Map tagCountsMap = CaseDataSourcesSummary.getCountsOfTags(); + final Map artifactCountsMap = DataSourceInfoUtilities.getCountsOfArtifacts(); + final Map tagCountsMap = DataSourceInfoUtilities.getCountsOfTags(); try { SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase(); for (DataSource dataSource : skCase.getDataSources()) { diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java new file mode 100644 index 0000000000..7e66e56682 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java @@ -0,0 +1,819 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 - 2020 Basis Technology Corp. + * Contact: carrier sleuthkit 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.io.File; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TskCoreException; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; +import org.apache.commons.lang.StringUtils; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; +import org.sleuthkit.datamodel.TskData; +import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; +import org.sleuthkit.datamodel.DataSource; + +/** + * Utilities for getting information about a data source or all data sources + * from the case database. + */ +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 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 getBaseQueryResult(String query, ResultSetHandler 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 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); + } + + /** + * 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> getStringLongResultSetHandler(String keyParam, String valueParam) { + return (resultSet) -> { + LinkedHashMap 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. + * + * @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 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"; + + String errorMessage = "Unable to get artifact type counts; returning null."; + return getBaseQueryResult(query, getStringLongResultSetHandler(nameParam, valueParam), errorMessage); + } + + /** + * Describes a result of a program run on a datasource. + */ + static class TopProgramsResult { + + private final String programName; + private final String programPath; + private final Long runTimes; + private final Date lastRun; + + /** + * Main constructor. + * + * @param programName The name of the program. + * @param programPath The path of the program. + * @param runTimes The number of runs. + */ + TopProgramsResult(String programName, String programPath, Long runTimes, Date lastRun) { + this.programName = programName; + this.programPath = programPath; + this.runTimes = runTimes; + this.lastRun = lastRun; + } + + /** + * @return The name of the program + */ + String getProgramName() { + return programName; + } + + /** + * @return The path of the program. + */ + String getProgramPath() { + return programPath; + } + + /** + * @return The number of run times or null if not present. + */ + Long getRunTimes() { + return runTimes; + } + + /** + * @return The last time the program was run or null if not present. + */ + public Date getLastRun() { + return lastRun; + } + } + + /** + * 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 clauses) { + if (clauses.isEmpty()) { + return ""; + } + + List 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 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> handler = (resultSet) -> { + List 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; + }; + + 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, 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 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, String> matchEntry : SHORT_FOLDER_MATCHERS) { + String result = matchEntry.apply(pathEls); + if (StringUtils.isNotBlank(result)) { + return result; + } + } + + return ""; + } + + /** + * Generates a string which is a concatenation of the value received from + * the result set. + * + * @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 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. + * + * @return Collection which maps datasource id to a String which displays a + * comma seperated list of values of data source usage types + * expected to be in the datasource + */ + static Map getDataSourceTypes() { + try { + SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase(); + List listOfArtifacts = skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_DATA_SOURCE_USAGE); + Map typeMap = new HashMap<>(); + for (BlackboardArtifact typeArtifact : listOfArtifacts) { + BlackboardAttribute descriptionAttr = typeArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION)); + if (typeArtifact.getDataSource() != null && descriptionAttr != null) { + long dsId = typeArtifact.getDataSource().getId(); + String type = typeMap.get(typeArtifact.getDataSource().getId()); + if (type == null) { + type = descriptionAttr.getValueString(); + } else { + type = type + ", " + descriptionAttr.getValueString(); + } + typeMap.put(dsId, type); + } + } + return typeMap; + } catch (TskCoreException | NoCurrentCaseException ex) { + logger.log(Level.WARNING, "Unable to get types of files for all datasources, providing empty results", ex); + return Collections.emptyMap(); + } + } + + /** + * Get a map containing the number of files in each data source in the + * current case. + * + * @return Collection which maps datasource id to a count for the number of + * files in the datasource, will only contain entries for + * datasources which have at least 1 file + */ + static Map getCountsOfFiles() { + try { + final String countFilesQuery = "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 name<>'' GROUP BY data_source_obj_id"; //NON-NLS + return getValuesMap(countFilesQuery); + } catch (TskCoreException | NoCurrentCaseException ex) { + logger.log(Level.WARNING, "Unable to get counts of files for all datasources, providing empty results", ex); + return Collections.emptyMap(); + } + } + + /** + * Get a map containing the number of artifacts in each data source in the + * current case. + * + * @return Collection which maps datasource id to a count for the number of + * artifacts in the datasource, will only contain entries for + * datasources which have at least 1 artifact + */ + static Map getCountsOfArtifacts() { + try { + final String countArtifactsQuery = "data_source_obj_id, COUNT(*) AS value" + + " FROM blackboard_artifacts WHERE review_status_id !=" + BlackboardArtifact.ReviewStatus.REJECTED.getID() + + " GROUP BY data_source_obj_id"; //NON-NLS + return getValuesMap(countArtifactsQuery); + } catch (TskCoreException | NoCurrentCaseException ex) { + logger.log(Level.WARNING, "Unable to get counts of artifacts for all datasources, providing empty results", ex); + return Collections.emptyMap(); + } + } + + /** + * Get a map containing the number of tags which have been applied in each + * data source in the current case. Not necessarily the same as the number + * of items tagged, as an item can have any number of tags. + * + * @return Collection which maps datasource id to a count for the number of + * tags which have been applied in the datasource, will only contain + * entries for datasources which have at least 1 item tagged. + */ + static Map getCountsOfTags() { + try { + final String countFileTagsQuery = "data_source_obj_id, COUNT(*) AS value" + + " FROM content_tags as content_tags, tsk_files as tsk_files" + + " WHERE content_tags.obj_id = tsk_files.obj_id" + + " GROUP BY data_source_obj_id"; //NON-NLS + //new hashmap so it can be modifiable + Map tagCountMap = new HashMap<>(getValuesMap(countFileTagsQuery)); + final String countArtifactTagsQuery = "data_source_obj_id, COUNT(*) AS value" + + " FROM blackboard_artifact_tags as artifact_tags, blackboard_artifacts AS arts" + + " WHERE artifact_tags.artifact_id = arts.artifact_id" + + " GROUP BY data_source_obj_id"; //NON-NLS + //combine the results from the count artifact tags query into the copy of the mapped results from the count file tags query + getValuesMap(countArtifactTagsQuery).forEach((key, value) -> tagCountMap.merge(key, value, (value1, value2) -> value1 + value2)); + return tagCountMap; + } catch (TskCoreException | NoCurrentCaseException ex) { + logger.log(Level.WARNING, "Unable to get counts of tags for all datasources, providing empty results", ex); + return Collections.emptyMap(); + } + } + + /** + * Get the number of files in the case database for the current data source + * which have the specified mimetypes. + * + * @param currentDataSource the data source which we are finding a file + * count + * + * @param setOfMimeTypes the set of mime types which we are finding the + * number of occurences of + * + * @return a Long value which represents the number of occurrences of the + * specified mime types in the current case for the specified data + * source, null if no count was retrieved + */ + static Long getCountOfFilesForMimeTypes(DataSource currentDataSource, Set setOfMimeTypes) { + if (currentDataSource != null) { + try { + String inClause = String.join("', '", setOfMimeTypes); + SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase(); + return skCase.countFilesWhere("data_source_obj_id=" + currentDataSource.getId() + + " AND type<>" + TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType() + + " AND dir_type<>" + TskData.TSK_FS_NAME_TYPE_ENUM.VIRT_DIR.getValue() + + " AND mime_type IN ('" + inClause + "')" + + " AND name<>''"); + } catch (TskCoreException | NoCurrentCaseException ex) { + logger.log(Level.WARNING, "Unable to get count of files for specified mime types", ex); + //unable to get count of files for the specified mimetypes cell will be displayed as empty + } + } + return null; + } + + /** + * Helper method to execute a select query with a + * DataSourceSingleValueCallback. + * + * @param query the portion of the query which should follow the SELECT + * + * @return a map of datasource object ID to a value of type Long + * + * @throws TskCoreException + * @throws NoCurrentCaseException + */ + private static Map getValuesMap(String query) throws TskCoreException, NoCurrentCaseException { + SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase(); + DataSourceSingleValueCallback callback = new DataSourceSingleValueCallback(); + skCase.getCaseDbAccessManager().select(query, callback); + return callback.getMapOfValues(); + } + + /** + * Empty private constructor + */ + private DataSourceInfoUtilities() { + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceLabeledValueCallback.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceLabeledValueCallback.java similarity index 97% rename from Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceLabeledValueCallback.java rename to Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceLabeledValueCallback.java index 6c9c918667..1a44e2a76b 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceLabeledValueCallback.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceLabeledValueCallback.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.datasourcesummary.datamodel; +package org.sleuthkit.autopsy.casemodule.datasourcesummary; import java.sql.ResultSet; import java.sql.SQLException; diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceSingleValueCallback.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSingleValueCallback.java similarity index 97% rename from Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceSingleValueCallback.java rename to Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSingleValueCallback.java index 6b1ff205fa..fc7eea15e5 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceSingleValueCallback.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSingleValueCallback.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.datasourcesummary.datamodel; +package org.sleuthkit.autopsy.casemodule.datasourcesummary; import java.sql.ResultSet; import java.sql.SQLException; diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummary.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummary.java similarity index 98% rename from Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummary.java rename to Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummary.java index 1894e8968b..b50c3596bd 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummary.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummary.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.datasourcesummary.ui; +package org.sleuthkit.autopsy.casemodule.datasourcesummary; import java.sql.ResultSet; import java.sql.SQLException; diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryAction.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryAction.java similarity index 95% rename from Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryAction.java rename to Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryAction.java index 12a3ed3059..7514d61599 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryAction.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryAction.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.datasourcesummary.ui; +package org.sleuthkit.autopsy.casemodule.datasourcesummary; import java.awt.event.ActionEvent; import java.beans.PropertyChangeEvent; @@ -28,7 +28,6 @@ import org.openide.util.HelpCtx; import org.openide.util.NbBundle.Messages; import org.openide.util.actions.CallableSystemAction; import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.datasourcesummary.ui.Bundle; @ActionID(category = "Case", id = "org.sleuthkit.autopsy.casemodule.datasourcesummary.DataSourceSummaryAction") @ActionRegistration(displayName = "#CTL_DataSourceSummaryAction", lazy = false) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryCountsPanel.form b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryCountsPanel.form new file mode 100644 index 0000000000..9a82746da6 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryCountsPanel.form @@ -0,0 +1,120 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryCountsPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryCountsPanel.java similarity index 53% rename from Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryCountsPanel.java rename to Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryCountsPanel.java index eff822f161..6bb6603c11 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryCountsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryCountsPanel.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.datasourcesummary.ui; +package org.sleuthkit.autopsy.casemodule.datasourcesummary; import java.util.Map; import org.sleuthkit.autopsy.coreutils.Logger; @@ -24,8 +24,7 @@ import javax.swing.JLabel; import javax.swing.table.DefaultTableCellRenderer; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataSourceCountsSummary; - +import org.sleuthkit.autopsy.coreutils.FileTypeUtils; import org.sleuthkit.datamodel.DataSource; /** @@ -41,8 +40,6 @@ import org.sleuthkit.datamodel.DataSource; }) class DataSourceSummaryCountsPanel extends javax.swing.JPanel { - private static final long serialVersionUID = 1L; - // Result returned for a data model if no data found. private static final Object[][] EMPTY_PAIRS = new Object[][]{}; @@ -64,11 +61,10 @@ class DataSourceSummaryCountsPanel extends javax.swing.JPanel { Bundle.DataSourceSummaryCountsPanel_ArtifactCountsTableModel_count_header() }; + private static final long serialVersionUID = 1L; private static final Logger logger = Logger.getLogger(DataSourceSummaryCountsPanel.class.getName()); private final DefaultTableCellRenderer rightAlignedRenderer = new DefaultTableCellRenderer(); - private final FileTypePieChart fileTypePieChart = new FileTypePieChart(); - private DataSource dataSource; /** @@ -77,6 +73,7 @@ class DataSourceSummaryCountsPanel extends javax.swing.JPanel { DataSourceSummaryCountsPanel() { rightAlignedRenderer.setHorizontalAlignment(JLabel.RIGHT); initComponents(); + fileCountsByMimeTypeTable.getTableHeader().setReorderingAllowed(false); fileCountsByCategoryTable.getTableHeader().setReorderingAllowed(false); artifactCountsTable.getTableHeader().setReorderingAllowed(false); setDataSource(null); @@ -99,20 +96,29 @@ class DataSourceSummaryCountsPanel extends javax.swing.JPanel { public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; if (dataSource == null || !Case.isCaseOpen()) { - updateCountsTableData(EMPTY_PAIRS, EMPTY_PAIRS); + updateCountsTableData(EMPTY_PAIRS, + EMPTY_PAIRS, + EMPTY_PAIRS); } else { - updateCountsTableData(getFileCategoryModel(dataSource), getArtifactCountsModel(dataSource)); + updateCountsTableData(getMimeTypeModel(dataSource), + getFileCategoryModel(dataSource), + getArtifactCountsModel(dataSource)); } - this.fileTypePieChart.setDataSource(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[][] fileCategoryDataModel, Object[][] artifactDataModel) { + 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); + fileCountsByCategoryTable.setModel(new NonEditableTableModel(fileCategoryDataModel, FILE_BY_CATEGORY_COLUMN_HEADERS)); fileCountsByCategoryTable.getColumnModel().getColumn(1).setCellRenderer(rightAlignedRenderer); fileCountsByCategoryTable.getColumnModel().getColumn(0).setPreferredWidth(130); @@ -124,6 +130,48 @@ class DataSourceSummaryCountsPanel extends javax.swing.JPanel { this.repaint(); } + /** + * Determines the JTable data model for datasource mime types. + * + * @param dataSource The DataSource. + * + * @return The model to be used with a JTable. + */ + @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. * @@ -139,11 +187,11 @@ class DataSourceSummaryCountsPanel extends javax.swing.JPanel { "DataSourceSummaryCountsPanel.FilesByCategoryTableModel.directory.row=Directory" }) private static Object[][] getFileCategoryModel(DataSource selectedDataSource) { - Long fileCount = zeroIfNull(DataSourceCountsSummary.getCountOfFiles(selectedDataSource)); - Long unallocatedFiles = zeroIfNull(DataSourceCountsSummary.getCountOfUnallocatedFiles(selectedDataSource)); - Long allocatedFiles = zeroIfNull(DataSourceCountsSummary.getCountOfAllocatedFiles(selectedDataSource)); - Long slackFiles = zeroIfNull(DataSourceCountsSummary.getCountOfSlackFiles(selectedDataSource)); - Long directories = zeroIfNull(DataSourceCountsSummary.getCountOfDirectories(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}, @@ -165,6 +213,24 @@ class DataSourceSummaryCountsPanel extends javax.swing.JPanel { 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; + } + } + /** * The counts of different artifact types found in a DataSource. * @@ -173,7 +239,7 @@ class DataSourceSummaryCountsPanel extends javax.swing.JPanel { * @return The JTable data model of counts of artifact types. */ private static Object[][] getArtifactCountsModel(DataSource selectedDataSource) { - Map artifactMapping = DataSourceCountsSummary.getCountsOfArtifactsByType(selectedDataSource); + Map artifactMapping = DataSourceInfoUtilities.getCountsOfArtifactsByType(selectedDataSource); if (artifactMapping == null) { return EMPTY_PAIRS; } @@ -194,120 +260,83 @@ class DataSourceSummaryCountsPanel extends javax.swing.JPanel { // //GEN-BEGIN:initComponents private void initComponents() { - javax.swing.JScrollPane scrollParent = new javax.swing.JScrollPane(); - javax.swing.JPanel parentPanel = new javax.swing.JPanel(); - javax.swing.JScrollPane fileCountsByCategoryScrollPane = new javax.swing.JScrollPane(); + fileCountsByMimeTypeScrollPane = new javax.swing.JScrollPane(); + fileCountsByMimeTypeTable = new javax.swing.JTable(); + byMimeTypeLabel = new javax.swing.JLabel(); + fileCountsByCategoryScrollPane = new javax.swing.JScrollPane(); fileCountsByCategoryTable = new javax.swing.JTable(); - javax.swing.JLabel byCategoryLabel = new javax.swing.JLabel(); - javax.swing.JLabel resultsByTypeLabel = new javax.swing.JLabel(); - javax.swing.JScrollPane artifactCountsScrollPane = new javax.swing.JScrollPane(); + byCategoryLabel = new javax.swing.JLabel(); + jLabel1 = new javax.swing.JLabel(); + artifactCountsScrollPane = new javax.swing.JScrollPane(); artifactCountsTable = new javax.swing.JTable(); - javax.swing.JPanel fileTypePiePanel = fileTypePieChart; - javax.swing.JPanel filesByCatParent = new javax.swing.JPanel(); - javax.swing.JPanel resultsByTypeParent = new javax.swing.JPanel(); - parentPanel.setMinimumSize(new java.awt.Dimension(840, 320)); + fileCountsByMimeTypeScrollPane.setViewportView(fileCountsByMimeTypeTable); + + org.openide.awt.Mnemonics.setLocalizedText(byMimeTypeLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryCountsPanel.class, "DataSourceSummaryCountsPanel.byMimeTypeLabel.text")); // NOI18N 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(resultsByTypeLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryCountsPanel.class, "DataSourceSummaryCountsPanel.resultsByTypeLabel.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(DataSourceSummaryCountsPanel.class, "DataSourceSummaryCountsPanel.jLabel1.text")); // NOI18N - artifactCountsTable.setAutoCreateRowSorter(true); artifactCountsScrollPane.setViewportView(artifactCountsTable); - fileTypePiePanel.setPreferredSize(new java.awt.Dimension(400, 300)); - - javax.swing.GroupLayout filesByCatParentLayout = new javax.swing.GroupLayout(filesByCatParent); - filesByCatParent.setLayout(filesByCatParentLayout); - filesByCatParentLayout.setHorizontalGroup( - filesByCatParentLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 0, Short.MAX_VALUE) - ); - filesByCatParentLayout.setVerticalGroup( - filesByCatParentLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 0, Short.MAX_VALUE) - ); - - javax.swing.GroupLayout resultsByTypeParentLayout = new javax.swing.GroupLayout(resultsByTypeParent); - resultsByTypeParent.setLayout(resultsByTypeParentLayout); - resultsByTypeParentLayout.setHorizontalGroup( - resultsByTypeParentLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 0, Short.MAX_VALUE) - ); - resultsByTypeParentLayout.setVerticalGroup( - resultsByTypeParentLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 0, Short.MAX_VALUE) - ); - - javax.swing.GroupLayout parentPanelLayout = new javax.swing.GroupLayout(parentPanel); - parentPanel.setLayout(parentPanelLayout); - parentPanelLayout.setHorizontalGroup( - parentPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(parentPanelLayout.createSequentialGroup() - .addContainerGap() - .addComponent(fileTypePiePanel, javax.swing.GroupLayout.PREFERRED_SIZE, 400, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(18, 18, 18) - .addGroup(parentPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(fileCountsByCategoryScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 140, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(byCategoryLabel) - .addComponent(filesByCatParent, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addGap(18, 18, 18) - .addGroup(parentPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(resultsByTypeLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 79, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGroup(parentPanelLayout.createSequentialGroup() - .addComponent(artifactCountsScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 244, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(resultsByTypeParent, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - ); - parentPanelLayout.setVerticalGroup( - parentPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(parentPanelLayout.createSequentialGroup() - .addContainerGap() - .addGroup(parentPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(parentPanelLayout.createSequentialGroup() - .addComponent(fileTypePiePanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - .addGroup(parentPanelLayout.createSequentialGroup() - .addGroup(parentPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(byCategoryLabel) - .addComponent(resultsByTypeLabel)) - .addGroup(parentPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, parentPanelLayout.createSequentialGroup() - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(resultsByTypeParent, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(148, 148, 148)) - .addGroup(parentPanelLayout.createSequentialGroup() - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(parentPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(artifactCountsScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE) - .addGroup(parentPanelLayout.createSequentialGroup() - .addComponent(fileCountsByCategoryScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 107, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(31, 31, 31) - .addComponent(filesByCatParent, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(0, 0, Short.MAX_VALUE))) - .addContainerGap()))))) - ); - - scrollParent.setViewportView(parentPanel); - javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(scrollParent) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(fileCountsByMimeTypeScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 140, Short.MAX_VALUE) + .addComponent(byMimeTypeLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addGap(18, 18, 18) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(byCategoryLabel) + .addComponent(fileCountsByCategoryScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 82, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(18, 18, 18) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, 79, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(artifactCountsScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 244, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); + + layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {fileCountsByCategoryScrollPane, fileCountsByMimeTypeScrollPane}); + layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(scrollParent) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(byMimeTypeLabel) + .addComponent(byCategoryLabel) + .addComponent(jLabel1)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(artifactCountsScrollPane, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(fileCountsByMimeTypeScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 107, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(fileCountsByCategoryScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 86, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(0, 0, Short.MAX_VALUE))) + .addContainerGap()) ); + + layout.linkSize(javax.swing.SwingConstants.VERTICAL, new java.awt.Component[] {fileCountsByCategoryScrollPane, fileCountsByMimeTypeScrollPane}); + }// //GEN-END:initComponents // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JScrollPane artifactCountsScrollPane; private javax.swing.JTable artifactCountsTable; + private javax.swing.JLabel byCategoryLabel; + private javax.swing.JLabel byMimeTypeLabel; + private javax.swing.JScrollPane fileCountsByCategoryScrollPane; private javax.swing.JTable fileCountsByCategoryTable; + private javax.swing.JScrollPane fileCountsByMimeTypeScrollPane; + private javax.swing.JTable fileCountsByMimeTypeTable; + private javax.swing.JLabel jLabel1; // End of variables declaration//GEN-END:variables } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryDetailsPanel.form b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDetailsPanel.form similarity index 84% rename from Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryDetailsPanel.form rename to Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDetailsPanel.form index a882623946..9efc00faa0 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryDetailsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDetailsPanel.form @@ -40,7 +40,7 @@ - + @@ -52,7 +52,7 @@ - + @@ -64,7 +64,7 @@ - + @@ -76,7 +76,7 @@ - + @@ -88,10 +88,10 @@ - + - + @@ -103,7 +103,7 @@ - + @@ -115,7 +115,7 @@ - + @@ -127,7 +127,7 @@ - + @@ -139,10 +139,10 @@ - + - + @@ -179,7 +179,7 @@ - <ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="DataSourceSummaryDetailsPanel.filePathsTable.columnModel.title0" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> + <ResourceString bundle="org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties" key="DataSourceSummaryDetailsPanel.filePathsTable.columnModel.title0" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> @@ -196,7 +196,7 @@ - + @@ -208,7 +208,7 @@ - + @@ -220,10 +220,10 @@ - + - + @@ -235,10 +235,10 @@ - + - + @@ -250,7 +250,7 @@ - + @@ -262,7 +262,7 @@ - + @@ -274,7 +274,7 @@ - + @@ -286,7 +286,7 @@ - + @@ -298,7 +298,7 @@ - + @@ -310,7 +310,7 @@ - + @@ -322,7 +322,7 @@ - + @@ -334,7 +334,7 @@ - + @@ -346,7 +346,7 @@ - + @@ -358,7 +358,7 @@ - + @@ -370,7 +370,7 @@ - + @@ -382,7 +382,7 @@ - + @@ -394,7 +394,7 @@ - + @@ -424,7 +424,7 @@ - + @@ -466,7 +466,7 @@ - + @@ -478,7 +478,7 @@ - + diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryDetailsPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDetailsPanel.java similarity index 99% rename from Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryDetailsPanel.java rename to Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDetailsPanel.java index f1972d9d89..21caa2c4b8 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryDetailsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDetailsPanel.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.datasourcesummary.ui; +package org.sleuthkit.autopsy.casemodule.datasourcesummary; import java.text.DecimalFormat; import java.util.logging.Level; @@ -25,7 +25,6 @@ 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.autopsy.datasourcesummary.datamodel.DataSourceDetailsSummary; import org.sleuthkit.datamodel.DataSource; import org.sleuthkit.datamodel.Image; import org.sleuthkit.datamodel.TskCoreException; @@ -74,9 +73,9 @@ class DataSourceSummaryDetailsPanel extends javax.swing.JPanel { updateDetailsPanelData(null, null, null, null); } else { updateDetailsPanelData(dataSource, - DataSourceDetailsSummary.getSizeOfUnallocatedFiles(dataSource), - DataSourceDetailsSummary.getOperatingSystems(dataSource), - DataSourceDetailsSummary.getDataSourceType(dataSource)); + DataSourceInfoUtilities.getSizeOfUnallocatedFiles(dataSource), + DataSourceInfoUtilities.getOperatingSystems(dataSource), + DataSourceInfoUtilities.getDataSourceType(dataSource)); } } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryDialog.form b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDialog.form similarity index 92% rename from Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryDialog.form rename to Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDialog.form index d4edf4b0ee..272fc9b041 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryDialog.form +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDialog.form @@ -22,7 +22,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -48,7 +48,7 @@ - + diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryDialog.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDialog.java similarity index 94% rename from Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryDialog.java rename to Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDialog.java index 1bc033822d..9cd7549c06 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryDialog.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDialog.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.datasourcesummary.ui; +package org.sleuthkit.autopsy.casemodule.datasourcesummary; import java.awt.Frame; import java.beans.PropertyChangeEvent; @@ -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.datasourcesummary.datamodel.CaseDataSourcesSummary; import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.events.DataSourceAnalysisCompletedEvent; import org.sleuthkit.autopsy.ingest.events.DataSourceAnalysisCompletedEvent.Reason; @@ -54,8 +53,8 @@ final class DataSourceSummaryDialog extends javax.swing.JDialog implements Obser }) DataSourceSummaryDialog(Frame owner) { super(owner, Bundle.DataSourceSummaryDialog_window_title(), true); - Map usageMap = CaseDataSourcesSummary.getDataSourceTypes(); - Map fileCountsMap = CaseDataSourcesSummary.getCountsOfFiles(); + Map usageMap = DataSourceInfoUtilities.getDataSourceTypes(); + Map fileCountsMap = DataSourceInfoUtilities.getCountsOfFiles(); dataSourcesPanel = new DataSourceBrowser(usageMap, fileCountsMap); dataSourceSummaryTabbedPane = new DataSourceSummaryTabbedPane(); initComponents(); @@ -126,7 +125,7 @@ final class DataSourceSummaryDialog extends javax.swing.JDialog implements Obser .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addComponent(dataSourceSummarySplitPane, javax.swing.GroupLayout.DEFAULT_SIZE, 860, Short.MAX_VALUE) + .addComponent(dataSourceSummarySplitPane, javax.swing.GroupLayout.DEFAULT_SIZE, 668, Short.MAX_VALUE) .addGroup(layout.createSequentialGroup() .addGap(0, 0, Short.MAX_VALUE) .addComponent(closeButton))) @@ -136,7 +135,7 @@ final class DataSourceSummaryDialog extends javax.swing.JDialog implements Obser layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() - .addComponent(dataSourceSummarySplitPane, javax.swing.GroupLayout.DEFAULT_SIZE, 540, Short.MAX_VALUE) + .addComponent(dataSourceSummarySplitPane, javax.swing.GroupLayout.DEFAULT_SIZE, 362, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(closeButton) .addContainerGap()) diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryNode.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryNode.java similarity index 98% rename from Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryNode.java rename to Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryNode.java index a4c48e7adf..0411d94bb2 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryNode.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryNode.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.datasourcesummary.ui; +package org.sleuthkit.autopsy.casemodule.datasourcesummary; import java.awt.event.ActionEvent; import java.util.ArrayList; @@ -30,7 +30,6 @@ import org.openide.nodes.Children; import org.openide.nodes.Node; import org.openide.nodes.Sheet; import org.openide.util.NbBundle.Messages; -import org.sleuthkit.autopsy.datasourcesummary.ui.Bundle; import org.sleuthkit.autopsy.datamodel.NodeProperty; import org.sleuthkit.autopsy.directorytree.ViewContextAction; import org.sleuthkit.datamodel.DataSource; diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryTabbedPane.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryTabbedPane.java similarity index 96% rename from Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryTabbedPane.java rename to Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryTabbedPane.java index 9298646a9d..dddc4ee123 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryTabbedPane.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryTabbedPane.java @@ -16,12 +16,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.datasourcesummary.ui; +package org.sleuthkit.autopsy.casemodule.datasourcesummary; import javax.swing.JTabbedPane; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.IngestJobInfoPanel; -import org.sleuthkit.autopsy.datasourcesummary.ui.Bundle; import org.sleuthkit.datamodel.DataSource; /** diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryUserActivityPanel.form b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.form similarity index 92% rename from Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryUserActivityPanel.form rename to Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.form index 0829b9e045..b938286e96 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryUserActivityPanel.form +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.form @@ -42,7 +42,7 @@ - + diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryUserActivityPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java similarity index 94% rename from Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryUserActivityPanel.java rename to Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java index 21b2a30f22..c97e2ec18c 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryUserActivityPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.datasourcesummary.ui; +package org.sleuthkit.autopsy.casemodule.datasourcesummary; import java.awt.Component; import java.text.DateFormat; @@ -32,8 +32,6 @@ 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.autopsy.datasourcesummary.datamodel.DataSourceTopProgramsSummary; -import org.sleuthkit.autopsy.datasourcesummary.datamodel.TopProgramsResult; import org.sleuthkit.datamodel.DataSource; /** @@ -116,8 +114,8 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { * @return The JTable data model of counts of program runs. */ private static TopProgramsModel getTopProgramsModel(DataSource selectedDataSource) { - List topProgramList - = DataSourceTopProgramsSummary.getTopPrograms(selectedDataSource, TOP_PROGS_COUNT); + List topProgramList + = DataSourceInfoUtilities.getTopPrograms(selectedDataSource, TOP_PROGS_COUNT); if (topProgramList == null) { return new TopProgramsModel(null); @@ -196,14 +194,14 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_lastrun_header() }; - private final List programResults; + private final List programResults; /** * Main constructor. * * @param programResults The results to display. */ - TopProgramsModel(List programResults) { + TopProgramsModel(List programResults) { this.programResults = programResults == null ? new ArrayList<>() : Collections.unmodifiableList(programResults); } @@ -228,12 +226,12 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { return null; } - TopProgramsResult result = programResults.get(rowIndex); + DataSourceInfoUtilities.TopProgramsResult result = programResults.get(rowIndex); switch (columnIndex) { case 0: return new ProgramNameCellValue(result.getProgramName(), result.getProgramPath()); case 1: - return DataSourceTopProgramsSummary.getShortFolderName(result.getProgramPath(), result.getProgramName()); + return DataSourceInfoUtilities.getShortFolderName(result.getProgramPath(), result.getProgramName()); case 2: return result.getRunTimes(); case 3: diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/NonEditableTableModel.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/NonEditableTableModel.java similarity index 94% rename from Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/NonEditableTableModel.java rename to Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/NonEditableTableModel.java index 3274415a89..8ed965e2ab 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/NonEditableTableModel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/NonEditableTableModel.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.datasourcesummary.ui; +package org.sleuthkit.autopsy.casemodule.datasourcesummary; import javax.swing.table.DefaultTableModel; diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RightAlignedTableCellRenderer.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/RightAlignedTableCellRenderer.java similarity index 97% rename from Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RightAlignedTableCellRenderer.java rename to Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/RightAlignedTableCellRenderer.java index 989e97da82..12f111bba2 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RightAlignedTableCellRenderer.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/RightAlignedTableCellRenderer.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.datasourcesummary.ui; +package org.sleuthkit.autopsy.casemodule.datasourcesummary; import java.awt.Component; import java.lang.reflect.InvocationTargetException; diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/ViewSummaryInformationAction.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/ViewSummaryInformationAction.java similarity index 95% rename from Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/ViewSummaryInformationAction.java rename to Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/ViewSummaryInformationAction.java index c62cb8c214..babe2197a1 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/ViewSummaryInformationAction.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/ViewSummaryInformationAction.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.datasourcesummary.ui; +package org.sleuthkit.autopsy.casemodule.datasourcesummary; import java.awt.Frame; import java.awt.event.ActionEvent; @@ -24,7 +24,6 @@ import javax.swing.AbstractAction; import javax.swing.SwingUtilities; import org.openide.util.NbBundle.Messages; import org.openide.windows.WindowManager; -import org.sleuthkit.autopsy.datasourcesummary.ui.Bundle; /** * ViewSummaryInformationAction action for opening a Data Sources Summary Dialog diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java index 06a50eb788..f668893eb1 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java @@ -36,7 +36,7 @@ import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.DeleteDataSourceAction; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; -import org.sleuthkit.autopsy.datasourcesummary.ui.ViewSummaryInformationAction; +import org.sleuthkit.autopsy.casemodule.datasourcesummary.ViewSummaryInformationAction; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable; import org.sleuthkit.autopsy.coreutils.Logger; diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/SpecialDirectoryNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/SpecialDirectoryNode.java index a3c6691ba2..ca73a64468 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/SpecialDirectoryNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/SpecialDirectoryNode.java @@ -24,7 +24,7 @@ import java.util.List; import javax.swing.Action; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.DeleteDataSourceAction; -import org.sleuthkit.autopsy.datasourcesummary.ui.ViewSummaryInformationAction; +import org.sleuthkit.autopsy.casemodule.datasourcesummary.ViewSummaryInformationAction; import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint; import org.sleuthkit.autopsy.directorytree.ExportCSVAction; import org.sleuthkit.autopsy.directorytree.ExtractAction; diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/CaseDataSourcesSummary.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/CaseDataSourcesSummary.java deleted file mode 100644 index 50e883e29a..0000000000 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/CaseDataSourcesSummary.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2020 Basis Technology Corp. - * Contact: carrier sleuthkit org - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sleuthkit.autopsy.datasourcesummary.datamodel; - -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.logging.Level; -import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; -import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.datamodel.BlackboardArtifact; -import org.sleuthkit.datamodel.BlackboardAttribute; -import org.sleuthkit.datamodel.SleuthkitCase; -import org.sleuthkit.datamodel.TskCoreException; -import org.sleuthkit.datamodel.TskData; - -/** - * Provides methods to query for information for all datasources in a case. - */ -public class CaseDataSourcesSummary { - - private static final Logger logger = Logger.getLogger(CaseDataSourcesSummary.class.getName()); - - /** - * Get a map containing the TSK_DATA_SOURCE_USAGE description attributes - * associated with each data source in the current case. - * - * @return Collection which maps datasource id to a String which displays a - * comma seperated list of values of data source usage types - * expected to be in the datasource - */ - public static Map getDataSourceTypes() { - try { - SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase(); - List listOfArtifacts = skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_DATA_SOURCE_USAGE); - Map typeMap = new HashMap<>(); - for (BlackboardArtifact typeArtifact : listOfArtifacts) { - BlackboardAttribute descriptionAttr = typeArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION)); - if (typeArtifact.getDataSource() != null && descriptionAttr != null) { - long dsId = typeArtifact.getDataSource().getId(); - String type = typeMap.get(typeArtifact.getDataSource().getId()); - if (type == null) { - type = descriptionAttr.getValueString(); - } else { - type = type + ", " + descriptionAttr.getValueString(); - } - typeMap.put(dsId, type); - } - } - return typeMap; - } catch (TskCoreException | NoCurrentCaseException ex) { - logger.log(Level.WARNING, "Unable to get types of files for all datasources, providing empty results", ex); - return Collections.emptyMap(); - } - } - - /** - * Get a map containing the number of files in each data source in the - * current case. - * - * @return Collection which maps datasource id to a count for the number of - * files in the datasource, will only contain entries for - * datasources which have at least 1 file - */ - public static Map getCountsOfFiles() { - try { - final String countFilesQuery = "data_source_obj_id, COUNT(*) AS value FROM tsk_files" - + " WHERE meta_type=" + TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_REG.getValue() - + " AND type<>" + TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.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(countFilesQuery); - } catch (TskCoreException | NoCurrentCaseException ex) { - logger.log(Level.WARNING, "Unable to get counts of files for all datasources, providing empty results", ex); - return Collections.emptyMap(); - } - } - - /** - * Get a map containing the number of artifacts in each data source in the - * current case. - * - * @return Collection which maps datasource id to a count for the number of - * artifacts in the datasource, will only contain entries for - * datasources which have at least 1 artifact - */ - public static Map getCountsOfArtifacts() { - try { - final String countArtifactsQuery = "data_source_obj_id, COUNT(*) AS value" - + " FROM blackboard_artifacts WHERE review_status_id !=" + BlackboardArtifact.ReviewStatus.REJECTED.getID() - + " GROUP BY data_source_obj_id"; //NON-NLS - return getValuesMap(countArtifactsQuery); - } catch (TskCoreException | NoCurrentCaseException ex) { - logger.log(Level.WARNING, "Unable to get counts of artifacts for all datasources, providing empty results", ex); - return Collections.emptyMap(); - } - } - - /** - * Get a map containing the number of tags which have been applied in each - * data source in the current case. Not necessarily the same as the number - * of items tagged, as an item can have any number of tags. - * - * @return Collection which maps datasource id to a count for the number of - * tags which have been applied in the datasource, will only contain - * entries for datasources which have at least 1 item tagged. - */ - public static Map getCountsOfTags() { - try { - final String countFileTagsQuery = "data_source_obj_id, COUNT(*) AS value" - + " FROM content_tags as content_tags, tsk_files as tsk_files" - + " WHERE content_tags.obj_id = tsk_files.obj_id" - + " GROUP BY data_source_obj_id"; //NON-NLS - //new hashmap so it can be modifiable - Map tagCountMap = new HashMap<>(getValuesMap(countFileTagsQuery)); - final String countArtifactTagsQuery = "data_source_obj_id, COUNT(*) AS value" - + " FROM blackboard_artifact_tags as artifact_tags, blackboard_artifacts AS arts" - + " WHERE artifact_tags.artifact_id = arts.artifact_id" - + " GROUP BY data_source_obj_id"; //NON-NLS - //combine the results from the count artifact tags query into the copy of the mapped results from the count file tags query - getValuesMap(countArtifactTagsQuery).forEach((key, value) -> tagCountMap.merge(key, value, (value1, value2) -> value1 + value2)); - return tagCountMap; - } catch (TskCoreException | NoCurrentCaseException ex) { - logger.log(Level.WARNING, "Unable to get counts of tags for all datasources, providing empty results", ex); - return Collections.emptyMap(); - } - } - - /** - * Helper method to execute a select query with a - * DataSourceSingleValueCallback. - * - * @param query the portion of the query which should follow the SELECT - * - * @return a map of datasource object ID to a value of type Long - * - * @throws TskCoreException - * @throws NoCurrentCaseException - */ - private static Map getValuesMap(String query) throws TskCoreException, NoCurrentCaseException { - SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase(); - DataSourceSingleValueCallback callback = new DataSourceSingleValueCallback(); - skCase.getCaseDbAccessManager().select(query, callback); - return callback.getMapOfValues(); - } - - private CaseDataSourcesSummary() { - } -} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceCountsSummary.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceCountsSummary.java deleted file mode 100644 index 0f2826636b..0000000000 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceCountsSummary.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2019 - 2020 Basis Technology Corp. - * Contact: carrier sleuthkit org - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sleuthkit.autopsy.datasourcesummary.datamodel; - -import java.sql.SQLException; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.logging.Level; -import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataSourceInfoUtilities.ResultSetHandler; -import org.sleuthkit.datamodel.DataSource; -import org.sleuthkit.datamodel.TskData; - -/** - * Provides information for the DataSourceSummaryCountsPanel. - */ -public class DataSourceCountsSummary { - - private static final Logger logger = Logger.getLogger(DataSourceCountsSummary.class.getName()); - - /** - * Get count of regular files (not directories) in a data source. - * - * @param currentDataSource The data source. - * - * @return The count. - */ - public static Long getCountOfFiles(DataSource currentDataSource) { - return DataSourceInfoUtilities.getCountOfRegularFiles(currentDataSource, null, - "Unable to get count of files, providing empty results"); - } - - /** - * Get count of allocated files in a data source. - * - * @param currentDataSource The data source. - * - * @return The count. - */ - public static Long getCountOfAllocatedFiles(DataSource currentDataSource) { - return DataSourceInfoUtilities.getCountOfRegularFiles(currentDataSource, - DataSourceInfoUtilities.getMetaFlagsContainsStatement(TskData.TSK_FS_META_FLAG_ENUM.ALLOC), - "Unable to get counts of unallocated files for datasource, providing empty results"); - } - - /** - * Get count of unallocated files in a data source. - * - * @param currentDataSource The data source. - * - * @return The count. - */ - public static Long getCountOfUnallocatedFiles(DataSource currentDataSource) { - return DataSourceInfoUtilities.getCountOfRegularFiles(currentDataSource, - DataSourceInfoUtilities.getMetaFlagsContainsStatement(TskData.TSK_FS_META_FLAG_ENUM.UNALLOC) - + " AND type<>" + TskData.TSK_DB_FILES_TYPE_ENUM.SLACK.getFileType(), - "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. - */ - public static Long getCountOfDirectories(DataSource currentDataSource) { - return DataSourceInfoUtilities.getCountOfTskFiles(currentDataSource, - "meta_type=" + TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_DIR.getValue() - + " AND type<>" + TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType(), - "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. - */ - public static Long getCountOfSlackFiles(DataSource currentDataSource) { - return DataSourceInfoUtilities.getCountOfRegularFiles(currentDataSource, - DataSourceInfoUtilities.getMetaFlagsContainsStatement(TskData.TSK_FS_META_FLAG_ENUM.UNALLOC) - + " AND type=" + TskData.TSK_DB_FILES_TYPE_ENUM.SLACK.getFileType(), - "Unable to get count of slack files for datasources, providing empty results"); - } - - /** - * 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. - */ - public static Map 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"; - - String errorMessage = "Unable to get artifact type counts; returning null."; - return DataSourceInfoUtilities.getBaseQueryResult(query, - getStringLongResultSetHandler(nameParam, valueParam), - 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> getStringLongResultSetHandler(String keyParam, String valueParam) { - return (resultSet) -> { - LinkedHashMap 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; - }; - } - - private DataSourceCountsSummary() { - } -} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceDetailsSummary.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceDetailsSummary.java deleted file mode 100644 index 3dd50131e5..0000000000 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceDetailsSummary.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2020 Basis Technology Corp. - * Contact: carrier sleuthkit org - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sleuthkit.autopsy.datasourcesummary.datamodel; - -import java.sql.SQLException; -import java.util.logging.Level; -import org.sleuthkit.autopsy.coreutils.Logger; -import static org.sleuthkit.autopsy.datasourcesummary.datamodel.DataSourceInfoUtilities.getBaseQueryResult; -import org.sleuthkit.datamodel.BlackboardArtifact; -import org.sleuthkit.datamodel.BlackboardAttribute; -import org.sleuthkit.datamodel.DataSource; -import org.sleuthkit.datamodel.TskData; - -/** - * Provides methods to query for data source overview details. - */ -public class DataSourceDetailsSummary { - - private static final Logger logger = Logger.getLogger(DataSourceDetailsSummary.class.getName()); - - /** - * 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. - */ - public 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 " + DataSourceInfoUtilities.getMetaFlagsContainsStatement(TskData.TSK_FS_META_FLAG_ENUM.UNALLOC) - + " AND type<>" + TskData.TSK_DB_FILES_TYPE_ENUM.SLACK.getFileType() - + " AND type<>" + TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType() - + " AND dir_type<>" + TskData.TSK_FS_NAME_TYPE_ENUM.VIRT_DIR.getValue() - + " AND name<>''" - + " AND data_source_obj_id=" + currentDataSource.getId(); - - DataSourceInfoUtilities.ResultSetHandler 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 DataSourceInfoUtilities.getBaseQueryResult(query, handler, errorMessage); - } - - /** - * 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. - */ - public 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. - */ - public 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()); - } - - /** - * 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) { - DataSourceInfoUtilities.ResultSetHandler 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); - } - - private DataSourceDetailsSummary() { - } -} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceInfoUtilities.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceInfoUtilities.java deleted file mode 100644 index b72bc14330..0000000000 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceInfoUtilities.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2019 - 2020 Basis Technology Corp. - * Contact: carrier sleuthkit org - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sleuthkit.autopsy.datasourcesummary.datamodel; - -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.logging.Level; -import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.datamodel.SleuthkitCase; -import org.sleuthkit.datamodel.TskCoreException; -import org.apache.commons.lang.StringUtils; -import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; -import org.sleuthkit.datamodel.TskData; -import org.sleuthkit.datamodel.DataSource; -import org.sleuthkit.datamodel.TskData.TSK_FS_META_FLAG_ENUM; -import org.sleuthkit.datamodel.TskData.TSK_FS_META_TYPE_ENUM; - -/** - * Utilities for getting information about a data source or all data sources - * from the case database. - */ -final class DataSourceInfoUtilities { - - private static final Logger logger = Logger.getLogger(DataSourceInfoUtilities.class.getName()); - - /** - * Gets a count of tsk_files for a particular datasource where dir_type 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. - */ - static Long getCountOfTskFiles(DataSource currentDataSource, String additionalWhere, String onError) { - if (currentDataSource != null) { - try { - SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase(); - return skCase.countFilesWhere( - "data_source_obj_id=" + currentDataSource.getId() - + " AND dir_type<>" + TskData.TSK_FS_NAME_TYPE_ENUM.VIRT_DIR.getValue() - + " AND name<>''" - + (StringUtils.isBlank(additionalWhere) ? "" : (" 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; - } - - /** - * Gets a count of regular files for a particular datasource where the - * dir_type and type are 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. - */ - static Long getCountOfRegularFiles(DataSource currentDataSource, String additionalWhere, String onError) { - String whereClause = "meta_type=" + TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_REG.getValue() - + " AND type<>" + TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType(); - - if (StringUtils.isNotBlank(additionalWhere)) { - whereClause += " AND " + additionalWhere; - } - - return getCountOfTskFiles(currentDataSource, whereClause, onError); - } - - /** - * An interface for handling a result set and returning a value. - */ - interface ResultSetHandler { - - 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. - */ - static T getBaseQueryResult(String query, ResultSetHandler 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; - } - - /** - * Creates sql where clause that does a bitwise check to see if flag is - * present. - * - * @param flag The flag for which to check. - * - * @return The clause. - */ - static String getMetaFlagsContainsStatement(TSK_FS_META_FLAG_ENUM flag) { - return "meta_flags & " + flag.getValue() + " > 0"; - } - - /** - * Empty private constructor - */ - private DataSourceInfoUtilities() { - } -} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceMimeTypeSummary.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceMimeTypeSummary.java deleted file mode 100644 index add28c0364..0000000000 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceMimeTypeSummary.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2020 Basis Technology Corp. - * Contact: carrier sleuthkit org - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sleuthkit.autopsy.datasourcesummary.datamodel; - -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; -import org.sleuthkit.datamodel.DataSource; - -/** - * Provides methods to query for datasource files by mime type. - */ -public class DataSourceMimeTypeSummary { - - /** - * Get the number of files in the case database for the current data source - * which have the specified mimetypes. - * - * @param currentDataSource the data source which we are finding a file - * count - * - * @param setOfMimeTypes the set of mime types which we are finding the - * number of occurences of - * - * @return a Long value which represents the number of occurrences of the - * specified mime types in the current case for the specified data - * source, null if no count was retrieved - */ - public static Long getCountOfFilesForMimeTypes(DataSource currentDataSource, Set setOfMimeTypes) { - return DataSourceInfoUtilities.getCountOfRegularFiles(currentDataSource, - "mime_type IN " + getSqlSet(setOfMimeTypes), - "Unable to get count of files for specified mime types"); - } - - /** - * Get the number of files in the case database for the current data source - * which do not have the specified mimetypes. - * - * @param currentDataSource the data source which we are finding a file - * count - * - * @param setOfMimeTypes the set of mime types that should be excluded. - * - * @return a Long value which represents the number of files that do not - * have the specific mime type, but do have a mime type. - */ - public static Long getCountOfFilesNotInMimeTypes(DataSource currentDataSource, Set setOfMimeTypes) { - return DataSourceInfoUtilities.getCountOfRegularFiles(currentDataSource, - "mime_type NOT IN " + getSqlSet(setOfMimeTypes) - + " AND mime_type IS NOT NULL AND mime_type <> '' ", - "Unable to get count of files without specified mime types"); - } - - /** - * Gets the number of files in the data source with no assigned mime type. - * - * @param currentDataSource The data source. - * - * @return The number of files with no mime type or null if there is an - * issue searching the data source. - * - */ - public static Long getCountOfFilesWithNoMimeType(DataSource currentDataSource) { - return DataSourceInfoUtilities.getCountOfRegularFiles(currentDataSource, - "(mime_type IS NULL OR mime_type = '') ", - "Unable to get count of files without a mime type"); - } - - /** - * Derives a sql set string (i.e. "('val1', 'val2', 'val3')"). A naive - * attempt is made to sanitize the strings by removing single quotes from - * values. - * - * @param setValues The values that should be present in the set. Single - * quotes are removed. - * - * @return The sql set string. - */ - private static String getSqlSet(Set setValues) { - List quotedValues = setValues - .stream() - .map(str -> String.format("'%s'", str.replace("'", ""))) - .collect(Collectors.toList()); - - String commaSeparatedQuoted = String.join(", ", quotedValues); - return String.format("(%s) ", commaSeparatedQuoted); - } - - private DataSourceMimeTypeSummary() { - } -} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceTopProgramsSummary.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceTopProgramsSummary.java deleted file mode 100644 index 7c6927cd0d..0000000000 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceTopProgramsSummary.java +++ /dev/null @@ -1,295 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2020 Basis Technology Corp. - * Contact: carrier sleuthkit org - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sleuthkit.autopsy.datasourcesummary.datamodel; - -import java.io.File; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Date; -import java.util.List; -import java.util.function.Function; -import java.util.logging.Level; -import java.util.stream.Collectors; -import org.apache.commons.lang.StringUtils; -import org.sleuthkit.autopsy.coreutils.Logger; -import static org.sleuthkit.autopsy.datasourcesummary.datamodel.DataSourceInfoUtilities.getBaseQueryResult; -import org.sleuthkit.datamodel.BlackboardArtifact; -import org.sleuthkit.datamodel.BlackboardAttribute; -import org.sleuthkit.datamodel.DataSource; - -/** - * Provides information to populate Top Programs Summary queries. - */ -public class DataSourceTopProgramsSummary { - - private static final Logger logger = Logger.getLogger(DataSourceTopProgramsSummary.class.getName()); - - /** - * 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, BlackboardAttribute.ATTRIBUTE_TYPE attrType, String keyName, String bbaName) { - String queryName = keyName + QUERY_SUFFIX; - String innerQueryName = "inner_attribute_" + queryName; - - return "\n" + joinType + " JOIN (\n" - + " SELECT \n" - + " " + innerQueryName + ".artifact_id,\n" - + " " + innerQueryName + "." + attributeColumn + " AS " + keyName + "\n" - + " FROM blackboard_attributes " + innerQueryName + "\n" - + " WHERE " + innerQueryName + ".attribute_type_id = " + attrType.getTypeID() + " -- " + attrType.name() + "\n" - + ") " + queryName + " ON " + queryName + ".artifact_id = " + bbaName + ".artifact_id\n"; - } - - /** - * Given a column key, creates the full name for the column key. - * - * @param key The column key. - * - * @return The full identifier for the column key. - */ - private static String getFullKey(String key) { - return key + QUERY_SUFFIX + "." + key; - } - - /** - * Constructs a SQL 'where' statement from a list of clauses and puts - * parenthesis around each clause. - * - * @param clauses The clauses - * - * @return The generated 'where' statement. - */ - private static String getWhereString(List clauses) { - if (clauses.isEmpty()) { - return ""; - } - - List 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 - */ - public static List 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, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, nameParam, bbaQuery) - + getAttributeJoin(JoinType.LEFT, AttributeColumn.value_text, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH, pathParam, bbaQuery) - + getAttributeJoin(JoinType.LEFT, AttributeColumn.value_int32, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COUNT, runCountParam, bbaQuery) - + getAttributeJoin(JoinType.LEFT, AttributeColumn.value_int64, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, lastRunParam, bbaQuery) - + getWhereString(Arrays.asList( - bbaQuery + ".artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_PROG_RUN.getTypeID(), - bbaQuery + ".data_source_obj_id = " + dataSource.getId(), - // exclude ntosBootIdentifier from results - getLikeClause(getFullKey(nameParam), ntosBootIdentifier, false), - // exclude windows directory items from results - getFullKey(pathParam) + " IS NULL OR " + getLikeClause(getFullKey(pathParam), windowsDir, false) - )) - + "GROUP BY " + getFullKey(nameParam) + ", " + getFullKey(pathParam) + "\n" - + "ORDER BY \n" - + " MAX(" + getFullKey(runCountParam) + ") DESC,\n" - + " MAX(" + getFullKey(lastRunParam) + ") DESC,\n" - + " " + getFullKey(nameParam) + " ASC"; - - final String errorMessage = "Unable to get top program results; returning null."; - - DataSourceInfoUtilities.ResultSetHandler> handler = (resultSet) -> { - List 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; - }; - - 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, 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. - */ - public static String getShortFolderName(String strPath, String applicationName) { - if (strPath == null) { - return ""; - } - - List 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, String> matchEntry : SHORT_FOLDER_MATCHERS) { - String result = matchEntry.apply(pathEls); - if (StringUtils.isNotBlank(result)) { - return result; - } - } - - return ""; - } - - private DataSourceTopProgramsSummary() { - } -} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TopProgramsResult.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TopProgramsResult.java deleted file mode 100644 index 7f682c3efb..0000000000 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TopProgramsResult.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2020 Basis Technology Corp. - * Contact: carrier sleuthkit org - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sleuthkit.autopsy.datasourcesummary.datamodel; - -import java.util.Date; - -/** - * Describes a result of a program run on a datasource. - */ -public class TopProgramsResult { - - private final String programName; - private final String programPath; - private final Long runTimes; - private final Date lastRun; - - /** - * Main constructor. - * - * @param programName The name of the program. - * @param programPath The path of the program. - * @param runTimes The number of runs. - */ - TopProgramsResult(String programName, String programPath, Long runTimes, Date lastRun) { - this.programName = programName; - this.programPath = programPath; - this.runTimes = runTimes; - this.lastRun = lastRun; - } - - /** - * @return The name of the program - */ - public String getProgramName() { - return programName; - } - - /** - * @return The path of the program. - */ - public String getProgramPath() { - return programPath; - } - - /** - * @return The number of run times or null if not present. - */ - public Long getRunTimes() { - return runTimes; - } - - /** - * @return The last time the program was run or null if not present. - */ - public Date getLastRun() { - return lastRun; - } -} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryCountsPanel.form b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryCountsPanel.form deleted file mode 100644 index c9c1930787..0000000000 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryCountsPanel.form +++ /dev/null @@ -1,222 +0,0 @@ - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/FileTypePieChart.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/FileTypePieChart.java deleted file mode 100644 index 691234cd48..0000000000 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/FileTypePieChart.java +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2020 Basis Technology Corp. - * Contact: carrier sleuthkit org - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sleuthkit.autopsy.datasourcesummary.ui; - -import java.awt.BorderLayout; -import java.awt.Font; -import javax.swing.JPanel; -import org.sleuthkit.datamodel.DataSource; - -import org.jfree.chart.ChartFactory; -import org.jfree.chart.ChartPanel; -import org.jfree.chart.JFreeChart; -import org.jfree.chart.labels.PieSectionLabelGenerator; -import org.jfree.chart.labels.StandardPieSectionLabelGenerator; -import org.jfree.chart.plot.PiePlot; -import org.jfree.data.general.DefaultPieDataset; - -import java.text.DecimalFormat; -import java.util.Arrays; -import java.util.Set; -import java.util.stream.Collectors; -import javax.swing.JLabel; -import org.openide.util.NbBundle.Messages; -import org.sleuthkit.autopsy.coreutils.FileTypeUtils; -import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataSourceMimeTypeSummary; -import static org.sleuthkit.autopsy.coreutils.FileTypeUtils.FileTypeCategory; - -/** - * A Pie Chart that shows file mime types in a data source. - */ -class FileTypePieChart extends JPanel { - - private static final long serialVersionUID = 1L; - - private static final Font DEFAULT_FONT = new JLabel().getFont(); - private static final Font DEFAULT_HEADER_FONT = new Font(DEFAULT_FONT.getName(), DEFAULT_FONT.getStyle(), (int) (DEFAULT_FONT.getSize() * 1.5)); - - private final DefaultPieDataset dataset = new DefaultPieDataset(); - private DataSource dataSource; - - // used for determining mime types that fall in the 'other' category - private static final Set ALL_CATEGORY_MIME_TYPES = Arrays.asList( - FileTypeCategory.IMAGE, - FileTypeCategory.VIDEO, - FileTypeCategory.AUDIO, - FileTypeCategory.DOCUMENTS, - FileTypeCategory.EXECUTABLE) - .stream() - .flatMap((cat) -> cat.getMediaTypes().stream()) - .collect(Collectors.toSet()); - - /** - * Default constructor for the pie chart. - */ - @Messages({ - "DataSourceSummaryCountsPanel.byMimeTypeLabel.text=Files by MIME Type", - "DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.audio.row=Audio", - "DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.documents.row=Documents", - "DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.executables.row=Executables", - "DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.images.row=Images", - "DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.videos.row=Videos", - "DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_other_label=Other", - "DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_notAnalyzed_label=Not Analyzed" - }) - FileTypePieChart() { - // Create chart - JFreeChart chart = ChartFactory.createPieChart( - Bundle.DataSourceSummaryCountsPanel_byMimeTypeLabel_text(), - dataset, - true, - true, - false); - - chart.setBackgroundPaint(null); - chart.getLegend().setItemFont(DEFAULT_FONT); - chart.getTitle().setFont(DEFAULT_HEADER_FONT); - - PiePlot plot = ((PiePlot) chart.getPlot()); - - //Format Label - PieSectionLabelGenerator labelGenerator = new StandardPieSectionLabelGenerator( - "{0}: {1} ({2})", new DecimalFormat("0"), new DecimalFormat("0.0%")); - - plot.setLabelGenerator(labelGenerator); - plot.setLabelFont(DEFAULT_FONT); - - plot.setBackgroundPaint(null); - plot.setOutlinePaint(null); - - // Create Panel - ChartPanel panel = new ChartPanel(chart); - this.setLayout(new BorderLayout()); - this.add(panel, BorderLayout.CENTER); - } - - /** - * The datasource currently used as the model with this pie chart. - * - * @return The datasource currently being used as the model in this pie - * chart. - */ - DataSource getDataSource() { - return dataSource; - } - - /** - * Sets datasource to visualize in the pie chart. - * - * @param dataSource The datasource to use in this pie chart. - */ - void setDataSource(DataSource dataSource) { - this.dataSource = dataSource; - this.dataset.clear(); - - if (dataSource != null) { - addIfPresent(Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_images_row(), - this.dataSource, FileTypeCategory.IMAGE); - addIfPresent(Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_videos_row(), - this.dataSource, FileTypeCategory.VIDEO); - addIfPresent(Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_audio_row(), - this.dataSource, FileTypeCategory.AUDIO); - addIfPresent(Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_documents_row(), - this.dataSource, FileTypeCategory.DOCUMENTS); - addIfPresent(Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_executables_row(), - this.dataSource, FileTypeCategory.EXECUTABLE); - addIfPresent(Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_other_label(), - DataSourceMimeTypeSummary.getCountOfFilesNotInMimeTypes(this.dataSource, ALL_CATEGORY_MIME_TYPES)); - addIfPresent(Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_notAnalyzed_label(), - DataSourceMimeTypeSummary.getCountOfFilesWithNoMimeType(this.dataSource)); - } - } - - /** - * Adds count for file type category if there is a value. Uses fields - * 'dataSource' and 'dataset'. - * - * @param label The label for this pie slice. - * @param dataSource The data source. - * @param category The category for the pie slice. - */ - private void addIfPresent(String label, DataSource dataSource, FileTypeUtils.FileTypeCategory category) { - if (dataSource == null) { - return; - } - - Long count = getCount(dataSource, category); - addIfPresent(label, count); - } - - /** - * Adds count for a a label if the count is non-null and greater than 0. - * - * @param label The label. - * @param count The count. - */ - private void addIfPresent(String label, Long count) { - if (count != null && count > 0) { - this.dataset.setValue(label, count); - } - } - - /** - * 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 DataSourceMimeTypeSummary.getCountOfFilesForMimeTypes(dataSource, category.getMediaTypes()); - } -} diff --git a/Core/src/org/sleuthkit/autopsy/resultviewers/summary/DataSourceSummaryResultViewer.java b/Core/src/org/sleuthkit/autopsy/resultviewers/summary/DataSourceSummaryResultViewer.java index 4cc90e9843..1b1b3cb127 100644 --- a/Core/src/org/sleuthkit/autopsy/resultviewers/summary/DataSourceSummaryResultViewer.java +++ b/Core/src/org/sleuthkit/autopsy/resultviewers/summary/DataSourceSummaryResultViewer.java @@ -26,7 +26,7 @@ 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.datasourcesummary.ui.DataSourceSummaryTabbedPane; +import org.sleuthkit.autopsy.casemodule.datasourcesummary.DataSourceSummaryTabbedPane; import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer; import org.sleuthkit.autopsy.corecomponents.AbstractDataResultViewer; import org.sleuthkit.datamodel.DataSource;