diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/AnalysisSummary.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/AnalysisSummary.java old mode 100644 new mode 100755 index 5fff3eac6c..60598f7529 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/AnalysisSummary.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/AnalysisSummary.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"); @@ -18,9 +18,7 @@ */ package org.sleuthkit.autopsy.datasourcesummary.datamodel; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultArtifactUpdateGovernor; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -31,30 +29,23 @@ import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException; +import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; import org.sleuthkit.datamodel.DataSource; -import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; /** - * Providing data for the data source analysis tab. + * Helper class for getting hash set hits, keyword hits, and interesting item + * hits within a datasource. */ -public class AnalysisSummary implements DefaultArtifactUpdateGovernor { +public class AnalysisSummary { private static final BlackboardAttribute.Type TYPE_SET_NAME = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_SET_NAME); - private static final Set EXCLUDED_KEYWORD_SEARCH_ITEMS = new HashSet<>(); - private static final Set ARTIFACT_UPDATE_TYPE_IDS = new HashSet<>(Arrays.asList( - ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID(), - ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID(), - ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID(), - ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID() - )); - private final SleuthkitCaseProvider provider; /** @@ -73,11 +64,6 @@ public class AnalysisSummary implements DefaultArtifactUpdateGovernor { this.provider = provider; } - @Override - public Set getArtifactTypeIdsForRefresh() { - return ARTIFACT_UPDATE_TYPE_IDS; - } - /** * Gets counts for hashset hits. * diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/Bundle.properties-MERGED old mode 100644 new mode 100755 diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/Bundle_ja.properties old mode 100644 new mode 100755 diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/CityRecord.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/CityRecord.java index d2a1182e6a..26fa46498c 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/CityRecord.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/CityRecord.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"); diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/ClosestCityMapper.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/ClosestCityMapper.java index 4427d25f23..ecadc0931e 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/ClosestCityMapper.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/ClosestCityMapper.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"); @@ -64,6 +64,7 @@ class ClosestCityMapper { * Retrieves singleton instance of this class. * * @return The singleton instance of this class. + * * @throws IOException */ static ClosestCityMapper getInstance() throws IOException { @@ -95,8 +96,9 @@ class ClosestCityMapper { * Main Constructor loading from an input stream. * * @param citiesInputStream The input stream for the csv text file - * containing the cities. - * @param logger The logger to be used with this. + * containing the cities. + * @param logger The logger to be used with this. + * * @throws IOException */ private ClosestCityMapper(InputStream citiesInputStream, java.util.logging.Logger logger) throws IOException { @@ -109,6 +111,7 @@ class ClosestCityMapper { * city can be determined. * * @param point The point to locate. + * * @return The closest city or null if no close city can be found. */ CityRecord findClosest(CityRecord point) { @@ -120,6 +123,7 @@ class ClosestCityMapper { * returned. * * @param s The string to parse. + * * @return The double value or null if value cannot be parsed. */ private Double tryParse(String s) { @@ -138,8 +142,9 @@ class ClosestCityMapper { * Parses a country name and transforms values like "last, first" to "first * last" (i.e. "Korea, South" becomes "South Korea"). * - * @param orig The original string value. + * @param orig The original string value. * @param lineNum The line number that this country was found. + * * @return The country name. */ private String parseCountryName(String orig, int lineNum) { @@ -159,9 +164,10 @@ class ClosestCityMapper { /** * Parses a row from the csv creating a city record. * - * @param csvRow The row of data where each item in the list is each column - * in the row. + * @param csvRow The row of data where each item in the list is each column + * in the row. * @param lineNum The line number for this csv row. + * * @return The parsed CityRecord or null if none can be determined. */ private CityRecord getCsvCityRecord(List csvRow, int lineNum) { @@ -199,8 +205,9 @@ class ClosestCityMapper { /** * Parses a row of the csv into individual column values. * - * @param line The line to parse. + * @param line The line to parse. * @param lineNum The line number in the csv where this line is. + * * @return The list of column values. */ private List parseCsvLine(String line, int lineNum) { @@ -222,10 +229,12 @@ class ClosestCityMapper { * Parses all lines in the csv file input stream into a list of city * records. * - * @param csvInputStream The csv file input stream. + * @param csvInputStream The csv file input stream. * @param ignoreHeaderRow Whether or not there is a header row in the csv - * file. + * file. + * * @return The list of city records. + * * @throws IOException */ private List parseCsvLines(InputStream csvInputStream, boolean ignoreHeaderRow) throws IOException { diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/ContainerSummary.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/ContainerSummary.java old mode 100644 new mode 100755 index 8129911500..4ecacdb059 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/ContainerSummary.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/ContainerSummary.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"); @@ -18,29 +18,25 @@ */ package org.sleuthkit.autopsy.datasourcesummary.datamodel; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultArtifactUpdateGovernor; 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 org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException; -import org.sleuthkit.autopsy.ingest.ModuleContentEvent; -import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.DataSource; +import org.sleuthkit.datamodel.Image; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; /** * Provides methods to query for data source overview details. */ -public class ContainerSummary implements DefaultArtifactUpdateGovernor { - - private static final Set ARTIFACT_UPDATE_TYPE_IDS = new HashSet<>(Arrays.asList( - BlackboardArtifact.ARTIFACT_TYPE.TSK_OS_INFO.getTypeID(), - BlackboardArtifact.ARTIFACT_TYPE.TSK_DATA_SOURCE_USAGE.getTypeID() - )); +public class ContainerSummary { private final SleuthkitCaseProvider provider; @@ -59,22 +55,7 @@ public class ContainerSummary implements DefaultArtifactUpdateGovernor { public ContainerSummary(SleuthkitCaseProvider provider) { this.provider = provider; } - - @Override - public boolean isRefreshRequired(ModuleContentEvent evt) { - return true; - } - - @Override - public boolean isRefreshRequired(AbstractFile file) { - return true; - } - - @Override - public Set getArtifactTypeIdsForRefresh() { - return ARTIFACT_UPDATE_TYPE_IDS; - } - + /** * Gets the size of unallocated files in a particular datasource. * @@ -231,4 +212,228 @@ public class ContainerSummary implements DefaultArtifactUpdateGovernor { String separator = ", "; return getConcattedStringsResult(query, valueParam, separator); } + + /** + * Data model data for data source images. + */ + public static class ImageDetails { + + private final long unallocatedSize; + private final long size; + private final long sectorSize; + + private final String timeZone; + private final String imageType; + + private final List paths; + private final String md5Hash; + private final String sha1Hash; + private final String sha256Hash; + + /** + * Main constructor. + * + * @param unallocatedSize Size in bytes of unallocated space. + * @param size Total size in bytes. + * @param sectorSize Sector size in bytes. + * @param timeZone The time zone. + * @param imageType The type of image. + * @param paths The source paths for the image. + * @param md5Hash The md5 hash or null. + * @param sha1Hash The sha1 hash or null. + * @param sha256Hash The sha256 hash or null. + */ + ImageDetails(long unallocatedSize, long size, long sectorSize, + String timeZone, String imageType, List paths, String md5Hash, + String sha1Hash, String sha256Hash) { + this.unallocatedSize = unallocatedSize; + this.size = size; + this.sectorSize = sectorSize; + this.timeZone = timeZone; + this.imageType = imageType; + this.paths = paths == null ? Collections.emptyList() : new ArrayList<>(paths); + this.md5Hash = md5Hash; + this.sha1Hash = sha1Hash; + this.sha256Hash = sha256Hash; + } + + /** + * @return Size in bytes of unallocated space. + */ + public long getUnallocatedSize() { + return unallocatedSize; + } + + /** + * @return Total size in bytes. + */ + public long getSize() { + return size; + } + + /** + * @return Sector size in bytes. + */ + public long getSectorSize() { + return sectorSize; + } + + /** + * @return The time zone. + */ + public String getTimeZone() { + return timeZone; + } + + /** + * @return The type of image. + */ + public String getImageType() { + return imageType; + } + + /** + * @return The source paths for the image. + */ + public List getPaths() { + return Collections.unmodifiableList(paths); + } + + /** + * @return The md5 hash or null. + */ + public String getMd5Hash() { + return md5Hash; + } + + /** + * @return The sha1 hash or null. + */ + public String getSha1Hash() { + return sha1Hash; + } + + /** + * @return The sha256 hash or null. + */ + public String getSha256Hash() { + return sha256Hash; + } + } + + /** + * Data model for container data. + */ + public static class ContainerDetails { + + private final String displayName; + private final String originalName; + private final String deviceIdValue; + private final String acquisitionDetails; + private final ImageDetails imageDetails; + + /** + * Main constructor. + * + * @param displayName The display name for this data source. + * @param originalName The original name for this data source. + * @param deviceIdValue The device id value for this data source. + * @param acquisitionDetails The acquisition details for this data + * source or null. + * @param imageDetails If the data source is an image, the image + * data model for this data source or null if + * non-image. + */ + ContainerDetails(String displayName, String originalName, String deviceIdValue, + String acquisitionDetails, ImageDetails imageDetails) { + this.displayName = displayName; + this.originalName = originalName; + this.deviceIdValue = deviceIdValue; + this.acquisitionDetails = acquisitionDetails; + this.imageDetails = imageDetails; + } + + /** + * @return The display name for this data source. + */ + public String getDisplayName() { + return displayName; + } + + /** + * @return The original name for this data source. + */ + public String getOriginalName() { + return originalName; + } + + /** + * @return The device id value for this data source. + */ + public String getDeviceId() { + return deviceIdValue; + } + + /** + * @return The acquisition details for this data source or null. + */ + public String getAcquisitionDetails() { + return acquisitionDetails; + } + + /** + * @return If the data source is an image, the image details for this + * data source or null if non-image. + */ + public ImageDetails getImageDetails() { + return imageDetails; + } + } + + /** + * Generates a container data model object containing data about the data + * source. + * + * @param ds The data source. + * + * @return The generated view model. + */ + public ContainerDetails getContainerDetails(DataSource ds) throws TskCoreException, SQLException, SleuthkitCaseProvider.SleuthkitCaseProviderException { + if (ds == null) { + return null; + } + + return new ContainerDetails( + ds.getName(), + ds.getName(), + ds.getDeviceId(), + ds.getAcquisitionDetails(), + ds instanceof Image ? getImageDetails((Image) ds) : null + ); + } + + /** + * Generates an image data model object containing data about the image. + * + * @param image The image. + * + * @return The generated view model. + */ + public ImageDetails getImageDetails(Image image) throws TskCoreException, SQLException, SleuthkitCaseProvider.SleuthkitCaseProviderException { + if (image == null) { + return null; + } + + Long unallocSize = getSizeOfUnallocatedFiles(image); + String imageType = image.getType().getName(); + Long size = image.getSize(); + Long sectorSize = image.getSsize(); + String timeZone = image.getTimeZone(); + List paths = image.getPaths() == null ? Collections.emptyList() : Arrays.asList(image.getPaths()); + String md5 = image.getMd5(); + String sha1 = image.getSha1(); + String sha256 = image.getSha256(); + + return new ImageDetails(unallocSize, size, sectorSize, timeZone, imageType, paths, md5, sha1, sha256); + } } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataFetcher.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataFetcher.java similarity index 96% rename from Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataFetcher.java rename to Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataFetcher.java index 6eabe79634..45dcae5f35 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataFetcher.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataFetcher.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.datasourcesummary.uiutils; +package org.sleuthkit.autopsy.datasourcesummary.datamodel; /** * A function that accepts input of type I and outputs type O. This function is diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceInfoUtilities.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceInfoUtilities.java index c711f3c9a0..becd78f418 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceInfoUtilities.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceInfoUtilities.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2019 - 2020 Basis Technology Corp. + * Copyright 2019 - 2021 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.datasourcesummary.datamodel; import java.sql.ResultSet; import java.sql.SQLException; +import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Comparator; import java.util.Date; @@ -41,7 +42,10 @@ 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 { +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. @@ -100,7 +104,7 @@ final class DataSourceInfoUtilities { * @throws TskCoreException * @throws SQLException */ - static Long getCountOfRegNonSlackFiles(SleuthkitCase skCase, DataSource currentDataSource, String additionalWhere) + public static Long getCountOfRegNonSlackFiles(SleuthkitCase skCase, DataSource currentDataSource, String additionalWhere) throws TskCoreException, SQLException { String whereClause = "meta_type=" + TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_REG.getValue() + " AND type<>" + TSK_DB_FILES_TYPE_ENUM.SLACK.getFileType(); @@ -115,7 +119,7 @@ final class DataSourceInfoUtilities { /** * An interface for handling a result set and returning a value. */ - interface ResultSetHandler { + public interface ResultSetHandler { T process(ResultSet resultset) throws SQLException; } @@ -149,14 +153,14 @@ final class DataSourceInfoUtilities { * * @return The clause. */ - static String getMetaFlagsContainsStatement(TSK_FS_META_FLAG_ENUM flag) { + public static String getMetaFlagsContainsStatement(TSK_FS_META_FLAG_ENUM flag) { return "meta_flags & " + flag.getValue() + " > 0"; } /** * Enum for specifying the sort order for getAttributes. */ - enum SortOrder { + public enum SortOrder { DESCENDING, ASCENDING } @@ -181,7 +185,7 @@ final class DataSourceInfoUtilities { * * @throws TskCoreException */ - static List getArtifacts(SleuthkitCase skCase, BlackboardArtifact.Type artifactType, DataSource dataSource, BlackboardAttribute.Type attributeType, SortOrder sortOrder) throws TskCoreException { + public static List getArtifacts(SleuthkitCase skCase, BlackboardArtifact.Type artifactType, DataSource dataSource, BlackboardAttribute.Type attributeType, SortOrder sortOrder) throws TskCoreException { return getArtifacts(skCase, artifactType, dataSource, attributeType, sortOrder, 0); } @@ -207,7 +211,7 @@ final class DataSourceInfoUtilities { * * @throws TskCoreException */ - static List getArtifacts(SleuthkitCase skCase, BlackboardArtifact.Type artifactType, DataSource dataSource, BlackboardAttribute.Type attributeType, SortOrder sortOrder, int maxCount) throws TskCoreException { + public static List getArtifacts(SleuthkitCase skCase, BlackboardArtifact.Type artifactType, DataSource dataSource, BlackboardAttribute.Type attributeType, SortOrder sortOrder, int maxCount) throws TskCoreException { if (maxCount < 0) { throw new IllegalArgumentException("Invalid maxCount passed to getArtifacts, value must be equal to or greater than 0"); } @@ -380,7 +384,7 @@ final class DataSourceInfoUtilities { * @return The 'getValueString()' value or null if the attribute or String * could not be retrieved. */ - static String getStringOrNull(BlackboardArtifact artifact, Type attributeType) { + public static String getStringOrNull(BlackboardArtifact artifact, Type attributeType) { BlackboardAttribute attr = getAttributeOrNull(artifact, attributeType); return (attr == null) ? null : attr.getValueString(); } @@ -394,11 +398,11 @@ final class DataSourceInfoUtilities { * @return The 'getValueLong()' value or null if the attribute could not be * retrieved. */ - static Long getLongOrNull(BlackboardArtifact artifact, Type attributeType) { + public static Long getLongOrNull(BlackboardArtifact artifact, Type attributeType) { BlackboardAttribute attr = getAttributeOrNull(artifact, attributeType); return (attr == null) ? null : attr.getValueLong(); } - + /** * Retrieves the int value of a certain attribute type from an artifact. * @@ -408,7 +412,7 @@ final class DataSourceInfoUtilities { * @return The 'getValueInt()' value or null if the attribute could not be * retrieved. */ - static Integer getIntOrNull(BlackboardArtifact artifact, Type attributeType) { + public static Integer getIntOrNull(BlackboardArtifact artifact, Type attributeType) { BlackboardAttribute attr = getAttributeOrNull(artifact, attributeType); return (attr == null) ? null : attr.getValueInt(); } @@ -423,8 +427,31 @@ final class DataSourceInfoUtilities { * @return The date determined from the 'getValueLong()' as seconds from * epoch or null if the attribute could not be retrieved or is 0. */ - static Date getDateOrNull(BlackboardArtifact artifact, Type attributeType) { + public static Date getDateOrNull(BlackboardArtifact artifact, Type attributeType) { 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/datasourcesummary/datamodel/GeolocationSummary.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/GeolocationSummary.java old mode 100644 new mode 100755 index d66bbaf30a..1b8f2feb8e --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/GeolocationSummary.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/GeolocationSummary.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"); @@ -32,7 +32,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import org.apache.commons.lang3.tuple.Pair; import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultArtifactUpdateGovernor; import org.sleuthkit.autopsy.geolocation.AbstractWaypointFetcher; import org.sleuthkit.autopsy.geolocation.GeoFilter; import org.sleuthkit.autopsy.geolocation.MapWaypoint; @@ -45,7 +44,7 @@ import org.sleuthkit.datamodel.DataSource; /** * Gathers summary data about Geolocation information for a data source. */ -public class GeolocationSummary implements DefaultArtifactUpdateGovernor { +public class GeolocationSummary { /** * A count of hits for a particular city. @@ -59,8 +58,8 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor { * Main constructor. * * @param cityRecord The record for the city including name, country, - * and location. - * @param count The number of hits in proximity to that city. + * and location. + * @param count The number of hits in proximity to that city. */ CityRecordCount(CityRecord cityRecord, int count) { this.cityRecord = cityRecord; @@ -69,7 +68,7 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor { /** * @return The record for the city including name, country, and - * location. + * location. */ public CityRecord getCityRecord() { return cityRecord; @@ -96,8 +95,8 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor { /** * Main constructor. * - * @param mostCommon The list of most common cities seen. - * @param mostRecent The list of most recent cities seen. + * @param mostCommon The list of most common cities seen. + * @param mostRecent The list of most recent cities seen. * @param mostRecentSeen */ CityData(CityCountsList mostCommon, CityCountsList mostRecent, Long mostRecentSeen) { @@ -122,7 +121,7 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor { /** * @return The time stamp in seconds from epoch of the most recently - * seen city + * seen city */ public Long getMostRecentSeen() { return mostRecentSeen; @@ -142,10 +141,10 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor { /** * Main constructor. * - * @param counts The list of cities and the count of how many points are - * closest to that city. + * @param counts The list of cities and the count of how many points + * are closest to that city. * @param otherCount The count of points where no closest city was - * determined due to not being close enough. + * determined due to not being close enough. */ CityCountsList(List counts, int otherCount) { this.counts = Collections.unmodifiableList(new ArrayList<>(counts)); @@ -154,7 +153,7 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor { /** * @return The list of cities and the count of how many points are - * closest to that city. + * closest to that city. */ public List getCounts() { return counts; @@ -162,7 +161,7 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor { /** * @return The count of points where no closest city was determined due - * to not being close enough. + * to not being close enough. */ public int getOtherCount() { return otherCount; @@ -183,10 +182,10 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor { * Main constructor. * * @param mapWaypoints The way points found for the data source. - * @param tracks A list of sets where each set is a track in the data - * source. - * @param areas A list of areas where each set is an area in the data - * source. + * @param tracks A list of sets where each set is a track in the + * data source. + * @param areas A list of areas where each set is an area in the + * data source. */ private GeoResult(Set mapWaypoints, List> tracks, List> areas) { this.mapWaypoints = mapWaypoints; @@ -250,6 +249,7 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor { * A supplier method that can throw an exception of E. * * @return The object type. + * * @throws E The exception type. */ T get() throws E; @@ -277,13 +277,12 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor { /** * @return Returns all the geolocation artifact types. */ - public List getGeoTypes() { + public static List getGeoTypes() { return GPS_ARTIFACT_TYPES; } - @Override - public Set getArtifactTypeIdsForRefresh() { - return GPS_ARTIFACT_TYPE_IDS; + public static Set getArtifactTypeIdsForRefresh() { + return Collections.unmodifiableSet(GPS_ARTIFACT_TYPE_IDS); } /** @@ -291,13 +290,14 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor { * the event where either time is null. * * @param minTime The minimum time. If null is provided, this function will - * return false. - * @param time The time to check. If null is provided and the min time is - * non-null, then this function will return false. + * return false. + * @param time The time to check. If null is provided and the min time is + * non-null, then this function will return false. + * * @return If minTime == null then false. If minTime != null && time == null - * then false. Otherwise time >= minTime. + * then false. Otherwise time >= minTime. */ - private boolean greaterThanOrEqual(Long minTime, Long time) { + private static boolean greaterThanOrEqual(Long minTime, Long time) { if (minTime != null && time != null && time >= minTime) { return true; } else { @@ -310,12 +310,13 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor { * a total of waypoints whose time stamp is greater than or equal to * minTime. * - * @param points The list of way points. + * @param points The list of way points. * @param minTime The minimum time for most recent points count. + * * @return A pair where the left value is the total count of way points and - * the right is the total list of way points that are >= minTime. + * the right is the total list of way points that are >= minTime. */ - private Pair getCounts(List points, Long minTime) { + private static Pair getCounts(List points, Long minTime) { if (points == null) { return EMPTY_COUNT; } @@ -332,7 +333,8 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor { * the point is null, null is returned. * * @param cityMapper The means of mapping a point to the closest city. - * @param pt The geolocation point. + * @param pt The geolocation point. + * * @return A tuple of the closest city and timestamp in seconds from epoch. */ private Pair getClosestWithTime(ClosestCityMapper cityMapper, MapWaypoint pt) { @@ -351,10 +353,12 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor { * into a stream of the unique cities identified in this grouping and the * latest time stamp for each grouping. * - * @param points The points in the grouping. + * @param points The points in the grouping. * @param cityMapper The means of mapping a point to the closest city. + * * @return A stream of tuples where each tuple will be a unique city (or - * null if a closest is not determined) and the latest timestamp for each. + * null if a closest is not determined) and the latest timestamp for + * each. */ private Stream> reduceGrouping(Set points, ClosestCityMapper cityMapper) { if (points == null) { @@ -367,7 +371,7 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor { if (pair == null) { continue; } - + CityRecord city = pair.getLeft(); Long prevTime = timeMapping.get(city); Long curTime = pair.getRight(); @@ -375,7 +379,7 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor { timeMapping.put(city, curTime); } } - + return timeMapping.entrySet().stream() .map(e -> Pair.of(e.getKey(), e.getValue())); } @@ -385,10 +389,12 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor { * of tuples where each tuple represents a point with the closest city and * the time stamp in seconds from epoch. * - * @param geoResult The result from the Geolocation API. + * @param geoResult The result from the Geolocation API. * @param cityMapper The means of mapping a point to the closest city. + * * @return A list of tuples where each tuple represents a point to be - * counted with a combination of the closest city and the timestamp. + * counted with a combination of the closest city and the timestamp. + * * @throws IOException */ private Stream> processGeoResult(GeoResult geoResult, ClosestCityMapper cityMapper) { @@ -398,7 +404,7 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor { List> areas = (geoResult.getAreas() == null) ? Collections.emptyList() : geoResult.getAreas(); List> tracks = (geoResult.getTracks() == null) ? Collections.emptyList() : geoResult.getTracks(); - + Stream> reducedGroupings = Stream.of(areas, tracks) .flatMap((groupingList) -> groupingList.stream()) .flatMap((grouping) -> reduceGrouping(grouping, cityMapper)); @@ -407,7 +413,7 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor { .flatMap((groupingList) -> groupingList.stream()) .flatMap((group) -> group.stream()) .collect(Collectors.toSet()); - + Set pointSet = geoResult.getMapWaypoints() == null ? Collections.emptySet() : geoResult.getMapWaypoints(); Stream> citiesForPoints = pointSet.stream() // it appears that AbstractWaypointFetcher.handleFilteredWaypointSet returns all points @@ -423,8 +429,8 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor { * number of found hits (i.e. most hits is first index). * * @param dataSource The data source. - * @param daysCount Number of days to go back. - * @param maxCount Maximum number of results. + * @param daysCount Number of days to go back. + * @param maxCount Maximum number of results. * * @return The sorted list. * @@ -507,11 +513,12 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor { * Main constructor. * * @param asyncResult Geolocation fetches results in a callback which is - * already handled by other mechanisms in data source summary. The - * BlockingQueue blocks until a result is received from geolocation. - * @param filters The applicable filters for geolocation. + * already handled by other mechanisms in data source + * summary. The BlockingQueue blocks until a result + * is received from geolocation. + * @param filters The applicable filters for geolocation. */ - public PointFetcher(BlockingQueue asyncResult, GeoFilter filters) { + PointFetcher(BlockingQueue asyncResult, GeoFilter filters) { super(filters); this.asyncResult = asyncResult; } @@ -531,6 +538,7 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor { * Fetches all GPS data for the data source from the current case. * * @param dataSource The data source. + * * @return The GPS data pertaining to the data source. * @throws SleuthkitCaseProviderException * @throws GeoLocationDataException diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/LatLngMap.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/LatLngMap.java index 01032f61da..22a004c03c 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/LatLngMap.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/LatLngMap.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"); @@ -64,8 +64,8 @@ class LatLngMap { * Main contructor. * * @param pointsToAdd The points to be added to the data structure. - * @param bucketSize The size of a grid square in kilometers. So, if this - * value is 100, each sqaure will be a 100 x 100 km. + * @param bucketSize The size of a grid square in kilometers. So, if this + * value is 100, each sqaure will be a 100 x 100 km. */ LatLngMap(List pointsToAdd, double bucketSize) { this.bucketSize = bucketSize; @@ -86,6 +86,7 @@ class LatLngMap { * closest neighboring buckets. * * @param point The point to calculate the bucket location pair. + * * @return The pair that was determined. */ private Pair getBucketLocation(XYZPoint point) { @@ -106,6 +107,7 @@ class LatLngMap { * Finds closest point within (.5 * bucketSize) distance. * * @param point The point for which to find closest. + * * @return Returns the found point. */ E findClosest(E point) { @@ -132,9 +134,10 @@ class LatLngMap { /** * Within the specific bucket, finds the closest point if any exists. * - * @param x The x axis bucket. - * @param y The y axis bucket. + * @param x The x axis bucket. + * @param y The y axis bucket. * @param point The point to search for. + * * @return The point, if any, that was found. */ private E findClosestInBucket(int x, int y, E point) { diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/MimeTypeSummary.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/MimeTypeSummary.java old mode 100644 new mode 100755 index e753a44a76..4c5e0331eb --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/MimeTypeSummary.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/MimeTypeSummary.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"); @@ -18,30 +18,22 @@ */ package org.sleuthkit.autopsy.datasourcesummary.datamodel; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultUpdateGovernor; import java.sql.SQLException; -import java.util.Arrays; -import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; 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; /** - * Provides methods to query for datasource files by mime type. + * Class to export summary information used by TypesPanel tab on the known files + * present in the specified DataSource. */ -public class MimeTypeSummary implements DefaultUpdateGovernor { +public class MimeTypeSummary { private final SleuthkitCaseProvider provider; - private static final Set INGEST_JOB_EVENTS = new HashSet<>( - Arrays.asList(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED)); - /** * Main constructor. */ @@ -58,26 +50,6 @@ public class MimeTypeSummary implements DefaultUpdateGovernor { this.provider = provider; } - @Override - public boolean isRefreshRequired(ModuleContentEvent evt) { - return true; - } - - @Override - public boolean isRefreshRequired(AbstractFile file) { - return true; - } - - @Override - public boolean isRefreshRequired(IngestManager.IngestJobEvent evt) { - return (evt != null && INGEST_JOB_EVENTS.contains(evt)); - } - - @Override - public Set getIngestJobEventUpdates() { - return INGEST_JOB_EVENTS; - } - /** * Get the number of files in the case database for the current data source * which have the specified mimetypes. @@ -98,12 +70,7 @@ 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) - ); + return DataSourceInfoUtilities.getCountOfRegNonSlackFiles(provider.get(), currentDataSource, "mime_type IN " + getSqlSet(setOfMimeTypes)); } /** @@ -124,13 +91,9 @@ public class MimeTypeSummary implements DefaultUpdateGovernor { */ public Long getCountOfFilesNotInMimeTypes(DataSource currentDataSource, Set setOfMimeTypes) throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException { - - return DataSourceInfoUtilities.getCountOfRegNonSlackFiles( - provider.get(), - currentDataSource, + return DataSourceInfoUtilities.getCountOfRegNonSlackFiles(provider.get(), currentDataSource, "mime_type NOT IN " + getSqlSet(setOfMimeTypes) - + " AND mime_type IS NOT NULL AND mime_type <> '' " - ); + + " AND mime_type IS NOT NULL AND mime_type <> '' "); } /** @@ -146,7 +109,6 @@ public class MimeTypeSummary implements DefaultUpdateGovernor { */ public Long getCountOfAllRegularFiles(DataSource dataSource) throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException { - return DataSourceInfoUtilities.getCountOfRegNonSlackFiles(provider.get(), dataSource, null); } @@ -164,12 +126,7 @@ 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 = '') " - ); + return DataSourceInfoUtilities.getCountOfRegNonSlackFiles(provider.get(), currentDataSource, "(mime_type IS NULL OR mime_type = '') "); } /** diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/PastCasesSummary.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/PastCasesSummary.java old mode 100644 new mode 100755 index ea6c089fca..66e265e128 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/PastCasesSummary.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/PastCasesSummary.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2019 Basis Technology Corp. + * Copyright 2021 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -31,7 +31,6 @@ import java.util.stream.Stream; import org.apache.commons.lang3.tuple.Pair; import org.sleuthkit.autopsy.centralrepository.ingestmodule.CentralRepoIngestModuleFactory; import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultArtifactUpdateGovernor; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.BlackboardAttribute; @@ -62,7 +61,7 @@ import org.sleuthkit.datamodel.TskCoreException; * d) The content of that TSK_COMMENT attribute will be of the form "Previous * Case: case1,case2...caseN" */ -public class PastCasesSummary implements DefaultArtifactUpdateGovernor { +public class PastCasesSummary { /** * Return type for results items in the past cases tab. @@ -87,22 +86,17 @@ public class PastCasesSummary implements DefaultArtifactUpdateGovernor { * @return Data for the cases with same id table. */ public List> getSameIdsResults() { - return sameIdsResults; + return Collections.unmodifiableList(sameIdsResults); } /** * @return Data for the tagged notable table. */ public List> getTaggedNotable() { - return taggedNotable; + return Collections.unmodifiableList(taggedNotable); } } - private static final Set ARTIFACT_UPDATE_TYPE_IDS = new HashSet<>(Arrays.asList( - ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID(), - ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID() - )); - private static final String CENTRAL_REPO_INGEST_NAME = CentralRepoIngestModuleFactory.getModuleName().toUpperCase().trim(); private static final BlackboardAttribute.Type TYPE_COMMENT = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_COMMENT); private static final BlackboardAttribute.Type TYPE_ASSOCIATED_ARTIFACT = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT); @@ -147,11 +141,6 @@ public class PastCasesSummary implements DefaultArtifactUpdateGovernor { this.logger = logger; } - @Override - public Set getArtifactTypeIdsForRefresh() { - return ARTIFACT_UPDATE_TYPE_IDS; - } - /** * Given the provided sources for an attribute, aims to determine if one of * those sources is the Central Repository Ingest Module. @@ -224,7 +213,7 @@ public class PastCasesSummary implements DefaultArtifactUpdateGovernor { * @return The list of unique cases and their occurrences sorted from max to * min. */ - private List> getCaseCounts(Stream cases) { + private static List> getCaseCounts(Stream cases) { Collection> groupedCases = cases // group by case insensitive compare of cases .collect(Collectors.groupingBy((caseStr) -> caseStr.toUpperCase().trim())) diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/RecentFilesSummary.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/RecentFilesSummary.java index 4f1a34fa73..1beb97766b 100755 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/RecentFilesSummary.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/RecentFilesSummary.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2020 Basis Technology Corp. + * Copyright 2021 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,21 +18,18 @@ */ package org.sleuthkit.autopsy.datasourcesummary.datamodel; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultArtifactUpdateGovernor; 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.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; @@ -40,13 +37,12 @@ import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.DataSource; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; -import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; /** - * Helper class for getting data for the Recent Files Data Summary tab. + * Helper class for getting Recent Activity data. */ -public class RecentFilesSummary implements DefaultArtifactUpdateGovernor { +public class RecentFilesSummary { private final static BlackboardAttribute.Type DATETIME_ACCESSED_ATT = new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED); private final static BlackboardAttribute.Type DOMAIN_ATT = new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN); @@ -58,14 +54,6 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor { private static final DateFormat DATETIME_FORMAT = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.getDefault()); - private static final Set ARTIFACT_UPDATE_TYPE_IDS = new HashSet<>(Arrays.asList( - ARTIFACT_TYPE.TSK_RECENT_OBJECT.getTypeID(), - ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID(), - ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT.getTypeID(), - ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID(), - ARTIFACT_TYPE.TSK_MESSAGE.getTypeID() - )); - private final SleuthkitCaseProvider provider; /** @@ -88,11 +76,6 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor { this.provider = provider; } - @Override - public Set getArtifactTypeIdsForRefresh() { - return ARTIFACT_UPDATE_TYPE_IDS; - } - /** * Removes fileDetails entries with redundant paths, sorts by date * descending and limits to the limit provided. @@ -101,7 +84,7 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor { * @param limit The maximum number of entries to return. * @return The sorted limited list with unique paths. */ - private List getSortedLimited(List fileDetails, int limit) { + private static List getSortedLimited(List fileDetails, int limit) { Map fileDetailsMap = fileDetails.stream() .filter(details -> details != null) .collect(Collectors.toMap( @@ -122,7 +105,7 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor { * @param artifact The artifact. * @return The derived object or null if artifact is invalid. */ - private RecentFileDetails getRecentlyOpenedDocument(BlackboardArtifact artifact) { + private static RecentFileDetails getRecentlyOpenedDocument(BlackboardArtifact artifact) { String path = DataSourceInfoUtilities.getStringOrNull(artifact, PATH_ATT); Long lastOpened = DataSourceInfoUtilities.getLongOrNull(artifact, DATETIME_ACCESSED_ATT); @@ -170,7 +153,7 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor { * @param artifact The artifact. * @return The derived object or null if artifact is invalid. */ - private RecentDownloadDetails getRecentDownload(BlackboardArtifact artifact) { + private static RecentDownloadDetails getRecentDownload(BlackboardArtifact artifact) { Long accessedTime = DataSourceInfoUtilities.getLongOrNull(artifact, DATETIME_ACCESSED_ATT); String domain = DataSourceInfoUtilities.getStringOrNull(artifact, DOMAIN_ATT); String path = DataSourceInfoUtilities.getStringOrNull(artifact, PATH_ATT); @@ -187,7 +170,7 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor { * * @param count The count. */ - private void throwOnNonPositiveCount(int count) { + private static void throwOnNonPositiveCount(int count) { if (count < 1) { throw new IllegalArgumentException("Invalid count: value must be greater than 0."); } @@ -268,7 +251,7 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor { * @return The derived object or null. * @throws TskCoreException */ - private RecentAttachmentDetails getRecentAttachment(BlackboardArtifact artifact, SleuthkitCase skCase) throws TskCoreException { + private static RecentAttachmentDetails getRecentAttachment(BlackboardArtifact artifact, SleuthkitCase skCase) throws TskCoreException { // get associated artifact or return no result BlackboardAttribute attribute = artifact.getAttribute(ASSOCATED_ATT); if (attribute == null) { @@ -309,7 +292,7 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor { * * @return True if the given artifact is a message artifact */ - private boolean isMessageArtifact(BlackboardArtifact nodeArtifact) { + private static boolean isMessageArtifact(BlackboardArtifact nodeArtifact) { final int artifactTypeID = nodeArtifact.getArtifactTypeID(); return artifactTypeID == ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID() || artifactTypeID == ARTIFACT_TYPE.TSK_MESSAGE.getTypeID(); diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/SleuthkitCaseProvider.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/SleuthkitCaseProvider.java old mode 100644 new mode 100755 diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TimelineDataSourceUtils.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TimelineDataSourceUtils.java old mode 100644 new mode 100755 index 40f76cce80..6528284911 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TimelineDataSourceUtils.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TimelineDataSourceUtils.java @@ -18,7 +18,6 @@ */ package org.sleuthkit.autopsy.datasourcesummary.datamodel; -import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.timeline.TimeLineController; import org.sleuthkit.autopsy.timeline.TimeLineModule; import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.FilterState; @@ -60,10 +59,9 @@ public class TimelineDataSourceUtils { * @param dataSource The data source. * @return The root filter representing a default filter with only this data * source selected. - * @throws NoCurrentCaseException * @throws TskCoreException */ - public RootFilter getDataSourceFilter(DataSource dataSource) throws NoCurrentCaseException, TskCoreException { + public RootFilter getDataSourceFilter(DataSource dataSource) throws TskCoreException { RootFilterState filterState = getDataSourceFilterState(dataSource); return filterState == null ? null : filterState.getActiveFilter(); } @@ -75,10 +73,9 @@ public class TimelineDataSourceUtils { * @param dataSource The data source. * @return The root filter state representing a default filter with only * this data source selected. - * @throws NoCurrentCaseException * @throws TskCoreException */ - public RootFilterState getDataSourceFilterState(DataSource dataSource) throws NoCurrentCaseException, TskCoreException { + public RootFilterState getDataSourceFilterState(DataSource dataSource) throws TskCoreException { TimeLineController controller = TimeLineModule.getController(); RootFilterState dataSourceState = controller.getEventsModel().getDefaultEventFilterState().copyOf(); diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TimelineSummary.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TimelineSummary.java old mode 100644 new mode 100755 index a43d46764b..28c616a89f --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TimelineSummary.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TimelineSummary.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"); @@ -18,6 +18,8 @@ */ package org.sleuthkit.autopsy.datasourcesummary.datamodel; +import java.text.DateFormat; +import java.text.SimpleDateFormat; import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; @@ -26,29 +28,25 @@ import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.TimeZone; import org.joda.time.Interval; -import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultUpdateGovernor; -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.TimelineEvent; import org.sleuthkit.datamodel.TimelineEventType; import org.sleuthkit.datamodel.TimelineFilter.RootFilter; import org.sleuthkit.datamodel.TimelineManager; import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException; import java.util.function.Supplier; -import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.core.UserPreferences; /** * Provides data source summary information pertaining to Timeline data. */ -public class TimelineSummary implements DefaultUpdateGovernor { +public class TimelineSummary { /** * A function for obtaining a Timeline RootFilter filtered to the specific @@ -61,16 +59,13 @@ public class TimelineSummary implements DefaultUpdateGovernor { * * @param dataSource The data source. * @return The timeline root filter. - * @throws NoCurrentCaseException + * @throws SleuthkitCaseProviderException * @throws TskCoreException */ - RootFilter apply(DataSource dataSource) throws NoCurrentCaseException, TskCoreException; + RootFilter apply(DataSource dataSource) throws SleuthkitCaseProviderException, TskCoreException; } private static final long DAY_SECS = 24 * 60 * 60; - private static final Set INGEST_JOB_EVENTS = new HashSet<>( - Arrays.asList(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED)); - private static final Set FILE_SYSTEM_EVENTS = new HashSet<>(Arrays.asList( TimelineEventType.FILE_MODIFIED, @@ -105,39 +100,19 @@ public class TimelineSummary implements DefaultUpdateGovernor { this.filterFunction = filterFunction; } - @Override - public boolean isRefreshRequired(ModuleContentEvent evt) { - return true; - } - - @Override - public boolean isRefreshRequired(AbstractFile file) { - return true; - } - - @Override - public boolean isRefreshRequired(IngestManager.IngestJobEvent evt) { - return (evt != null && INGEST_JOB_EVENTS.contains(evt)); - } - - @Override - public Set getIngestJobEventUpdates() { - return INGEST_JOB_EVENTS; - } - /** * Retrieves timeline summary data. * - * @param dataSource The data source for which timeline data will be - * retrieved. + * @param dataSource The data source for which timeline data will be + * retrieved. * @param recentDaysNum The maximum number of most recent days' activity to - * include. + * include. + * * @return The retrieved data. * @throws SleuthkitCaseProviderException * @throws TskCoreException - * @throws NoCurrentCaseException */ - public TimelineSummaryData getData(DataSource dataSource, int recentDaysNum) throws SleuthkitCaseProviderException, TskCoreException, NoCurrentCaseException { + public TimelineSummaryData getTimelineSummaryData(DataSource dataSource, int recentDaysNum) throws SleuthkitCaseProviderException, TskCoreException { TimeZone timeZone = this.timeZoneProvider.get(); TimelineManager timelineManager = this.caseProvider.get().getTimelineManager(); @@ -174,10 +149,11 @@ public class TimelineSummary implements DefaultUpdateGovernor { * Given activity by day, converts to most recent days' activity handling * empty values. * - * @param dateCounts The day from epoch mapped to activity amounts for that - * day. + * @param dateCounts The day from epoch mapped to activity amounts for + * that day. * @param minRecentDay The minimum recent day in days from epoch. - * @param maxDay The maximum recent day in days from epoch; + * @param maxDay The maximum recent day in days from epoch; + * * @return The most recent daily activity amounts. */ private List getMostRecentActivityAmounts(Map dateCounts, long minRecentDay, long maxDay) { @@ -197,17 +173,18 @@ public class TimelineSummary implements DefaultUpdateGovernor { /** * Fetches timeline events per day for a particular data source. * - * @param dataSource The data source. + * @param dataSource The data source. * @param timelineManager The timeline manager to use while fetching the - * data. - * @param timeZone The time zone to use to determine which day activity - * belongs. + * data. + * @param timeZone The time zone to use to determine which day + * activity belongs. + * * @return A Map mapping days from epoch to the activity for that day. + * * @throws TskCoreException - * @throws NoCurrentCaseException */ private Map getTimelineEventsByDay(DataSource dataSource, TimelineManager timelineManager, TimeZone timeZone) - throws TskCoreException, NoCurrentCaseException { + throws TskCoreException, SleuthkitCaseProviderException { RootFilter rootFilter = this.filterFunction.apply(dataSource); // get events for data source @@ -251,12 +228,14 @@ public class TimelineSummary implements DefaultUpdateGovernor { /** * Main constructor. * - * @param minDate Earliest usage date recorded for the data source. - * @param maxDate Latest usage date recorded for the data source. + * @param minDate Earliest usage date recorded for the data + * source. + * @param maxDate Latest usage date recorded for the data + * source. * @param recentDaysActivity A list of activity prior to and including - * max date sorted by min to max date. - * @param dataSource The data source for which this data applies. the - * latest usage date by day. + * max date sorted by min to max date. + * @param dataSource The data source for which this data + * applies. the latest usage date by day. */ TimelineSummaryData(Date minDate, Date maxDate, List recentDaysActivity, DataSource dataSource) { this.minDate = minDate; @@ -281,7 +260,7 @@ public class TimelineSummary implements DefaultUpdateGovernor { /** * @return A list of activity prior to and including the latest usage - * date by day sorted min to max date. + * date by day sorted min to max date. */ public List getMostRecentDaysActivity() { return histogramActivity; @@ -307,8 +286,10 @@ public class TimelineSummary implements DefaultUpdateGovernor { /** * Main constructor. * - * @param day The day for which activity is being measured. - * @param fileActivityCount The amount of file activity timeline events. + * @param day The day for which activity is being + * measured. + * @param fileActivityCount The amount of file activity timeline + * events. * @param artifactActivityCount The amount of artifact timeline events. */ DailyActivityAmount(Date day, long fileActivityCount, long artifactActivityCount) { @@ -337,6 +318,29 @@ public class TimelineSummary implements DefaultUpdateGovernor { public long getArtifactActivityCount() { return artifactActivityCount; } - } + + /** + * Creates a DateFormat formatter that uses UTC for time zone. + * + * @param formatString The date format string. + * @return The data format. + */ + public static DateFormat getUtcFormat(String formatString) { + return new SimpleDateFormat(formatString, Locale.getDefault()); + } + + /** + * Formats a date using a DateFormat. In the event that the date is null, + * returns a null string. + * + * @param date The date to format. + * @param formatter The DateFormat to use to format the date. + * + * @return The formatted string generated from the formatter or null if the + * date is null. + */ + public static String formatDate(Date date, DateFormat formatter) { + return date == null ? null : formatter.format(date); + } } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TypesSummary.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TypesSummary.java old mode 100644 new mode 100755 index ff4bcae0a0..b13e852b95 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TypesSummary.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TypesSummary.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2019 - 2020 Basis Technology Corp. + * Copyright 2019 - 2021 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,26 +18,19 @@ */ package org.sleuthkit.autopsy.datasourcesummary.datamodel; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultUpdateGovernor; +import java.awt.Color; import java.sql.SQLException; -import java.util.Arrays; -import java.util.HashSet; import java.util.Set; -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.autopsy.coreutils.FileTypeUtils; import org.sleuthkit.datamodel.DataSource; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; /** - * Provides information for the DataSourceSummaryCountsPanel. + * Helper class for getting summary information on the known files present in the + * specified DataSource.. */ -public class TypesSummary implements DefaultUpdateGovernor { - - private static final Set INGEST_JOB_EVENTS = new HashSet<>( - Arrays.asList(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED)); +public class TypesSummary { private final SleuthkitCaseProvider provider; @@ -57,25 +50,6 @@ public class TypesSummary implements DefaultUpdateGovernor { this.provider = provider; } - @Override - public boolean isRefreshRequired(ModuleContentEvent evt) { - return true; - } - - @Override - public boolean isRefreshRequired(AbstractFile file) { - return true; - } - - @Override - public boolean isRefreshRequired(IngestManager.IngestJobEvent evt) { - return (evt != null && INGEST_JOB_EVENTS.contains(evt)); - } - - @Override - public Set getIngestJobEventUpdates() { - return INGEST_JOB_EVENTS; - } /** * Get count of regular files (not directories) in a data source. @@ -169,4 +143,59 @@ public class TypesSummary implements DefaultUpdateGovernor { return DataSourceInfoUtilities.getCountOfRegularFiles(provider.get(), 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/UserActivitySummary.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/UserActivitySummary.java old mode 100644 new mode 100755 index 10f700d51f..a32867ffce --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/UserActivitySummary.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/UserActivitySummary.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"); @@ -19,7 +19,6 @@ package org.sleuthkit.autopsy.datasourcesummary.datamodel; import java.io.File; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultArtifactUpdateGovernor; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -54,7 +53,7 @@ import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; * time, the data being provided for domains is fictitious and is done as a * placeholder. */ -public class UserActivitySummary implements DefaultArtifactUpdateGovernor { +public class UserActivitySummary { /** * Functions that determine the folder name of a list of path elements. If @@ -138,16 +137,6 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { .compareToIgnoreCase((b.getProgramName() == null ? "" : b.getProgramName())); }; - private static final Set ARTIFACT_UPDATE_TYPE_IDS = new HashSet<>(Arrays.asList( - ARTIFACT_TYPE.TSK_WEB_SEARCH_QUERY.getTypeID(), - ARTIFACT_TYPE.TSK_MESSAGE.getTypeID(), - ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID(), - ARTIFACT_TYPE.TSK_CALLLOG.getTypeID(), - ARTIFACT_TYPE.TSK_DEVICE_ATTACHED.getTypeID(), - ARTIFACT_TYPE.TSK_WEB_HISTORY.getTypeID(), - ARTIFACT_TYPE.TSK_PROG_RUN.getTypeID() - )); - private static final Set DEVICE_EXCLUDE_LIST = new HashSet<>(Arrays.asList("ROOT_HUB", "ROOT_HUB20")); private static final Set DOMAIN_EXCLUDE_LIST = new HashSet<>(Arrays.asList("127.0.0.1", "LOCALHOST")); @@ -186,27 +175,55 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { this.logger = logger; } - @Override - public Set getArtifactTypeIdsForRefresh() { - return ARTIFACT_UPDATE_TYPE_IDS; - } - /** * Throws an IllegalArgumentException if count <= 0. * * @param count The count being checked. */ - private void assertValidCount(int count) { + private static void assertValidCount(int count) { if (count <= 0) { throw new IllegalArgumentException("Count must be greater than 0"); } } + + /** + * Determines a short folder name if any. Otherwise, returns empty string. + * + * @param strPath The string path. + * @param applicationName The application name. + * + * @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 && org.apache.commons.lang.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 (org.apache.commons.lang.StringUtils.isNotBlank(result)) { + return result; + } + } + + return ""; + } /** * Gets a list of recent domains based on the datasource. * * @param dataSource The datasource to query for recent domains. - * @param count The max count of items to return. + * @param count The max count of items to return. * * @return The list of items retrieved from the database. * @@ -242,13 +259,13 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { * Creates a TopDomainsResult from data or null if no visit date exists * within DOMAIN_WINDOW_MS of mostRecentMs. * - * @param domain The domain. - * @param visits The list of the artifact and its associated time in - * milliseconds. + * @param domain The domain. + * @param visits The list of the artifact and its associated time in + * milliseconds. * @param mostRecentMs The most recent visit of any domain. * * @return The TopDomainsResult or null if no visits to this domain within - * 30 days of mostRecentMs. + * 30 days of mostRecentMs. */ private TopDomainsResult getDomainsResult(String domain, List> visits, long mostRecentMs) { long visitCount = 0; @@ -288,9 +305,9 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { * @param dataSource The datasource. * * @return A tuple where the first value is the latest web history accessed - * date in milliseconds and the second value maps normalized (lowercase; - * trimmed) domain names to when those domains were visited and the relevant - * artifact. + * date in milliseconds and the second value maps normalized + * (lowercase; trimmed) domain names to when those domains were + * visited and the relevant artifact. * * @throws TskCoreException * @throws SleuthkitCaseProviderException @@ -357,7 +374,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { * @param artifact The artifact. * * @return The TopWebSearchResult or null if the search string or date - * accessed cannot be determined. + * accessed cannot be determined. */ private static TopWebSearchResult getWebSearchResult(BlackboardArtifact artifact) { String searchString = DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_TEXT); @@ -372,10 +389,11 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { * term. * * @param dataSource The data source. - * @param count The maximum number of records to be shown (must be > 0). + * @param count The maximum number of records to be shown (must be > + * 0). * * @return The list of most recent web searches where most recent search - * appears first. + * appears first. * * @throws * org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException @@ -462,6 +480,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { * * @param r1 A result. * @param r2 Another result. + * * @return The most recent one with a non-null date. */ private TopDeviceAttachedResult getMostRecentDevice(TopDeviceAttachedResult r1, TopDeviceAttachedResult r2) { @@ -480,10 +499,11 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { * Retrieves most recent devices used by most recent date attached. * * @param dataSource The data source. - * @param count The maximum number of records to be shown (must be > 0). + * @param count The maximum number of records to be shown (must be > + * 0). * * @return The list of most recent devices attached where most recent device - * attached appears first. + * attached appears first. * * @throws * org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException @@ -528,7 +548,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { * @param artifact The artifact. * * @return The TopAccountResult or null if the account type or message date - * cannot be determined. + * cannot be determined. */ private static TopAccountResult getMessageAccountResult(BlackboardArtifact artifact) { String type = DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_MESSAGE_TYPE); @@ -542,12 +562,12 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { * Obtains a TopAccountResult from a blackboard artifact. The date is * maximum of any found dates for attribute types provided. * - * @param artifact The artifact. + * @param artifact The artifact. * @param messageType The type of message this is. - * @param dateAttrs The date attribute types. + * @param dateAttrs The date attribute types. * * @return The TopAccountResult or null if the account type or max date are - * not provided. + * not provided. */ private static TopAccountResult getAccountResult(BlackboardArtifact artifact, String messageType, BlackboardAttribute.Type... dateAttrs) { String type = messageType; @@ -638,39 +658,6 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { .collect(Collectors.toList()); } - /** - * Determines a short folder name if any. Otherwise, returns empty string. - * - * @param strPath The string path. - * @param applicationName The application name. - * - * @return The short folder name or empty string if not found. - */ - public 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 && org.apache.commons.lang.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 (org.apache.commons.lang.StringUtils.isNotBlank(result)) { - return result; - } - } - - return ""; - } - /** * Creates a TopProgramsResult from a TSK_PROG_RUN blackboard artifact. * @@ -764,12 +751,12 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { * be ignored and all items will be returned. * * @param dataSource The datasource. If the datasource is null, an empty - * list will be returned. - * @param count The number of results to return. This value must be > 0 or - * an IllegalArgumentException will be thrown. + * list will be returned. + * @param count The number of results to return. This value must be > 0 + * or an IllegalArgumentException will be thrown. * * @return The sorted list and limited to the count if last run or run count - * information is available on any item. + * information is available on any item. * * @throws SleuthkitCaseProviderException * @throws TskCoreException @@ -840,7 +827,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { * Main constructor. * * @param lastAccessed The date of last access. - * @param artifact The relevant blackboard artifact. + * @param artifact The relevant blackboard artifact. */ public LastAccessedArtifact(Date lastAccessed, BlackboardArtifact artifact) { this.lastAccessed = lastAccessed; @@ -875,7 +862,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { * * @param searchString The search string. * @param dateAccessed The latest date searched. - * @param artifact The relevant blackboard artifact. + * @param artifact The relevant blackboard artifact. */ public TopWebSearchResult(String searchString, Date dateAccessed, BlackboardArtifact artifact) { super(dateAccessed, artifact); @@ -918,11 +905,11 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { /** * Main constructor. * - * @param deviceId The device id. + * @param deviceId The device id. * @param dateAccessed The date last attached. - * @param deviceMake The device make. - * @param deviceModel The device model. - * @param artifact The relevant blackboard artifact. + * @param deviceMake The device make. + * @param deviceModel The device model. + * @param artifact The relevant blackboard artifact. */ public TopDeviceAttachedResult(String deviceId, Date dateAccessed, String deviceMake, String deviceModel, BlackboardArtifact artifact) { super(dateAccessed, artifact); @@ -965,8 +952,8 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { * Main constructor. * * @param accountType The account type. - * @param lastAccess The date the account was last accessed. - * @param artifact The artifact indicating last access. + * @param lastAccess The date the account was last accessed. + * @param artifact The artifact indicating last access. */ public TopAccountResult(String accountType, Date lastAccess, BlackboardArtifact artifact) { super(lastAccess, artifact); @@ -992,10 +979,10 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { /** * Describes a top domain result. * - * @param domain The domain. + * @param domain The domain. * @param visitTimes The number of times it was visited. - * @param lastVisit The date of the last visit. - * @param artifact The relevant blackboard artifact. + * @param lastVisit The date of the last visit. + * @param artifact The relevant blackboard artifact. */ public TopDomainsResult(String domain, Long visitTimes, Date lastVisit, BlackboardArtifact artifact) { super(lastVisit, artifact); @@ -1032,8 +1019,8 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { * * @param programName The name of the program. * @param programPath The path of the program. - * @param runTimes The number of runs. - * @param artifact The relevant blackboard artifact. + * @param runTimes The number of runs. + * @param artifact The relevant blackboard artifact. */ TopProgramsResult(String programName, String programPath, Long runTimes, Date lastRun, BlackboardArtifact artifact) { super(lastRun, artifact); diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/AnalysisPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/AnalysisPanel.java index 736dce7f3c..c8b708d371 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/AnalysisPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/AnalysisPanel.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,16 +21,12 @@ package org.sleuthkit.autopsy.datasourcesummary.ui; import java.util.Arrays; import java.util.List; import java.util.function.Function; -import java.util.stream.Collectors; -import java.util.stream.Stream; import org.apache.commons.lang3.tuple.Pair; import org.openide.util.NbBundle.Messages; -import org.sleuthkit.autopsy.datasourcesummary.datamodel.AnalysisSummary; import org.sleuthkit.autopsy.datasourcesummary.uiutils.ColumnModel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetcher; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataFetcher; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultCellModel; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport.ExcelSheetExport; import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel; import org.sleuthkit.datamodel.DataSource; @@ -101,10 +97,10 @@ public class AnalysisPanel extends BaseDataSourceSummaryPanel { * Creates a new DataSourceUserActivityPanel. */ public AnalysisPanel() { - this(new AnalysisSummary()); + this(new AnalysisSummaryGetter()); } - public AnalysisPanel(AnalysisSummary analysisData) { + public AnalysisPanel(AnalysisSummaryGetter analysisData) { super(analysisData); hashsetsFetcher = (dataSource) -> analysisData.getHashsetCounts(dataSource); @@ -229,17 +225,6 @@ public class AnalysisPanel extends BaseDataSourceSummaryPanel { ); }// //GEN-END:initComponents - @Override - List getExports(DataSource dataSource) { - return Stream.of( - getTableExport(hashsetsFetcher, DEFAULT_COLUMNS, Bundle.AnalysisPanel_hashsetHits_tabName(), dataSource), - getTableExport(keywordsFetcher, DEFAULT_COLUMNS, Bundle.AnalysisPanel_keywordHits_tabName(), dataSource), - getTableExport(interestingItemsFetcher, DEFAULT_COLUMNS, Bundle.AnalysisPanel_interestingItemHits_tabName(), dataSource)) - .filter(sheet -> sheet != null) - .collect(Collectors.toList()); - } - - // Variables declaration - do not modify//GEN-BEGIN:variables // End of variables declaration//GEN-END:variables } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/AnalysisSummaryGetter.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/AnalysisSummaryGetter.java new file mode 100644 index 0000000000..135a5ef757 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/AnalysisSummaryGetter.java @@ -0,0 +1,105 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2020-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.datasourcesummary.ui; + +import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultArtifactUpdateGovernor; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.apache.commons.lang3.tuple.Pair; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.AnalysisSummary; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException; +import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; +import org.sleuthkit.datamodel.DataSource; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Wrapper class for converting + * org.sleuthkit.autopsy.contentutils.AnalysisSummary functionality into a + * DefaultArtifactUpdateGovernor used by data source analysis tab. + */ +public class AnalysisSummaryGetter implements DefaultArtifactUpdateGovernor { + + private static final Set ARTIFACT_UPDATE_TYPE_IDS = new HashSet<>(Arrays.asList( + ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID(), + ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID(), + ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID(), + ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID() + )); + + private final AnalysisSummary analysisSummary; + + /** + * Main constructor. + */ + public AnalysisSummaryGetter() { + analysisSummary = new AnalysisSummary(); + } + + @Override + public Set getArtifactTypeIdsForRefresh() { + return Collections.unmodifiableSet(ARTIFACT_UPDATE_TYPE_IDS); + } + + /** + * Gets counts for hashset hits. + * + * @param dataSource The datasource for which to identify hashset hits. + * + * @return The hashset set name with the number of hits in descending order. + * + * @throws SleuthkitCaseProviderException + * @throws TskCoreException + */ + public List> getHashsetCounts(DataSource dataSource) throws SleuthkitCaseProviderException, TskCoreException { + return analysisSummary.getHashsetCounts(dataSource); + } + + /** + * Gets counts for keyword hits. + * + * @param dataSource The datasource for which to identify keyword hits. + * + * @return The keyword set name with the number of hits in descending order. + * + * @throws SleuthkitCaseProviderException + * @throws TskCoreException + */ + public List> getKeywordCounts(DataSource dataSource) throws SleuthkitCaseProviderException, TskCoreException { + return analysisSummary.getKeywordCounts(dataSource); + } + + /** + * Gets counts for interesting item hits. + * + * @param dataSource The datasource for which to identify interesting item + * hits. + * + * @return The interesting item set name with the number of hits in + * descending order. + * + * @throws SleuthkitCaseProviderException + * @throws TskCoreException + */ + public List> getInterestingItemCounts(DataSource dataSource) throws SleuthkitCaseProviderException, TskCoreException { + return analysisSummary.getInterestingItemCounts(dataSource); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryPanel.java index 01773e5626..1cf103052a 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryPanel.java @@ -38,16 +38,11 @@ import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.ColumnModel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetcher; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataFetcher; import org.sleuthkit.autopsy.datasourcesummary.uiutils.EventUpdateHandler; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport.ExcelExportException; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport.ExcelSheetExport; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelTableExport; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelCellModel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.GuiCellModel.DefaultMenuItem; import org.sleuthkit.autopsy.datasourcesummary.uiutils.GuiCellModel.MenuItem; import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableComponent; @@ -453,14 +448,6 @@ abstract class BaseDataSourceSummaryPanel extends JPanel { */ protected abstract void onNewDataSource(DataSource dataSource); - /** - * Returns all the excel exportable items associated with the tab. - * - * @param dataSource The data source that results should be filtered. - * @return The excel exportable objects. - */ - abstract List getExports(DataSource dataSource); - /** * Runs a data fetcher and returns the result handling any possible errors * with a log message. @@ -485,100 +472,6 @@ abstract class BaseDataSourceSummaryPanel extends JPanel { } } - /** - * Function that converts data into a excel sheet data. - */ - protected interface ExcelExportFunction { - - /** - * Function that converts data into an excel sheet. - * - * @param data The data. - * @return The excel sheet export. - * @throws ExcelExportException - */ - ExcelSheetExport convert(T data) throws ExcelExportException; - } - - /** - * Helper method that converts data into an excel sheet export handling - * possible excel exceptions. - * - * @param excelConverter Function to convert data to an excel sheet export. - * @param data The data. If data is null, null will be returned. - * @param sheetName The name(s) of the sheet (to be used in the error - * message). - * @return The excel sheet export. - */ - protected static ExcelSheetExport convertToExcel(ExcelExportFunction excelConverter, T data, String sheetName) { - if (data == null) { - return null; - } - - try { - return excelConverter.convert(data); - } catch (ExcelExportException ex) { - logger.log(Level.WARNING, - String.format("There was an error while preparing export of worksheet(s): '%s'", - sheetName == null ? "" : sheetName), ex); - return null; - } - } - - /** - * Returns an excel sheet export given the fetching of data or null if no - * export created. - * - * @param dataFetcher The means of fetching data. - * @param excelConverter The means of converting data to excel. - * @param sheetName The name of the sheet (for error handling reporting). - * @param ds The data source to use for fetching data. - * @return The excel sheet export or null if no export could be generated. - */ - protected static ExcelSheetExport getExport( - DataFetcher dataFetcher, ExcelExportFunction excelConverter, - String sheetName, DataSource ds) { - - T data = getFetchResult(dataFetcher, sheetName, ds); - return convertToExcel(excelConverter, data, sheetName); - } - - /** - * Returns an excel table export of the data or null if no export created. - * - * @param columnsModel The model for the columns. - * @param sheetName The name for the sheet. - * @param data The data to be exported. - * @return The excel table export or null if no export could be generated. - */ - protected static ExcelSheetExport getTableExport(List> columnsModel, - String sheetName, List data) { - - return convertToExcel((dataList) -> new ExcelTableExport(sheetName, columnsModel, dataList), - data, - sheetName); - } - - /** - * Returns an excel table export of the data or null if no export created. - * - * @param dataFetcher The means of fetching data for the data source and the - * export. - * @param columnsModel The model for the columns. - * @param sheetName The name for the sheet. - * @param ds The data source. - * @return The excel export or null if no export created. - */ - protected static ExcelSheetExport getTableExport( - DataFetcher> dataFetcher, List> columnsModel, - String sheetName, DataSource ds) { - - return getExport(dataFetcher, - (dataList) -> new ExcelTableExport(sheetName, columnsModel, dataList), - sheetName, - ds); - } - /** * Utility method that shows a loading screen with loadable components, * create swing workers from the datafetch components and data source diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED index a7b3c8870c..a7a4e5870f 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED @@ -6,19 +6,6 @@ AnalysisPanel_keywordHits_tabName=Keyword Hits AnalysisPanel_keywordSearchModuleName=Keyword Search BaseDataSourceSummaryPanel_goToArtifact=View Source Result BaseDataSourceSummaryPanel_goToFile=View Source File in Directory -ContainerPanel_export_acquisitionDetails=Acquisition Details: -ContainerPanel_export_deviceId=Device ID: -ContainerPanel_export_displayName=Display Name: -ContainerPanel_export_filePaths=File Paths: -ContainerPanel_export_imageType=Image Type: -ContainerPanel_export_md5=MD5: -ContainerPanel_export_originalName=Name: -ContainerPanel_export_sectorSize=Sector Size: -ContainerPanel_export_sha1=SHA1: -ContainerPanel_export_sha256=SHA256: -ContainerPanel_export_size=Size: -ContainerPanel_export_timeZone=Time Zone: -ContainerPanel_export_unallocatedSize=Unallocated Space: ContainerPanel_setFieldsForNonImageDataSource_na=N/A ContainerPanel_tabName=Container CTL_DataSourceSummaryAction=Data Source Summary @@ -62,7 +49,6 @@ DataSourceSummaryNode.column.type.header=Type DataSourceSummaryNode.viewDataSourceAction.text=Go to Data Source DataSourceSummaryTabbedPane_analysisTab_title=Analysis DataSourceSummaryTabbedPane_detailsTab_title=Container -DataSourceSummaryTabbedPane_exportTab_title=Export DataSourceSummaryTabbedPane_geolocationTab_title=Geolocation DataSourceSummaryTabbedPane_ingestHistoryTab_title=Ingest History DataSourceSummaryTabbedPane_pastCasesTab_title=Past Cases @@ -70,45 +56,27 @@ DataSourceSummaryTabbedPane_recentFileTab_title=Recent Files DataSourceSummaryTabbedPane_timelineTab_title=Timeline DataSourceSummaryTabbedPane_typesTab_title=Types DataSourceSummaryTabbedPane_userActivityTab_title=User Activity -ExcelExportAction_exportToXLSX_beginExport=Beginning Export... -# {0} - tabName -ExcelExportAction_exportToXLSX_gatheringTabData=Fetching Data for {0} Tab... -ExcelExportAction_exportToXLSX_writingToFile=Writing to File... -ExcelExportAction_getXLSXPath_directory=DataSourceSummary -ExcelExportAction_moduleName=Data Source Summary -ExcelExportAction_runXLSXExport_errorMessage=There was an error while exporting. -ExcelExportAction_runXLSXExport_errorTitle=Error While Exporting -ExcelExportAction_runXLSXExport_progressCancelActionTitle=Cancelling... -ExcelExportAction_runXLSXExport_progressCancelTitle=Cancel -# {0} - dataSource -ExcelExportAction_runXLSXExport_progressTitle=Exporting {0} to XLSX -ExcelExportDialog_title=Data Source Summary Exported +DataSourceUserActivitySummary_getRecentAccounts_calllogMessage=Call Log +DataSourceUserActivitySummary_getRecentAccounts_emailMessage=Email Message GeolocationPanel_cityColumn_title=Closest City GeolocationPanel_countColumn_title=Count GeolocationPanel_mostCommon_tabName=Most Common Cities GeolocationPanel_mostRecent_tabName=Most Recent Cities GeolocationPanel_onNoCrIngest_message=No results will be shown because the GPX Parser was not run. GeolocationPanel_unknownRow_title=Unknown -IngestJobExcelExport_endTimeColumn=End Time -IngestJobExcelExport_ingestStatusTimeColumn=Ingest Status -IngestJobExcelExport_moduleNameTimeColumn=Module Name -IngestJobExcelExport_sheetName=Ingest History -IngestJobExcelExport_startTimeColumn=Start Time -IngestJobExcelExport_versionColumn=Module Version PastCasesPanel_caseColumn_title=Case PastCasesPanel_countColumn_title=Count PastCasesPanel_notableFileTable_tabName=Cases with Common Notable PastCasesPanel_onNoCrIngest_message=No results will be shown because the Central Repository module was not run. PastCasesPanel_sameIdsTable_tabName=Past Cases with the Same Devices -RecentFilePanel_col_header_domain=Domain -RecentFilePanel_col_header_path=Path -RecentFilePanel_col_header_sender=Sender -RecentFilePanel_emailParserModuleName=Email Parser -RecentFilePanel_no_open_documents=No recently open documents found. RecentFilesPanel_attachmentsTable_tabName=Recent Attachments RecentFilesPanel_col_head_date=Date +RecentFilesPanel_col_header_domain=Domain +RecentFilesPanel_col_header_path=Path +RecentFilesPanel_col_header_sender=Sender RecentFilesPanel_docsTable_tabName=Recently Opened Documents RecentFilesPanel_downloadsTable_tabName=Recently Downloads +RecentFilesPanel_no_open_documents=No recently open documents found. SizeRepresentationUtil_units_bytes=bytes SizeRepresentationUtil_units_gigabytes=GB SizeRepresentationUtil_units_kilobytes=KB @@ -116,12 +84,6 @@ SizeRepresentationUtil_units_megabytes=MB SizeRepresentationUtil_units_petabytes=PB SizeRepresentationUtil_units_terabytes=TB TimelinePanel_earliestLabel_title=Earliest -TimelinePanel_getExports_activityRange=Activity Range -TimelinePanel_getExports_chartName=Last 30 Days -TimelinePanel_getExports_dateColumnHeader=Date -TimelinePanel_getExports_earliest=Earliest: -TimelinePanel_getExports_latest=Latest: -TimelinePanel_getExports_sheetName=Timeline TimelinePanel_latestLabel_title=Latest TimlinePanel_last30DaysChart_artifactEvts_title=Result Events TimlinePanel_last30DaysChart_fileEvts_title=File Events diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/ContainerPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/ContainerPanel.java index a8e5fef64e..9278b3e349 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/ContainerPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/ContainerPanel.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"); @@ -19,39 +19,23 @@ package org.sleuthkit.autopsy.datasourcesummary.ui; import java.beans.PropertyChangeEvent; -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.logging.Level; -import java.util.stream.Collectors; -import java.util.stream.Stream; import org.sleuthkit.autopsy.coreutils.Logger; import javax.swing.table.DefaultTableModel; -import org.apache.commons.lang.StringUtils; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.datasourcesummary.datamodel.ContainerSummary; -import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException; -import static org.sleuthkit.autopsy.datasourcesummary.ui.BaseDataSourceSummaryPanel.getFetchResult; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.ContainerSummary.ContainerDetails; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.ContainerSummary.ImageDetails; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult.ResultType; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetcher; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultCellModel; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataFetcher; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultUpdateGovernor; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport.ExcelSheetExport; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport.ExcelItemExportable; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport.KeyValueItemExportable; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport.SingleCellExportable; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport.TitledExportable; import org.sleuthkit.autopsy.datasourcesummary.uiutils.UpdateGovernor; import org.sleuthkit.datamodel.DataSource; -import org.sleuthkit.datamodel.Image; -import org.sleuthkit.datamodel.TskCoreException; /** * Panel to display additional details associated with a specific DataSource @@ -61,182 +45,6 @@ import org.sleuthkit.datamodel.TskCoreException; }) class ContainerPanel extends BaseDataSourceSummaryPanel { - /** - * View model data for data source images. - */ - private static class ImageViewModel { - - private final long unallocatedSize; - private final long size; - private final long sectorSize; - - private final String timeZone; - private final String imageType; - - private final List paths; - private final String md5Hash; - private final String sha1Hash; - private final String sha256Hash; - - /** - * Main constructor. - * - * @param unallocatedSize Size in bytes of unallocated space. - * @param size Total size in bytes. - * @param sectorSize Sector size in bytes. - * @param timeZone The time zone. - * @param imageType The type of image. - * @param paths The source paths for the image. - * @param md5Hash The md5 hash or null. - * @param sha1Hash The sha1 hash or null. - * @param sha256Hash The sha256 hash or null. - */ - ImageViewModel(long unallocatedSize, long size, long sectorSize, - String timeZone, String imageType, List paths, String md5Hash, - String sha1Hash, String sha256Hash) { - this.unallocatedSize = unallocatedSize; - this.size = size; - this.sectorSize = sectorSize; - this.timeZone = timeZone; - this.imageType = imageType; - this.paths = paths == null ? Collections.emptyList() : new ArrayList<>(paths); - this.md5Hash = md5Hash; - this.sha1Hash = sha1Hash; - this.sha256Hash = sha256Hash; - } - - /** - * @return Size in bytes of unallocated space. - */ - long getUnallocatedSize() { - return unallocatedSize; - } - - /** - * @return Total size in bytes. - */ - long getSize() { - return size; - } - - /** - * @return Sector size in bytes. - */ - long getSectorSize() { - return sectorSize; - } - - /** - * @return The time zone. - */ - String getTimeZone() { - return timeZone; - } - - /** - * @return The type of image. - */ - String getImageType() { - return imageType; - } - - /** - * @return The source paths for the image. - */ - List getPaths() { - return paths; - } - - /** - * @return The md5 hash or null. - */ - String getMd5Hash() { - return md5Hash; - } - - /** - * @return The sha1 hash or null. - */ - String getSha1Hash() { - return sha1Hash; - } - - /** - * @return The sha256 hash or null. - */ - String getSha256Hash() { - return sha256Hash; - } - } - - /** - * View model for container data. - */ - private static class ContainerViewModel { - - private final String displayName; - private final String originalName; - private final String deviceIdValue; - private final String acquisitionDetails; - private final ImageViewModel imageViewModel; - - /** - * Main constructor. - * - * @param displayName The display name for this data source. - * @param originalName The original name for this data source. - * @param deviceIdValue The device id value for this data source. - * @param acquisitionDetails The acquisition details for this data - * source or null. - * @param imageViewModel If the data source is an image, the image view - * model for this data source or null if non-image. - */ - ContainerViewModel(String displayName, String originalName, String deviceIdValue, - String acquisitionDetails, ImageViewModel imageViewModel) { - this.displayName = displayName; - this.originalName = originalName; - this.deviceIdValue = deviceIdValue; - this.acquisitionDetails = acquisitionDetails; - this.imageViewModel = imageViewModel; - } - - /** - * @return The display name for this data source. - */ - String getDisplayName() { - return displayName; - } - - /** - * @return The original name for this data source. - */ - String getOriginalName() { - return originalName; - } - - /** - * @return The device id value for this data source. - */ - String getDeviceId() { - return deviceIdValue; - } - - /** - * @return The acquisition details for this data source or null. - */ - String getAcquisitionDetails() { - return acquisitionDetails; - } - - /** - * @return If the data source is an image, the image view model for this - * data source or null if non-image. - */ - ImageViewModel getImageViewModel() { - return imageViewModel; - } - } - // set of case events for which to call update (if the name changes, that will impact data shown) private static final Set CASE_EVENT_SET = new HashSet<>(Arrays.asList( Case.Events.DATA_SOURCE_NAME_CHANGED @@ -262,29 +70,29 @@ class ContainerPanel extends BaseDataSourceSummaryPanel { private static final Logger logger = Logger.getLogger(ContainerPanel.class.getName()); private final List> dataFetchComponents; - private final DataFetcher containerDataFetcher; + private final DataFetcher containerDataFetcher; /** * Creates a new form ContainerPanel. */ ContainerPanel() { - this(new ContainerSummary()); + this(new ContainerSummaryGetter()); } /** * Creates new form ContainerPanel. */ - ContainerPanel(ContainerSummary containerSummary) { + ContainerPanel(ContainerSummaryGetter containerSummary) { super(containerSummary, CONTAINER_UPDATES); - containerDataFetcher = (dataSource) -> getContainerViewModel(containerSummary, dataSource); + containerDataFetcher = (dataSource) -> containerSummary.getContainerDetails(dataSource); dataFetchComponents = Arrays.asList( new DataFetchComponents<>( containerDataFetcher, (result) -> { if (result != null && result.getResultType() == ResultType.SUCCESS) { - ContainerViewModel data = result.getData(); + ContainerDetails data = result.getData(); updateDetailsPanelData(data); } else { if (result == null) { @@ -313,92 +121,12 @@ class ContainerPanel extends BaseDataSourceSummaryPanel { fetchInformation(dataFetchComponents, dataSource); } - /** - * A means of retrieving data that could potentially throw an exception. - */ - private interface Retriever { - - /** - * Retrieves data of a certain type and possibly throws an exception. - * - * @return The data type. - * @throws TskCoreException - * @throws SleuthkitCaseProviderException - * @throws SQLException - */ - O retrieve() throws TskCoreException, SleuthkitCaseProviderException, SQLException; - } - - /** - * Retrieves data of a particular type and handles any exceptions that may - * be thrown by logging. - * - * @param retriever The retrieving function. - * @return The retrieved data. - */ - private static O retrieve(Retriever retriever) { - try { - return retriever.retrieve(); - } catch (TskCoreException | SleuthkitCaseProviderException | SQLException ex) { - logger.log(Level.WARNING, "Error while retrieving data.", ex); - return null; - } - } - - /** - * Generates a container view model object containing data to display about - * the data source. - * - * @param containerSummary The service providing data about the data source. - * @param ds The data source. - * @return The generated view model. - */ - private static ContainerViewModel getContainerViewModel(ContainerSummary containerSummary, DataSource ds) { - if (ds == null) { - return null; - } - - return new ContainerViewModel( - ds.getName(), - ds.getName(), - ds.getDeviceId(), - retrieve(() -> ds.getAcquisitionDetails()), - ds instanceof Image ? getImageViewModel(containerSummary, (Image) ds) : null - ); - } - - /** - * Generates an image view model object containing data to display about the - * image. - * - * @param containerSummary The service providing data about the image. - * @param image The image. - * @return The generated view model. - */ - private static ImageViewModel getImageViewModel(ContainerSummary containerSummary, Image image) { - if (image == null) { - return null; - } - - Long unallocSize = retrieve(() -> containerSummary.getSizeOfUnallocatedFiles(image)); - String imageType = image.getType().getName(); - Long size = image.getSize(); - Long sectorSize = image.getSsize(); - String timeZone = image.getTimeZone(); - List paths = image.getPaths() == null ? Collections.emptyList() : Arrays.asList(image.getPaths()); - String md5 = retrieve(() -> image.getMd5()); - String sha1 = retrieve(() -> image.getSha1()); - String sha256 = retrieve(() -> image.getSha256()); - - return new ImageViewModel(unallocSize, size, sectorSize, timeZone, imageType, paths, md5, sha1, sha256); - } - /** * Update the swing components with fetched data. * * @param viewModel The data source view model data. */ - private void updateDetailsPanelData(ContainerViewModel viewModel) { + private void updateDetailsPanelData(ContainerDetails viewModel) { clearTableValues(); if (viewModel == null) { return; @@ -409,8 +137,8 @@ class ContainerPanel extends BaseDataSourceSummaryPanel { deviceIdValue.setText(viewModel.getDeviceId()); acquisitionDetailsTextArea.setText(viewModel.getAcquisitionDetails()); - if (viewModel.getImageViewModel() != null) { - setFieldsForImage(viewModel.getImageViewModel()); + if (viewModel.getImageDetails() != null) { + setFieldsForImage(viewModel.getImageDetails()); } else { setFieldsForNonImageDataSource(); } @@ -445,7 +173,7 @@ class ContainerPanel extends BaseDataSourceSummaryPanel { * * @param viewModel The image view model data. */ - private void setFieldsForImage(ImageViewModel viewModel) { + private void setFieldsForImage(ImageDetails viewModel) { unallocatedSizeValue.setText(SizeRepresentationUtil.getSizeString(viewModel.getUnallocatedSize())); imageTypeValue.setText(viewModel.getImageType()); sizeValue.setText(SizeRepresentationUtil.getSizeString(viewModel.getSize())); @@ -480,84 +208,6 @@ class ContainerPanel extends BaseDataSourceSummaryPanel { ((DefaultTableModel) filePathsTable.getModel()).setRowCount(0); } - /** - * Divides acquisition details into key/value pairs to be displayed in - * separate cells in an excel export. - * - * @param acquisitionDetails The acquisition details. - * @return The list of key value pairs that can be incorporated into the - * excel export. - */ - private static List getAcquisitionDetails(String acquisitionDetails) { - if (StringUtils.isBlank(acquisitionDetails)) { - return Collections.emptyList(); - } else { - return Stream.of(acquisitionDetails.split("\\r?\\n")) - .map((line) -> (StringUtils.isBlank(line)) ? null : new SingleCellExportable(line)) - .filter(item -> item != null) - .collect(Collectors.toList()); - } - } - - @Override - @Messages({ - "ContainerPanel_export_displayName=Display Name:", - "ContainerPanel_export_originalName=Name:", - "ContainerPanel_export_deviceId=Device ID:", - "ContainerPanel_export_timeZone=Time Zone:", - "ContainerPanel_export_acquisitionDetails=Acquisition Details:", - "ContainerPanel_export_imageType=Image Type:", - "ContainerPanel_export_size=Size:", - "ContainerPanel_export_sectorSize=Sector Size:", - "ContainerPanel_export_md5=MD5:", - "ContainerPanel_export_sha1=SHA1:", - "ContainerPanel_export_sha256=SHA256:", - "ContainerPanel_export_unallocatedSize=Unallocated Space:", - "ContainerPanel_export_filePaths=File Paths:",}) - protected List getExports(DataSource ds) { - ContainerViewModel result = getFetchResult(containerDataFetcher, "Container sheets", ds); - if (ds == null || result == null) { - return Collections.emptyList(); - } - - String NA = Bundle.ContainerPanel_setFieldsForNonImageDataSource_na(); - DefaultCellModel NACell = new DefaultCellModel<>(NA); - - ImageViewModel imageModel = result.getImageViewModel(); - boolean hasImage = imageModel != null; - - DefaultCellModel timeZone = hasImage ? new DefaultCellModel<>(imageModel.getTimeZone()) : NACell; - DefaultCellModel imageType = hasImage ? new DefaultCellModel<>(imageModel.getImageType()) : NACell; - DefaultCellModel size = hasImage ? SizeRepresentationUtil.getBytesCell(imageModel.getSize()) : NACell; - DefaultCellModel sectorSize = hasImage ? SizeRepresentationUtil.getBytesCell(imageModel.getSectorSize()) : NACell; - DefaultCellModel md5 = hasImage ? new DefaultCellModel<>(imageModel.getMd5Hash()) : NACell; - DefaultCellModel sha1 = hasImage ? new DefaultCellModel<>(imageModel.getSha1Hash()) : NACell; - DefaultCellModel sha256 = hasImage ? new DefaultCellModel<>(imageModel.getSha256Hash()) : NACell; - DefaultCellModel unallocatedSize = hasImage ? SizeRepresentationUtil.getBytesCell(imageModel.getUnallocatedSize()) : NACell; - List paths = result.getImageViewModel() == null ? Collections.singletonList(NA) : result.getImageViewModel().getPaths(); - List cellPaths = paths.stream() - .map(SingleCellExportable::new) - .collect(Collectors.toList()); - - return Arrays.asList( - new ExcelSpecialFormatExport(Bundle.ContainerPanel_tabName(), Arrays.asList( - new KeyValueItemExportable(Bundle.ContainerPanel_export_displayName(), new DefaultCellModel<>(result.getDisplayName())), - new KeyValueItemExportable(Bundle.ContainerPanel_export_originalName(), new DefaultCellModel<>(result.getOriginalName())), - new KeyValueItemExportable(Bundle.ContainerPanel_export_deviceId(), new DefaultCellModel<>(result.getDeviceId())), - new KeyValueItemExportable(Bundle.ContainerPanel_export_timeZone(), timeZone), - new TitledExportable(Bundle.ContainerPanel_export_acquisitionDetails(), getAcquisitionDetails(result.getAcquisitionDetails())), - new KeyValueItemExportable(Bundle.ContainerPanel_export_imageType(), imageType), - new KeyValueItemExportable(Bundle.ContainerPanel_export_size(), size), - new KeyValueItemExportable(Bundle.ContainerPanel_export_sectorSize(), sectorSize), - new KeyValueItemExportable(Bundle.ContainerPanel_export_md5(), md5), - new KeyValueItemExportable(Bundle.ContainerPanel_export_sha1(), sha1), - new KeyValueItemExportable(Bundle.ContainerPanel_export_sha256(), sha256), - new KeyValueItemExportable(Bundle.ContainerPanel_export_unallocatedSize(), unallocatedSize), - new TitledExportable(Bundle.ContainerPanel_export_filePaths(), cellPaths) - ))); - - } - /** * This method is called from within the constructor to initialize the form. * WARNING: Do NOT modify this code. The content of this method is always diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/ContainerSummaryGetter.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/ContainerSummaryGetter.java new file mode 100644 index 0000000000..41be8c4644 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/ContainerSummaryGetter.java @@ -0,0 +1,140 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2020-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.datasourcesummary.ui; + +import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultArtifactUpdateGovernor; +import java.sql.SQLException; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.ContainerSummary; +import org.sleuthkit.autopsy.ingest.ModuleContentEvent; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.DataSource; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Wrapper class for converting + * org.sleuthkit.autopsy.contentutils.ContainerSummary functionality into a + * DefaultArtifactUpdateGovernor used by Container tab. + */ +public class ContainerSummaryGetter implements DefaultArtifactUpdateGovernor { + + private static final Set ARTIFACT_UPDATE_TYPE_IDS = new HashSet<>(Arrays.asList( + BlackboardArtifact.ARTIFACT_TYPE.TSK_OS_INFO.getTypeID(), + BlackboardArtifact.ARTIFACT_TYPE.TSK_DATA_SOURCE_USAGE.getTypeID() + )); + + private final ContainerSummary containerSummary; + + /** + * Main constructor. + */ + public ContainerSummaryGetter() { + containerSummary = new ContainerSummary(); + } + + @Override + public boolean isRefreshRequired(ModuleContentEvent evt) { + return true; + } + + @Override + public boolean isRefreshRequired(AbstractFile file) { + return true; + } + + @Override + public Set getArtifactTypeIdsForRefresh() { + return Collections.unmodifiableSet(ARTIFACT_UPDATE_TYPE_IDS); + } + + /** + * 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. + * + * @throws SleuthkitCaseProviderException + * @throws TskCoreException + * @throws SQLException + */ + public Long getSizeOfUnallocatedFiles(DataSource currentDataSource) + throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException { + return containerSummary.getSizeOfUnallocatedFiles(currentDataSource); + } + + /** + * 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. + * + * @throws SleuthkitCaseProviderException + * @throws TskCoreException + * @throws SQLException + */ + public String getOperatingSystems(DataSource dataSource) + throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException { + return containerSummary.getOperatingSystems(dataSource); + } + + /** + * 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. + * + * @throws SleuthkitCaseProviderException + * @throws TskCoreException + * @throws SQLException + */ + public String getDataSourceType(DataSource dataSource) + throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException { + return containerSummary.getDataSourceType(dataSource); + } + + /** + * Retrieves a container data model object containing data about the data + * source. + * + * @param dataSource The data source. + * + * @return The concatenated value or null if the query could not be + * executed. + * + * @throws SleuthkitCaseProviderException + * @throws TskCoreException + * @throws SQLException + */ + public ContainerSummary.ContainerDetails getContainerDetails(DataSource dataSource) + throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException { + return containerSummary.getContainerDetails(dataSource); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceBrowser.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceBrowser.java index 51433534e4..e6942058e9 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceBrowser.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceBrowser.java @@ -18,6 +18,7 @@ */ package org.sleuthkit.autopsy.datasourcesummary.ui; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.CaseDataSourcesSummary; import java.awt.Cursor; import org.sleuthkit.autopsy.datasourcesummary.uiutils.RightAlignedTableCellRenderer; import java.awt.EventQueue; @@ -42,7 +43,6 @@ import static javax.swing.SwingConstants.RIGHT; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; 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; diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryTabbedPane.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryTabbedPane.java index 146863b58e..af53ab9260 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryTabbedPane.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryTabbedPane.java @@ -25,12 +25,9 @@ import java.util.Arrays; import java.util.EnumSet; import java.util.List; import java.util.function.Consumer; -import java.util.function.Function; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.IngestJobInfoPanel; -import org.sleuthkit.autopsy.datasourcesummary.ui.ExcelExportAction.ExportableTab; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport.ExcelSheetExport; import org.sleuthkit.datamodel.DataSource; /** @@ -46,8 +43,7 @@ import org.sleuthkit.datamodel.DataSource; "DataSourceSummaryTabbedPane_pastCasesTab_title=Past Cases", "DataSourceSummaryTabbedPane_analysisTab_title=Analysis", "DataSourceSummaryTabbedPane_geolocationTab_title=Geolocation", - "DataSourceSummaryTabbedPane_timelineTab_title=Timeline", - "DataSourceSummaryTabbedPane_exportTab_title=Export" + "DataSourceSummaryTabbedPane_timelineTab_title=Timeline" }) public class DataSourceSummaryTabbedPane extends javax.swing.JPanel { @@ -55,12 +51,11 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel { * Records of tab information (i.e. title, component, function to call on * new data source). */ - private class DataSourceTab implements ExportableTab { + private class DataSourceTab { private final String tabTitle; private final Component component; private final Consumer onDataSource; - private final Function> excelExporter; private final Runnable onClose; private final Runnable onInit; @@ -71,7 +66,7 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel { * @param panel The component to be displayed in the tab. */ DataSourceTab(String tabTitle, BaseDataSourceSummaryPanel panel) { - this(tabTitle, panel, panel::setDataSource, panel::getExports, panel::close, panel::init); + this(tabTitle, panel, panel::setDataSource, panel::close, panel::init); panel.setParentCloseListener(() -> notifyParentClose()); } @@ -90,12 +85,10 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel { * added to the tabbed pane. */ DataSourceTab(String tabTitle, Component component, Consumer onDataSource, - Function> excelExporter, Runnable onClose, - Runnable onInit) { + Runnable onClose, Runnable onInit) { this.tabTitle = tabTitle; this.component = component; this.onDataSource = onDataSource; - this.excelExporter = excelExporter; this.onClose = onClose; this.onInit = onInit; } @@ -103,7 +96,6 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel { /** * @return The title for the tab. */ - @Override public String getTabTitle() { return tabTitle; } @@ -122,11 +114,6 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel { return onDataSource; } - @Override - public List getExcelExports(DataSource dataSource) { - return excelExporter == null ? null : excelExporter.apply(dataSource); - } - /** * @return The action for closing resources in the tab. */ @@ -152,9 +139,6 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel { private Runnable notifyParentClose = null; private final IngestJobInfoPanel ingestHistoryPanel = new IngestJobInfoPanel(); - // create an export panel whose button triggers the export to XLSX action - private final ExportPanel exportPanel = new ExportPanel(); - private final List tabs = Arrays.asList( new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_typesTab_title(), new TypesPanel()), new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_userActivityTab_title(), new UserActivityPanel()), @@ -168,22 +152,11 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel { Bundle.DataSourceSummaryTabbedPane_ingestHistoryTab_title(), ingestHistoryPanel, ingestHistoryPanel::setDataSource, - IngestJobExcelExport::getExports, null, null), - new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_detailsTab_title(), new ContainerPanel()), - new DataSourceTab( - Bundle.DataSourceSummaryTabbedPane_exportTab_title(), - exportPanel, - null, - null, - null, - null) + new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_detailsTab_title(), new ContainerPanel()) ); - // the action that does the export - private final ExcelExportAction exportAction = new ExcelExportAction(tabs); - private DataSource dataSource = null; private CardLayout cardLayout; @@ -243,9 +216,6 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel { // set this to no datasource initially cardLayout.show(this, NO_DATASOURCE_PANE); - - // set action for when user requests xlsx export - exportPanel.setXlsxExportAction(() -> exportAction.accept(getDataSource())); } /** diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/ExcelExportAction.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/ExcelExportAction.java deleted file mode 100644 index 22e06c07ed..0000000000 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/ExcelExportAction.java +++ /dev/null @@ -1,301 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 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.datasourcesummary.ui; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.io.File; -import java.io.IOException; -import java.nio.file.Paths; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Date; -import java.util.List; -import java.util.concurrent.CancellationException; -import java.util.concurrent.ExecutionException; -import java.util.function.Consumer; -import java.util.logging.Level; -import javax.swing.JOptionPane; -import javax.swing.SwingUtilities; -import javax.swing.SwingWorker; -import org.openide.util.NbBundle; -import org.openide.util.NbBundle.Messages; -import org.openide.windows.WindowManager; -import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; -import org.sleuthkit.autopsy.coreutils.FileUtil; -import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport.ExcelExportException; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport.ExcelSheetExport; -import org.sleuthkit.autopsy.progress.ModalDialogProgressIndicator; -import org.sleuthkit.autopsy.progress.ProgressIndicator; -import org.sleuthkit.datamodel.DataSource; -import org.sleuthkit.datamodel.TskCoreException; - -/** - * Action that exports tab data to an excel workbook. - */ -@Messages({ - "ExcelExportAction_moduleName=Data Source Summary",}) -class ExcelExportAction implements Consumer { - - private static final Logger logger = Logger.getLogger(ExcelExportAction.class.getName()); - - /** - * A tab that can be exported. - */ - interface ExportableTab { - - /** - * Returns the name of the tab. - * - * @return The tab name. - */ - String getTabTitle(); - - /** - * Given the data source, provides the excel exports for this tab. - * - * @param dataSource The data source. - * @return The excel exports or null. - */ - List getExcelExports(DataSource dataSource); - } - - private final ExcelExport excelExport = ExcelExport.getInstance(); - private final List tabExports; - - /** - * Main constructor. - * - * @param tabExports The different tabs that may have excel exports. - */ - ExcelExportAction(List tabExports) { - this.tabExports = Collections.unmodifiableList(new ArrayList<>(tabExports)); - } - - /** - * Accepts the data source for which this export pertains, prompts user for - * output location, and exports the data. - * - * @param ds The data source. - */ - @Override - public void accept(DataSource ds) { - if (ds == null) { - return; - } - - File outputLoc = getXLSXPath(ds.getName()); - if (outputLoc == null) { - return; - } - - runXLSXExport(ds, outputLoc); - } - - /** - * Generates an xlsx path for the data source summary export. - * - * @param dataSourceName The name of the data source. - * @return The file to which the excel document should be written or null if - * file already exists or cancellation. - */ - @NbBundle.Messages({ - "ExcelExportAction_getXLSXPath_directory=DataSourceSummary",}) - private File getXLSXPath(String dataSourceName) { - // set initial path to reports directory with filename that is - // a combination of the data source name and time stamp - DateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy-HH-mm-ss"); - String fileName = String.format("%s-%s.xlsx", dataSourceName == null ? "" : FileUtil.escapeFileName(dataSourceName), dateFormat.format(new Date())); - try { - String reportsDir = Case.getCurrentCaseThrows().getReportDirectory(); - File reportsDirFile = Paths.get(reportsDir, Bundle.ExcelExportAction_getXLSXPath_directory()).toFile(); - if (!reportsDirFile.exists()) { - reportsDirFile.mkdirs(); - } - - return Paths.get(reportsDirFile.getAbsolutePath(), fileName).toFile(); - } catch (NoCurrentCaseException ex) { - logger.log(Level.WARNING, "Unable to find reports directory.", ex); - } - - return null; - } - - /** - * An action listener that handles cancellation of the export process. - */ - private class CancelExportListener implements ActionListener { - - private SwingWorker worker = null; - - @Override - public void actionPerformed(ActionEvent e) { - if (worker != null && !worker.isCancelled() && !worker.isDone()) { - worker.cancel(true); - } - } - - /** - * Returns the swing worker that could be cancelled. - * - * @return The swing worker that could be cancelled. - */ - SwingWorker getWorker() { - return worker; - } - - /** - * Sets the swing worker that could be cancelled. - * - * @param worker The swing worker that could be cancelled. - */ - void setWorker(SwingWorker worker) { - this.worker = worker; - } - } - - /** - * Handles managing the gui and exporting data from the tabs into an excel - * document. - * - * @param dataSource The data source. - * @param path The output path. - */ - @NbBundle.Messages({ - "# {0} - dataSource", - "ExcelExportAction_runXLSXExport_progressTitle=Exporting {0} to XLSX", - "ExcelExportAction_runXLSXExport_progressCancelTitle=Cancel", - "ExcelExportAction_runXLSXExport_progressCancelActionTitle=Cancelling...", - "ExcelExportAction_runXLSXExport_errorTitle=Error While Exporting", - "ExcelExportAction_runXLSXExport_errorMessage=There was an error while exporting.", - }) - private void runXLSXExport(DataSource dataSource, File path) { - - CancelExportListener cancelButtonListener = new CancelExportListener(); - - ProgressIndicator progressIndicator = new ModalDialogProgressIndicator( - WindowManager.getDefault().getMainWindow(), - Bundle.ExcelExportAction_runXLSXExport_progressTitle(dataSource.getName()), - new String[]{Bundle.ExcelExportAction_runXLSXExport_progressCancelTitle()}, - Bundle.ExcelExportAction_runXLSXExport_progressCancelTitle(), - cancelButtonListener - ); - - SwingWorker worker = new SwingWorker() { - @Override - protected Boolean doInBackground() throws Exception { - exportToXLSX(progressIndicator, dataSource, path); - return true; - } - - @Override - protected void done() { - try { - get(); - } catch (ExecutionException ex) { - logger.log(Level.WARNING, "Error while trying to export data source summary to xlsx.", ex); - JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), - Bundle.ExcelExportAction_runXLSXExport_errorMessage(), - Bundle.ExcelExportAction_runXLSXExport_errorTitle(), - JOptionPane.ERROR_MESSAGE); - } catch (InterruptedException | CancellationException ex) { - // no op on cancellation - } finally { - progressIndicator.finish(); - } - } - }; - - cancelButtonListener.setWorker(worker); - worker.execute(); - } - - /** - * Action that handles updating progress and exporting data from the tabs. - * - * @param progressIndicator The progress indicator. - * @param dataSource The data source to be exported. - * @param path The path of the excel export. - * @throws InterruptedException - * @throws IOException - * @throws ExcelExportException - */ - @NbBundle.Messages({ - "ExcelExportAction_exportToXLSX_beginExport=Beginning Export...", - "# {0} - tabName", - "ExcelExportAction_exportToXLSX_gatheringTabData=Fetching Data for {0} Tab...", - "ExcelExportAction_exportToXLSX_writingToFile=Writing to File...",}) - - private void exportToXLSX(ProgressIndicator progressIndicator, DataSource dataSource, File path) - throws InterruptedException, IOException, ExcelExport.ExcelExportException { - - int exportWeight = 3; - int totalWeight = tabExports.size() + exportWeight; - progressIndicator.start(Bundle.ExcelExportAction_exportToXLSX_beginExport(), totalWeight); - List sheetExports = new ArrayList<>(); - for (int i = 0; i < tabExports.size(); i++) { - if (Thread.interrupted()) { - throw new InterruptedException("Export has been cancelled."); - } - - ExportableTab tab = tabExports.get(i); - progressIndicator.progress(Bundle.ExcelExportAction_exportToXLSX_gatheringTabData(tab == null ? "" : tab.getTabTitle()), i); - - List exports = tab.getExcelExports(dataSource); - if (exports != null) { - sheetExports.addAll(exports); - } - } - - if (Thread.interrupted()) { - throw new InterruptedException("Export has been cancelled."); - } - - progressIndicator.progress(Bundle.ExcelExportAction_exportToXLSX_writingToFile(), tabExports.size()); - excelExport.writeExcel(sheetExports, path); - - progressIndicator.finish(); - - try { - // add to reports - Case curCase = Case.getCurrentCaseThrows(); - curCase.addReport(path.getParent(), - Bundle.ExcelExportAction_moduleName(), - path.getName(), - dataSource); - - // and show finished dialog - SwingUtilities.invokeLater(() -> { - ExcelExportDialog dialog = new ExcelExportDialog(WindowManager.getDefault().getMainWindow(), path); - dialog.setResizable(false); - dialog.setLocationRelativeTo(WindowManager.getDefault().getMainWindow()); - dialog.setVisible(true); - dialog.toFront(); - }); - - } catch (NoCurrentCaseException | TskCoreException ex) { - logger.log(Level.WARNING, "There was an error attaching report to case.", ex); - } - } -} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/ExcelExportDialog.form b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/ExcelExportDialog.form deleted file mode 100644 index 8342ce5326..0000000000 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/ExcelExportDialog.form +++ /dev/null @@ -1,114 +0,0 @@ - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/ExcelExportDialog.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/ExcelExportDialog.java deleted file mode 100644 index ec16d08e46..0000000000 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/ExcelExportDialog.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 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.datasourcesummary.ui; - -import java.awt.Cursor; -import java.awt.Desktop; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.io.File; -import java.io.IOException; -import java.util.logging.Level; -import javax.swing.SwingUtilities; -import org.openide.util.NbBundle.Messages; -import org.sleuthkit.autopsy.coreutils.Logger; - -/** - * Dialog showing where the data source summary excel export can be located. - */ -@Messages({ - "ExcelExportDialog_title=Data Source Summary Exported" -}) -public class ExcelExportDialog extends javax.swing.JDialog { - - private static final Logger logger = Logger.getLogger(ExcelExportDialog.class.getName()); - - /** - * Creates new form ExcelExportDialog - */ - public ExcelExportDialog(java.awt.Frame parent, File filePath) { - super(parent, true); - - initComponents(); - setTitle(Bundle.ExcelExportDialog_title()); - - this.linkText.setText(filePath.getAbsolutePath()); - this.linkText.addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - SwingUtilities.invokeLater(() -> { - try { - Desktop.getDesktop().open(filePath); - } catch (IOException ex) { - logger.log(Level.WARNING, "Unable to open: " + filePath.getAbsolutePath(), ex); - } - }); - } - - }); - this.linkText.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); - } - - /** - * This method is called from within the constructor to initialize the form. - * WARNING: Do NOT modify this code. The content of this method is always - * regenerated by the Form Editor. - */ - @SuppressWarnings("unchecked") - // //GEN-BEGIN:initComponents - private void initComponents() { - - javax.swing.JLabel titleLabel = new javax.swing.JLabel(); - javax.swing.JButton okButton = new javax.swing.JButton(); - javax.swing.JScrollPane linkTextScrollPane = new javax.swing.JScrollPane(); - linkText = new javax.swing.JTextArea(); - - setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); - - org.openide.awt.Mnemonics.setLocalizedText(titleLabel, org.openide.util.NbBundle.getMessage(ExcelExportDialog.class, "ExcelExportDialog.titleLabel.text")); // NOI18N - - org.openide.awt.Mnemonics.setLocalizedText(okButton, org.openide.util.NbBundle.getMessage(ExcelExportDialog.class, "ExcelExportDialog.okButton.text")); // NOI18N - okButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - okButtonActionPerformed(evt); - } - }); - - linkText.setEditable(false); - linkText.setBackground(null); - linkText.setColumns(20); - linkText.setForeground(java.awt.Color.BLUE); - linkText.setLineWrap(true); - linkText.setRows(1); - linkText.setWrapStyleWord(true); - linkText.setBorder(null); - linkText.setOpaque(false); - linkTextScrollPane.setViewportView(linkText); - - javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); - getContentPane().setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addContainerGap() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addComponent(linkTextScrollPane) - .addGroup(layout.createSequentialGroup() - .addGap(0, 0, Short.MAX_VALUE) - .addComponent(okButton)) - .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup() - .addComponent(titleLabel) - .addGap(0, 116, Short.MAX_VALUE))) - .addContainerGap()) - ); - layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addContainerGap() - .addComponent(titleLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(linkTextScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 39, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(okButton) - .addContainerGap()) - ); - - pack(); - }// //GEN-END:initComponents - - private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed - dispose(); - }//GEN-LAST:event_okButtonActionPerformed - - - // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JTextArea linkText; - // End of variables declaration//GEN-END:variables -} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/ExportPanel.form b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/ExportPanel.form deleted file mode 100644 index 908facb8d6..0000000000 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/ExportPanel.form +++ /dev/null @@ -1,68 +0,0 @@ - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/ExportPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/ExportPanel.java deleted file mode 100644 index 7e635c3f64..0000000000 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/ExportPanel.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 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.datasourcesummary.ui; - -/** - * The panel that provides options for exporting data source summary data. - */ -public class ExportPanel extends javax.swing.JPanel { - - private Runnable xlsxExportAction; - - /** - * Creates new form ExportPanel - */ - public ExportPanel() { - initComponents(); - } - - /** - * Returns the action that handles exporting to excel. - * - * @return The action that handles exporting to excel. - */ - public Runnable getXlsxExportAction() { - return xlsxExportAction; - } - - /** - * Sets the action that handles exporting to excel. - * - * @param onXlsxExport The action that handles exporting to excel. - */ - public void setXlsxExportAction(Runnable onXlsxExport) { - this.xlsxExportAction = onXlsxExport; - } - - /** - * This method is called from within the constructor to initialize the form. - * WARNING: Do NOT modify this code. The content of this method is always - * regenerated by the Form Editor. - */ - @SuppressWarnings("unchecked") - // //GEN-BEGIN:initComponents - private void initComponents() { - - javax.swing.JButton xlsxExportButton = new javax.swing.JButton(); - javax.swing.JLabel xlsxExportMessage = new javax.swing.JLabel(); - - org.openide.awt.Mnemonics.setLocalizedText(xlsxExportButton, org.openide.util.NbBundle.getMessage(ExportPanel.class, "ExportPanel.xlsxExportButton.text")); // NOI18N - xlsxExportButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - xlsxExportButtonActionPerformed(evt); - } - }); - - org.openide.awt.Mnemonics.setLocalizedText(xlsxExportMessage, org.openide.util.NbBundle.getMessage(ExportPanel.class, "ExportPanel.xlsxExportMessage.text")); // NOI18N - - javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); - this.setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addContainerGap() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(xlsxExportMessage) - .addComponent(xlsxExportButton)) - .addContainerGap(62, Short.MAX_VALUE)) - ); - layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addContainerGap() - .addComponent(xlsxExportMessage) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(xlsxExportButton) - .addContainerGap(250, Short.MAX_VALUE)) - ); - }// //GEN-END:initComponents - - private void xlsxExportButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_xlsxExportButtonActionPerformed - if (this.xlsxExportAction != null) { - xlsxExportAction.run(); - } - }//GEN-LAST:event_xlsxExportButtonActionPerformed - - - // Variables declaration - do not modify//GEN-BEGIN:variables - // End of variables declaration//GEN-END:variables -} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/GeolocationPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/GeolocationPanel.java index be7279afca..2551e7a3ae 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/GeolocationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/GeolocationPanel.java @@ -34,7 +34,6 @@ import org.openide.util.NbBundle.Messages; import org.openide.util.actions.CallableSystemAction; import org.openide.windows.TopComponent; import org.openide.windows.WindowManager; -import org.sleuthkit.autopsy.datasourcesummary.datamodel.GeolocationSummary; import org.sleuthkit.autopsy.datasourcesummary.datamodel.GeolocationSummary.CityCountsList; import org.sleuthkit.autopsy.datasourcesummary.datamodel.GeolocationSummary.CityData; import org.sleuthkit.autopsy.datasourcesummary.datamodel.GeolocationSummary.CityRecordCount; @@ -43,9 +42,8 @@ import org.sleuthkit.autopsy.datasourcesummary.uiutils.ColumnModel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetcher; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataFetcher; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultCellModel; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport; import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel; import org.sleuthkit.autopsy.geolocation.GeoFilter; @@ -79,9 +77,9 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel { * Main constructor. * * @param mostRecentData The data to be displayed in the most recent - * table. + * table. * @param mostCommonData The data to be displayed in the most common - * table. + * table. */ GeolocationViewModel(List> mostRecentData, List> mostCommonData) { this.mostRecentData = mostRecentData; @@ -147,7 +145,7 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel { private final IngestRunningLabel ingestRunningLabel = new IngestRunningLabel(); - private final GeolocationSummary whereUsedData; + private final GeolocationSummaryGetter whereUsedData; private final DataFetcher geolocationFetcher; @@ -155,15 +153,15 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel { * Main constructor. */ public GeolocationPanel() { - this(new GeolocationSummary()); + this(new GeolocationSummaryGetter()); } /** * Main constructor. * - * @param whereUsedData The GeolocationSummary instance to use. + * @param whereUsedData The GeolocationSummaryGetter instance to use. */ - public GeolocationPanel(GeolocationSummary whereUsedData) { + public GeolocationPanel(GeolocationSummaryGetter whereUsedData) { super(whereUsedData); this.whereUsedData = whereUsedData; @@ -183,7 +181,7 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel { * Means of rendering data to be shown in the tables. * * @param result The result of fetching data for a data source and - * processing into view model data. + * processing into view model data. */ private void handleData(DataFetchResult result) { showCityContent(DataFetchResult.getSubResult(result, (dr) -> dr.getMostCommonData()), mostCommonTable, commonViewInGeolocationBtn); @@ -194,6 +192,7 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel { * Retrieves the city name to display from the record. * * @param record The record for the city to display. + * * @return The display name (city, country). */ private static String getCityName(CityRecord record) { @@ -221,6 +220,7 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel { * formats the city name). * * @param cityCount The CityRecordCount representing a row. + * * @return The city/count pair to be displayed as a row. */ private Pair formatRecord(CityRecordCount cityCount) { @@ -239,7 +239,8 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel { * 'unknown'). * * @param countsList The CityCountsList object representing the data to be - * displayed in the table. + * displayed in the table. + * * @return The list of city/count tuples to be displayed as a row. */ private List> formatList(CityCountsList countsList) { @@ -263,10 +264,11 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel { } /** - * Converts CityData from GeolocationSummary into data that can be directly - * put into table in this panel. + * Converts CityData from GeolocationSummaryGetter into data that can be + * directly put into table in this panel. * * @param cityData The city data. + * * @return The view model data. */ private GeolocationViewModel convertToViewModel(CityData cityData) { @@ -280,8 +282,8 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel { /** * Shows data in a particular table. * - * @param result The result to be displayed in the table. - * @param table The table where the data will be displayed. + * @param result The result to be displayed in the table. + * @param table The table where the data will be displayed. * @param goToGeolocation The corresponding geolocation navigation button. */ private void showCityContent(DataFetchResult>> result, JTablePanel> table, JButton goToGeolocation) { @@ -296,9 +298,9 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel { * Action to open the geolocation window. * * @param dataSource The data source for which the window should filter. - * @param daysLimit The limit for how recently the waypoints should be (for - * most recent table) or null for most recent filter to not be set (for most - * common table). + * @param daysLimit The limit for how recently the waypoints should be (for + * most recent table) or null for most recent filter to + * not be set (for most common table). */ private void openGeolocationWindow(DataSource dataSource, Integer daysLimit) { // notify dialog (if in dialog) should close. @@ -349,19 +351,6 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel { onNewDataSource(dataFetchComponents, tables, dataSource); } - @Override - List getExports(DataSource dataSource) { - GeolocationViewModel model = getFetchResult(geolocationFetcher, "Geolocation sheets", dataSource); - if (model == null) { - return Collections.emptyList(); - } - - return Arrays.asList( - getTableExport(DEFAULT_TEMPLATE, Bundle.GeolocationPanel_mostRecent_tabName(), model.getMostRecentData()), - getTableExport(DEFAULT_TEMPLATE, Bundle.GeolocationPanel_mostCommon_tabName(), model.getMostCommonData()) - ); - } - @Override public void close() { ingestRunningLabel.unregister(); diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/GeolocationSummaryGetter.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/GeolocationSummaryGetter.java new file mode 100644 index 0000000000..2278b4ac60 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/GeolocationSummaryGetter.java @@ -0,0 +1,78 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2020-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.datasourcesummary.ui; + +import java.io.IOException; +import java.util.List; +import java.util.Set; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.GeolocationSummary; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.GeolocationSummary.CityData; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultArtifactUpdateGovernor; +import org.sleuthkit.autopsy.geolocation.datamodel.GeoLocationDataException; +import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; +import org.sleuthkit.datamodel.DataSource; + +/** + * Wrapper class for converting + * org.sleuthkit.autopsy.contentutils.GeolocationSummary functionality into a + * DefaultArtifactUpdateGovernor used by GeolocationPanel tab. + */ +public class GeolocationSummaryGetter implements DefaultArtifactUpdateGovernor { + + private final GeolocationSummary geoSummary; + + /** + * Default constructor. + */ + public GeolocationSummaryGetter() { + geoSummary = new GeolocationSummary(); + } + + /** + * @return Returns all the geolocation artifact types. + */ + public List getGeoTypes() { + return GeolocationSummary.getGeoTypes(); + } + + @Override + public Set getArtifactTypeIdsForRefresh() { + return GeolocationSummary.getArtifactTypeIdsForRefresh(); + } + + /** + * Get this list of hits per city where the list is sorted descending by + * number of found hits (i.e. most hits is first index). + * + * @param dataSource The data source. + * @param daysCount Number of days to go back. + * @param maxCount Maximum number of results. + * + * @return The sorted list. + * + * @throws SleuthkitCaseProviderException + * @throws GeoLocationDataException + * @throws InterruptedException + */ + public CityData getCityCounts(DataSource dataSource, int daysCount, int maxCount) + throws SleuthkitCaseProviderException, GeoLocationDataException, InterruptedException, IOException { + return geoSummary.getCityCounts(dataSource, daysCount, maxCount); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/MimeTypeSummaryGetter.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/MimeTypeSummaryGetter.java new file mode 100644 index 0000000000..5468ec668a --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/MimeTypeSummaryGetter.java @@ -0,0 +1,150 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2020-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.datasourcesummary.ui; + +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.datasourcesummary.datamodel.SleuthkitCaseProvider; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.MimeTypeSummary; +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; + +/** + * Wrapper class for converting org.sleuthkit.autopsy.contentutils.TypesSummary + * functionality into a DefaultArtifactUpdateGovernor used by TypesPanel tab. + */ +public class MimeTypeSummaryGetter implements DefaultUpdateGovernor { + + private static final Set INGEST_JOB_EVENTS = new HashSet<>( + Arrays.asList(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED)); + + private final MimeTypeSummary mimeTypeSummary; + + /** + * Main constructor. + */ + public MimeTypeSummaryGetter() { + mimeTypeSummary = new MimeTypeSummary(); + } + + @Override + public boolean isRefreshRequired(ModuleContentEvent evt) { + return true; + } + + @Override + public boolean isRefreshRequired(AbstractFile file) { + return true; + } + + @Override + public boolean isRefreshRequired(IngestManager.IngestJobEvent evt) { + return (evt != null && INGEST_JOB_EVENTS.contains(evt)); + } + + @Override + public Set getIngestJobEventUpdates() { + return Collections.unmodifiableSet(INGEST_JOB_EVENTS); + } + + /** + * 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 SleuthkitCaseProviderException + * @throws TskCoreException + * @throws SQLException + */ + public Long getCountOfFilesForMimeTypes(DataSource currentDataSource, Set setOfMimeTypes) + throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException { + return mimeTypeSummary.getCountOfFilesForMimeTypes(currentDataSource, setOfMimeTypes); + } + + /** + * 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. + * + * @throws SleuthkitCaseProviderException + * @throws TskCoreException + * @throws SQLException + */ + public Long getCountOfFilesNotInMimeTypes(DataSource currentDataSource, Set setOfMimeTypes) + throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException { + return mimeTypeSummary.getCountOfFilesNotInMimeTypes(currentDataSource, setOfMimeTypes); + } + + /** + * Get a count of all regular files in a datasource. + * + * @param dataSource The datasource. + * + * @return The count of regular files. + * + * @throws SleuthkitCaseProviderException + * @throws TskCoreException + * @throws SQLException + */ + public Long getCountOfAllRegularFiles(DataSource dataSource) + throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException { + return mimeTypeSummary.getCountOfAllRegularFiles(dataSource); + } + + /** + * 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 SleuthkitCaseProviderException + * @throws TskCoreException + * @throws SQLException + */ + public Long getCountOfFilesWithNoMimeType(DataSource currentDataSource) + throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException { + return mimeTypeSummary.getCountOfFilesWithNoMimeType(currentDataSource); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/PastCasesPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/PastCasesPanel.java index a3169db375..08679169d3 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/PastCasesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/PastCasesPanel.java @@ -19,21 +19,16 @@ package org.sleuthkit.autopsy.datasourcesummary.ui; import java.util.Arrays; -import java.util.Collections; import java.util.List; import org.apache.commons.lang3.tuple.Pair; import org.openide.util.NbBundle.Messages; -import org.sleuthkit.autopsy.datasourcesummary.datamodel.PastCasesSummary; import org.sleuthkit.autopsy.datasourcesummary.datamodel.PastCasesSummary.PastCasesResult; -import static org.sleuthkit.autopsy.datasourcesummary.ui.BaseDataSourceSummaryPanel.getFetchResult; -import static org.sleuthkit.autopsy.datasourcesummary.ui.BaseDataSourceSummaryPanel.getTableExport; import org.sleuthkit.autopsy.datasourcesummary.uiutils.ColumnModel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetcher; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataFetcher; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultCellModel; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport; import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel; import org.sleuthkit.datamodel.DataSource; @@ -84,19 +79,19 @@ public class PastCasesPanel extends BaseDataSourceSummaryPanel { private final IngestRunningLabel ingestRunningLabel = new IngestRunningLabel(); private final DataFetcher pastCasesFetcher; - + public PastCasesPanel() { - this(new PastCasesSummary()); + this(new PastCasesSummaryGetter()); } /** * Creates new form PastCasesPanel */ - public PastCasesPanel(PastCasesSummary pastCaseData) { + public PastCasesPanel(PastCasesSummaryGetter pastCaseData) { super(pastCaseData); this.pastCasesFetcher = (dataSource) -> pastCaseData.getPastCasesData(dataSource); - + // set up data acquisition methods dataFetchComponents = Arrays.asList( new DataFetchWorker.DataFetchComponents<>( @@ -128,19 +123,6 @@ public class PastCasesPanel extends BaseDataSourceSummaryPanel { onNewDataSource(dataFetchComponents, tables, dataSource); } - @Override - List getExports(DataSource dataSource) { - PastCasesResult result = getFetchResult(pastCasesFetcher, "Past cases sheets", dataSource); - if (result == null) { - return Collections.emptyList(); - } - - return Arrays.asList( - getTableExport(DEFAULT_TEMPLATE, Bundle.PastCasesPanel_notableFileTable_tabName(), result.getTaggedNotable()), - getTableExport(DEFAULT_TEMPLATE, Bundle.PastCasesPanel_sameIdsTable_tabName(), result.getSameIdsResults()) - ); - } - @Override public void close() { ingestRunningLabel.unregister(); diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/PastCasesSummaryGetter.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/PastCasesSummaryGetter.java new file mode 100644 index 0000000000..2a2c0b8a55 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/PastCasesSummaryGetter.java @@ -0,0 +1,71 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultArtifactUpdateGovernor; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.PastCasesSummary; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.PastCasesSummary.PastCasesResult; +import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; +import org.sleuthkit.datamodel.DataSource; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Wrapper class for converting + * org.sleuthkit.autopsy.contentutils.PastCasesSummary functionality into a + * DefaultArtifactUpdateGovernor used by PastCases tab. + */ +public class PastCasesSummaryGetter implements DefaultArtifactUpdateGovernor { + + private static final Set ARTIFACT_UPDATE_TYPE_IDS = new HashSet<>(Arrays.asList( + ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID(), + ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID() + )); + + private final PastCasesSummary pastSummary; + + public PastCasesSummaryGetter() { + pastSummary = new PastCasesSummary(); + } + + @Override + public Set getArtifactTypeIdsForRefresh() { + return Collections.unmodifiableSet(ARTIFACT_UPDATE_TYPE_IDS); + } + + /** + * Returns the past cases data to be shown in the past cases tab. + * + * @param dataSource The data source. + * + * @return The retrieved data or null if null dataSource. + * + * @throws SleuthkitCaseProviderException + * @throws TskCoreException + */ + public PastCasesResult getPastCasesData(DataSource dataSource) + throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException { + return pastSummary.getPastCasesData(dataSource); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RecentFilesGetter.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RecentFilesGetter.java new file mode 100755 index 0000000000..5275ec2c62 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RecentFilesGetter.java @@ -0,0 +1,116 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2020-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.datasourcesummary.ui; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultArtifactUpdateGovernor; +import java.util.List; +import java.util.Set; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary.RecentAttachmentDetails; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary.RecentDownloadDetails; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary.RecentFileDetails; +import org.sleuthkit.datamodel.DataSource; +import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException; +import org.sleuthkit.datamodel.BlackboardArtifact; + +/** + * Wrapper class for converting + * org.sleuthkit.autopsy.contentutils.RecentFilesSummary functionality into a + * DefaultArtifactUpdateGovernor used by Recent Files Data Summary tab. + */ +public class RecentFilesGetter implements DefaultArtifactUpdateGovernor { + + private static final Set ARTIFACT_UPDATE_TYPE_IDS = new HashSet<>(Arrays.asList( + BlackboardArtifact.ARTIFACT_TYPE.TSK_RECENT_OBJECT.getTypeID(), + BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID(), + BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT.getTypeID(), + BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID(), + BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE.getTypeID() + )); + + private final RecentFilesSummary recentSummary; + + /** + * Default constructor. + */ + public RecentFilesGetter() { + recentSummary = new RecentFilesSummary(); + } + + @Override + public Set getArtifactTypeIdsForRefresh() { + return Collections.unmodifiableSet(ARTIFACT_UPDATE_TYPE_IDS); + } + + /** + * Return a list of the most recently opened documents based on the + * TSK_RECENT_OBJECT artifact. + * + * @param dataSource The data source to query. + * @param maxCount The maximum number of results to return, pass 0 to get + * a list of all results. + * + * @return A list RecentFileDetails representing the most recently opened + * documents or an empty list if none were found. + * + * @throws SleuthkitCaseProviderException + * @throws TskCoreException + */ + public List getRecentlyOpenedDocuments(DataSource dataSource, int maxCount) throws SleuthkitCaseProviderException, TskCoreException { + return recentSummary.getRecentlyOpenedDocuments(dataSource, maxCount); + } + + /** + * Return a list of the most recent downloads based on the value of the the + * artifact TSK_DATETIME_ACCESSED attribute. + * + * @param dataSource Data source to query. + * @param maxCount Maximum number of results to return, passing 0 will + * return all results. + * + * @return A list of RecentFileDetails objects or empty list if none were + * found. + * + * @throws TskCoreException + * @throws SleuthkitCaseProviderException + */ + public List getRecentDownloads(DataSource dataSource, int maxCount) throws TskCoreException, SleuthkitCaseProviderException { + return recentSummary.getRecentDownloads(dataSource, maxCount); + } + + /** + * Returns a list of the most recent message attachments. + * + * @param dataSource Data source to query. + * @param maxCount Maximum number of results to return, passing 0 will + * return all results. + * + * @return A list of RecentFileDetails of the most recent attachments. + * + * @throws SleuthkitCaseProviderException + * @throws TskCoreException + */ + public List getRecentAttachments(DataSource dataSource, int maxCount) throws SleuthkitCaseProviderException, TskCoreException { + return recentSummary.getRecentAttachments(dataSource, maxCount); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RecentFilesPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RecentFilesPanel.java index f89651072c..0bc09d59ec 100755 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RecentFilesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RecentFilesPanel.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"); @@ -27,20 +27,15 @@ import java.util.List; import java.util.Locale; import java.util.function.Function; import java.util.function.Supplier; -import java.util.stream.Collectors; -import java.util.stream.Stream; import org.openide.util.NbBundle.Messages; -import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary; import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary.RecentAttachmentDetails; import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary.RecentDownloadDetails; import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary.RecentFileDetails; -import static org.sleuthkit.autopsy.datasourcesummary.ui.BaseDataSourceSummaryPanel.getTableExport; import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer; import org.sleuthkit.autopsy.datasourcesummary.uiutils.ColumnModel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetcher; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataFetcher; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultCellModel; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport; import org.sleuthkit.autopsy.datasourcesummary.uiutils.GuiCellModel.MenuItem; import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel; @@ -70,7 +65,7 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel { private final DataFetcher> attachmentsFetcher; private final List>> docsTemplate = Arrays.asList( - new ColumnModel<>(Bundle.RecentFilePanel_col_header_path(), + new ColumnModel<>(Bundle.RecentFilesPanel_col_header_path(), (prog) -> { return new DefaultCellModel<>(prog.getPath()) .setPopupMenuRetriever(getPopupFunct(prog)); @@ -80,12 +75,12 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel { 80)); private final List>> downloadsTemplate = Arrays.asList( - new ColumnModel<>(Bundle.RecentFilePanel_col_header_domain(), + new ColumnModel<>(Bundle.RecentFilesPanel_col_header_domain(), (prog) -> { return new DefaultCellModel<>(prog.getWebDomain()) .setPopupMenuRetriever(getPopupFunct(prog)); }, 100), - new ColumnModel<>(Bundle.RecentFilePanel_col_header_path(), + new ColumnModel<>(Bundle.RecentFilesPanel_col_header_path(), (prog) -> { return new DefaultCellModel<>(prog.getPath()) .setPopupMenuRetriever(getPopupFunct(prog)); @@ -95,7 +90,7 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel { 80)); private final List>> attachmentsTemplate = Arrays.asList( - new ColumnModel<>(Bundle.RecentFilePanel_col_header_path(), + new ColumnModel<>(Bundle.RecentFilesPanel_col_header_path(), (prog) -> { return new DefaultCellModel<>(prog.getPath()) .setPopupMenuRetriever(getPopupFunct(prog)); @@ -103,7 +98,7 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel { new ColumnModel<>(Bundle.RecentFilesPanel_col_head_date(), getDateFunct(), 80), - new ColumnModel<>(Bundle.RecentFilePanel_col_header_sender(), + new ColumnModel<>(Bundle.RecentFilesPanel_col_header_sender(), (prog) -> { return new DefaultCellModel<>(prog.getSender()) .setPopupMenuRetriever(getPopupFunct(prog)); @@ -114,19 +109,18 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel { */ @Messages({ "RecentFilesPanel_col_head_date=Date", - "RecentFilePanel_col_header_domain=Domain", - "RecentFilePanel_col_header_path=Path", - "RecentFilePanel_col_header_sender=Sender", - "RecentFilePanel_emailParserModuleName=Email Parser" + "RecentFilesPanel_col_header_domain=Domain", + "RecentFilesPanel_col_header_path=Path", + "RecentFilesPanel_col_header_sender=Sender" }) public RecentFilesPanel() { - this(new RecentFilesSummary()); + this(new RecentFilesGetter()); } /** * Creates new form RecentFilesPanel */ - public RecentFilesPanel(RecentFilesSummary dataHandler) { + public RecentFilesPanel(RecentFilesGetter dataHandler) { super(dataHandler); docsFetcher = (dataSource) -> dataHandler.getRecentlyOpenedDocuments(dataSource, 10); downloadsFetcher = (dataSource) -> dataHandler.getRecentDownloads(dataSource, 10); @@ -137,15 +131,16 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel { } /** - * Returns a function that gets the date from the RecentFileDetails object and - * converts into a DefaultCellModel to be displayed in a table. + * Returns a function that gets the date from the RecentFileDetails object + * and converts into a DefaultCellModel to be displayed in a table. * - * @return The function that determines the date cell from a RecentFileDetails object. + * @return The function that determines the date cell from a + * RecentFileDetails object. */ private Function> getDateFunct() { return (T lastAccessed) -> { Function dateParser = (dt) -> dt == null ? "" : DATETIME_FORMAT.format(dt); - return new DefaultCellModel<>(new Date(lastAccessed.getDateAsLong() * 1000), dateParser, DATETIME_FORMAT_STR) + return new DefaultCellModel<>(new Date(lastAccessed.getDateAsLong() * 1000), dateParser) .setPopupMenuRetriever(getPopupFunct(lastAccessed)); }; } @@ -155,9 +150,10 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel { * items. * * @param record The RecentFileDetails instance. + * * @return The menu items list containing one action or navigating to the - * appropriate artifact/file and closing the data source summary dialog if - * open. + * appropriate artifact/file and closing the data source summary + * dialog if open. */ private Supplier> getPopupFunct(RecentFileDetails record) { return () -> { @@ -190,16 +186,6 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel { onNewDataSource(dataFetchComponents, tablePanelList, dataSource); } - @Override - List getExports(DataSource dataSource) { - return Stream.of( - getTableExport(docsFetcher, docsTemplate, Bundle.RecentFilesPanel_docsTable_tabName(), dataSource), - getTableExport(downloadsFetcher, downloadsTemplate, Bundle.RecentFilesPanel_downloadsTable_tabName(), dataSource), - getTableExport(attachmentsFetcher, attachmentsTemplate, Bundle.RecentFilesPanel_attachmentsTable_tabName(), dataSource)) - .filter(sheet -> sheet != null) - .collect(Collectors.toList()); - } - @Override public void close() { ingestRunningLabel.unregister(); @@ -216,7 +202,7 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel { } @Messages({ - "RecentFilePanel_no_open_documents=No recently open documents found." + "RecentFilesPanel_no_open_documents=No recently open documents found." }) /** * Setup the data model and columns for the recently open table. diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/SizeRepresentationUtil.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/SizeRepresentationUtil.java index 74b9be06a8..65f7402b04 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/SizeRepresentationUtil.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/SizeRepresentationUtil.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"); @@ -19,8 +19,6 @@ package org.sleuthkit.autopsy.datasourcesummary.ui; import java.text.DecimalFormat; -import java.util.Arrays; -import java.util.List; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultCellModel; @@ -45,28 +43,23 @@ public final class SizeRepresentationUtil { "SizeRepresentationUtil_units_petabytes=PB" }) enum SizeUnit { - BYTES(Bundle.SizeRepresentationUtil_units_bytes(), "#", 0), - KB(Bundle.SizeRepresentationUtil_units_kilobytes(), "#,##0.00,", 1), - MB(Bundle.SizeRepresentationUtil_units_megabytes(), "#,##0.00,,", 2), - GB(Bundle.SizeRepresentationUtil_units_gigabytes(), "#,##0.00,,,", 3), - TB(Bundle.SizeRepresentationUtil_units_terabytes(), "#,##0.00,,,,", 4), - PB(Bundle.SizeRepresentationUtil_units_petabytes(), "#,##0.00,,,,,", 5); + BYTES(Bundle.SizeRepresentationUtil_units_bytes(), 0), + KB(Bundle.SizeRepresentationUtil_units_kilobytes(), 1), + MB(Bundle.SizeRepresentationUtil_units_megabytes(), 2), + GB(Bundle.SizeRepresentationUtil_units_gigabytes(), 3), + TB(Bundle.SizeRepresentationUtil_units_terabytes(), 4), + PB(Bundle.SizeRepresentationUtil_units_petabytes(), 5); private final String suffix; - private final String excelFormatString; private final long divisor; /** * Main constructor. * @param suffix The string suffix to use for size unit. - * @param excelFormatString The excel format string to use for this size unit. * @param power The power of 1000 of bytes for this size unit. */ - SizeUnit(String suffix, String excelFormatString, int power) { + SizeUnit(String suffix, int power) { this.suffix = suffix; - - // based on https://www.mrexcel.com/board/threads/how-do-i-format-cells-to-show-gb-mb-kb.140135/ - this.excelFormatString = String.format("%s \"%s\"", excelFormatString, suffix); this.divisor = (long) Math.pow(SIZE_CONVERSION_CONSTANT, power); } @@ -77,13 +70,6 @@ public final class SizeRepresentationUtil { return suffix; } - /** - * @return The excel format string to use for this size unit. - */ - public String getExcelFormatString() { - return excelFormatString; - } - /** * @return The divisor to convert from bytes to this unit. */ @@ -114,8 +100,7 @@ public final class SizeRepresentationUtil { return SizeUnit.values()[0]; } - for (int unitsIndex = 0; unitsIndex < SizeUnit.values().length; unitsIndex++) { - SizeUnit unit = SizeUnit.values()[unitsIndex]; + for (SizeUnit unit : SizeUnit.values()) { long result = size / unit.getDivisor(); if (result < SIZE_CONVERSION_CONSTANT) { return unit; @@ -126,14 +111,14 @@ public final class SizeRepresentationUtil { } /** - * Get a long size in bytes as a string formated to be read by users. + * Get a long size in bytes as a string formatted to be read by users. * * @param size Long value representing a size in byte.s * @param format The means of formatting the number. * @param showFullSize Optionally show the number of bytes in the * datasource. * - * @return Return a string formated with a user friendly version of the size + * @return Return a string formatted with a user friendly version of the size * as a string, returns empty String when provided empty size. */ static String getSizeString(Long size, DecimalFormat format, boolean showFullSize) { @@ -168,12 +153,7 @@ public final class SizeRepresentationUtil { if (bytes == null) { return new DefaultCellModel<>(""); } else { - SizeUnit unit = SizeRepresentationUtil.getSizeUnit(bytes); - if (unit == null) { - unit = SizeUnit.BYTES; - } - - return new DefaultCellModel(bytes, SizeRepresentationUtil::getSizeString, unit.getExcelFormatString()); + return new DefaultCellModel<>(bytes, SizeRepresentationUtil::getSizeString); } } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TimelinePanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TimelinePanel.java index 94af4b7545..4b16aef7a3 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TimelinePanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TimelinePanel.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"); @@ -20,39 +20,29 @@ package org.sleuthkit.autopsy.datasourcesummary.ui; import java.awt.Color; import java.text.DateFormat; -import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.Date; import java.util.List; -import java.util.Locale; import java.util.logging.Level; import org.apache.commons.collections.CollectionUtils; import org.joda.time.DateTime; import org.joda.time.Interval; import org.openide.util.NbBundle.Messages; import org.openide.util.actions.CallableSystemAction; -import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineDataSourceUtils; import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineSummary; import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineSummary.DailyActivityAmount; import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineSummary.TimelineSummaryData; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartExport; import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartPanel; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartSeries; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartPanel.OrderedKey; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartSeries.OrderedKey; import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartSeries.BarChartItem; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetcher; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultCellModel; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport.KeyValueItemExportable; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport.TitledExportable; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataFetcher; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartSeries; import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableComponent; import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableLabel; @@ -78,20 +68,10 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel { private static final long serialVersionUID = 1L; private static final String EARLIEST_LATEST_FORMAT_STR = "MMM d, yyyy"; - private static final DateFormat EARLIEST_LATEST_FORMAT = getUtcFormat(EARLIEST_LATEST_FORMAT_STR); - private static final DateFormat CHART_FORMAT = getUtcFormat("MMM d, yyyy"); + private static final DateFormat EARLIEST_LATEST_FORMAT = TimelineSummary.getUtcFormat(EARLIEST_LATEST_FORMAT_STR); + private static final DateFormat CHART_FORMAT = TimelineSummary.getUtcFormat("MMM d, yyyy"); private static final int MOST_RECENT_DAYS_COUNT = 30; - /** - * Creates a DateFormat formatter that uses UTC for time zone. - * - * @param formatString The date format string. - * @return The data format. - */ - private static DateFormat getUtcFormat(String formatString) { - return new SimpleDateFormat(formatString, Locale.getDefault()); - } - // components displayed in the tab private final IngestRunningLabel ingestRunningLabel = new IngestRunningLabel(); private final LoadableLabel earliestLabel = new LoadableLabel(Bundle.TimelinePanel_earliestLabel_title()); @@ -108,13 +88,13 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel { private final List> dataFetchComponents; public TimelinePanel() { - this(new TimelineSummary()); + this(new TimelineSummaryGetter()); } /** * Creates new form PastCasesPanel */ - public TimelinePanel(TimelineSummary timelineData) { + public TimelinePanel(TimelineSummaryGetter timelineData) { super(timelineData); dataFetcher = (dataSource) -> timelineData.getData(dataSource, MOST_RECENT_DAYS_COUNT); @@ -126,29 +106,18 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel { initComponents(); } - /** - * Formats a date using a DateFormat. In the event that the date is null, - * returns a null string. - * - * @param date The date to format. - * @param formatter The DateFormat to use to format the date. - * @return The formatted string generated from the formatter or null if the - * date is null. - */ - private static String formatDate(Date date, DateFormat formatter) { - return date == null ? null : formatter.format(date); - } - private static final Color FILE_EVT_COLOR = new Color(228, 22, 28); private static final Color ARTIFACT_EVT_COLOR = new Color(21, 227, 100); /** - * Converts DailyActivityAmount data retrieved from TimelineSummary into - * data to be displayed as a bar chart. + * Converts DailyActivityAmount data retrieved from TimelineSummaryGetter + * into data to be displayed as a bar chart. * - * @param recentDaysActivity The data retrieved from TimelineSummary. + * @param recentDaysActivity The data retrieved from + * TimelineSummaryGetter. * @param showIntermediateDates If true, shows all dates. If false, shows - * only first and last date. + * only first and last date. + * * @return The data to be displayed in the BarChart. */ private List parseChartData(List recentDaysActivity, boolean showIntermediateDates) { @@ -167,7 +136,7 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel { long fileAmt = curItem.getFileActivityCount(); long artifactAmt = curItem.getArtifactActivityCount() * 100; String formattedDate = (showIntermediateDates || i == 0 || i == recentDaysActivity.size() - 1) - ? formatDate(curItem.getDay(), CHART_FORMAT) : ""; + ? TimelineSummary.formatDate(curItem.getDay(), CHART_FORMAT) : ""; OrderedKey thisKey = new OrderedKey(formattedDate, i); fileEvtCounts.add(new BarChartItem(thisKey, fileAmt)); @@ -191,8 +160,8 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel { * @param result The result to be displayed on this tab. */ private void handleResult(DataFetchResult result) { - earliestLabel.showDataFetchResult(DataFetchResult.getSubResult(result, r -> formatDate(r.getMinDate(), EARLIEST_LATEST_FORMAT))); - latestLabel.showDataFetchResult(DataFetchResult.getSubResult(result, r -> formatDate(r.getMaxDate(), EARLIEST_LATEST_FORMAT))); + earliestLabel.showDataFetchResult(DataFetchResult.getSubResult(result, r -> TimelineSummary.formatDate(r.getMinDate(), EARLIEST_LATEST_FORMAT))); + latestLabel.showDataFetchResult(DataFetchResult.getSubResult(result, r -> TimelineSummary.formatDate(r.getMaxDate(), EARLIEST_LATEST_FORMAT))); last30DaysChart.showDataFetchResult(DataFetchResult.getSubResult(result, r -> parseChartData(r.getMostRecentDaysActivity(), false))); if (result != null @@ -242,8 +211,8 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel { * Action that occurs when 'View in Timeline' button is pressed. * * @param dataSource The data source to filter to. - * @param minDate The min date for the zoom of the window. - * @param maxDate The max date for the zoom of the window. + * @param minDate The min date for the zoom of the window. + * @param maxDate The max date for the zoom of the window. */ private void openFilteredChart(DataSource dataSource, Date minDate, Date maxDate) { OpenTimelineAction openTimelineAction = CallableSystemAction.get(OpenTimelineAction.class); @@ -266,7 +235,7 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel { if (minDate != null && maxDate != null) { timeSpan = new Interval(new DateTime(minDate), new DateTime(maxDate)); } - } catch (NoCurrentCaseException | TskCoreException ex) { + } catch (TskCoreException ex) { logger.log(Level.WARNING, "Unable to view time range in Timeline view", ex); } @@ -293,43 +262,6 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel { super.close(); } - /** - * Create a default cell model to be use with excel export in the earliest / - * latest date format. - * - * @param date The date. - * @return The cell model. - */ - private static DefaultCellModel getEarliestLatestCell(Date date) { - return new DefaultCellModel<>(date, (dt) -> dt == null ? "" : EARLIEST_LATEST_FORMAT.format(dt), EARLIEST_LATEST_FORMAT_STR); - } - - @Messages({ - "TimelinePanel_getExports_sheetName=Timeline", - "TimelinePanel_getExports_activityRange=Activity Range", - "TimelinePanel_getExports_earliest=Earliest:", - "TimelinePanel_getExports_latest=Latest:", - "TimelinePanel_getExports_dateColumnHeader=Date", - "TimelinePanel_getExports_chartName=Last 30 Days",}) - @Override - List getExports(DataSource dataSource) { - TimelineSummaryData summaryData = getFetchResult(dataFetcher, "Timeline", dataSource); - if (summaryData == null) { - return Collections.emptyList(); - } - - return Arrays.asList( - new ExcelSpecialFormatExport(Bundle.TimelinePanel_getExports_sheetName(), - Arrays.asList( - new TitledExportable(Bundle.TimelinePanel_getExports_activityRange(), Collections.emptyList()), - new KeyValueItemExportable(Bundle.TimelinePanel_getExports_earliest(), getEarliestLatestCell(summaryData.getMinDate())), - new KeyValueItemExportable(Bundle.TimelinePanel_getExports_latest(), getEarliestLatestCell(summaryData.getMaxDate())), - new BarChartExport(Bundle.TimelinePanel_getExports_dateColumnHeader(), - "#,###", - Bundle.TimelinePanel_getExports_chartName(), - parseChartData(summaryData.getMostRecentDaysActivity(), true))))); - } - /** * This method is called from within the constructor to initialize the form. * WARNING: Do NOT modify this code. The content of this method is always diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TimelineSummaryGetter.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TimelineSummaryGetter.java new file mode 100644 index 0000000000..81be37577f --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TimelineSummaryGetter.java @@ -0,0 +1,88 @@ +/* + * 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.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultUpdateGovernor; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineSummary; +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.autopsy.datasourcesummary.datamodel.TimelineSummary.TimelineSummaryData; + +/** + * Provides data source summary information pertaining to Timeline data. + */ +public class TimelineSummaryGetter implements DefaultUpdateGovernor { + + private static final Set INGEST_JOB_EVENTS = new HashSet<>( + Arrays.asList(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED)); + + private final TimelineSummary timelineSummary; + + /** + * Default constructor. + */ + public TimelineSummaryGetter() { + timelineSummary = new TimelineSummary(); + } + + @Override + public boolean isRefreshRequired(ModuleContentEvent evt) { + return true; + } + + @Override + public boolean isRefreshRequired(AbstractFile file) { + return true; + } + + @Override + public boolean isRefreshRequired(IngestManager.IngestJobEvent evt) { + return (evt != null && INGEST_JOB_EVENTS.contains(evt)); + } + + @Override + public Set getIngestJobEventUpdates() { + return Collections.unmodifiableSet(INGEST_JOB_EVENTS); + } + + /** + * Retrieves timeline summary data. + * + * @param dataSource The data source for which timeline data will be + * retrieved. + * @param recentDaysNum The maximum number of most recent days' activity to + * include. + * + * @return The retrieved data. + * + * @throws SleuthkitCaseProviderException + * @throws TskCoreException + */ + public TimelineSummaryData getData(DataSource dataSource, int recentDaysNum) throws SleuthkitCaseProviderException, TskCoreException { + return timelineSummary.getTimelineSummaryData(dataSource, recentDaysNum); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TypesPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TypesPanel.java index e11e638459..f1421665c5 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,31 +23,23 @@ 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.datasourcesummary.datamodel.ContainerSummary; -import org.sleuthkit.autopsy.datasourcesummary.datamodel.MimeTypeSummary; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataSourceInfoUtilities; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.TypesSummary.FileTypeCategoryData; import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult.ResultType; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetcher; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultCellModel; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport.KeyValueItemExportable; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataFetcher; import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableComponent; import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableLabel; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.PieChartExport; import org.sleuthkit.autopsy.datasourcesummary.uiutils.PieChartPanel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.PieChartItem; @@ -95,7 +87,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; } @@ -103,78 +95,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; @@ -186,13 +120,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; @@ -237,8 +171,8 @@ class TypesPanel extends BaseDataSourceSummaryPanel { /** * Creates a new TypesPanel. */ - public TypesPanel() { - this(new MimeTypeSummary(), new TypesSummary(), new ContainerSummary()); + TypesPanel() { + this(new MimeTypeSummaryGetter(), new TypesSummaryGetter(), new ContainerSummaryGetter()); } @Override @@ -254,10 +188,10 @@ class TypesPanel extends BaseDataSourceSummaryPanel { * @param typeData The service for file types data. * @param containerData The service for container information. */ - public TypesPanel( - MimeTypeSummary mimeTypeData, - TypesSummary typeData, - ContainerSummary containerData) { + TypesPanel( + MimeTypeSummaryGetter mimeTypeData, + TypesSummaryGetter typeData, + ContainerSummaryGetter containerData) { super(mimeTypeData, typeData, containerData); @@ -282,13 +216,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(); @@ -312,7 +246,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) { @@ -323,8 +257,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( @@ -334,10 +268,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); @@ -390,89 +324,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. - * - * @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 = 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 = getFetchResult(fetcher, "Types", dataSource); - return (count == null) ? null : new KeyValueItemExportable(key, - new DefaultCellModel(count, COMMA_FORMATTER::format, COMMA_FORMAT_STR)); - } - - @Override - List getExports(DataSource dataSource) { - if (dataSource == null) { - return Collections.emptyList(); - } - - // Retrieve data to create the types pie chart - TypesPieChartData typesData = TypesPanel.getFetchResult(typesFetcher, "Types", dataSource); - PieChartExport typesChart = (typesData == null || !typesData.isUsefulContent()) ? null : - new PieChartExport( - Bundle.TypesPanel_fileMimeTypesChart_title(), - Bundle.TypesPanel_fileMimeTypesChart_valueLabel(), - "#,###", - Bundle.TypesPanel_fileMimeTypesChart_title(), - typesData.getPieSlices()); - - return Arrays.asList(new ExcelSpecialFormatExport(Bundle.TypesPanel_excelTabName(), - Stream.of( - getStrExportable(usageFetcher, Bundle.TypesPanel_usageLabel_title(), dataSource), - getStrExportable(osFetcher, Bundle.TypesPanel_osLabel_title(), dataSource), - new KeyValueItemExportable(Bundle.TypesPanel_sizeLabel_title(), - SizeRepresentationUtil.getBytesCell(getFetchResult(sizeFetcher, "Types", dataSource))), - typesChart, - getCountExportable(allocatedFetcher, Bundle.TypesPanel_filesByCategoryTable_allocatedRow_title(), dataSource), - getCountExportable(unallocatedFetcher, Bundle.TypesPanel_filesByCategoryTable_unallocatedRow_title(), dataSource), - getCountExportable(slackFetcher, Bundle.TypesPanel_filesByCategoryTable_slackRow_title(), dataSource), - getCountExportable(directoriesFetcher, Bundle.TypesPanel_filesByCategoryTable_directoryRow_title(), dataSource)) - .filter(sheet -> sheet != null) - .collect(Collectors.toList()) - )); - } - /** * This method is called from within the constructor to initialize the form. * WARNING: Do NOT modify this code. The content of this method is always diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TypesSummaryGetter.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TypesSummaryGetter.java new file mode 100644 index 0000000000..fee3f178cc --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TypesSummaryGetter.java @@ -0,0 +1,154 @@ +/* + * 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.ui; + +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.datasourcesummary.datamodel.SleuthkitCaseProvider; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.TypesSummary; +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; + +/** + * Wrapper class for converting org.sleuthkit.autopsy.contentutils.TypesSummary + * functionality into a DefaultArtifactUpdateGovernor used by + * DataSourceSummaryCountsPanel. + */ +public class TypesSummaryGetter implements DefaultUpdateGovernor { + + private static final Set INGEST_JOB_EVENTS = new HashSet<>( + Arrays.asList(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED)); + + private final TypesSummary typesSummary; + + /** + * Main constructor. + */ + public TypesSummaryGetter() { + typesSummary = new TypesSummary(); + } + + @Override + public boolean isRefreshRequired(ModuleContentEvent evt) { + return true; + } + + @Override + public boolean isRefreshRequired(AbstractFile file) { + return true; + } + + @Override + public boolean isRefreshRequired(IngestManager.IngestJobEvent evt) { + return (evt != null && INGEST_JOB_EVENTS.contains(evt)); + } + + @Override + public Set getIngestJobEventUpdates() { + return Collections.unmodifiableSet(INGEST_JOB_EVENTS); + } + + /** + * Get count of regular files (not directories) in a data source. + * + * @param currentDataSource The data source. + * + * @return The count. + * + * @throws SleuthkitCaseProviderException + * @throws TskCoreException + * @throws SQLException + */ + public Long getCountOfFiles(DataSource currentDataSource) + throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException { + return typesSummary.getCountOfFiles(currentDataSource); + } + + /** + * Get count of allocated files in a data source. + * + * @param currentDataSource The data source. + * + * @return The count. + * + * @throws SleuthkitCaseProviderException + * @throws TskCoreException + * @throws SQLException + */ + public Long getCountOfAllocatedFiles(DataSource currentDataSource) + throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException { + return typesSummary.getCountOfAllocatedFiles(currentDataSource); + } + + /** + * Get count of unallocated files in a data source. + * + * @param currentDataSource The data source. + * + * @return The count. + * + * @throws SleuthkitCaseProviderException + * @throws TskCoreException + * @throws SQLException + */ + public Long getCountOfUnallocatedFiles(DataSource currentDataSource) + throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException { + return typesSummary.getCountOfUnallocatedFiles(currentDataSource); + } + + /** + * Get count of directories in a data source. + * + * @param currentDataSource The data source. + * + * @return The count. + * + * @throws SleuthkitCaseProviderException + * @throws TskCoreException + * @throws SQLException + */ + public Long getCountOfDirectories(DataSource currentDataSource) + throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException { + return typesSummary.getCountOfDirectories(currentDataSource); + } + + /** + * Get count of slack files in a data source. + * + * @param currentDataSource The data source. + * + * @return The count. + * + * @throws SleuthkitCaseProviderException + * @throws TskCoreException + * @throws SQLException + */ + public Long getCountOfSlackFiles(DataSource currentDataSource) + throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException { + return typesSummary.getCountOfSlackFiles(currentDataSource); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/UserActivityPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/UserActivityPanel.java index 2cde769403..46f5deca36 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/UserActivityPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/UserActivityPanel.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"); @@ -26,23 +26,19 @@ import java.util.Date; import java.util.List; import java.util.Locale; import java.util.function.Function; -import java.util.stream.Collectors; -import java.util.stream.Stream; import org.apache.commons.lang.StringUtils; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary; import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.LastAccessedArtifact; import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopAccountResult; import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopDeviceAttachedResult; -import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopWebSearchResult; import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopDomainsResult; import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopProgramsResult; -import static org.sleuthkit.autopsy.datasourcesummary.ui.BaseDataSourceSummaryPanel.getTableExport; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopWebSearchResult; import org.sleuthkit.autopsy.datasourcesummary.uiutils.ColumnModel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetcher; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataFetcher; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultCellModel; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport; import org.sleuthkit.autopsy.datasourcesummary.uiutils.GuiCellModel.MenuItem; import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel; @@ -265,22 +261,22 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { private final IngestRunningLabel ingestRunningLabel = new IngestRunningLabel(); private final List> dataFetchComponents; - private final UserActivitySummary userActivityData; + private final UserActivitySummaryGetter userActivityData; /** * Creates a new UserActivityPanel. */ public UserActivityPanel() { - this(new UserActivitySummary()); + this(new UserActivitySummaryGetter()); } /** * Creates a new UserActivityPanel. * * @param userActivityData Class from which to obtain remaining user - * activity data. + * activity data. */ - public UserActivityPanel(UserActivitySummary userActivityData) { + public UserActivityPanel(UserActivitySummaryGetter userActivityData) { super(userActivityData); this.userActivityData = userActivityData; @@ -320,7 +316,7 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { private Function> getDateFunct() { return (T lastAccessed) -> { Function dateParser = (dt) -> dt == null ? "" : DATETIME_FORMAT.format(dt); - return new DefaultCellModel<>(lastAccessed.getLastAccessed(), dateParser, DATETIME_FORMAT_STR) + return new DefaultCellModel<>(lastAccessed.getLastAccessed(), dateParser) .setPopupMenu(getPopup(lastAccessed)); }; } @@ -332,7 +328,8 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { * @param record The LastAccessedArtifact instance. * * @return The menu items list containing one action or navigating to the - * appropriate artifact and closing the data source summary dialog if open. + * appropriate artifact and closing the data source summary dialog + * if open. */ private List getPopup(LastAccessedArtifact record) { return record == null ? null : Arrays.asList(getArtifactNavigateItem(record.getArtifact())); @@ -341,13 +338,13 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { /** * Queries DataSourceTopProgramsSummary instance for short folder name. * - * @param path The path for the application. + * @param path The path for the application. * @param appName The application name. * * @return The underlying short folder name if one exists. */ - private String getShortFolderName(String path, String appName) { - return this.userActivityData.getShortFolderName(path, appName); + private static String getShortFolderName(String path, String appName) { + return UserActivitySummary.getShortFolderName(path, appName); } @Override @@ -366,18 +363,6 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { super.close(); } - @Override - List getExports(DataSource dataSource) { - return Stream.of( - getTableExport(topProgramsFetcher, topProgramsTemplate, Bundle.UserActivityPanel_TopProgramsTableModel_tabName(), dataSource), - getTableExport(topDomainsFetcher, topDomainsTemplate, Bundle.UserActivityPanel_TopDomainsTableModel_tabName(), dataSource), - getTableExport(topWebSearchesFetcher, topWebSearchesTemplate, Bundle.UserActivityPanel_TopWebSearchTableModel_tabName(), dataSource), - getTableExport(topDevicesAttachedFetcher, topDevicesTemplate, Bundle.UserActivityPanel_TopDeviceAttachedTableModel_tabName(), dataSource), - getTableExport(topAccountsFetcher, topAccountsTemplate, Bundle.UserActivityPanel_TopAccountTableModel_tabName(), dataSource)) - .filter(sheet -> sheet != null) - .collect(Collectors.toList()); - } - /** * This method is called from within the constructor to initialize the form. * WARNING: Do NOT modify this code. The content of this method is always diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/UserActivitySummaryGetter.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/UserActivitySummaryGetter.java new file mode 100644 index 0000000000..e9f9dd30b0 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/UserActivitySummaryGetter.java @@ -0,0 +1,162 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2020-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.datasourcesummary.ui; + +import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultArtifactUpdateGovernor; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException; +import org.sleuthkit.datamodel.DataSource; +import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopAccountResult; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopDeviceAttachedResult; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopDomainsResult; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopProgramsResult; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopWebSearchResult; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; + +/** + * Wrapper class for converting + * org.sleuthkit.autopsy.contentutils.UserActivitySummary functionality into a + * DefaultArtifactUpdateGovernor used by UserActivityPanel tab. + */ +public class UserActivitySummaryGetter implements DefaultArtifactUpdateGovernor { + + private static final Set ARTIFACT_UPDATE_TYPE_IDS = new HashSet<>(Arrays.asList( + ARTIFACT_TYPE.TSK_WEB_SEARCH_QUERY.getTypeID(), + ARTIFACT_TYPE.TSK_MESSAGE.getTypeID(), + ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID(), + ARTIFACT_TYPE.TSK_CALLLOG.getTypeID(), + ARTIFACT_TYPE.TSK_DEVICE_ATTACHED.getTypeID(), + ARTIFACT_TYPE.TSK_WEB_HISTORY.getTypeID(), + ARTIFACT_TYPE.TSK_PROG_RUN.getTypeID() + )); + + private final UserActivitySummary userActivity; + + public UserActivitySummaryGetter() { + userActivity = new UserActivitySummary(); + } + + @Override + public Set getArtifactTypeIdsForRefresh() { + return Collections.unmodifiableSet(ARTIFACT_UPDATE_TYPE_IDS); + } + + /** + * Gets a list of recent domains based on the datasource. + * + * @param dataSource The datasource to query for recent domains. + * @param count The max count of items to return. + * + * @return The list of items retrieved from the database. + * + * @throws InterruptedException + */ + public List getRecentDomains(DataSource dataSource, int count) throws TskCoreException, SleuthkitCaseProviderException { + return userActivity.getRecentDomains(dataSource, count); + } + + /** + * Retrieves most recent web searches by most recent date grouped by search + * term. + * + * @param dataSource The data source. + * @param count The maximum number of records to be shown (must be > + * 0). + * + * @return The list of most recent web searches where most recent search + * appears first. + * + * @throws + * org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException + * @throws TskCoreException + */ + public List getMostRecentWebSearches(DataSource dataSource, int count) throws SleuthkitCaseProviderException, TskCoreException { + return userActivity.getMostRecentWebSearches(dataSource, count); + } + + /** + * Retrieves most recent devices used by most recent date attached. + * + * @param dataSource The data source. + * @param count The maximum number of records to be shown (must be > + * 0). + * + * @return The list of most recent devices attached where most recent device + * attached appears first. + * + * @throws + * org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException + * @throws TskCoreException + */ + public List getRecentDevices(DataSource dataSource, int count) throws SleuthkitCaseProviderException, TskCoreException { + return userActivity.getRecentDevices(dataSource, count); + } + + /** + * Retrieves most recent account used by most recent date for a message + * sent. + * + * @param dataSource The data source. + * @param count The maximum number of records to be shown (must be > + * 0). + * + * @return The list of most recent accounts used where the most recent + * account by last message sent occurs first. + * + * @throws + * org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException + * @throws TskCoreException + */ + @Messages({ + "DataSourceUserActivitySummary_getRecentAccounts_emailMessage=Email Message", + "DataSourceUserActivitySummary_getRecentAccounts_calllogMessage=Call Log",}) + public List getRecentAccounts(DataSource dataSource, int count) throws SleuthkitCaseProviderException, TskCoreException { + return userActivity.getRecentAccounts(dataSource, count); + } + + /** + * Retrieves the top programs results for the given data source limited to + * the count provided as a parameter. The highest run times are at the top + * of the list. If that information isn't available the last run date is + * used. If both, the last run date and the number of run times are + * unavailable, the programs will be sorted alphabetically, the count will + * be ignored and all items will be returned. + * + * @param dataSource The datasource. If the datasource is null, an empty + * list will be returned. + * @param count The number of results to return. This value must be > 0 + * or an IllegalArgumentException will be thrown. + * + * @return The sorted list and limited to the count if last run or run count + * information is available on any item. + * + * @throws SleuthkitCaseProviderException + * @throws TskCoreException + */ + public List getTopPrograms(DataSource dataSource, int count) throws SleuthkitCaseProviderException, TskCoreException { + return userActivity.getTopPrograms(dataSource, count); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/BarChartPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/BarChartPanel.java index 21f3bca572..eb363a8a47 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/BarChartPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/BarChartPanel.java @@ -39,84 +39,6 @@ import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartSeries.BarChartIt */ public class BarChartPanel extends AbstractLoadableComponent> { - /** - * JFreeChart bar charts don't preserve the order of bars provided to the - * chart, but instead uses the comparable nature to order items. This - * provides order using a provided index as well as the value for the axis. - */ - public static class OrderedKey implements Comparable { - - private final Object keyValue; - private final int keyIndex; - - /** - * Main constructor. - * - * @param keyValue The value for the key to be displayed in the domain - * axis. - * @param keyIndex The index at which it will be displayed. - */ - public OrderedKey(Object keyValue, int keyIndex) { - this.keyValue = keyValue; - this.keyIndex = keyIndex; - } - - /** - * @return The value for the key to be displayed in the domain axis. - */ - Object getKeyValue() { - return keyValue; - } - - /** - * @return The index at which it will be displayed. - */ - int getKeyIndex() { - return keyIndex; - } - - @Override - public int compareTo(OrderedKey o) { - // this will have a higher value than null. - if (o == null) { - return 1; - } - - // compare by index - return Integer.compare(this.getKeyIndex(), o.getKeyIndex()); - } - - @Override - public int hashCode() { - int hash = 3; - return hash; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final OrderedKey other = (OrderedKey) obj; - if (this.keyIndex != other.keyIndex) { - return false; - } - return true; - } - - @Override - public String toString() { - // use toString on the key. - return this.getKeyValue() == null ? null : this.getKeyValue().toString(); - } - } - private static final long serialVersionUID = 1L; private static final Font DEFAULT_FONT = new JLabel().getFont(); diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/BarChartSeries.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/BarChartSeries.java index c1626f34b2..8709fc8ae1 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/BarChartSeries.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/BarChartSeries.java @@ -98,4 +98,82 @@ public class BarChartSeries { return key; } + /** + * JFreeChart bar charts don't preserve the order of bars provided to the + * chart, but instead uses the comparable nature to order items. This + * provides order using a provided index as well as the value for the axis. + */ + public static class OrderedKey implements Comparable { + + private final Object keyValue; + private final int keyIndex; + + /** + * Main constructor. + * + * @param keyValue The value for the key to be displayed in the domain + * axis. + * @param keyIndex The index at which it will be displayed. + */ + public OrderedKey(Object keyValue, int keyIndex) { + this.keyValue = keyValue; + this.keyIndex = keyIndex; + } + + /** + * @return The value for the key to be displayed in the domain axis. + */ + Object getKeyValue() { + return keyValue; + } + + /** + * @return The index at which it will be displayed. + */ + int getKeyIndex() { + return keyIndex; + } + + @Override + public int compareTo(OrderedKey o) { + // this will have a higher value than null. + if (o == null) { + return 1; + } + + // compare by index + return Integer.compare(this.getKeyIndex(), o.getKeyIndex()); + } + + @Override + public int hashCode() { + int hash = 3; + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final OrderedKey other = (OrderedKey) obj; + if (this.keyIndex != other.keyIndex) { + return false; + } + return true; + } + + @Override + public String toString() { + // use toString on the key. + return this.getKeyValue() == null ? null : this.getKeyValue().toString(); + } + } + } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/Bundle.properties-MERGED index ad10f70c22..c06bc6850a 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/Bundle.properties-MERGED @@ -1,7 +1,5 @@ AbstractLoadableComponent_errorMessage_defaultText=There was an error loading results. AbstractLoadableComponent_loadingMessage_defaultText=Loading results... AbstractLoadableComponent_noDataExists_defaultText=No data exists. -# {0} - sheetNumber -ExcelExport_writeExcel_noSheetName=Sheet {0} IngestRunningLabel_defaultMessage=Ingest is currently running. PieChartPanel_noDataLabel=No Data diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataFetchWorker.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataFetchWorker.java index 46e68a1f55..8d007c2a01 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataFetchWorker.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DataFetchWorker.java @@ -18,6 +18,7 @@ */ package org.sleuthkit.autopsy.datasourcesummary.uiutils; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataFetcher; import java.util.concurrent.ExecutionException; import java.util.function.Consumer; import javax.swing.SwingWorker; diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DefaultCellModel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DefaultCellModel.java index 215f71469e..81c176954a 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DefaultCellModel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DefaultCellModel.java @@ -27,7 +27,7 @@ import java.util.function.Supplier; /** * The default cell model. */ -public class DefaultCellModel implements GuiCellModel, ExcelCellModel { +public class DefaultCellModel implements GuiCellModel { private final T data; private final String text; @@ -35,7 +35,6 @@ public class DefaultCellModel implements GuiCellModel, ExcelCellModel { private CellModel.HorizontalAlign horizontalAlignment; private List popupMenu; private Supplier> menuItemSupplier; - private final String excelFormatString; /** * Main constructor. @@ -43,18 +42,7 @@ public class DefaultCellModel implements GuiCellModel, ExcelCellModel { * @param data The data to be displayed in the cell. */ public DefaultCellModel(T data) { - this(data, null, null); - } - - /** - * Constructor. - * - * @param data The data to be displayed in the cell. - * @param stringConverter The means of converting that data to a string or - * null to use .toString method on object. - */ - public DefaultCellModel(T data, Function stringConverter) { - this(data, stringConverter, null); + this(data, null); } /** @@ -63,15 +51,9 @@ public class DefaultCellModel implements GuiCellModel, ExcelCellModel { * @param data The data to be displayed in the cell. * @param stringConverter The means of converting that data to a string or * null to use .toString method on object. - * @param excelFormatString The apache poi excel format string to use with - * the data. - * - * NOTE: Only certain data types can be exported. See - * ExcelTableExport.createCell() for types. */ - public DefaultCellModel(T data, Function stringConverter, String excelFormatString) { + public DefaultCellModel(T data, Function stringConverter) { this.data = data; - this.excelFormatString = excelFormatString; if (stringConverter == null) { text = this.data == null ? "" : this.data.toString(); @@ -86,11 +68,6 @@ public class DefaultCellModel implements GuiCellModel, ExcelCellModel { return this.data; } - @Override - public String getExcelFormatString() { - return this.excelFormatString; - } - @Override public String getText() { return text; diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ExcelCellModel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ExcelCellModel.java deleted file mode 100644 index 0ca52f12e4..0000000000 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ExcelCellModel.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 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.datasourcesummary.uiutils; - -/** - * Basic interface for a cell model. - */ -public interface ExcelCellModel extends CellModel { - - /** - * @return The format string to be used with Apache POI during excel - * export or null if none necessary. - */ - String getExcelFormatString(); - -} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/BarChartExport.java b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/BarChartExport.java similarity index 93% rename from Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/BarChartExport.java rename to Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/BarChartExport.java index 15a93092cc..330aac4cff 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/BarChartExport.java +++ b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/BarChartExport.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.datasourcesummary.uiutils; +package org.sleuthkit.autopsy.report.modules.datasourcesummaryexport; import java.awt.Color; import java.nio.ByteBuffer; @@ -49,15 +49,16 @@ import org.apache.poi.xssf.usermodel.XSSFChart; import org.apache.poi.xssf.usermodel.XSSFClientAnchor; import org.apache.poi.xssf.usermodel.XSSFDrawing; import org.apache.poi.xssf.usermodel.XSSFSheet; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport.ExcelExportException; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport.ExcelSheetExport; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport.ExcelItemExportable; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport.ItemDimensions; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartSeries; +import org.sleuthkit.autopsy.report.modules.datasourcesummaryexport.ExcelExport.ExcelExportException; +import org.sleuthkit.autopsy.report.modules.datasourcesummaryexport.ExcelExport.ExcelSheetExport; +import org.sleuthkit.autopsy.report.modules.datasourcesummaryexport.ExcelSpecialFormatExport.ExcelItemExportable; +import org.sleuthkit.autopsy.report.modules.datasourcesummaryexport.ExcelSpecialFormatExport.ItemDimensions; /** * Class that creates an excel stacked bar chart along with data table. */ -public class BarChartExport implements ExcelItemExportable, ExcelSheetExport { +class BarChartExport implements ExcelItemExportable, ExcelSheetExport { /** * Creates an excel table model to be written to an excel sheet and used as @@ -70,7 +71,7 @@ public class BarChartExport implements ExcelItemExportable, ExcelSheetExport { * @return An excel table export to be used as the data source for the chart * in the excel document. */ - private static ExcelTableExport>, ? extends ExcelCellModel> getTableModel( + private static ExcelTableExport>, ? extends CellModel> getTableModel( List categories, String keyColumnHeader, String chartTitle) { // get the row keys by finding the series with the largest set of bar items @@ -134,7 +135,7 @@ public class BarChartExport implements ExcelItemExportable, ExcelSheetExport { private static final int DEFAULT_ROW_PADDING = 1; private static final int DEFAULT_COL_OFFSET = 1; - private final ExcelTableExport>, ? extends ExcelCellModel> tableExport; + private final ExcelTableExport>, ? extends CellModel> tableExport; private final int colOffset; private final int rowPadding; private final int colSize; @@ -154,7 +155,7 @@ public class BarChartExport implements ExcelItemExportable, ExcelSheetExport { * @param chartTitle The title for the chart. * @param categories The categories along with data. */ - public BarChartExport(String keyColumnHeader, + BarChartExport(String keyColumnHeader, String valueFormatString, String chartTitle, List categories) { @@ -177,7 +178,7 @@ public class BarChartExport implements ExcelItemExportable, ExcelSheetExport { * @param colSize The column size of the chart. * @param rowSize The row size of the chart. */ - public BarChartExport(String keyColumnHeader, String valueFormatString, + BarChartExport(String keyColumnHeader, String valueFormatString, String chartTitle, String sheetName, List categories, int colOffset, int rowPadding, int colSize, int rowSize) { diff --git a/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/Bundle.properties b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/Bundle.properties new file mode 100755 index 0000000000..6c86626cdb --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/Bundle.properties @@ -0,0 +1,3 @@ +DataSourceSummaryReport.getName.text=Data Source Summary Report +DataSourceSummaryReport.getDesc.text=Data source summary report in Excel (XLS) format. +DataSourceSummaryReport.endReport.srcModuleName.text=Excel Report \ No newline at end of file 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 new file mode 100755 index 0000000000..1767afc028 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/Bundle.properties-MERGED @@ -0,0 +1,123 @@ +DataSourceSummaryReport.error.noDataSources=No data sources selected for report. +DataSourceSummaryReport.error.noOpenCase=No currently open case. +DataSourceSummaryReport.excelFileWriteError=Could not write the KML file. +DataSourceSummaryReport.failedToCompleteReport=Failed to complete report. +DataSourceSummaryReport.getName.text=Data Source Summary Report +DataSourceSummaryReport.getDesc.text=Data source summary report in Excel (XLS) format. +DataSourceSummaryReport.endReport.srcModuleName.text=Excel Report +# {0} - sheetNumber +ExcelExport_writeExcel_noSheetName=Sheet {0} +ExcelExportAction_exportToXLSX_beginExport=Beginning Export... +ExcelExportAction_exportToXLSX_gatheringAnalysisData=Fetching Analysis Data +ExcelExportAction_exportToXLSX_gatheringContainerData=Fetching Container & Image Data +ExcelExportAction_exportToXLSX_gatheringFileData=Fetching File and MIME Type Data +ExcelExportAction_exportToXLSX_gatheringGeoData=Fetching Geolocation Data +ExcelExportAction_exportToXLSX_gatheringIngestData=Fetching Ingest History Data +ExcelExportAction_exportToXLSX_gatheringPastData=Fetching Historical Data +ExcelExportAction_exportToXLSX_gatheringRecentActivityData=Fetching Recent Activity Data +ExcelExportAction_exportToXLSX_gatheringTimelineData=Fetching Timeline Data +ExcelExportAction_exportToXLSX_gatheringUserData=Fetching User Activity Data +ExcelExportAction_exportToXLSX_writingToFile=Writing to File... +ExcelExportAction_getXLSXPath_directory=DataSourceSummary +ExcelExportAction_moduleName=Data Source Summary +ExportAnalysisResults_countColumn_title=Count +ExportAnalysisResults_hashsetHits_tabName=Hashset Hits +ExportAnalysisResults_interestingItemHits_tabName=Interesting Item Hits +ExportAnalysisResults_keyColumn_title=Name +ExportAnalysisResults_keywordHits_tabName=Keyword Hits +ExportAnalysisResults_keywordSearchModuleName=Keyword Search +ExportContainerInfo_export_acquisitionDetails=Acquisition Details: +ExportContainerInfo_export_deviceId=Device ID: +ExportContainerInfo_export_displayName=Display Name: +ExportContainerInfo_export_filePaths=File Paths: +ExportContainerInfo_export_imageType=Image Type: +ExportContainerInfo_export_md5=MD5: +ExportContainerInfo_export_originalName=Name: +ExportContainerInfo_export_sectorSize=Sector Size: +ExportContainerInfo_export_sha1=SHA1: +ExportContainerInfo_export_sha256=SHA256: +ExportContainerInfo_export_size=Size: +ExportContainerInfo_export_timeZone=Time Zone: +ExportContainerInfo_export_unallocatedSize=Unallocated Space: +ExportContainerInfo_setFieldsForNonImageDataSource_na=N/A +ExportContainerInfo_tabName=Container +ExportGeolocation_cityColumn_title=Closest City +ExportGeolocation_countColumn_title=Count +ExportGeolocation_mostCommon_tabName=Most Common Cities +ExportGeolocation_mostRecent_tabName=Most Recent Cities +ExportGeolocation_unknownRow_title=Unknown +ExportIngestHistory_endTimeColumn=End Time +ExportIngestHistory_ingestStatusTimeColumn=Ingest Status +ExportIngestHistory_moduleNameTimeColumn=Module Name +ExportIngestHistory_sheetName=Ingest History +ExportIngestHistory_startTimeColumn=Start Time +ExportIngestHistory_versionColumn=Module Version +ExportPastCases_caseColumn_title=Case +ExportPastCases_countColumn_title=Count +ExportPastCases_notableFileTable_tabName=Cases with Common Notable +ExportPastCases_sameIdsTable_tabName=Past Cases with the Same Devices +ExportRecentFiles_attachmentsTable_tabName=Recent Attachments +ExportRecentFiles_col_head_date=Date +ExportRecentFiles_col_header_domain=Domain +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 +ExportUserActivity_noDataExists=No communication data exists +ExportUserActivity_tab_title=User Activity +ExportUserActivity_TopAccountTableModel_accountType_header=Account Type +ExportUserActivity_TopAccountTableModel_lastAccess_header=Last Accessed +ExportUserActivity_TopAccountTableModel_tabName=Recent Account Types Used +ExportUserActivity_TopDeviceAttachedTableModel_dateAccessed_header=Last Accessed +ExportUserActivity_TopDeviceAttachedTableModel_deviceId_header=Device Id +ExportUserActivity_TopDeviceAttachedTableModel_makeModel_header=Make and Model +ExportUserActivity_TopDeviceAttachedTableModel_tabName=Recent Devices Attached +ExportUserActivity_TopDomainsTableModel_count_header=Visits +ExportUserActivity_TopDomainsTableModel_domain_header=Domain +ExportUserActivity_TopDomainsTableModel_lastAccess_header=Last Accessed +ExportUserActivity_TopDomainsTableModel_tabName=Recent Domains +ExportUserActivity_TopProgramsTableModel_count_header=Run Times +ExportUserActivity_TopProgramsTableModel_folder_header=Folder +ExportUserActivity_TopProgramsTableModel_lastrun_header=Last Run +ExportUserActivity_TopProgramsTableModel_name_header=Program +ExportUserActivity_TopProgramsTableModel_tabName=Recent Programs +ExportUserActivity_TopWebSearchTableModel_dateAccessed_header=Date Accessed +ExportUserActivity_TopWebSearchTableModel_searchString_header=Search String +ExportUserActivity_TopWebSearchTableModel_tabName=Recent Web Searches +ExportUserActivity_TopWebSearchTableModel_translatedResult_header=Translated +SizeRepresentationUtil_units_bytes=bytes +SizeRepresentationUtil_units_gigabytes=GB +SizeRepresentationUtil_units_kilobytes=KB +SizeRepresentationUtil_units_megabytes=MB +SizeRepresentationUtil_units_petabytes=PB +SizeRepresentationUtil_units_terabytes=TB +TimelinePanel_earliestLabel_title=Earliest +TimelinePanel_getExports_activityRange=Activity Range +TimelinePanel_getExports_chartName=Last 30 Days +TimelinePanel_getExports_dateColumnHeader=Date +TimelinePanel_getExports_earliest=Earliest: +TimelinePanel_getExports_latest=Latest: +TimelinePanel_getExports_sheetName=Timeline +TimelinePanel_latestLabel_title=Latest +TimlinePanel_last30DaysChart_artifactEvts_title=Result Events +TimlinePanel_last30DaysChart_fileEvts_title=File Events +TimlinePanel_last30DaysChart_title=Last 30 Days diff --git a/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/CellModel.java b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/CellModel.java new file mode 100755 index 0000000000..ce072525ea --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/CellModel.java @@ -0,0 +1,91 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 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 javax.swing.JLabel; +import org.apache.poi.ss.usermodel.HorizontalAlignment; + +/** + * Basic interface for a cell model. + */ +interface CellModel { + + /** + * Describes the horizontal alignment. + */ + enum HorizontalAlign { + LEFT(JLabel.LEFT, HorizontalAlignment.LEFT), + CENTER(JLabel.CENTER, HorizontalAlignment.CENTER), + RIGHT(JLabel.RIGHT, HorizontalAlignment.RIGHT); + + private final int jlabelAlignment; + private final HorizontalAlignment poiAlignment; + + /** + * Constructor for a HorizontalAlign enum. + * + * @param jlabelAlignment The corresponding JLabel horizontal alignment + * number. + * @param poiAlignment Horizontal alignment for Apache POI. + */ + HorizontalAlign(int jlabelAlignment, HorizontalAlignment poiAlignment) { + this.jlabelAlignment = jlabelAlignment; + this.poiAlignment = poiAlignment; + } + + /** + * @return The corresponding JLabel horizontal alignment (i.e. + * JLabel.LEFT). + */ + int getJLabelAlignment() { + return this.jlabelAlignment; + } + + /** + * @return Horizontal alignment for Apache POI. + */ + HorizontalAlignment getPoiAlignment() { + return poiAlignment; + } + } + + /** + * @return The root data object. + */ + Object getData(); + + /** + * @return The text to be shown in the cell. + */ + default String getText() { + Object data = getData(); + return (data == null) ? null : data.toString(); + } + + /** + * @return The horizontal alignment for the text in the cell. + */ + HorizontalAlign getHorizontalAlignment(); + + /** + * @return The format string to be used with Apache POI during excel + * export or null if none necessary. + */ + String getExcelFormatString(); +} diff --git a/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ColumnModel.java b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ColumnModel.java new file mode 100755 index 0000000000..9c68de3992 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ColumnModel.java @@ -0,0 +1,80 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 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.util.function.Function; + +/** + * Describes aspects of a column which can be used with getTableModel or + * getJTablePanel. 'T' represents the object that will represent rows in the + * table. + */ +class ColumnModel { + + private final String headerTitle; + private final Function cellRenderer; + private final Integer width; + + /** + * Constructor for a DataResultColumnModel. + * + * @param headerTitle The title for the column. + * @param cellRenderer The method that generates a CellModel for the column + * based on the data. + */ + ColumnModel(String headerTitle, Function cellRenderer) { + this(headerTitle, cellRenderer, null); + } + + /** + * Constructor for a DataResultColumnModel. + * + * @param headerTitle The title for the column. + * @param cellRenderer The method that generates a CellModel for the column + * based on the data. + * @param width The preferred width of the column. + */ + ColumnModel(String headerTitle, Function cellRenderer, Integer width) { + this.headerTitle = headerTitle; + this.cellRenderer = cellRenderer; + this.width = width; + } + + /** + * @return The title for the column. + */ + String getHeaderTitle() { + return headerTitle; + } + + /** + * @return The method that generates a CellModel for the column based on the + * data. + */ + Function getCellRenderer() { + return cellRenderer; + } + + /** + * @return The preferred width of the column (can be null). + */ + Integer getWidth() { + return width; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/DataSourceSummaryReport.java b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/DataSourceSummaryReport.java new file mode 100755 index 0000000000..a52f897897 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/DataSourceSummaryReport.java @@ -0,0 +1,162 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 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.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.stream.Collectors; +import javax.swing.JPanel; +import org.openide.util.NbBundle; +import org.openide.util.lookup.ServiceProvider; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.report.GeneralReportModule; +import org.sleuthkit.autopsy.report.GeneralReportSettings; +import org.sleuthkit.autopsy.report.ReportProgressPanel; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.DataSource; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Instances of this class plug in to the reporting infrastructure to provide a + * convenient way to extract data source summary information into Excel. + */ +@ServiceProvider(service = GeneralReportModule.class) +public class DataSourceSummaryReport implements GeneralReportModule { + + private static final Logger logger = Logger.getLogger(DataSourceSummaryReport.class.getName()); + private static DataSourceSummaryReport instance; + + // Get the default instance of this report + public static synchronized DataSourceSummaryReport getDefault() { + if (instance == null) { + instance = new DataSourceSummaryReport(); + } + return instance; + } + + public DataSourceSummaryReport() { + } + + + @Override + public String getName() { + String name = NbBundle.getMessage(this.getClass(), "DataSourceSummaryReport.getName.text"); + return name; + } + + @Override + public String getRelativeFilePath() { + return ""; + } + + @Override + public String getDescription() { + String desc = NbBundle.getMessage(this.getClass(), "DataSourceSummaryReport.getDesc.text"); + return desc; + } + + @Override + public JPanel getConfigurationPanel() { + return null; + } + + @Override + public boolean supportsDataSourceSelection() { + return true; + } + + @NbBundle.Messages({ + "DataSourceSummaryReport.error.noOpenCase=No currently open case.", + "DataSourceSummaryReport.error.noDataSources=No data sources selected for report.", + "DataSourceSummaryReport.failedToCompleteReport=Failed to complete report.", + "DataSourceSummaryReport.excelFileWriteError=Could not write the KML file.",}) + @Override + public void generateReport(GeneralReportSettings settings, ReportProgressPanel progressPanel) { + progressPanel.start(); + Case currentCase; + try { + currentCase = Case.getCurrentCaseThrows(); + } catch (NoCurrentCaseException ex) { + progressPanel.complete(ReportProgressPanel.ReportStatus.ERROR, Bundle.DataSourceSummaryReport_error_noOpenCase()); + logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS + return; + } + + String errorMessage = ""; + ReportProgressPanel.ReportStatus result = ReportProgressPanel.ReportStatus.COMPLETE; + List selectedDataSources = new ArrayList<>(); + if(settings.getSelectedDataSources() == null) { + // Process all data sources if the list is null. + try { + selectedDataSources = currentCase.getDataSources(); + List dsIDs = selectedDataSources + .stream() + .map(Content::getId) + .collect(Collectors.toList()); + settings.setSelectedDataSources(dsIDs); + } catch (TskCoreException ex) { + result = ReportProgressPanel.ReportStatus.ERROR; + errorMessage = Bundle.DataSourceSummaryReport_failedToCompleteReport(); + logger.log(Level.SEVERE, "Could not get the datasources from the case", ex); + progressPanel.complete(result, errorMessage); + return; + } + } else { + for (Long dsID : settings.getSelectedDataSources()) { + try { + selectedDataSources.add(currentCase.getSleuthkitCase().getContentById(dsID)); + } catch (TskCoreException ex) { + result = ReportProgressPanel.ReportStatus.ERROR; + errorMessage = Bundle.DataSourceSummaryReport_failedToCompleteReport(); + logger.log(Level.SEVERE, "Could not get the datasources from the case", ex); + progressPanel.complete(result, errorMessage); + return; + } + } + } + + if (selectedDataSources.isEmpty()) { + result = ReportProgressPanel.ReportStatus.ERROR; + progressPanel.complete(result, Bundle.DataSourceSummaryReport_error_noDataSources()); + logger.log(Level.SEVERE, "No data sources selected for report."); //NON-NLS + return; + } + + // looop over all selected data sources + for (Content dataSource : selectedDataSources){ + if (dataSource instanceof DataSource) { + try { + new ExcelExportAction().exportToXLSX(progressPanel, (DataSource) dataSource, settings.getReportDirectoryPath()); + } catch (IOException | ExcelExport.ExcelExportException ex) { + errorMessage = Bundle.DataSourceSummaryReport_excelFileWriteError(); + logger.log(Level.SEVERE, errorMessage, ex); //NON-NLS + progressPanel.complete(ReportProgressPanel.ReportStatus.ERROR, errorMessage); + return; + } + } + } + + progressPanel.complete(result, errorMessage); + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/DefaultCellModel.java b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/DefaultCellModel.java new file mode 100755 index 0000000000..c2e2b7d085 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/DefaultCellModel.java @@ -0,0 +1,111 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 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.util.function.Function; +/** + * The default cell model. + */ +class DefaultCellModel implements CellModel { + + private final T data; + private final String text; + private CellModel.HorizontalAlign horizontalAlignment; + private final String excelFormatString; + + /** + * Main constructor. + * + * @param data The data to be displayed in the cell. + */ + DefaultCellModel(T data) { + this(data, null, null); + } + + /** + * Constructor. + * + * @param data The data to be displayed in the cell. + * @param stringConverter The means of converting that data to a string or + * null to use .toString method on object. + */ + DefaultCellModel(T data, Function stringConverter) { + this(data, stringConverter, null); + } + + /** + * Constructor. + * + * @param data The data to be displayed in the cell. + * @param stringConverter The means of converting that data to a string or + * null to use .toString method on object. + * @param excelFormatString The apache poi excel format string to use with + * the data. + * + * NOTE: Only certain data types can be exported. See + * ExcelTableExport.createCell() for types. + */ + DefaultCellModel(T data, Function stringConverter, String excelFormatString) { + this.data = data; + this.excelFormatString = excelFormatString; + + if (stringConverter == null) { + text = this.data == null ? "" : this.data.toString(); + } else { + text = stringConverter.apply(this.data); + } + } + + @Override + public T getData() { + return this.data; + } + + @Override + public String getText() { + return text; + } + + @Override + public HorizontalAlign getHorizontalAlignment() { + return horizontalAlignment; + } + + @Override + public String getExcelFormatString() { + return this.excelFormatString; + } + + /** + * Sets the horizontal alignment for this cell model. + * + * @param alignment The horizontal alignment for the cell model. + * + * @return As a utility, returns this. + */ + DefaultCellModel setHorizontalAlignment(CellModel.HorizontalAlign alignment) { + this.horizontalAlignment = alignment; + return this; + } + + @Override + public String toString() { + return getText(); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ExcelExport.java b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExcelExport.java similarity index 91% rename from Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ExcelExport.java rename to Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExcelExport.java index fab6558c4a..47087dd985 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ExcelExport.java +++ b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExcelExport.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.datasourcesummary.uiutils; +package org.sleuthkit.autopsy.report.modules.datasourcesummaryexport; import java.io.File; import java.io.FileOutputStream; @@ -37,24 +37,24 @@ import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.openide.util.NbBundle.Messages; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModel.HorizontalAlign; +import org.sleuthkit.autopsy.report.modules.datasourcesummaryexport.CellModel.HorizontalAlign; /** * Class for handling Excel exporting. */ -public class ExcelExport { +class ExcelExport { /** * Exception thrown in the event of an excel export issue. */ - public static class ExcelExportException extends Exception { + static class ExcelExportException extends Exception { /** * Constructor. * * @param string The message. */ - public ExcelExportException(String string) { + ExcelExportException(String string) { super(string); } @@ -64,7 +64,7 @@ public class ExcelExport { * @param string The message. * @param thrwbl The inner exception. */ - public ExcelExportException(String string, Throwable thrwbl) { + ExcelExportException(String string, Throwable thrwbl) { super(string, thrwbl); } } @@ -153,7 +153,7 @@ public class ExcelExport { /** * Class detailing aspects of the worksheet. */ - public static class WorksheetEnv { + static class WorksheetEnv { private final CellStyle headerStyle; private final Workbook parentWorkbook; @@ -182,7 +182,7 @@ public class ExcelExport { * @param cellStyleKey The key. * @return The cell style representing this key. */ - public CellStyle getCellStyle(CellStyleKey cellStyleKey) { + CellStyle getCellStyle(CellStyleKey cellStyleKey) { return cellStyleCache.computeIfAbsent(cellStyleKey, (pair) -> { CellStyle computed = this.parentWorkbook.createCellStyle(); computed.cloneStyleFrom(cellStyleKey.getCellStyle() == null ? defaultStyle : cellStyleKey.getCellStyle()); @@ -203,7 +203,7 @@ public class ExcelExport { * * @return The cell style to use for headers. */ - public CellStyle getHeaderStyle() { + CellStyle getHeaderStyle() { return headerStyle; } @@ -212,7 +212,7 @@ public class ExcelExport { * * @return The cell style for default items. */ - public CellStyle getDefaultCellStyle() { + CellStyle getDefaultCellStyle() { return defaultStyle; } @@ -221,7 +221,7 @@ public class ExcelExport { * * @return The parent workbook. */ - public Workbook getParentWorkbook() { + Workbook getParentWorkbook() { return parentWorkbook; } } @@ -229,7 +229,7 @@ public class ExcelExport { /** * An item to be exported as a sheet during export. */ - public static interface ExcelSheetExport { + static interface ExcelSheetExport { /** * Returns the name of the sheet to use with this item. @@ -250,23 +250,7 @@ public class ExcelExport { void renderSheet(Sheet sheet, WorksheetEnv env) throws ExcelExportException; } - private static ExcelExport instance = null; - - /** - * Retrieves a singleton instance of this class. - * - * @return The instance. - */ - public static ExcelExport getInstance() { - if (instance == null) { - instance = new ExcelExport(); - } - - return instance; - } - private ExcelExport() { - } /** @@ -281,7 +265,7 @@ public class ExcelExport { "# {0} - sheetNumber", "ExcelExport_writeExcel_noSheetName=Sheet {0}" }) - public void writeExcel(List exports, File path) throws IOException, ExcelExportException { + static void writeExcel(List exports, File path) throws IOException, ExcelExportException { // Create a Workbook Workbook workbook = new XSSFWorkbook(); // new HSSFWorkbook() for generating `.xls` file @@ -337,7 +321,7 @@ public class ExcelExport { * @param cellStyle The style to use. * @return The created cell. */ - static Cell createCell(WorksheetEnv env, Row row, int colNum, ExcelCellModel cellModel, Optional cellStyle) { + static Cell createCell(WorksheetEnv env, Row row, int colNum, CellModel cellModel, Optional cellStyle) { CellStyle cellStyleToUse = cellStyle.orElse(env.getDefaultCellStyle()); if (cellModel.getExcelFormatString() != null || cellModel.getHorizontalAlignment() != null) { diff --git a/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExcelExportAction.java b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExcelExportAction.java new file mode 100644 index 0000000000..f9b1b2b988 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExcelExportAction.java @@ -0,0 +1,332 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 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.io.File; +import java.io.IOException; +import java.nio.file.Paths; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.logging.Level; +import org.openide.util.NbBundle; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.coreutils.FileUtil; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataFetcher; +import org.sleuthkit.autopsy.report.ReportProgressPanel; +import org.sleuthkit.autopsy.report.modules.datasourcesummaryexport.ExcelExport.ExcelExportException; +import org.sleuthkit.autopsy.report.modules.datasourcesummaryexport.ExcelExport.ExcelSheetExport; +import org.sleuthkit.datamodel.DataSource; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Action that exports tab data to an excel workbook. + */ +@Messages({ + "ExcelExportAction_moduleName=Data Source Summary",}) +class ExcelExportAction { + + private static final Logger logger = Logger.getLogger(ExcelExportAction.class.getName()); + + /** + * Main constructor. + * + * @param tabExports The different tabs that may have excel exports. + */ + ExcelExportAction() { + } + + /** + * Generates an xlsx path for the data source summary export. + * + * @param dataSourceName The name of the data source. + * + * @return The file to which the excel document should be written or null if + * file already exists or cancellation. + */ + @NbBundle.Messages({ + "ExcelExportAction_getXLSXPath_directory=DataSourceSummary",}) + File getXLSXPath(String dataSourceName, String baseReportDir) { + // set initial path to reports directory with filename that is + // a combination of the data source name and time stamp + DateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy-HH-mm-ss"); + String fileName = String.format("%s-%s.xlsx", dataSourceName == null ? "" : FileUtil.escapeFileName(dataSourceName), dateFormat.format(new Date())); + File reportsDirFile = Paths.get(baseReportDir, Bundle.ExcelExportAction_getXLSXPath_directory()).toFile(); + if (!reportsDirFile.exists()) { + reportsDirFile.mkdirs(); + } + + return Paths.get(reportsDirFile.getAbsolutePath(), fileName).toFile(); + } + + /** + * Action that handles updating progress and exporting data from the tabs. + * + * @param progressPanel The progress indicator. + * @param dataSource The data source to be exported. + * @param path The path of the excel export. + * + * @throws InterruptedException + * @throws IOException + * @throws ExcelExportException + */ + @NbBundle.Messages({ + "ExcelExportAction_exportToXLSX_beginExport=Beginning Export...", + "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_gatheringAnalysisData=Fetching Analysis Data", + "ExcelExportAction_exportToXLSX_gatheringPastData=Fetching Historical Data", + "ExcelExportAction_exportToXLSX_gatheringUserData=Fetching User Activity Data", + "ExcelExportAction_exportToXLSX_gatheringGeoData=Fetching Geolocation Data", + "ExcelExportAction_exportToXLSX_gatheringIngestData=Fetching Ingest History Data", + "ExcelExportAction_exportToXLSX_writingToFile=Writing to File...",}) + + void exportToXLSX(ReportProgressPanel progressPanel, DataSource dataSource, String baseReportDir) + throws IOException, ExcelExport.ExcelExportException { + + File reportFile = getXLSXPath(dataSource.getName(), baseReportDir); + int totalWeight = 11; + int step = 1; + progressPanel.setIndeterminate(false); + progressPanel.setLabels(dataSource.getName(), reportFile.getPath()); + progressPanel.setMaximumProgress(totalWeight); + progressPanel.updateStatusLabel(Bundle.ExcelExportAction_exportToXLSX_beginExport()); + List sheetExports = new ArrayList<>(); + + // Export file and MIME type data + progressPanel.updateStatusLabel(Bundle.ExcelExportAction_exportToXLSX_gatheringFileData()); + progressPanel.setProgress(step); + List exports = new ExportTypes().getExports(dataSource); + if (exports != null) { + sheetExports.addAll(exports); + } + + // Export user activity + progressPanel.updateStatusLabel(Bundle.ExcelExportAction_exportToXLSX_gatheringUserData()); + progressPanel.setProgress(++step); + exports = new ExportUserActivity().getExports(dataSource); + if (exports != null) { + sheetExports.addAll(exports); + } + + // Export Recent Activity data + progressPanel.updateStatusLabel(Bundle.ExcelExportAction_exportToXLSX_gatheringRecentActivityData()); + progressPanel.setProgress(++step); + exports = new ExportRecentFiles().getExports(dataSource); + if (exports != null) { + sheetExports.addAll(exports); + } + + // Export hash set hits, keyword hits, and interesting item hits + progressPanel.updateStatusLabel(Bundle.ExcelExportAction_exportToXLSX_gatheringAnalysisData()); + progressPanel.setProgress(++step); + exports = new ExportAnalysisResults().getExports(dataSource); + if (exports != null) { + sheetExports.addAll(exports); + } + + // Export past cases data + progressPanel.updateStatusLabel(Bundle.ExcelExportAction_exportToXLSX_gatheringPastData()); + progressPanel.setProgress(++step); + exports = new ExportPastCases().getExports(dataSource); + if (exports != null) { + sheetExports.addAll(exports); + } + + // Export geolocation data + progressPanel.updateStatusLabel(Bundle.ExcelExportAction_exportToXLSX_gatheringGeoData()); + progressPanel.setProgress(++step); + exports = new ExportGeolocation().getExports(dataSource); + if (exports != null) { + sheetExports.addAll(exports); + } + + // Export Timeline data + progressPanel.updateStatusLabel(Bundle.ExcelExportAction_exportToXLSX_gatheringTimelineData()); + progressPanel.setProgress(++step); + exports = new ExportTimeline().getExports(dataSource); + if (exports != null) { + sheetExports.addAll(exports); + } + + // Export ingest history + progressPanel.updateStatusLabel(Bundle.ExcelExportAction_exportToXLSX_gatheringIngestData()); + progressPanel.setProgress(++step); + exports = ExportIngestHistory.getExports(dataSource); + if (exports != null) { + sheetExports.addAll(exports); + } + + // Export Container & Image info data + progressPanel.updateStatusLabel(Bundle.ExcelExportAction_exportToXLSX_gatheringContainerData()); + progressPanel.setProgress(++step); + exports = new ExportContainerInfo().getExports(dataSource); + if (exports != null) { + sheetExports.addAll(exports); + } + + progressPanel.updateStatusLabel(Bundle.ExcelExportAction_exportToXLSX_writingToFile()); + progressPanel.setProgress(++step); + ExcelExport.writeExcel(sheetExports, reportFile); + + try { + // add to reports + Case curCase = Case.getCurrentCaseThrows(); + curCase.addReport(reportFile.getParent(), + Bundle.ExcelExportAction_moduleName(), + reportFile.getName(), + dataSource); + } catch (NoCurrentCaseException | TskCoreException ex) { + logger.log(Level.WARNING, "There was an error attaching report to case.", ex); + } + } + + /** + * Function that converts data into a excel sheet data. + */ + protected interface ExcelExportFunction { + + /** + * Function that converts data into an excel sheet. + * + * @param data The data. + * + * @return The excel sheet export. + * + * @throws ExcelExportException + */ + ExcelSheetExport convert(T data) throws ExcelExportException; + } + + /** + * Runs a data fetcher and returns the result handling any possible errors + * with a log message. + * + * @param dataFetcher The means of fetching the data. + * @param sheetName The name of the sheet. + * @param ds The data source. + * + * @return The fetched data. + */ + protected static T getFetchResult( + DataFetcher dataFetcher, + String sheetName, DataSource ds) { + + try { + return dataFetcher.runQuery(ds); + } catch (Exception ex) { + logger.log(Level.WARNING, + String.format("There was an error while acquiring data for exporting worksheet(s): '%s' for dataSource: %s", + sheetName == null ? "" : sheetName, + ds == null || ds.getName() == null ? "" : ds.getName()), ex); + return null; + } + } + + /** + * Helper method that converts data into an excel sheet export handling + * possible excel exceptions. + * + * @param excelConverter Function to convert data to an excel sheet export. + * @param data The data. If data is null, null will be returned. + * @param sheetName The name(s) of the sheet (to be used in the error + * message). + * + * @return The excel sheet export. + */ + protected static ExcelSheetExport convertToExcel(ExcelExportFunction excelConverter, T data, String sheetName) { + if (data == null) { + return null; + } + + try { + return excelConverter.convert(data); + } catch (ExcelExportException ex) { + logger.log(Level.WARNING, + String.format("There was an error while preparing export of worksheet(s): '%s'", + sheetName == null ? "" : sheetName), ex); + return null; + } + } + + /** + * Returns an excel sheet export given the fetching of data or null if no + * export created. + * + * @param dataFetcher The means of fetching data. + * @param excelConverter The means of converting data to excel. + * @param sheetName The name of the sheet (for error handling + * reporting). + * @param ds The data source to use for fetching data. + * + * @return The excel sheet export or null if no export could be generated. + */ + protected static ExcelSheetExport getExport( + DataFetcher dataFetcher, ExcelExportFunction excelConverter, + String sheetName, DataSource ds) { + + T data = getFetchResult(dataFetcher, sheetName, ds); + return convertToExcel(excelConverter, data, sheetName); + } + + /** + * Returns an excel table export of the data or null if no export created. + * + * @param columnsModel The model for the columns. + * @param sheetName The name for the sheet. + * @param data The data to be exported. + * + * @return The excel table export or null if no export could be generated. + */ + protected static ExcelSheetExport getTableExport(List> columnsModel, + String sheetName, List data) { + + return convertToExcel((dataList) -> new ExcelTableExport<>(sheetName, columnsModel, dataList), + data, + sheetName); + } + + /** + * Returns an excel table export of the data or null if no export created. + * + * @param dataFetcher The means of fetching data for the data source and + * the export. + * @param columnsModel The model for the columns. + * @param sheetName The name for the sheet. + * @param ds The data source. + * + * @return The excel export or null if no export created. + */ + protected static ExcelSheetExport getTableExport( + DataFetcher> dataFetcher, List> columnsModel, + String sheetName, DataSource ds) { + + return getExport(dataFetcher, + (dataList) -> new ExcelTableExport<>(sheetName, columnsModel, dataList), + sheetName, + ds); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ExcelSpecialFormatExport.java b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExcelSpecialFormatExport.java similarity index 84% rename from Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ExcelSpecialFormatExport.java rename to Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExcelSpecialFormatExport.java index f9c46fa5e2..cd30e7d816 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ExcelSpecialFormatExport.java +++ b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExcelSpecialFormatExport.java @@ -16,25 +16,25 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.datasourcesummary.uiutils; +package org.sleuthkit.autopsy.report.modules.datasourcesummaryexport; import java.util.Collections; import java.util.List; import java.util.Optional; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport.ExcelExportException; +import org.sleuthkit.autopsy.report.modules.datasourcesummaryexport.ExcelExport.ExcelExportException; /** * An excel export that has special row-by-row formatting. */ -public class ExcelSpecialFormatExport implements ExcelExport.ExcelSheetExport { +class ExcelSpecialFormatExport implements ExcelExport.ExcelSheetExport { /** * The dimensions consumed by an item in an ExcelSpecialFormatExport list of * items to be rendered. */ - public static class ItemDimensions { + static class ItemDimensions { private final int rowStart; private final int rowEnd; @@ -49,7 +49,7 @@ public class ExcelSpecialFormatExport implements ExcelExport.ExcelSheetExport { * @param rowEnd The last excel row of the the item. * @param colEnd The last excel column of the item. */ - public ItemDimensions(int rowStart, int colStart, int rowEnd, int colEnd) { + ItemDimensions(int rowStart, int colStart, int rowEnd, int colEnd) { this.rowStart = rowStart; this.colStart = colStart; this.rowEnd = rowEnd; @@ -59,28 +59,28 @@ public class ExcelSpecialFormatExport implements ExcelExport.ExcelSheetExport { /** * @return The starting excel row of the item. */ - public int getRowStart() { + int getRowStart() { return rowStart; } /** * @return The last excel row of the the item. */ - public int getRowEnd() { + int getRowEnd() { return rowEnd; } /** * @return The starting excel column of the item. */ - public int getColStart() { + int getColStart() { return colStart; } /** * @return The last excel column of the item. */ - public int getColEnd() { + int getColEnd() { return colEnd; } } @@ -88,7 +88,7 @@ public class ExcelSpecialFormatExport implements ExcelExport.ExcelSheetExport { /** * An item to be exported in a specially formatted excel export. */ - public interface ExcelItemExportable { + interface ExcelItemExportable { /** * Writes the item to the sheet in the special format export sheet. @@ -106,16 +106,16 @@ public class ExcelSpecialFormatExport implements ExcelExport.ExcelSheetExport { /** * Writes a string to a single cell in a specially formatted excel export. */ - public static class SingleCellExportable implements ExcelItemExportable { + static class SingleCellExportable implements ExcelItemExportable { - private final ExcelCellModel item; + private final CellModel item; /** * Main constructor. * * @param key The text to be written. */ - public SingleCellExportable(String key) { + SingleCellExportable(String key) { this(new DefaultCellModel<>(key)); } @@ -124,7 +124,7 @@ public class ExcelSpecialFormatExport implements ExcelExport.ExcelSheetExport { * * @param item The cell model to be written. */ - public SingleCellExportable(ExcelCellModel item) { + SingleCellExportable(CellModel item) { this.item = item; } @@ -140,10 +140,10 @@ public class ExcelSpecialFormatExport implements ExcelExport.ExcelSheetExport { * Writes a row consisting of first column as a key and second column as a * value. */ - public static class KeyValueItemExportable implements ExcelItemExportable { + static class KeyValueItemExportable implements ExcelItemExportable { - private final ExcelCellModel key; - private final ExcelCellModel value; + private final CellModel key; + private final CellModel value; /** * Main constructor. @@ -151,7 +151,7 @@ public class ExcelSpecialFormatExport implements ExcelExport.ExcelSheetExport { * @param key The string key to be exported. * @param value The cell model to be exported. */ - public KeyValueItemExportable(String key, ExcelCellModel value) { + KeyValueItemExportable(String key, CellModel value) { this(new DefaultCellModel<>(key), value); } @@ -161,7 +161,7 @@ public class ExcelSpecialFormatExport implements ExcelExport.ExcelSheetExport { * @param key The cell key to be exported. * @param value The cell model to be exported. */ - public KeyValueItemExportable(ExcelCellModel key, ExcelCellModel value) { + KeyValueItemExportable(CellModel key, CellModel value) { this.key = key; this.value = value; } @@ -186,7 +186,7 @@ public class ExcelSpecialFormatExport implements ExcelExport.ExcelSheetExport { * item 2 * */ - public static class TitledExportable implements ExcelItemExportable { + static class TitledExportable implements ExcelItemExportable { private static final int DEFAULT_INDENT = 1; @@ -199,7 +199,7 @@ public class ExcelSpecialFormatExport implements ExcelExport.ExcelSheetExport { * @param title The title for the export. * @param children The children to be indented and enumerated. */ - public TitledExportable(String title, List children) { + TitledExportable(String title, List children) { this.title = title; this.children = children; } @@ -232,7 +232,7 @@ public class ExcelSpecialFormatExport implements ExcelExport.ExcelSheetExport { * @param sheetName The name of the sheet. * @param exports The row-by-row items to be exported. */ - public ExcelSpecialFormatExport(String sheetName, List exports) { + ExcelSpecialFormatExport(String sheetName, List exports) { this.sheetName = sheetName; this.exports = exports == null ? Collections.emptyList() : exports; } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ExcelTableExport.java b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExcelTableExport.java similarity index 81% rename from Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ExcelTableExport.java rename to Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExcelTableExport.java index c79cb381aa..b31c7da507 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ExcelTableExport.java +++ b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExcelTableExport.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.datasourcesummary.uiutils; +package org.sleuthkit.autopsy.report.modules.datasourcesummaryexport; import java.util.Collections; import java.util.List; @@ -24,15 +24,15 @@ import java.util.Optional; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport.ExcelExportException; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport.ExcelSheetExport; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport.ExcelItemExportable; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport.ItemDimensions; +import org.sleuthkit.autopsy.report.modules.datasourcesummaryexport.ExcelExport.ExcelExportException; +import org.sleuthkit.autopsy.report.modules.datasourcesummaryexport.ExcelExport.ExcelSheetExport; +import org.sleuthkit.autopsy.report.modules.datasourcesummaryexport.ExcelSpecialFormatExport.ExcelItemExportable; +import org.sleuthkit.autopsy.report.modules.datasourcesummaryexport.ExcelSpecialFormatExport.ItemDimensions; /** * An excel sheet export of table data. */ -public class ExcelTableExport implements ExcelSheetExport, ExcelItemExportable { +class ExcelTableExport implements ExcelSheetExport, ExcelItemExportable { private final String sheetName; private final List> columns; @@ -47,7 +47,7 @@ public class ExcelTableExport implements ExcelSheet * @param columns The columns of the table. * @param data The data to export. */ - public ExcelTableExport(String sheetName, List> columns, List data) { + ExcelTableExport(String sheetName, List> columns, List data) { this(sheetName, columns, data, 0); } @@ -60,7 +60,7 @@ public class ExcelTableExport implements ExcelSheet * @param data The data to export. * @param columnIndent The column indent. */ - public ExcelTableExport(String sheetName, List> columns, List data, int columnIndent) { + ExcelTableExport(String sheetName, List> columns, List data, int columnIndent) { this.sheetName = sheetName; this.columns = columns; this.data = data; @@ -104,7 +104,7 @@ public class ExcelTableExport implements ExcelSheet * @throws ExcelExportException * @return The number of rows (including the header) written. */ - private static int renderSheet( + private static int renderSheet( Sheet sheet, ExcelExport.WorksheetEnv worksheetEnv, int rowStart, @@ -127,8 +127,8 @@ public class ExcelTableExport implements ExcelSheet T rowData = safeData.get(rowNum); Row row = sheet.createRow(rowNum + rowStart + 1); for (int colNum = 0; colNum < columns.size(); colNum++) { - ColumnModel colModel = columns.get(colNum); - ExcelCellModel cellModel = colModel.getCellRenderer().apply(rowData); + ColumnModel colModel = columns.get(colNum); + CellModel cellModel = colModel.getCellRenderer().apply(rowData); ExcelExport.createCell(worksheetEnv, row, colNum + colStart, cellModel, Optional.empty()); } } diff --git a/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExportAnalysisResults.java b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExportAnalysisResults.java new file mode 100755 index 0000000000..57786f1f8e --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExportAnalysisResults.java @@ -0,0 +1,79 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 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.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.apache.commons.lang3.tuple.Pair; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataFetcher; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.AnalysisSummary; +import org.sleuthkit.autopsy.report.modules.datasourcesummaryexport.ExcelExport.ExcelSheetExport; +import static org.sleuthkit.autopsy.report.modules.datasourcesummaryexport.ExcelExportAction.getTableExport; +import org.sleuthkit.datamodel.DataSource; + +/** + * Class to export data hash set hits, keyword hits, and interesting item hits + * within a datasource. + */ +@Messages({ + "ExportAnalysisResults_keyColumn_title=Name", + "ExportAnalysisResults_countColumn_title=Count", + "ExportAnalysisResults_keywordSearchModuleName=Keyword Search", + "ExportAnalysisResults_hashsetHits_tabName=Hashset Hits", + "ExportAnalysisResults_keywordHits_tabName=Keyword Hits", + "ExportAnalysisResults_interestingItemHits_tabName=Interesting Item Hits",}) +class ExportAnalysisResults { + + // Default Column definitions for each table + private static final List, DefaultCellModel>> DEFAULT_COLUMNS = Arrays.asList( + new ColumnModel<>( + Bundle.ExportAnalysisResults_keyColumn_title(), + (pair) -> new DefaultCellModel<>(pair.getKey()), + 300 + ), + new ColumnModel<>( + Bundle.ExportAnalysisResults_countColumn_title(), + (pair) -> new DefaultCellModel<>(pair.getValue()), + 100 + ) + ); + + private final AnalysisSummary analysisSummary; + + ExportAnalysisResults() { + analysisSummary = new AnalysisSummary(); + } + + List getExports(DataSource dataSource) { + + DataFetcher>> hashsetsFetcher = (ds) -> analysisSummary.getHashsetCounts(ds); + DataFetcher>> keywordsFetcher = (ds) -> analysisSummary.getKeywordCounts(ds); + DataFetcher>> interestingItemsFetcher = (ds) -> analysisSummary.getInterestingItemCounts(ds); + + return Stream.of( + getTableExport(hashsetsFetcher, DEFAULT_COLUMNS, Bundle.ExportAnalysisResults_hashsetHits_tabName(), dataSource), + getTableExport(keywordsFetcher, DEFAULT_COLUMNS, Bundle.ExportAnalysisResults_keywordHits_tabName(), dataSource), + getTableExport(interestingItemsFetcher, DEFAULT_COLUMNS, Bundle.ExportAnalysisResults_interestingItemHits_tabName(), dataSource)) + .filter(sheet -> sheet != null) + .collect(Collectors.toList()); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExportContainerInfo.java b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExportContainerInfo.java new file mode 100755 index 0000000000..44ede9064c --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExportContainerInfo.java @@ -0,0 +1,130 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 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.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.apache.commons.lang.StringUtils; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataFetcher; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.ContainerSummary; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.ContainerSummary.ContainerDetails; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.ContainerSummary.ImageDetails; +import org.sleuthkit.autopsy.report.modules.datasourcesummaryexport.ExcelExport.ExcelSheetExport; +import org.sleuthkit.autopsy.report.modules.datasourcesummaryexport.ExcelSpecialFormatExport.ExcelItemExportable; +import org.sleuthkit.autopsy.report.modules.datasourcesummaryexport.ExcelSpecialFormatExport.KeyValueItemExportable; +import org.sleuthkit.autopsy.report.modules.datasourcesummaryexport.ExcelSpecialFormatExport.SingleCellExportable; +import org.sleuthkit.autopsy.report.modules.datasourcesummaryexport.ExcelSpecialFormatExport.TitledExportable; +import org.sleuthkit.datamodel.DataSource; + +/** + * Class to export additional details associated with a specific DataSource + */ +class ExportContainerInfo { + + private final ContainerSummary containerSummary; + + /** + * Creates new form ExportContainerInfo. + */ + ExportContainerInfo() { + containerSummary = new ContainerSummary(); + } + + /** + * Divides acquisition details into key/value pairs to be displayed in + * separate cells in an excel export. + * + * @param acquisitionDetails The acquisition details. + * @return The list of key value pairs that can be incorporated into the + * excel export. + */ + private static List getAcquisitionDetails(String acquisitionDetails) { + if (StringUtils.isBlank(acquisitionDetails)) { + return Collections.emptyList(); + } else { + return Stream.of(acquisitionDetails.split("\\r?\\n")) + .map((line) -> (StringUtils.isBlank(line)) ? null : new SingleCellExportable(line)) + .filter(item -> item != null) + .collect(Collectors.toList()); + } + } + + @Messages({ + "ExportContainerInfo_setFieldsForNonImageDataSource_na=N/A", + "ExportContainerInfo_tabName=Container", + "ExportContainerInfo_export_displayName=Display Name:", + "ExportContainerInfo_export_originalName=Name:", + "ExportContainerInfo_export_deviceId=Device ID:", + "ExportContainerInfo_export_timeZone=Time Zone:", + "ExportContainerInfo_export_acquisitionDetails=Acquisition Details:", + "ExportContainerInfo_export_imageType=Image Type:", + "ExportContainerInfo_export_size=Size:", + "ExportContainerInfo_export_sectorSize=Sector Size:", + "ExportContainerInfo_export_md5=MD5:", + "ExportContainerInfo_export_sha1=SHA1:", + "ExportContainerInfo_export_sha256=SHA256:", + "ExportContainerInfo_export_unallocatedSize=Unallocated Space:", + "ExportContainerInfo_export_filePaths=File Paths:",}) + List getExports(DataSource ds) { + DataFetcher containerDataFetcher = (dataSource) -> containerSummary.getContainerDetails(dataSource); + ContainerDetails containerDetails = ExcelExportAction.getFetchResult(containerDataFetcher, "Container sheets", ds); + if (ds == null || containerDetails == null) { + return Collections.emptyList(); + } + + String NA = Bundle.ExportContainerInfo_setFieldsForNonImageDataSource_na(); + DefaultCellModel NACell = new DefaultCellModel<>(NA); + + ImageDetails imageDetails = containerDetails.getImageDetails(); + boolean hasImage = imageDetails != null; + + DefaultCellModel timeZone = hasImage ? new DefaultCellModel<>(imageDetails.getTimeZone()) : NACell; + DefaultCellModel imageType = hasImage ? new DefaultCellModel<>(imageDetails.getImageType()) : NACell; + DefaultCellModel size = hasImage ? SizeRepresentationUtil.getBytesCell(imageDetails.getSize()) : NACell; + DefaultCellModel sectorSize = hasImage ? SizeRepresentationUtil.getBytesCell(imageDetails.getSectorSize()) : NACell; + DefaultCellModel md5 = hasImage ? new DefaultCellModel<>(imageDetails.getMd5Hash()) : NACell; + DefaultCellModel sha1 = hasImage ? new DefaultCellModel<>(imageDetails.getSha1Hash()) : NACell; + DefaultCellModel sha256 = hasImage ? new DefaultCellModel<>(imageDetails.getSha256Hash()) : NACell; + DefaultCellModel unallocatedSize = hasImage ? SizeRepresentationUtil.getBytesCell(imageDetails.getUnallocatedSize()) : NACell; + List paths = containerDetails.getImageDetails() == null ? Collections.singletonList(NA) : containerDetails.getImageDetails().getPaths(); + List cellPaths = paths.stream() + .map(SingleCellExportable::new) + .collect(Collectors.toList()); + + return Arrays.asList(new ExcelSpecialFormatExport(Bundle.ExportContainerInfo_tabName(), Arrays.asList(new KeyValueItemExportable(Bundle.ExportContainerInfo_export_displayName(), new DefaultCellModel<>(containerDetails.getDisplayName())), + new KeyValueItemExportable(Bundle.ExportContainerInfo_export_originalName(), new DefaultCellModel<>(containerDetails.getOriginalName())), + new KeyValueItemExportable(Bundle.ExportContainerInfo_export_deviceId(), new DefaultCellModel<>(containerDetails.getDeviceId())), + new KeyValueItemExportable(Bundle.ExportContainerInfo_export_timeZone(), timeZone), + new TitledExportable(Bundle.ExportContainerInfo_export_acquisitionDetails(), getAcquisitionDetails(containerDetails.getAcquisitionDetails())), + new KeyValueItemExportable(Bundle.ExportContainerInfo_export_imageType(), imageType), + new KeyValueItemExportable(Bundle.ExportContainerInfo_export_size(), size), + new KeyValueItemExportable(Bundle.ExportContainerInfo_export_sectorSize(), sectorSize), + new KeyValueItemExportable(Bundle.ExportContainerInfo_export_md5(), md5), + new KeyValueItemExportable(Bundle.ExportContainerInfo_export_sha1(), sha1), + new KeyValueItemExportable(Bundle.ExportContainerInfo_export_sha256(), sha256), + new KeyValueItemExportable(Bundle.ExportContainerInfo_export_unallocatedSize(), unallocatedSize), + new TitledExportable(Bundle.ExportContainerInfo_export_filePaths(), cellPaths) + ))); + + } +} diff --git a/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExportGeolocation.java b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExportGeolocation.java new file mode 100755 index 0000000000..a0a6b0874c --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExportGeolocation.java @@ -0,0 +1,227 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 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.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataFetcher; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.GeolocationSummary.CityCountsList; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.GeolocationSummary.CityData; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.GeolocationSummary.CityRecordCount; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.CityRecord; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.GeolocationSummary; +import static org.sleuthkit.autopsy.report.modules.datasourcesummaryexport.ExcelExportAction.getFetchResult; +import static org.sleuthkit.autopsy.report.modules.datasourcesummaryexport.ExcelExportAction.getTableExport; +import org.sleuthkit.datamodel.DataSource; + +/** + * Class to export information about a data source's geolocation data. + */ +@Messages({ + "ExportGeolocation_cityColumn_title=Closest City", + "ExportGeolocation_countColumn_title=Count", + "ExportGeolocation_unknownRow_title=Unknown", + "ExportGeolocation_mostCommon_tabName=Most Common Cities", + "ExportGeolocation_mostRecent_tabName=Most Recent Cities",}) +class ExportGeolocation { + + private final GeolocationSummary geoSummary; + + /** + * Object encapsulating geolocation data. + */ + private static class GeolocationData { + + private final List> mostRecentData; + private final List> mostCommonData; + + /** + * Main constructor. + * + * @param mostRecentData The data to be displayed in the most recent + * tab. + * @param mostCommonData The data to be displayed in the most common + * tab. + */ + GeolocationData(List> mostRecentData, List> mostCommonData) { + this.mostRecentData = mostRecentData; + this.mostCommonData = mostCommonData; + } + + /** + * Returns the data to be displayed in the most recent tab. + * + * @return The data to be displayed in the most recent tab. + */ + List> getMostRecentData() { + return mostRecentData; + } + + /** + * Returns the data to be displayed in the most common tab. + * + * @return The data to be displayed in the most common tab. + */ + List> getMostCommonData() { + return mostCommonData; + } + } + + private static final int DAYS_COUNT = 30; + private static final int MAX_COUNT = 10; + + // The column indicating the city + private static final ColumnModel, DefaultCellModel> CITY_COL = new ColumnModel<>( + Bundle.ExportGeolocation_cityColumn_title(), + (pair) -> new DefaultCellModel<>(pair.getLeft()), + 300 + ); + + // The column indicating the count of points seen close to that city + private static final ColumnModel, DefaultCellModel> COUNT_COL = new ColumnModel<>( + Bundle.ExportGeolocation_countColumn_title(), + (pair) -> new DefaultCellModel<>(pair.getRight()), + 100 + ); + + private static final List, DefaultCellModel>> DEFAULT_TEMPLATE = Arrays.asList( + CITY_COL, + COUNT_COL + ); + + ExportGeolocation() { + geoSummary = new GeolocationSummary(); + } + + /** + * Retrieves the city name to display from the record. + * + * @param record The record for the city to display. + * + * @return The display name (city, country). + */ + private static String getCityName(CityRecord record) { + if (record == null) { + return null; + } + + List cityIdentifiers = Stream.of(record.getCityName(), record.getState(), record.getCountry()) + .filter(StringUtils::isNotBlank) + .collect(Collectors.toList()); + + if (cityIdentifiers.size() == 1) { + return cityIdentifiers.get(0); + } else if (cityIdentifiers.size() == 2) { + return String.format("%s, %s", cityIdentifiers.get(0), cityIdentifiers.get(1)); + } else if (cityIdentifiers.size() >= 3) { + return String.format("%s, %s; %s", cityIdentifiers.get(0), cityIdentifiers.get(1), cityIdentifiers.get(2)); + } + + return null; + } + + /** + * Formats one record to be displayed as a row in the tab (specifically, + * formats the city name). + * + * @param cityCount The CityRecordCount representing a row. + * + * @return The city/count pair to be displayed as a row. + */ + private static Pair formatRecord(CityRecordCount cityCount) { + if (cityCount == null) { + return null; + } + + String cityName = getCityName(cityCount.getCityRecord()); + int count = cityCount.getCount(); + return Pair.of(cityName, count); + } + + /** + * Formats a list of records to be displayed in a tab (specifically, + * includes the count of points where no closest city could be determined as + * 'unknown'). + * + * @param countsList The CityCountsList object representing the data to be + * displayed in the tab. + * + * @return The list of city/count tuples to be displayed as a row. + */ + private static List> formatList(CityCountsList countsList) { + if (countsList == null) { + return Collections.emptyList(); + } + + Stream countsStream = ((countsList.getCounts() == null) + ? new ArrayList() + : countsList.getCounts()).stream(); + + Stream> pairStream = countsStream.map((r) -> formatRecord(r)); + + Pair unknownRecord = Pair.of(Bundle.ExportGeolocation_unknownRow_title(), countsList.getOtherCount()); + + return Stream.concat(pairStream, Stream.of(unknownRecord)) + .filter((p) -> p != null && p.getRight() != null && p.getRight() > 0) + .sorted((a, b) -> -Integer.compare(a.getRight(), b.getRight())) + .limit(MAX_COUNT) + .collect(Collectors.toList()); + } + + /** + * Converts CityData from GeolocationSummaryGetter into data that can be + * directly put into tab in this panel. + * + * @param cityData The city data. + * + * @return The geolocation data. + */ + private static GeolocationData convertToViewModel(CityData cityData) { + if (cityData == null) { + return new GeolocationData(Collections.emptyList(), Collections.emptyList()); + } else { + return new GeolocationData(formatList(cityData.getMostRecent()), formatList(cityData.getMostCommon())); + } + } + + List getExports(DataSource dataSource) { + + DataFetcher geolocationFetcher = (ds) -> convertToViewModel(geoSummary.getCityCounts(ds, DAYS_COUNT, MAX_COUNT)); + + GeolocationData model + = getFetchResult(geolocationFetcher, "Geolocation sheets", dataSource); + if (model == null) { + return Collections.emptyList(); + } + + return Arrays.asList(getTableExport(DEFAULT_TEMPLATE, + Bundle.ExportGeolocation_mostRecent_tabName(), model.getMostRecentData()), + getTableExport(DEFAULT_TEMPLATE, + Bundle.ExportGeolocation_mostCommon_tabName(), model.getMostCommonData()) + ); + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/IngestJobExcelExport.java b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExportIngestHistory.java similarity index 83% rename from Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/IngestJobExcelExport.java rename to Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExportIngestHistory.java index 3ad757e7be..4c355f10aa 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/IngestJobExcelExport.java +++ b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExportIngestHistory.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.report.modules.datasourcesummaryexport; import java.text.DateFormat; import java.text.SimpleDateFormat; @@ -34,27 +34,24 @@ import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.ColumnModel; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultCellModel; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport.ExcelSheetExport; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelTableExport; +import org.sleuthkit.autopsy.report.modules.datasourcesummaryexport.ExcelExport.ExcelSheetExport; import org.sleuthkit.datamodel.DataSource; import org.sleuthkit.datamodel.IngestJobInfo; import org.sleuthkit.datamodel.IngestModuleInfo; import org.sleuthkit.datamodel.TskCoreException; /** - * Class that handles exporting information in IngestJobInfoPanel to excel. + * Class that handles exporting ingest job information to excel. */ @Messages({ - "IngestJobExcelExport_startTimeColumn=Start Time", - "IngestJobExcelExport_endTimeColumn=End Time", - "IngestJobExcelExport_ingestStatusTimeColumn=Ingest Status", - "IngestJobExcelExport_moduleNameTimeColumn=Module Name", - "IngestJobExcelExport_versionColumn=Module Version", - "IngestJobExcelExport_sheetName=Ingest History" + "ExportIngestHistory_startTimeColumn=Start Time", + "ExportIngestHistory_endTimeColumn=End Time", + "ExportIngestHistory_ingestStatusTimeColumn=Ingest Status", + "ExportIngestHistory_moduleNameTimeColumn=Module Name", + "ExportIngestHistory_versionColumn=Module Version", + "ExportIngestHistory_sheetName=Ingest History" }) -class IngestJobExcelExport { +class ExportIngestHistory { /** * An entry to display in an excel export. @@ -70,10 +67,10 @@ class IngestJobExcelExport { /** * Main constructor. * - * @param startTime The ingest start time. - * @param endTime The ingest stop time. - * @param status The ingest status. - * @param ingestModule The ingest module. + * @param startTime The ingest start time. + * @param endTime The ingest stop time. + * @param status The ingest status. + * @param ingestModule The ingest module. * @param ingestModuleVersion The ingest module version. */ IngestJobEntry(Date startTime, Date endTime, String status, String ingestModule, String ingestModuleVersion) { @@ -120,26 +117,26 @@ class IngestJobExcelExport { } } - private static final Logger logger = Logger.getLogger(IngestJobExcelExport.class.getName()); + private static final Logger logger = Logger.getLogger(ExportIngestHistory.class.getName()); private static final String DATETIME_FORMAT_STR = "yyyy/MM/dd HH:mm:ss"; private static final DateFormat DATETIME_FORMAT = new SimpleDateFormat(DATETIME_FORMAT_STR, Locale.getDefault()); // columns in the excel export table to be created. private static final List>> COLUMNS = Arrays.asList( new ColumnModel<>( - Bundle.IngestJobExcelExport_startTimeColumn(), + Bundle.ExportIngestHistory_startTimeColumn(), (entry) -> getDateCell(entry.getStartTime())), new ColumnModel<>( - Bundle.IngestJobExcelExport_endTimeColumn(), + Bundle.ExportIngestHistory_endTimeColumn(), (entry) -> getDateCell(entry.getEndTime())), new ColumnModel<>( - Bundle.IngestJobExcelExport_ingestStatusTimeColumn(), + Bundle.ExportIngestHistory_ingestStatusTimeColumn(), (entry) -> new DefaultCellModel<>(entry.getStatus())), new ColumnModel<>( - Bundle.IngestJobExcelExport_moduleNameTimeColumn(), + Bundle.ExportIngestHistory_moduleNameTimeColumn(), (entry) -> new DefaultCellModel<>(entry.getIngestModule())), new ColumnModel<>( - Bundle.IngestJobExcelExport_versionColumn(), + Bundle.ExportIngestHistory_versionColumn(), (entry) -> new DefaultCellModel<>(entry.getIngestModuleVersion())) ); @@ -147,6 +144,7 @@ class IngestJobExcelExport { * Retrieves data for a date cell. * * @param date The date. + * * @return The data cell to be used in the excel export. */ private static DefaultCellModel getDateCell(Date date) { @@ -158,6 +156,7 @@ class IngestJobExcelExport { * Retrieves all the ingest job modules and versions for a job. * * @param job The ingest job. + * * @return All of the corresponding entries sorted by module name. */ private static List getEntries(IngestJobInfo job) { @@ -190,6 +189,7 @@ class IngestJobExcelExport { * to null. * * @param list The list of entries for an ingest job. + * * @return The stream of entries to be displayed. */ private static Stream showFirstRowOnly(List list) { @@ -209,6 +209,7 @@ class IngestJobExcelExport { * Returns a list of sheets to be exported for the Ingest History tab. * * @param dataSource The data source. + * * @return The list of sheets to be included in an export. */ static List getExports(DataSource dataSource) { @@ -245,9 +246,9 @@ class IngestJobExcelExport { .filter(item -> item != null) .collect(Collectors.toList()); - return Arrays.asList(new ExcelTableExport<>(Bundle.IngestJobExcelExport_sheetName(), COLUMNS, toDisplay)); + return Arrays.asList(new ExcelTableExport<>(Bundle.ExportIngestHistory_sheetName(), COLUMNS, toDisplay)); } - private IngestJobExcelExport() { + private ExportIngestHistory() { } } diff --git a/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExportPastCases.java b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExportPastCases.java new file mode 100755 index 0000000000..be824477a1 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExportPastCases.java @@ -0,0 +1,80 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 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.util.Arrays; +import java.util.Collections; +import java.util.List; +import org.apache.commons.lang3.tuple.Pair; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataFetcher; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.PastCasesSummary; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.PastCasesSummary.PastCasesResult; +import static org.sleuthkit.autopsy.report.modules.datasourcesummaryexport.ExcelExportAction.getFetchResult; +import static org.sleuthkit.autopsy.report.modules.datasourcesummaryexport.ExcelExportAction.getTableExport; +import org.sleuthkit.datamodel.DataSource; + +/** + * Class to export information about a datasource and how it pertains to other + * cases. + */ +@Messages({ + "ExportPastCases_caseColumn_title=Case", + "ExportPastCases_countColumn_title=Count", + "ExportPastCases_notableFileTable_tabName=Cases with Common Notable", + "ExportPastCases_sameIdsTable_tabName=Past Cases with the Same Devices",}) +class ExportPastCases { + + private final PastCasesSummary pastSummary; + + // model for column indicating the case + private static final ColumnModel, DefaultCellModel> CASE_COL = new ColumnModel<>( + Bundle.ExportPastCases_caseColumn_title(), + (pair) -> new DefaultCellModel<>(pair.getKey()), + 300 + ); + + // model for column indicating the count + private static final ColumnModel, DefaultCellModel> COUNT_COL = new ColumnModel<>( + Bundle.ExportPastCases_countColumn_title(), + (pair) -> new DefaultCellModel<>(pair.getValue()), + 100 + ); + + // the template for columns in both tables in this tab + private static List, DefaultCellModel>> DEFAULT_TEMPLATE + = Arrays.asList(CASE_COL, COUNT_COL); + + ExportPastCases() { + pastSummary = new PastCasesSummary(); + } + + List getExports(DataSource dataSource) { + DataFetcher pastCasesFetcher = (ds) -> pastSummary.getPastCasesData(ds); + PastCasesResult result = getFetchResult(pastCasesFetcher, "Past cases sheets", dataSource); + if (result == null) { + return Collections.emptyList(); + } + + return Arrays.asList( + getTableExport(DEFAULT_TEMPLATE, Bundle.ExportPastCases_notableFileTable_tabName(), result.getTaggedNotable()), + getTableExport(DEFAULT_TEMPLATE, Bundle.ExportPastCases_sameIdsTable_tabName(), result.getSameIdsResults()) + ); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExportRecentFiles.java b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExportRecentFiles.java new file mode 100755 index 0000000000..a7d5471343 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExportRecentFiles.java @@ -0,0 +1,123 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 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.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataFetcher; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary.RecentAttachmentDetails; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary.RecentDownloadDetails; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary.RecentFileDetails; +import org.sleuthkit.datamodel.DataSource; + +/** + * Class to export data source summary info. + */ +@Messages({ + "ExportRecentFiles_docsTable_tabName=Recently Opened Documents", + "ExportRecentFiles_downloadsTable_tabName=Recently Downloads", + "ExportRecentFiles_attachmentsTable_tabName=Recent Attachments", + "ExportRecentFiles_col_head_date=Date", + "ExportRecentFiles_col_header_domain=Domain", + "ExportRecentFiles_col_header_path=Path", + "ExportRecentFiles_col_header_sender=Sender" +}) +final class ExportRecentFiles { + + private final RecentFilesSummary recentSummary; + + private static final String DATETIME_FORMAT_STR = "yyyy/MM/dd HH:mm:ss"; + private static final DateFormat DATETIME_FORMAT = new SimpleDateFormat(DATETIME_FORMAT_STR, Locale.getDefault()); + + private static final List>> docsTemplate = Arrays.asList( + new ColumnModel<>(Bundle.ExportRecentFiles_col_header_path(), + (prog) -> { + return new DefaultCellModel<>(prog.getPath()); + }, 250), + new ColumnModel<>(Bundle.ExportRecentFiles_col_head_date(), + getDateFunct(), + 80)); + + private static final List>> downloadsTemplate = Arrays.asList( + new ColumnModel<>(Bundle.ExportRecentFiles_col_header_domain(), + (prog) -> { + return new DefaultCellModel<>(prog.getWebDomain()); + }, 100), + new ColumnModel<>(Bundle.ExportRecentFiles_col_header_path(), + (prog) -> { + return new DefaultCellModel<>(prog.getPath()); + }, 250), + new ColumnModel<>(Bundle.ExportRecentFiles_col_head_date(), + getDateFunct(), + 80)); + + private static final List>> attachmentsTemplate = Arrays.asList( + new ColumnModel<>(Bundle.ExportRecentFiles_col_header_path(), + (prog) -> { + return new DefaultCellModel<>(prog.getPath()); + }, 250), + new ColumnModel<>(Bundle.ExportRecentFiles_col_head_date(), + getDateFunct(), + 80), + new ColumnModel<>(Bundle.ExportRecentFiles_col_header_sender(), + (prog) -> { + return new DefaultCellModel<>(prog.getSender()); + }, 150)); + + ExportRecentFiles() { + recentSummary = new RecentFilesSummary(); + } + + /** + * Returns a function that gets the date from the RecentFileDetails object + * and converts into a DefaultCellModel to be displayed in a table. + * + * @return The function that determines the date cell from a + * RecentFileDetails object. + */ + private static Function> getDateFunct() { + return (T lastAccessed) -> { + Function dateParser = (dt) -> dt == null ? "" : DATETIME_FORMAT.format(dt); + return new DefaultCellModel<>(new Date(lastAccessed.getDateAsLong() * 1000), dateParser, DATETIME_FORMAT_STR); + }; + } + + List getExports(DataSource dataSource) { + + DataFetcher> docsFetcher = (ds) -> recentSummary.getRecentlyOpenedDocuments(ds, 10); + DataFetcher> downloadsFetcher = (ds) -> recentSummary.getRecentDownloads(ds, 10); + DataFetcher> attachmentsFetcher = (ds) -> recentSummary.getRecentAttachments(ds, 10); + + return Stream.of( + ExcelExportAction.getTableExport(docsFetcher, docsTemplate, Bundle.ExportRecentFiles_docsTable_tabName(), dataSource), + ExcelExportAction.getTableExport(downloadsFetcher, downloadsTemplate, Bundle.ExportRecentFiles_downloadsTable_tabName(), dataSource), + ExcelExportAction.getTableExport(attachmentsFetcher, attachmentsTemplate, Bundle.ExportRecentFiles_attachmentsTable_tabName(), dataSource)) + .filter(sheet -> sheet != null) + .collect(Collectors.toList()); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExportTimeline.java b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExportTimeline.java new file mode 100755 index 0000000000..cbb385b60a --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExportTimeline.java @@ -0,0 +1,144 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 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.text.DateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import org.apache.commons.collections.CollectionUtils; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartSeries; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartSeries.BarChartItem; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartSeries.OrderedKey; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataFetcher; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineSummary; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineSummary.DailyActivityAmount; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineSummary.TimelineSummaryData; +import org.sleuthkit.autopsy.report.modules.datasourcesummaryexport.ExcelSpecialFormatExport.KeyValueItemExportable; +import org.sleuthkit.autopsy.report.modules.datasourcesummaryexport.ExcelSpecialFormatExport.TitledExportable; +import org.sleuthkit.datamodel.DataSource; + +/** + * Class to export information about a data source's timeline events. + */ +@Messages({ + "TimelinePanel_earliestLabel_title=Earliest", + "TimelinePanel_latestLabel_title=Latest", + "TimlinePanel_last30DaysChart_title=Last 30 Days", + "TimlinePanel_last30DaysChart_fileEvts_title=File Events", + "TimlinePanel_last30DaysChart_artifactEvts_title=Result Events",}) +class ExportTimeline { + + private final TimelineSummary timelineSummary; + + private static final String EARLIEST_LATEST_FORMAT_STR = "MMM d, yyyy"; + private static final DateFormat EARLIEST_LATEST_FORMAT = TimelineSummary.getUtcFormat(EARLIEST_LATEST_FORMAT_STR); + private static final DateFormat CHART_FORMAT = TimelineSummary.getUtcFormat("MMM d, yyyy"); + private static final int MOST_RECENT_DAYS_COUNT = 30; + + private static final Color FILE_EVT_COLOR = new Color(228, 22, 28); + private static final Color ARTIFACT_EVT_COLOR = new Color(21, 227, 100); + + /** + * Creates new form PastCasesPanel + */ + ExportTimeline() { + timelineSummary = new TimelineSummary(); + } + + /** + * Converts DailyActivityAmount data retrieved from TimelineSummaryGetter + * into data to be displayed as a bar chart. + * + * @param recentDaysActivity The data retrieved from + * TimelineSummaryGetter. + * @param showIntermediateDates If true, shows all dates. If false, shows + * only first and last date. + * + * @return The data to be displayed in the BarChart. + */ + private static List parseChartData(List recentDaysActivity, boolean showIntermediateDates) { + // if no data, return null indicating no result. + if (CollectionUtils.isEmpty(recentDaysActivity)) { + return null; + } + + // Create a bar chart item for each recent days activity item + List fileEvtCounts = new ArrayList<>(); + List artifactEvtCounts = new ArrayList<>(); + + for (int i = 0; i < recentDaysActivity.size(); i++) { + DailyActivityAmount curItem = recentDaysActivity.get(i); + + long fileAmt = curItem.getFileActivityCount(); + long artifactAmt = curItem.getArtifactActivityCount() * 100; + String formattedDate = (showIntermediateDates || i == 0 || i == recentDaysActivity.size() - 1) + ? TimelineSummary.formatDate(curItem.getDay(), CHART_FORMAT) : ""; + + OrderedKey thisKey = new OrderedKey(formattedDate, i); + fileEvtCounts.add(new BarChartItem(thisKey, fileAmt)); + artifactEvtCounts.add(new BarChartItem(thisKey, artifactAmt)); + } + + return Arrays.asList( + new BarChartSeries(Bundle.TimlinePanel_last30DaysChart_fileEvts_title(), FILE_EVT_COLOR, fileEvtCounts), + new BarChartSeries(Bundle.TimlinePanel_last30DaysChart_artifactEvts_title(), ARTIFACT_EVT_COLOR, artifactEvtCounts)); + } + + /** + * Create a default cell model to be use with excel export in the earliest / + * latest date format. + * + * @param date The date. + * @return The cell model. + */ + private static DefaultCellModel getEarliestLatestCell(Date date) { + return new DefaultCellModel<>(date, (dt) -> dt == null ? "" : EARLIEST_LATEST_FORMAT.format(dt), EARLIEST_LATEST_FORMAT_STR); + } + + @Messages({ + "TimelinePanel_getExports_sheetName=Timeline", + "TimelinePanel_getExports_activityRange=Activity Range", + "TimelinePanel_getExports_earliest=Earliest:", + "TimelinePanel_getExports_latest=Latest:", + "TimelinePanel_getExports_dateColumnHeader=Date", + "TimelinePanel_getExports_chartName=Last 30 Days",}) + List getExports(DataSource dataSource) { + DataFetcher dataFetcher = (ds) -> timelineSummary.getTimelineSummaryData(ds, MOST_RECENT_DAYS_COUNT); + TimelineSummaryData summaryData = ExcelExportAction.getFetchResult(dataFetcher, "Timeline", dataSource); + if (summaryData == null) { + return Collections.emptyList(); + } + + return Arrays.asList( + new ExcelSpecialFormatExport(Bundle.TimelinePanel_getExports_sheetName(), + Arrays.asList( + new TitledExportable(Bundle.TimelinePanel_getExports_activityRange(), Collections.emptyList()), + new KeyValueItemExportable(Bundle.TimelinePanel_getExports_earliest(), getEarliestLatestCell(summaryData.getMinDate())), + new KeyValueItemExportable(Bundle.TimelinePanel_getExports_latest(), getEarliestLatestCell(summaryData.getMaxDate())), + new BarChartExport(Bundle.TimelinePanel_getExports_dateColumnHeader(), + "#,###", + Bundle.TimelinePanel_getExports_chartName(), + parseChartData(summaryData.getMostRecentDaysActivity(), true))))); + } +} 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..f3787a4a98 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExportTypes.java @@ -0,0 +1,265 @@ +/* + * 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.stream.Collectors; +import java.util.stream.Stream; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.PieChartItem; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.ContainerSummary; +import org.sleuthkit.autopsy.coreutils.FileTypeUtils.FileTypeCategory; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataFetcher; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataSourceInfoUtilities; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.MimeTypeSummary; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.TypesSummary; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.TypesSummary.FileTypeCategoryData; +import org.sleuthkit.autopsy.report.modules.datasourcesummaryexport.ExcelSpecialFormatExport.KeyValueItemExportable; +import org.sleuthkit.datamodel.DataSource; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Class to export 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 final MimeTypeSummary mimeTypeSummary; + private final ContainerSummary containerSummary; + private final TypesSummary typesSummary; + private final SleuthkitCaseProvider provider; + + 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) + ); + + ExportTypes() { + this.provider = SleuthkitCaseProvider.DEFAULT; + containerSummary = new ContainerSummary(); + typesSummary = new TypesSummary(); + mimeTypeSummary = new MimeTypeSummary(); + } + + /** + * 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 TypesPieChartData getMimeTypeCategoriesModel(DataSource dataSource) + throws SQLException, TskCoreException, SleuthkitCaseProvider.SleuthkitCaseProviderException { + + 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(mimeTypeSummary.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(mimeTypeSummary.getCountOfFilesWithNoMimeType(dataSource)); + + // get a count of all regular files + long allRegularFiles = DataSourceInfoUtilities.getLongOrZero(DataSourceInfoUtilities.getCountOfRegNonSlackFiles(provider.get(), 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)); + } + + 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()) + )); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExportUserActivity.java b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExportUserActivity.java new file mode 100755 index 0000000000..f1a4198b42 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/ExportUserActivity.java @@ -0,0 +1,240 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 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.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.apache.commons.lang.StringUtils; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.datamodel.DataSource; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataFetcher; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.LastAccessedArtifact; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopAccountResult; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopDeviceAttachedResult; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopDomainsResult; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopProgramsResult; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopWebSearchResult; +import static org.sleuthkit.autopsy.report.modules.datasourcesummaryexport.ExcelExportAction.getTableExport; + +/** + * Class to export user activity present in the specified DataSource. + */ +@Messages({ + "ExportUserActivity_tab_title=User Activity", + "ExportUserActivity_TopProgramsTableModel_tabName=Recent Programs", + "ExportUserActivity_TopDomainsTableModel_tabName=Recent Domains", + "ExportUserActivity_TopWebSearchTableModel_tabName=Recent Web Searches", + "ExportUserActivity_TopDeviceAttachedTableModel_tabName=Recent Devices Attached", + "ExportUserActivity_TopAccountTableModel_tabName=Recent Account Types Used", + "ExportUserActivity_TopProgramsTableModel_name_header=Program", + "ExportUserActivity_TopProgramsTableModel_folder_header=Folder", + "ExportUserActivity_TopProgramsTableModel_count_header=Run Times", + "ExportUserActivity_TopProgramsTableModel_lastrun_header=Last Run", + "ExportUserActivity_TopDomainsTableModel_domain_header=Domain", + "ExportUserActivity_TopDomainsTableModel_count_header=Visits", + "ExportUserActivity_TopDomainsTableModel_lastAccess_header=Last Accessed", + "ExportUserActivity_TopWebSearchTableModel_searchString_header=Search String", + "ExportUserActivity_TopWebSearchTableModel_dateAccessed_header=Date Accessed", + "ExportUserActivity_TopWebSearchTableModel_translatedResult_header=Translated", + "ExportUserActivity_TopDeviceAttachedTableModel_deviceId_header=Device Id", + "ExportUserActivity_TopDeviceAttachedTableModel_makeModel_header=Make and Model", + "ExportUserActivity_TopDeviceAttachedTableModel_dateAccessed_header=Last Accessed", + "ExportUserActivity_TopAccountTableModel_accountType_header=Account Type", + "ExportUserActivity_TopAccountTableModel_lastAccess_header=Last Accessed", + "ExportUserActivity_noDataExists=No communication data exists"}) +class ExportUserActivity { + + private final UserActivitySummary userSummary; + + private static final String DATETIME_FORMAT_STR = "yyyy/MM/dd HH:mm:ss"; + private static final DateFormat DATETIME_FORMAT = new SimpleDateFormat(DATETIME_FORMAT_STR, Locale.getDefault()); + private static final int TOP_PROGS_COUNT = 10; + private static final int TOP_DOMAINS_COUNT = 10; + private static final int TOP_SEARCHES_COUNT = 10; + private static final int TOP_ACCOUNTS_COUNT = 5; + private static final int TOP_DEVICES_COUNT = 10; + + // set up recent programs + private static final List>> topProgramsTemplate = Arrays.asList( + // program name column + new ColumnModel<>( + Bundle.ExportUserActivity_TopProgramsTableModel_name_header(), + (prog) -> { + return new DefaultCellModel<>(prog.getProgramName()); + }, + 250), + // program folder column + new ColumnModel<>( + Bundle.ExportUserActivity_TopProgramsTableModel_folder_header(), + (prog) -> { + return new DefaultCellModel<>( + UserActivitySummary.getShortFolderName( + prog.getProgramPath(), + prog.getProgramName())); + }, + 150), + // run count column + new ColumnModel<>( + Bundle.ExportUserActivity_TopProgramsTableModel_count_header(), + (prog) -> { + return new DefaultCellModel<>(prog.getRunTimes(), (num) -> num == null ? "" : num.toString()); + }, + 80), + // last run date column + new ColumnModel<>( + Bundle.ExportUserActivity_TopProgramsTableModel_lastrun_header(), + getDateFunct(), + 150) + ); + + // set up recent domains + private static final List>> topDomainsTemplate = Arrays.asList( + // domain column + new ColumnModel<>( + Bundle.ExportUserActivity_TopDomainsTableModel_domain_header(), + (recentDomain) -> { + return new DefaultCellModel<>(recentDomain.getDomain()); + }, + 250), + // count column + new ColumnModel<>( + Bundle.ExportUserActivity_TopDomainsTableModel_count_header(), + (recentDomain) -> { + return new DefaultCellModel<>(recentDomain.getVisitTimes(), (num) -> num == null ? "" : num.toString()); + }, + 100), + // last accessed column + new ColumnModel<>( + Bundle.ExportUserActivity_TopDomainsTableModel_lastAccess_header(), + getDateFunct(), + 150) + ); + + // top web searches + private static final List>> topWebSearchesTemplate = Arrays.asList( + // search string column + new ColumnModel<>( + Bundle.ExportUserActivity_TopWebSearchTableModel_searchString_header(), + (webSearch) -> { + return new DefaultCellModel<>(webSearch.getSearchString()); + }, + 250 + ), + // last accessed + new ColumnModel<>( + Bundle.ExportUserActivity_TopWebSearchTableModel_dateAccessed_header(), + getDateFunct(), + 150 + ), + // translated value + new ColumnModel<>( + Bundle.ExportUserActivity_TopWebSearchTableModel_translatedResult_header(), + (webSearch) -> { + return new DefaultCellModel<>(webSearch.getTranslatedResult()); + }, + 250 + ) + ); + + // top devices attached + private static final List>> topDevicesTemplate = Arrays.asList( + // device id column + new ColumnModel<>( + Bundle.ExportUserActivity_TopDeviceAttachedTableModel_deviceId_header(), + (device) -> { + return new DefaultCellModel<>(device.getDeviceId()); + }, + 250 + ), + // last accessed + new ColumnModel<>( + Bundle.ExportUserActivity_TopDeviceAttachedTableModel_dateAccessed_header(), + getDateFunct(), + 150 + ), + // make and model + new ColumnModel<>( + Bundle.ExportUserActivity_TopDeviceAttachedTableModel_makeModel_header(), + (device) -> { + String make = StringUtils.isBlank(device.getDeviceMake()) ? "" : device.getDeviceMake().trim(); + String model = StringUtils.isBlank(device.getDeviceModel()) ? "" : device.getDeviceModel().trim(); + String makeModelString = (make.isEmpty() || model.isEmpty()) + ? make + model + : String.format("%s - %s", make, model); + return new DefaultCellModel<>(makeModelString); + }, + 250 + ) + ); + + // top accounts + private static final List>> topAccountsTemplate = Arrays.asList( + // account type column + new ColumnModel<>( + Bundle.ExportUserActivity_TopAccountTableModel_accountType_header(), + (account) -> { + return new DefaultCellModel<>(account.getAccountType()); + }, + 250 + ), + // last accessed + new ColumnModel<>( + Bundle.ExportUserActivity_TopAccountTableModel_lastAccess_header(), + getDateFunct(), + 150 + ) + ); + + ExportUserActivity() { + userSummary = new UserActivitySummary(); + } + + private static Function> getDateFunct() { + return (T lastAccessed) -> { + Function dateParser = (dt) -> dt == null ? "" : DATETIME_FORMAT.format(dt); + return new DefaultCellModel<>(lastAccessed.getLastAccessed(), dateParser, DATETIME_FORMAT_STR); + }; + } + + List getExports(DataSource dataSource) { + + DataFetcher> topProgramsFetcher = (ds) -> userSummary.getTopPrograms(ds, TOP_PROGS_COUNT); + DataFetcher> topDomainsFetcher = (ds) -> userSummary.getRecentDomains(ds, TOP_DOMAINS_COUNT); + DataFetcher> topWebSearchesFetcher = (ds) -> userSummary.getMostRecentWebSearches(ds, TOP_SEARCHES_COUNT); + DataFetcher> topDevicesAttachedFetcher = (ds) -> userSummary.getRecentDevices(ds, TOP_DEVICES_COUNT); + DataFetcher> topAccountsFetcher = (ds) -> userSummary.getRecentAccounts(ds, TOP_ACCOUNTS_COUNT); + + return Stream.of( + getTableExport(topProgramsFetcher, topProgramsTemplate, Bundle.ExportUserActivity_TopProgramsTableModel_tabName(), dataSource), + getTableExport(topDomainsFetcher, topDomainsTemplate, Bundle.ExportUserActivity_TopDomainsTableModel_tabName(), dataSource), + getTableExport(topWebSearchesFetcher, topWebSearchesTemplate, Bundle.ExportUserActivity_TopWebSearchTableModel_tabName(), dataSource), + getTableExport(topDevicesAttachedFetcher, topDevicesTemplate, Bundle.ExportUserActivity_TopDeviceAttachedTableModel_tabName(), dataSource), + getTableExport(topAccountsFetcher, topAccountsTemplate, Bundle.ExportUserActivity_TopAccountTableModel_tabName(), dataSource)) + .filter(sheet -> sheet != null) + .collect(Collectors.toList()); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/PieChartExport.java b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/PieChartExport.java similarity index 91% rename from Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/PieChartExport.java rename to Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/PieChartExport.java index 4005a34ebc..395637acb1 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/PieChartExport.java +++ b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/PieChartExport.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.datasourcesummary.uiutils; +package org.sleuthkit.autopsy.report.modules.datasourcesummaryexport; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; @@ -35,23 +35,24 @@ import org.apache.poi.xssf.usermodel.XSSFClientAnchor; import org.apache.poi.xssf.usermodel.XSSFDrawing; import org.apache.poi.xssf.usermodel.XSSFSheet; import org.openxmlformats.schemas.drawingml.x2006.chart.CTPieChart; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport.ExcelExportException; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport.ExcelSheetExport; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport.ExcelItemExportable; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport.ItemDimensions; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.PieChartItem; +import org.sleuthkit.autopsy.report.modules.datasourcesummaryexport.ExcelExport.ExcelExportException; +import org.sleuthkit.autopsy.report.modules.datasourcesummaryexport.ExcelExport.ExcelSheetExport; +import org.sleuthkit.autopsy.report.modules.datasourcesummaryexport.ExcelSpecialFormatExport.ExcelItemExportable; +import org.sleuthkit.autopsy.report.modules.datasourcesummaryexport.ExcelSpecialFormatExport.ItemDimensions; /** * * Class that creates an excel pie chart along with data table. */ -public class PieChartExport implements ExcelItemExportable, ExcelSheetExport { +class PieChartExport implements ExcelItemExportable, ExcelSheetExport { private static final int DEFAULT_ROW_SIZE = 20; private static final int DEFAULT_COL_SIZE = 10; private static final int DEFAULT_ROW_PADDING = 1; private static final int DEFAULT_COL_OFFSET = 1; - private final ExcelTableExport tableExport; + private final ExcelTableExport tableExport; private final int colOffset; private final int rowPadding; private final int colSize; @@ -69,7 +70,7 @@ public class PieChartExport implements ExcelItemExportable, ExcelSheetExport { * @param chartTitle The title for the chart. * @param slices The values for the pie slices. */ - public PieChartExport(String keyColumnHeader, + PieChartExport(String keyColumnHeader, String valueColumnHeader, String valueFormatString, String chartTitle, List slices) { @@ -93,7 +94,7 @@ public class PieChartExport implements ExcelItemExportable, ExcelSheetExport { * @param colSize The column size of the chart. * @param rowSize The row size of the chart. */ - public PieChartExport(String keyColumnHeader, + PieChartExport(String keyColumnHeader, String valueColumnHeader, String valueFormatString, String chartTitle, String sheetName, List slices, diff --git a/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/SizeRepresentationUtil.java b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/SizeRepresentationUtil.java new file mode 100755 index 0000000000..7c9018c9ff --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/report/modules/datasourcesummaryexport/SizeRepresentationUtil.java @@ -0,0 +1,178 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 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.text.DecimalFormat; +import org.openide.util.NbBundle; + +/** + * This class provides utilities for representing storage size in most relevant + * units (i.e. bytes, megabytes, etc.). + */ +final class SizeRepresentationUtil { + + private static final int SIZE_CONVERSION_CONSTANT = 1000; + private static final DecimalFormat APPROXIMATE_SIZE_FORMAT = new DecimalFormat("#.##"); + + /** + * A size unit corresponding to orders of magnitude of bytes (kilobyte, gigabytes, etc.). + */ + @NbBundle.Messages({ + "SizeRepresentationUtil_units_bytes=bytes", + "SizeRepresentationUtil_units_kilobytes=KB", + "SizeRepresentationUtil_units_megabytes=MB", + "SizeRepresentationUtil_units_gigabytes=GB", + "SizeRepresentationUtil_units_terabytes=TB", + "SizeRepresentationUtil_units_petabytes=PB" + }) + enum SizeUnit { + BYTES(Bundle.SizeRepresentationUtil_units_bytes(), "#", 0), + KB(Bundle.SizeRepresentationUtil_units_kilobytes(), "#,##0.00,", 1), + MB(Bundle.SizeRepresentationUtil_units_megabytes(), "#,##0.00,,", 2), + GB(Bundle.SizeRepresentationUtil_units_gigabytes(), "#,##0.00,,,", 3), + TB(Bundle.SizeRepresentationUtil_units_terabytes(), "#,##0.00,,,,", 4), + PB(Bundle.SizeRepresentationUtil_units_petabytes(), "#,##0.00,,,,,", 5); + + private final String suffix; + private final String excelFormatString; + private final long divisor; + + /** + * Main constructor. + * @param suffix The string suffix to use for size unit. + * @param excelFormatString The excel format string to use for this size unit. + * @param power The power of 1000 of bytes for this size unit. + */ + SizeUnit(String suffix, String excelFormatString, int power) { + this.suffix = suffix; + + // based on https://www.mrexcel.com/board/threads/how-do-i-format-cells-to-show-gb-mb-kb.140135/ + this.excelFormatString = String.format("%s \"%s\"", excelFormatString, suffix); + this.divisor = (long) Math.pow(SIZE_CONVERSION_CONSTANT, power); + } + + /** + * @return The string suffix to use for size unit. + */ + String getSuffix() { + return suffix; + } + + /** + * @return The excel format string to use for this size unit. + */ + String getExcelFormatString() { + return excelFormatString; + } + + /** + * @return The divisor to convert from bytes to this unit. + */ + long getDivisor() { + return divisor; + } + } + + /** + * Get a long size in bytes as a string formated to be read by users. + * + * @param size Long value representing a size in bytes. + * + * @return Return a string formated with a user friendly version of the size + * as a string, returns empty String when provided empty size. + */ + static String getSizeString(Long size) { + return getSizeString(size, APPROXIMATE_SIZE_FORMAT, true); + } + + /** + * Determines the relevant size unit that should be used for a particular size. + * @param size The size in bytes. + * @return The relevant size unit. + */ + static SizeUnit getSizeUnit(Long size) { + if (size == null) { + return SizeUnit.values()[0]; + } + + for (SizeUnit unit : SizeUnit.values()) { + long result = size / unit.getDivisor(); + if (result < SIZE_CONVERSION_CONSTANT) { + return unit; + } + } + + return SizeUnit.values()[SizeUnit.values().length - 1]; + } + + /** + * Get a long size in bytes as a string formatted to be read by users. + * + * @param size Long value representing a size in byte.s + * @param format The means of formatting the number. + * @param showFullSize Optionally show the number of bytes in the + * datasource. + * + * @return Return a string formatted with a user friendly version of the size + * as a string, returns empty String when provided empty size. + */ + static String getSizeString(Long size, DecimalFormat format, boolean showFullSize) { + if (size == null) { + return ""; + } + + SizeUnit sizeUnit = getSizeUnit(size); + if (sizeUnit == null) { + sizeUnit = SizeUnit.BYTES; + } + + String closestUnitSize = String.format("%s %s", + format.format(((double) size) / sizeUnit.getDivisor()), sizeUnit.getSuffix()); + + String fullSize = String.format("%d %s", size, SizeUnit.BYTES.getSuffix()); + if (sizeUnit.equals(SizeUnit.BYTES)) { + return fullSize; + } else if (showFullSize) { + return String.format("%s (%s)", closestUnitSize, fullSize); + } else { + return closestUnitSize; + } + } + + /** + * Returns a default cell model using size units. + * @param bytes The number of bytes. + * @return The default cell model. + */ + static DefaultCellModel getBytesCell(Long bytes) { + if (bytes == null) { + return new DefaultCellModel<>(""); + } else { + SizeUnit unit = SizeRepresentationUtil.getSizeUnit(bytes); + if (unit == null) { + unit = SizeUnit.BYTES; + } + + return new DefaultCellModel<>(bytes, SizeRepresentationUtil::getSizeString, unit.getExcelFormatString()); + } + } + + private SizeRepresentationUtil() { + } +}