From e948b14c86f35e7ba04d2a77192aaace7a692ecd Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Fri, 6 Aug 2021 17:31:44 -0400 Subject: [PATCH] Moved file type export functionality --- .../contentutils/DataSourceInfoUtilities.java | 51 ++- .../contentutils/RecentFilesSummary.java | 3 - .../autopsy/contentutils/TypesSummary.java | 182 ++++++++++ ...ummary.java => MimeTypeSummaryGetter.java} | 68 ++-- ...esSummary.java => TypesSummaryGetter.java} | 69 ++-- .../datasourcesummary/ui/TypesPanel.java | 135 ++------ .../Bundle.properties-MERGED | 20 ++ .../ExcelExportAction.java | 11 +- .../datasourcesummaryexport/ExportTypes.java | 312 ++++++++++++++++++ 9 files changed, 653 insertions(+), 198 deletions(-) create mode 100755 Core/src/org/sleuthkit/autopsy/contentutils/TypesSummary.java rename Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/{MimeTypeSummary.java => MimeTypeSummaryGetter.java} (77%) rename Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/{TypesSummary.java => TypesSummaryGetter.java} (72%) create mode 100755 Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExportTypes.java diff --git a/Core/src/org/sleuthkit/autopsy/contentutils/DataSourceInfoUtilities.java b/Core/src/org/sleuthkit/autopsy/contentutils/DataSourceInfoUtilities.java index b8bc2a54b6..2b69808eba 100644 --- a/Core/src/org/sleuthkit/autopsy/contentutils/DataSourceInfoUtilities.java +++ b/Core/src/org/sleuthkit/autopsy/contentutils/DataSourceInfoUtilities.java @@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.contentutils; import java.sql.ResultSet; import java.sql.SQLException; +import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Comparator; import java.util.Date; @@ -45,10 +46,12 @@ import org.sleuthkit.datamodel.TskData.TSK_FS_META_TYPE_ENUM; */ public final class DataSourceInfoUtilities { + public static final String COMMA_FORMAT_STR = "#,###"; + public static final DecimalFormat COMMA_FORMATTER = new DecimalFormat(COMMA_FORMAT_STR); + /** * Gets a count of tsk_files for a particular datasource. * - * @param skCase The current SleuthkitCase. * @param currentDataSource The datasource. * @param additionalWhere Additional sql where clauses. * @@ -56,11 +59,12 @@ public final class DataSourceInfoUtilities { * * @throws TskCoreException * @throws SQLException + * @throws NoCurrentCaseException */ - public static Long getCountOfTskFiles(SleuthkitCase skCase, DataSource currentDataSource, String additionalWhere) - throws TskCoreException, SQLException { + public static Long getCountOfTskFiles(DataSource currentDataSource, String additionalWhere) + throws TskCoreException, SQLException, NoCurrentCaseException { if (currentDataSource != null) { - return skCase.countFilesWhere( + return Case.getCurrentCaseThrows().getSleuthkitCase().countFilesWhere( "data_source_obj_id=" + currentDataSource.getId() + (StringUtils.isBlank(additionalWhere) ? "" : (" AND " + additionalWhere))); } @@ -70,7 +74,6 @@ public final class DataSourceInfoUtilities { /** * Gets a count of regular files for a particular datasource. * - * @param skCase The current SleuthkitCase. * @param currentDataSource The datasource. * @param additionalWhere Additional sql where clauses. * @@ -78,22 +81,22 @@ public final class DataSourceInfoUtilities { * * @throws TskCoreException * @throws SQLException + * @throws NoCurrentCaseException */ - public static Long getCountOfRegularFiles(SleuthkitCase skCase, DataSource currentDataSource, String additionalWhere) - throws TskCoreException, SQLException { + public static Long getCountOfRegularFiles(DataSource currentDataSource, String additionalWhere) + throws TskCoreException, SQLException, NoCurrentCaseException { String whereClause = "meta_type=" + TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_REG.getValue(); if (StringUtils.isNotBlank(additionalWhere)) { whereClause += " AND " + additionalWhere; } - return getCountOfTskFiles(skCase, currentDataSource, whereClause); + return getCountOfTskFiles(currentDataSource, whereClause); } /** * Gets a count of regular non-slack files for a particular datasource. * - * @param skCase The current SleuthkitCase. * @param currentDataSource The datasource. * @param additionalWhere Additional sql where clauses. * @@ -101,9 +104,10 @@ public final class DataSourceInfoUtilities { * * @throws TskCoreException * @throws SQLException + * @throws NoCurrentCaseException */ - public static Long getCountOfRegNonSlackFiles(SleuthkitCase skCase, DataSource currentDataSource, String additionalWhere) - throws TskCoreException, SQLException { + public static Long getCountOfRegNonSlackFiles(DataSource currentDataSource, String additionalWhere) + throws TskCoreException, SQLException, NoCurrentCaseException { String whereClause = "meta_type=" + TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_REG.getValue() + " AND type<>" + TSK_DB_FILES_TYPE_ENUM.SLACK.getFileType(); @@ -111,7 +115,7 @@ public final class DataSourceInfoUtilities { whereClause += " AND " + additionalWhere; } - return getCountOfTskFiles(skCase, currentDataSource, whereClause); + return getCountOfTskFiles(currentDataSource, whereClause); } /** @@ -429,4 +433,27 @@ public final class DataSourceInfoUtilities { Long longVal = getLongOrNull(artifact, attributeType); return (longVal == null || longVal == 0) ? null : new Date(longVal * 1000); } + + /** + * Returns the long value or zero if longVal is null. + * + * @param longVal The long value. + * + * @return The long value or 0 if provided value is null. + */ + public static long getLongOrZero(Long longVal) { + return longVal == null ? 0 : longVal; + } + + /** + * Returns string value of long with comma separators. If null returns a + * string of '0'. + * + * @param longVal The long value. + * + * @return The string value of the long. + */ + public static String getStringOrZero(Long longVal) { + return longVal == null ? "0" : COMMA_FORMATTER.format(longVal); + } } diff --git a/Core/src/org/sleuthkit/autopsy/contentutils/RecentFilesSummary.java b/Core/src/org/sleuthkit/autopsy/contentutils/RecentFilesSummary.java index f298602351..fd2d8341e7 100755 --- a/Core/src/org/sleuthkit/autopsy/contentutils/RecentFilesSummary.java +++ b/Core/src/org/sleuthkit/autopsy/contentutils/RecentFilesSummary.java @@ -22,14 +22,11 @@ import java.nio.file.Paths; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; -import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Objects; -import java.util.Set; import java.util.stream.Collectors; import org.apache.commons.lang.StringUtils; import org.sleuthkit.autopsy.casemodule.Case; diff --git a/Core/src/org/sleuthkit/autopsy/contentutils/TypesSummary.java b/Core/src/org/sleuthkit/autopsy/contentutils/TypesSummary.java new file mode 100755 index 0000000000..4c6944de37 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/contentutils/TypesSummary.java @@ -0,0 +1,182 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 - 2021 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.contentutils; + +import java.awt.Color; +import java.sql.SQLException; +import java.util.Set; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.coreutils.FileTypeUtils; +import org.sleuthkit.datamodel.DataSource; +import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.TskData; + +/** + * Helper class for getting summary information on the known files present in the + * specified DataSource.. + */ +public class TypesSummary { + + private TypesSummary() { + } + + /** + * Get count of regular files (not directories) in a data source. + * + * @param currentDataSource The data source. + * + * @return The count. + * + * @throws TskCoreException + * @throws SQLException + * @throws NoCurrentCaseException + */ + public static Long getCountOfFiles(DataSource currentDataSource) + throws TskCoreException, SQLException, NoCurrentCaseException { + return DataSourceInfoUtilities.getCountOfRegularFiles(currentDataSource, null); + } + + /** + * Get count of allocated files in a data source. + * + * @param currentDataSource The data source. + * + * @return The count. + * + * @throws TskCoreException + * @throws SQLException + * @throws NoCurrentCaseException + */ + public static Long getCountOfAllocatedFiles(DataSource currentDataSource) + throws TskCoreException, SQLException, NoCurrentCaseException { + + return DataSourceInfoUtilities.getCountOfRegNonSlackFiles(currentDataSource, + DataSourceInfoUtilities.getMetaFlagsContainsStatement(TskData.TSK_FS_META_FLAG_ENUM.ALLOC)); + } + + /** + * Get count of unallocated files in a data source. + * + * @param currentDataSource The data source. + * + * @return The count. + * + * @throws TskCoreException + * @throws SQLException + * @throws NoCurrentCaseException + */ + public static Long getCountOfUnallocatedFiles(DataSource currentDataSource) + throws NoCurrentCaseException, TskCoreException, SQLException { + + return DataSourceInfoUtilities.getCountOfRegNonSlackFiles(currentDataSource, + DataSourceInfoUtilities.getMetaFlagsContainsStatement(TskData.TSK_FS_META_FLAG_ENUM.UNALLOC)); + } + + /** + * Get count of directories in a data source. + * + * @param currentDataSource The data source. + * + * @return The count. + * + * @throws TskCoreException + * @throws SQLException + * @throws NoCurrentCaseException + */ + public static Long getCountOfDirectories(DataSource currentDataSource) + throws NoCurrentCaseException, TskCoreException, SQLException { + + 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()); + } + + /** + * Get count of slack files in a data source. + * + * @param currentDataSource The data source. + * + * @return The count. + * + * @throws TskCoreException + * @throws SQLException + * @throws NoCurrentCaseException + */ + public static Long getCountOfSlackFiles(DataSource currentDataSource) + throws NoCurrentCaseException, TskCoreException, SQLException { + + return DataSourceInfoUtilities.getCountOfRegularFiles(currentDataSource, + "type=" + TskData.TSK_DB_FILES_TYPE_ENUM.SLACK.getFileType()); + } + + /** + * Information concerning a particular file type category. + */ + public static class FileTypeCategoryData { + + private final String label; + private final Set mimeTypes; + private final Color color; + + /** + * Main constructor. + * + * @param label The label for this slice. + * @param mimeTypes The mime types associated with this slice. + * @param color The color associated with this slice. + */ + public FileTypeCategoryData(String label, Set mimeTypes, Color color) { + this.label = label; + this.mimeTypes = mimeTypes; + this.color = color; + } + + /** + * Constructor that accepts FileTypeCategory. + * + * @param label The label for this slice. + * @param mimeTypes The mime types associated with this slice. + * @param color The color associated with this slice. + */ + public FileTypeCategoryData(String label, FileTypeUtils.FileTypeCategory fileCategory, Color color) { + this(label, fileCategory.getMediaTypes(), color); + } + + /** + * @return The label for this category. + */ + public String getLabel() { + return label; + } + + /** + * @return The mime types associated with this category. + */ + public Set getMimeTypes() { + return mimeTypes; + } + + /** + * @return The color associated with this category. + */ + public Color getColor() { + return color; + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/MimeTypeSummary.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/MimeTypeSummaryGetter.java similarity index 77% rename from Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/MimeTypeSummary.java rename to Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/MimeTypeSummaryGetter.java index 1b8ac3fc1c..a0b29e8a7f 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/MimeTypeSummary.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/MimeTypeSummaryGetter.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2020 Basis Technology Corp. + * Copyright 2020-2021 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,10 +21,12 @@ package org.sleuthkit.autopsy.datasourcesummary.datamodel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultUpdateGovernor; import java.sql.SQLException; import java.util.Arrays; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException; import org.sleuthkit.autopsy.contentutils.DataSourceInfoUtilities; import org.sleuthkit.autopsy.ingest.IngestManager; @@ -34,11 +36,10 @@ import org.sleuthkit.datamodel.DataSource; import org.sleuthkit.datamodel.TskCoreException; /** - * Provides methods to query for datasource files by mime type. + * Wrapper class for converting org.sleuthkit.autopsy.contentutils.TypesSummary + * functionality into a DefaultArtifactUpdateGovernor used by TypesPanel tab. */ -public class MimeTypeSummary implements DefaultUpdateGovernor { - - private final SleuthkitCaseProvider provider; +public class MimeTypeSummaryGetter implements DefaultUpdateGovernor { private static final Set INGEST_JOB_EVENTS = new HashSet<>( Arrays.asList(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED)); @@ -46,17 +47,7 @@ public class MimeTypeSummary implements DefaultUpdateGovernor { /** * Main constructor. */ - public MimeTypeSummary() { - this(SleuthkitCaseProvider.DEFAULT); - } - - /** - * Main constructor. - * - * @param provider The means of obtaining a sleuthkit case. - */ - public MimeTypeSummary(SleuthkitCaseProvider provider) { - this.provider = provider; + public MimeTypeSummaryGetter() { } @Override @@ -76,7 +67,7 @@ public class MimeTypeSummary implements DefaultUpdateGovernor { @Override public Set getIngestJobEventUpdates() { - return INGEST_JOB_EVENTS; + return Collections.unmodifiableSet(INGEST_JOB_EVENTS); } /** @@ -99,12 +90,11 @@ public class MimeTypeSummary implements DefaultUpdateGovernor { */ public Long getCountOfFilesForMimeTypes(DataSource currentDataSource, Set setOfMimeTypes) throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException { - - return DataSourceInfoUtilities.getCountOfRegNonSlackFiles( - provider.get(), - currentDataSource, - "mime_type IN " + getSqlSet(setOfMimeTypes) - ); + try { + return DataSourceInfoUtilities.getCountOfRegNonSlackFiles(currentDataSource, "mime_type IN " + getSqlSet(setOfMimeTypes)); + } catch (NoCurrentCaseException ex) { + throw new SleuthkitCaseProviderException("No currently open case.", ex); + } } /** @@ -125,13 +115,13 @@ public class MimeTypeSummary implements DefaultUpdateGovernor { */ public Long getCountOfFilesNotInMimeTypes(DataSource currentDataSource, Set setOfMimeTypes) throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException { - - return DataSourceInfoUtilities.getCountOfRegNonSlackFiles( - provider.get(), - currentDataSource, - "mime_type NOT IN " + getSqlSet(setOfMimeTypes) - + " AND mime_type IS NOT NULL AND mime_type <> '' " - ); + try { + return DataSourceInfoUtilities.getCountOfRegNonSlackFiles(currentDataSource, + "mime_type NOT IN " + getSqlSet(setOfMimeTypes) + + " AND mime_type IS NOT NULL AND mime_type <> '' "); + } catch (NoCurrentCaseException ex) { + throw new SleuthkitCaseProviderException("No currently open case.", ex); + } } /** @@ -147,8 +137,11 @@ public class MimeTypeSummary implements DefaultUpdateGovernor { */ public Long getCountOfAllRegularFiles(DataSource dataSource) throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException { - - return DataSourceInfoUtilities.getCountOfRegNonSlackFiles(provider.get(), dataSource, null); + try { + return DataSourceInfoUtilities.getCountOfRegNonSlackFiles(dataSource, null); + } catch (NoCurrentCaseException ex) { + throw new SleuthkitCaseProviderException("No currently open case.", ex); + } } /** @@ -165,12 +158,11 @@ public class MimeTypeSummary implements DefaultUpdateGovernor { */ public Long getCountOfFilesWithNoMimeType(DataSource currentDataSource) throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException { - - return DataSourceInfoUtilities.getCountOfRegNonSlackFiles( - provider.get(), - currentDataSource, - "(mime_type IS NULL OR mime_type = '') " - ); + try { + return DataSourceInfoUtilities.getCountOfRegNonSlackFiles(currentDataSource, "(mime_type IS NULL OR mime_type = '') "); + } catch (NoCurrentCaseException ex) { + throw new SleuthkitCaseProviderException("No currently open case.", ex); + } } /** diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TypesSummary.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TypesSummaryGetter.java similarity index 72% rename from Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TypesSummary.java rename to Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TypesSummaryGetter.java index 3b21654c14..7e0129382d 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TypesSummary.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TypesSummaryGetter.java @@ -21,41 +21,31 @@ package org.sleuthkit.autopsy.datasourcesummary.datamodel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultUpdateGovernor; import java.sql.SQLException; import java.util.Arrays; +import java.util.Collections; import java.util.HashSet; import java.util.Set; -import org.sleuthkit.autopsy.contentutils.DataSourceInfoUtilities; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.contentutils.TypesSummary; import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException; import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.ModuleContentEvent; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.DataSource; import org.sleuthkit.datamodel.TskCoreException; -import org.sleuthkit.datamodel.TskData; /** - * Provides information for the DataSourceSummaryCountsPanel. + * Wrapper class for converting org.sleuthkit.autopsy.contentutils.TypesSummary functionality into a + * DefaultArtifactUpdateGovernor used by DataSourceSummaryCountsPanel. */ -public class TypesSummary implements DefaultUpdateGovernor { +public class TypesSummaryGetter implements DefaultUpdateGovernor { private static final Set INGEST_JOB_EVENTS = new HashSet<>( Arrays.asList(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED)); - private final SleuthkitCaseProvider provider; - /** * Main constructor. */ - public TypesSummary() { - this(SleuthkitCaseProvider.DEFAULT); - } - - /** - * Main constructor. - * - * @param provider The means of obtaining a sleuthkit case. - */ - public TypesSummary(SleuthkitCaseProvider provider) { - this.provider = provider; + public TypesSummaryGetter() { } @Override @@ -75,7 +65,7 @@ public class TypesSummary implements DefaultUpdateGovernor { @Override public Set getIngestJobEventUpdates() { - return INGEST_JOB_EVENTS; + return Collections.unmodifiableSet(INGEST_JOB_EVENTS); } /** @@ -91,11 +81,11 @@ public class TypesSummary implements DefaultUpdateGovernor { */ public Long getCountOfFiles(DataSource currentDataSource) throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException { - return DataSourceInfoUtilities.getCountOfRegularFiles( - provider.get(), - currentDataSource, - null - ); + try { + return TypesSummary.getCountOfFiles(currentDataSource); + } catch (NoCurrentCaseException ex) { + throw new SleuthkitCaseProviderException("No currently open case.", ex); + } } /** @@ -111,9 +101,11 @@ public class TypesSummary implements DefaultUpdateGovernor { */ public Long getCountOfAllocatedFiles(DataSource currentDataSource) throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException { - - return DataSourceInfoUtilities.getCountOfRegNonSlackFiles(provider.get(), currentDataSource, - DataSourceInfoUtilities.getMetaFlagsContainsStatement(TskData.TSK_FS_META_FLAG_ENUM.ALLOC)); + try { + return TypesSummary.getCountOfAllocatedFiles(currentDataSource); + } catch (NoCurrentCaseException ex) { + throw new SleuthkitCaseProviderException("No currently open case.", ex); + } } /** @@ -129,9 +121,11 @@ public class TypesSummary implements DefaultUpdateGovernor { */ public Long getCountOfUnallocatedFiles(DataSource currentDataSource) throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException { - - return DataSourceInfoUtilities.getCountOfRegNonSlackFiles(provider.get(), currentDataSource, - DataSourceInfoUtilities.getMetaFlagsContainsStatement(TskData.TSK_FS_META_FLAG_ENUM.UNALLOC)); + try { + return TypesSummary.getCountOfUnallocatedFiles(currentDataSource); + } catch (NoCurrentCaseException ex) { + throw new SleuthkitCaseProviderException("No currently open case.", ex); + } } /** @@ -147,10 +141,11 @@ public class TypesSummary implements DefaultUpdateGovernor { */ public Long getCountOfDirectories(DataSource currentDataSource) throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException { - - return DataSourceInfoUtilities.getCountOfTskFiles(provider.get(), 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()); + try { + return TypesSummary.getCountOfDirectories(currentDataSource); + } catch (NoCurrentCaseException ex) { + throw new SleuthkitCaseProviderException("No currently open case.", ex); + } } /** @@ -166,8 +161,10 @@ public class TypesSummary implements DefaultUpdateGovernor { */ public Long getCountOfSlackFiles(DataSource currentDataSource) throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException { - - return DataSourceInfoUtilities.getCountOfRegularFiles(provider.get(), currentDataSource, - "type=" + TskData.TSK_DB_FILES_TYPE_ENUM.SLACK.getFileType()); + try { + return TypesSummary.getCountOfSlackFiles(currentDataSource); + } catch (NoCurrentCaseException ex) { + throw new SleuthkitCaseProviderException("No currently open case.", ex); + } } } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TypesPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TypesPanel.java index e333888d0c..a5040a355e 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TypesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TypesPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2019 Basis Technology Corp. + * Copyright 2019-2021 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -23,17 +23,17 @@ import java.sql.SQLException; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.HashSet; import java.util.List; -import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.coreutils.FileTypeUtils.FileTypeCategory; -import org.sleuthkit.autopsy.datasourcesummary.datamodel.TypesSummary; +import org.sleuthkit.autopsy.contentutils.DataSourceInfoUtilities; +import org.sleuthkit.autopsy.contentutils.TypesSummary.FileTypeCategoryData; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.TypesSummaryGetter; import org.sleuthkit.autopsy.datasourcesummary.datamodel.ContainerSummaryGetter; -import org.sleuthkit.autopsy.datasourcesummary.datamodel.MimeTypeSummary; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.MimeTypeSummaryGetter; import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult.ResultType; @@ -90,7 +90,7 @@ class TypesPanel extends BaseDataSourceSummaryPanel { * @param usefulContent True if this is useful content; false if there * is 0 mime type information. */ - public TypesPieChartData(List pieSlices, boolean usefulContent) { + TypesPieChartData(List pieSlices, boolean usefulContent) { this.pieSlices = pieSlices; this.usefulContent = usefulContent; } @@ -98,78 +98,20 @@ class TypesPanel extends BaseDataSourceSummaryPanel { /** * @return The pie chart data. */ - public List getPieSlices() { + List getPieSlices() { return pieSlices; } /** * @return Whether or not the data is usefulContent. */ - public boolean isUsefulContent() { + boolean isUsefulContent() { return usefulContent; } } - /** - * Information concerning a particular category in the file types pie chart. - */ - private static class TypesPieCategory { - - private final String label; - private final Set mimeTypes; - private final Color color; - - /** - * Main constructor. - * - * @param label The label for this slice. - * @param mimeTypes The mime types associated with this slice. - * @param color The color associated with this slice. - */ - TypesPieCategory(String label, Set mimeTypes, Color color) { - this.label = label; - this.mimeTypes = mimeTypes; - this.color = color; - } - - /** - * Constructor that accepts FileTypeCategory. - * - * @param label The label for this slice. - * @param mimeTypes The mime types associated with this slice. - * @param color The color associated with this slice. - */ - TypesPieCategory(String label, FileTypeCategory fileCategory, Color color) { - this(label, fileCategory.getMediaTypes(), color); - } - - /** - * @return The label for this category. - */ - String getLabel() { - return label; - } - - /** - * @return The mime types associated with this category. - */ - Set getMimeTypes() { - return mimeTypes; - } - - /** - * @return The color associated with this category. - */ - Color getColor() { - return color; - } - } - private static final long serialVersionUID = 1L; private static final DecimalFormat INTEGER_SIZE_FORMAT = new DecimalFormat("#"); - private static final String COMMA_FORMAT_STR = "#,###"; - - private static final DecimalFormat COMMA_FORMATTER = new DecimalFormat(COMMA_FORMAT_STR); private static final Color IMAGES_COLOR = new Color(156, 39, 176); private static final Color VIDEOS_COLOR = Color.YELLOW; @@ -181,13 +123,13 @@ class TypesPanel extends BaseDataSourceSummaryPanel { private static final Color NOT_ANALYZED_COLOR = Color.WHITE; // All file type categories. - private static final List FILE_MIME_TYPE_CATEGORIES = Arrays.asList( - new TypesPieCategory(Bundle.TypesPanel_fileMimeTypesChart_images_title(), FileTypeCategory.IMAGE.getMediaTypes(), IMAGES_COLOR), - new TypesPieCategory(Bundle.TypesPanel_fileMimeTypesChart_videos_title(), FileTypeCategory.VIDEO.getMediaTypes(), VIDEOS_COLOR), - new TypesPieCategory(Bundle.TypesPanel_fileMimeTypesChart_audio_title(), FileTypeCategory.AUDIO.getMediaTypes(), AUDIO_COLOR), - new TypesPieCategory(Bundle.TypesPanel_fileMimeTypesChart_documents_title(), FileTypeCategory.DOCUMENTS.getMediaTypes(), DOCUMENTS_COLOR), - new TypesPieCategory(Bundle.TypesPanel_fileMimeTypesChart_executables_title(), FileTypeCategory.EXECUTABLE.getMediaTypes(), EXECUTABLES_COLOR), - new TypesPieCategory(Bundle.TypesPanel_fileMimeTypesChart_unknown_title(), new HashSet<>(Arrays.asList("application/octet-stream")), UNKNOWN_COLOR) + private static final List FILE_MIME_TYPE_CATEGORIES = Arrays.asList( + new FileTypeCategoryData(Bundle.TypesPanel_fileMimeTypesChart_images_title(), FileTypeCategory.IMAGE.getMediaTypes(), IMAGES_COLOR), + new FileTypeCategoryData(Bundle.TypesPanel_fileMimeTypesChart_videos_title(), FileTypeCategory.VIDEO.getMediaTypes(), VIDEOS_COLOR), + new FileTypeCategoryData(Bundle.TypesPanel_fileMimeTypesChart_audio_title(), FileTypeCategory.AUDIO.getMediaTypes(), AUDIO_COLOR), + new FileTypeCategoryData(Bundle.TypesPanel_fileMimeTypesChart_documents_title(), FileTypeCategory.DOCUMENTS.getMediaTypes(), DOCUMENTS_COLOR), + new FileTypeCategoryData(Bundle.TypesPanel_fileMimeTypesChart_executables_title(), FileTypeCategory.EXECUTABLE.getMediaTypes(), EXECUTABLES_COLOR), + new FileTypeCategoryData(Bundle.TypesPanel_fileMimeTypesChart_unknown_title(), new HashSet<>(Arrays.asList("application/octet-stream")), UNKNOWN_COLOR) ); private final DataFetcher usageFetcher; @@ -233,7 +175,7 @@ class TypesPanel extends BaseDataSourceSummaryPanel { * Creates a new TypesPanel. */ public TypesPanel() { - this(new MimeTypeSummary(), new TypesSummary(), new ContainerSummaryGetter()); + this(new MimeTypeSummaryGetter(), new TypesSummaryGetter(), new ContainerSummaryGetter()); } @Override @@ -250,8 +192,8 @@ class TypesPanel extends BaseDataSourceSummaryPanel { * @param containerData The service for container information. */ public TypesPanel( - MimeTypeSummary mimeTypeData, - TypesSummary typeData, + MimeTypeSummaryGetter mimeTypeData, + TypesSummaryGetter typeData, ContainerSummaryGetter containerData) { super(mimeTypeData, typeData, containerData); @@ -277,13 +219,13 @@ class TypesPanel extends BaseDataSourceSummaryPanel { size -> SizeRepresentationUtil.getSizeString(size, INTEGER_SIZE_FORMAT, false)))), new DataFetchWorker.DataFetchComponents<>(typesFetcher, this::showMimeTypeCategories), new DataFetchWorker.DataFetchComponents<>(allocatedFetcher, - countRes -> allocatedLabel.showDataFetchResult(DataFetchResult.getSubResult(countRes, (count) -> getStringOrZero(count)))), + countRes -> allocatedLabel.showDataFetchResult(DataFetchResult.getSubResult(countRes, (count) -> DataSourceInfoUtilities.getStringOrZero(count)))), new DataFetchWorker.DataFetchComponents<>(unallocatedFetcher, - countRes -> unallocatedLabel.showDataFetchResult(DataFetchResult.getSubResult(countRes, (count) -> getStringOrZero(count)))), + countRes -> unallocatedLabel.showDataFetchResult(DataFetchResult.getSubResult(countRes, (count) -> DataSourceInfoUtilities.getStringOrZero(count)))), new DataFetchWorker.DataFetchComponents<>(slackFetcher, - countRes -> slackLabel.showDataFetchResult(DataFetchResult.getSubResult(countRes, (count) -> getStringOrZero(count)))), + countRes -> slackLabel.showDataFetchResult(DataFetchResult.getSubResult(countRes, (count) -> DataSourceInfoUtilities.getStringOrZero(count)))), new DataFetchWorker.DataFetchComponents<>(directoriesFetcher, - countRes -> directoriesLabel.showDataFetchResult(DataFetchResult.getSubResult(countRes, (count) -> getStringOrZero(count)))) + countRes -> directoriesLabel.showDataFetchResult(DataFetchResult.getSubResult(countRes, (count) -> DataSourceInfoUtilities.getStringOrZero(count)))) ); initComponents(); @@ -307,7 +249,7 @@ class TypesPanel extends BaseDataSourceSummaryPanel { * * @return The pie chart items. */ - private TypesPieChartData getMimeTypeCategoriesModel(MimeTypeSummary mimeTypeData, DataSource dataSource) + private TypesPieChartData getMimeTypeCategoriesModel(MimeTypeSummaryGetter mimeTypeData, DataSource dataSource) throws SQLException, SleuthkitCaseProviderException, TskCoreException { if (dataSource == null) { @@ -318,8 +260,8 @@ class TypesPanel extends BaseDataSourceSummaryPanel { List fileCategoryItems = new ArrayList<>(); long categoryTotalCount = 0; - for (TypesPieCategory cat : FILE_MIME_TYPE_CATEGORIES) { - long thisValue = getLongOrZero(mimeTypeData.getCountOfFilesForMimeTypes(dataSource, cat.getMimeTypes())); + for (FileTypeCategoryData cat : FILE_MIME_TYPE_CATEGORIES) { + long thisValue = DataSourceInfoUtilities.getLongOrZero(mimeTypeData.getCountOfFilesForMimeTypes(dataSource, cat.getMimeTypes())); categoryTotalCount += thisValue; fileCategoryItems.add(new PieChartItem( @@ -329,10 +271,10 @@ class TypesPanel extends BaseDataSourceSummaryPanel { } // get a count of all files with no mime type - long noMimeTypeCount = getLongOrZero(mimeTypeData.getCountOfFilesWithNoMimeType(dataSource)); + long noMimeTypeCount = DataSourceInfoUtilities.getLongOrZero(mimeTypeData.getCountOfFilesWithNoMimeType(dataSource)); // get a count of all regular files - long allRegularFiles = getLongOrZero(mimeTypeData.getCountOfAllRegularFiles(dataSource)); + long allRegularFiles = DataSourceInfoUtilities.getLongOrZero(mimeTypeData.getCountOfAllRegularFiles(dataSource)); // create entry for mime types in other category long otherCount = allRegularFiles - (categoryTotalCount + noMimeTypeCount); @@ -385,29 +327,6 @@ class TypesPanel extends BaseDataSourceSummaryPanel { } } - /** - * Returns the long value or zero if longVal is null. - * - * @param longVal The long value. - * - * @return The long value or 0 if provided value is null. - */ - private static long getLongOrZero(Long longVal) { - return longVal == null ? 0 : longVal; - } - - /** - * Returns string value of long with comma separators. If null returns a - * string of '0'. - * - * @param longVal The long value. - * - * @return The string value of the long. - */ - private static String getStringOrZero(Long longVal) { - return longVal == null ? "0" : COMMA_FORMATTER.format(longVal); - } - /** * Returns a key value pair to be exported in a sheet. * diff --git a/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/Bundle.properties-MERGED index 07d26f8e14..6b90d2414d 100755 --- a/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/Bundle.properties-MERGED @@ -5,6 +5,7 @@ DataSourceSummaryReport.endReport.srcModuleName.text=Excel Report ExcelExport_writeExcel_noSheetName=Sheet {0} ExcelExportAction_exportToXLSX_beginExport=Beginning Export... ExcelExportAction_exportToXLSX_gatheringContainerData=Fetching Container & Image Data +ExcelExportAction_exportToXLSX_gatheringFileData=Fetching File and MIME Type Data ExcelExportAction_exportToXLSX_gatheringRecentActivityData=Fetching Recent Activity Data ExcelExportAction_exportToXLSX_gatheringTimelineData=Fetching Timeline Data ExcelExportAction_exportToXLSX_writingToFile=Writing to File... @@ -32,6 +33,25 @@ ExportRecentFiles_col_header_path=Path ExportRecentFiles_col_header_sender=Sender ExportRecentFiles_docsTable_tabName=Recently Opened Documents ExportRecentFiles_downloadsTable_tabName=Recently Downloads +ExportTypes_artifactsTypesPieChart_title=Artifact Types +ExportTypes_excelTabName=Types +ExportTypes_fileMimeTypesChart_audio_title=Audio +ExportTypes_fileMimeTypesChart_documents_title=Documents +ExportTypes_fileMimeTypesChart_executables_title=Executables +ExportTypes_fileMimeTypesChart_images_title=Images +ExportTypes_fileMimeTypesChart_notAnalyzed_title=Not Analyzed +ExportTypes_fileMimeTypesChart_other_title=Other +ExportTypes_fileMimeTypesChart_title=File Types +ExportTypes_fileMimeTypesChart_unknown_title=Unknown +ExportTypes_fileMimeTypesChart_valueLabel=Count +ExportTypes_fileMimeTypesChart_videos_title=Videos +ExportTypes_filesByCategoryTable_allocatedRow_title=Allocated Files +ExportTypes_filesByCategoryTable_directoryRow_title=Directories +ExportTypes_filesByCategoryTable_slackRow_title=Slack Files +ExportTypes_filesByCategoryTable_unallocatedRow_title=Unallocated Files +ExportTypes_osLabel_title=OS +ExportTypes_sizeLabel_title=Size +ExportTypes_usageLabel_title=Usage SizeRepresentationUtil_units_bytes=bytes SizeRepresentationUtil_units_gigabytes=GB SizeRepresentationUtil_units_kilobytes=KB diff --git a/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExcelExportAction.java b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExcelExportAction.java index 8492b4aca6..842ad9179a 100644 --- a/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExcelExportAction.java +++ b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExcelExportAction.java @@ -93,6 +93,7 @@ class ExcelExportAction { "ExcelExportAction_exportToXLSX_gatheringRecentActivityData=Fetching Recent Activity Data", "ExcelExportAction_exportToXLSX_gatheringContainerData=Fetching Container & Image Data", "ExcelExportAction_exportToXLSX_gatheringTimelineData=Fetching Timeline Data", + "ExcelExportAction_exportToXLSX_gatheringFileData=Fetching File and MIME Type Data", "ExcelExportAction_exportToXLSX_writingToFile=Writing to File...",}) void exportToXLSX(ReportProgressPanel progressPanel, DataSource dataSource, String baseReportDir) @@ -127,7 +128,15 @@ class ExcelExportAction { exports = ExportTimeline.getExports(dataSource); if (exports != null) { sheetExports.addAll(exports); - } + } + + // Export file and MIME type data + progressPanel.updateStatusLabel(Bundle.ExcelExportAction_exportToXLSX_gatheringFileData()); + progressPanel.setProgress(4); + exports = ExportTypes.getExports(dataSource); + if (exports != null) { + sheetExports.addAll(exports); + } progressPanel.updateStatusLabel(Bundle.ExcelExportAction_exportToXLSX_writingToFile()); progressPanel.setProgress(9); diff --git a/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExportTypes.java b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExportTypes.java new file mode 100755 index 0000000000..5453a879c8 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExportTypes.java @@ -0,0 +1,312 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019-2021 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.report.modules.datasourcesummaryexport; + +import java.awt.Color; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.contentutils.ContainerSummary; +import org.sleuthkit.autopsy.coreutils.FileTypeUtils.FileTypeCategory; +import org.sleuthkit.autopsy.contentutils.DataSourceInfoUtilities; +import org.sleuthkit.autopsy.contentutils.TypesSummary; +import org.sleuthkit.autopsy.contentutils.TypesSummary.FileTypeCategoryData; +import org.sleuthkit.autopsy.report.modules.datasourcesummaryexport.ExcelSpecialFormatExport.KeyValueItemExportable; +import org.sleuthkit.datamodel.DataSource; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Panel for displaying summary information on the known files present in the + * specified DataSource. + */ +@Messages({ + "ExportTypes_artifactsTypesPieChart_title=Artifact Types", + "ExportTypes_filesByCategoryTable_allocatedRow_title=Allocated Files", + "ExportTypes_filesByCategoryTable_unallocatedRow_title=Unallocated Files", + "ExportTypes_filesByCategoryTable_slackRow_title=Slack Files", + "ExportTypes_filesByCategoryTable_directoryRow_title=Directories", + "ExportTypes_fileMimeTypesChart_title=File Types", + "ExportTypes_fileMimeTypesChart_valueLabel=Count", + "ExportTypes_fileMimeTypesChart_audio_title=Audio", + "ExportTypes_fileMimeTypesChart_documents_title=Documents", + "ExportTypes_fileMimeTypesChart_executables_title=Executables", + "ExportTypes_fileMimeTypesChart_images_title=Images", + "ExportTypes_fileMimeTypesChart_videos_title=Videos", + "ExportTypes_fileMimeTypesChart_other_title=Other", + "ExportTypes_fileMimeTypesChart_unknown_title=Unknown", + "ExportTypes_fileMimeTypesChart_notAnalyzed_title=Not Analyzed", + "ExportTypes_usageLabel_title=Usage", + "ExportTypes_osLabel_title=OS", + "ExportTypes_sizeLabel_title=Size", + "ExportTypes_excelTabName=Types"}) +class ExportTypes { + + /** + * Data for types pie chart. + */ + private static class TypesPieChartData { + + private final List pieSlices; + private final boolean usefulContent; + + /** + * Main constructor. + * + * @param pieSlices The pie slices. + * @param usefulContent True if this is useful content; false if there + * is 0 mime type information. + */ + TypesPieChartData(List pieSlices, boolean usefulContent) { + this.pieSlices = pieSlices; + this.usefulContent = usefulContent; + } + + /** + * @return The pie chart data. + */ + List getPieSlices() { + return pieSlices; + } + + /** + * @return Whether or not the data is usefulContent. + */ + boolean isUsefulContent() { + return usefulContent; + } + } + + private static final Color IMAGES_COLOR = new Color(156, 39, 176); + private static final Color VIDEOS_COLOR = Color.YELLOW; + private static final Color AUDIO_COLOR = Color.BLUE; + private static final Color DOCUMENTS_COLOR = Color.GREEN; + private static final Color EXECUTABLES_COLOR = new Color(0, 188, 212); + private static final Color UNKNOWN_COLOR = Color.ORANGE; + private static final Color OTHER_COLOR = new Color(78, 52, 46); + private static final Color NOT_ANALYZED_COLOR = Color.WHITE; + + // All file type categories. + private static final List FILE_MIME_TYPE_CATEGORIES = Arrays.asList( + new FileTypeCategoryData(Bundle.ExportTypes_fileMimeTypesChart_images_title(), FileTypeCategory.IMAGE.getMediaTypes(), IMAGES_COLOR), + new FileTypeCategoryData(Bundle.ExportTypes_fileMimeTypesChart_videos_title(), FileTypeCategory.VIDEO.getMediaTypes(), VIDEOS_COLOR), + new FileTypeCategoryData(Bundle.ExportTypes_fileMimeTypesChart_audio_title(), FileTypeCategory.AUDIO.getMediaTypes(), AUDIO_COLOR), + new FileTypeCategoryData(Bundle.ExportTypes_fileMimeTypesChart_documents_title(), FileTypeCategory.DOCUMENTS.getMediaTypes(), DOCUMENTS_COLOR), + new FileTypeCategoryData(Bundle.ExportTypes_fileMimeTypesChart_executables_title(), FileTypeCategory.EXECUTABLE.getMediaTypes(), EXECUTABLES_COLOR), + new FileTypeCategoryData(Bundle.ExportTypes_fileMimeTypesChart_unknown_title(), new HashSet<>(Arrays.asList("application/octet-stream")), UNKNOWN_COLOR) + ); + + private ExportTypes() { + } + + /** + * 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); + } + + /** + * 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 + * + * @throws NoCurrentCaseException + * @throws TskCoreException + * @throws SQLException + */ + private static Long getCountOfFilesForMimeTypes(DataSource currentDataSource, Set setOfMimeTypes) throws TskCoreException, SQLException, NoCurrentCaseException { + return DataSourceInfoUtilities.getCountOfRegNonSlackFiles(currentDataSource, "mime_type IN " + getSqlSet(setOfMimeTypes)); + } + + /** + * 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. + * + * @throws NoCurrentCaseException + * @throws TskCoreException + * @throws SQLException + */ + private static Long getCountOfFilesWithNoMimeType(DataSource currentDataSource) throws TskCoreException, SQLException, NoCurrentCaseException { + return DataSourceInfoUtilities.getCountOfRegNonSlackFiles(currentDataSource, "(mime_type IS NULL OR mime_type = '') "); + } + + /** + * Gets all the data for the file type pie chart. + * + * @param mimeTypeData The means of acquiring data. + * @param dataSource The datasource. + * + * @return The pie chart items. + */ + private static TypesPieChartData getMimeTypeCategoriesModel(DataSource dataSource) + throws SQLException, TskCoreException, NoCurrentCaseException { + + if (dataSource == null) { + return null; + } + + // for each category of file types, get the counts of files + List fileCategoryItems = new ArrayList<>(); + long categoryTotalCount = 0; + + for (FileTypeCategoryData cat : FILE_MIME_TYPE_CATEGORIES) { + long thisValue = DataSourceInfoUtilities.getLongOrZero(getCountOfFilesForMimeTypes(dataSource, cat.getMimeTypes())); + categoryTotalCount += thisValue; + + fileCategoryItems.add(new PieChartItem( + cat.getLabel(), + thisValue, + cat.getColor())); + } + + // get a count of all files with no mime type + long noMimeTypeCount = DataSourceInfoUtilities.getLongOrZero(getCountOfFilesWithNoMimeType(dataSource)); + + // get a count of all regular files + long allRegularFiles = DataSourceInfoUtilities.getLongOrZero(DataSourceInfoUtilities.getCountOfRegNonSlackFiles(dataSource, null)); + + // create entry for mime types in other category + long otherCount = allRegularFiles - (categoryTotalCount + noMimeTypeCount); + PieChartItem otherPieItem = new PieChartItem(Bundle.ExportTypes_fileMimeTypesChart_other_title(), + otherCount, OTHER_COLOR); + + // check at this point to see if these are all 0; if so, we don't have useful content. + boolean usefulContent = categoryTotalCount > 0 || otherCount > 0; + + // create entry for not analyzed mime types category + PieChartItem notAnalyzedItem = new PieChartItem(Bundle.ExportTypes_fileMimeTypesChart_notAnalyzed_title(), + noMimeTypeCount, NOT_ANALYZED_COLOR); + + // combine categories with 'other' and 'not analyzed' + List items = Stream.concat( + fileCategoryItems.stream(), + Stream.of(otherPieItem, notAnalyzedItem)) + // remove items that have no value + .filter(slice -> slice.getValue() > 0) + .collect(Collectors.toList()); + + return new TypesPieChartData(items, usefulContent); + } + + /** + * Returns a key value pair to be exported in a sheet. + * + * @param fetcher The means of fetching the data. + * @param key The key to use. + * @param dataSource The data source containing the data. + * + * @return The key value pair to be exported. + */ + private static KeyValueItemExportable getStrExportable(DataFetcher fetcher, String key, DataSource dataSource) { + String result = ExcelExportAction.getFetchResult(fetcher, "Types", dataSource); + return (result == null) ? null : new KeyValueItemExportable(key, new DefaultCellModel<>(result)); + } + + /** + * Returns a key value pair to be exported in a sheet formatting the long + * with commas separated by orders of 1000. + * + * @param fetcher The means of fetching the data. + * @param key The string key for this key value pair. + * @param dataSource The data source. + * + * @return The key value pair. + */ + private static KeyValueItemExportable getCountExportable(DataFetcher fetcher, String key, DataSource dataSource) { + Long count = ExcelExportAction.getFetchResult(fetcher, "Types", dataSource); + return (count == null) ? null : new KeyValueItemExportable(key, + new DefaultCellModel(count, DataSourceInfoUtilities.COMMA_FORMATTER::format, DataSourceInfoUtilities.COMMA_FORMAT_STR)); + } + + static List getExports(DataSource dataSource) { + if (dataSource == null) { + return Collections.emptyList(); + } + + DataFetcher usageFetcher = (ds) -> ContainerSummary.getDataSourceType(ds); + DataFetcher osFetcher = (ds) -> ContainerSummary.getOperatingSystems(ds); + DataFetcher sizeFetcher = (ds) -> ds == null ? null : ds.getSize(); + + DataFetcher typesFetcher = (ds) -> getMimeTypeCategoriesModel(ds); + + DataFetcher allocatedFetcher = (ds) -> TypesSummary.getCountOfAllocatedFiles(ds); + DataFetcher unallocatedFetcher = (ds) -> TypesSummary.getCountOfUnallocatedFiles(ds); + DataFetcher slackFetcher = (ds) -> TypesSummary.getCountOfSlackFiles(ds); + DataFetcher directoriesFetcher = (ds) -> TypesSummary.getCountOfDirectories(ds); + + // Retrieve data to create the types pie chart + TypesPieChartData typesData = ExcelExportAction.getFetchResult(typesFetcher, "Types", dataSource); + PieChartExport typesChart = (typesData == null || !typesData.isUsefulContent()) ? null + : new PieChartExport( + Bundle.ExportTypes_fileMimeTypesChart_title(), + Bundle.ExportTypes_fileMimeTypesChart_valueLabel(), + "#,###", + Bundle.ExportTypes_fileMimeTypesChart_title(), + typesData.getPieSlices()); + + return Arrays.asList(new ExcelSpecialFormatExport(Bundle.ExportTypes_excelTabName(), + Stream.of( + getStrExportable(usageFetcher, Bundle.ExportTypes_usageLabel_title(), dataSource), + getStrExportable(osFetcher, Bundle.ExportTypes_osLabel_title(), dataSource), + new KeyValueItemExportable(Bundle.ExportTypes_sizeLabel_title(), + SizeRepresentationUtil.getBytesCell(ExcelExportAction.getFetchResult(sizeFetcher, "Types", dataSource))), + typesChart, + getCountExportable(allocatedFetcher, Bundle.ExportTypes_filesByCategoryTable_allocatedRow_title(), dataSource), + getCountExportable(unallocatedFetcher, Bundle.ExportTypes_filesByCategoryTable_unallocatedRow_title(), dataSource), + getCountExportable(slackFetcher, Bundle.ExportTypes_filesByCategoryTable_slackRow_title(), dataSource), + getCountExportable(directoriesFetcher, Bundle.ExportTypes_filesByCategoryTable_directoryRow_title(), dataSource)) + .filter(sheet -> sheet != null) + .collect(Collectors.toList()) + )); + } +}