Merge pull request #7199 from eugene7646/ds_summary_report_7893

Data source summary report module (7893)
This commit is contained in:
Richard Cordovano 2021-08-25 09:31:39 -04:00 committed by GitHub
commit 578a3d24bb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
73 changed files with 4335 additions and 2354 deletions

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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<String> EXCLUDED_KEYWORD_SEARCH_ITEMS = new HashSet<>();
private static final Set<Integer> 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<Integer> getArtifactTypeIdsForRefresh() {
return ARTIFACT_UPDATE_TYPE_IDS;
}
/**
* Gets counts for hashset hits.
*

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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 {
@ -97,6 +98,7 @@ class ClosestCityMapper {
* @param citiesInputStream The input stream for the csv text file
* 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) {
@ -140,6 +144,7 @@ class ClosestCityMapper {
*
* @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) {
@ -162,6 +167,7 @@ class ClosestCityMapper {
* @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<String> csvRow, int lineNum) {
@ -201,6 +207,7 @@ class ClosestCityMapper {
*
* @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<String> parseCsvLine(String line, int lineNum) {
@ -225,7 +232,9 @@ class ClosestCityMapper {
* @param csvInputStream The csv file input stream.
* @param ignoreHeaderRow Whether or not there is a header row in the csv
* file.
*
* @return The list of city records.
*
* @throws IOException
*/
private List<CityRecord> parseCsvLines(InputStream csvInputStream, boolean ignoreHeaderRow) throws IOException {

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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<Integer> 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;
@ -60,21 +56,6 @@ public class ContainerSummary implements DefaultArtifactUpdateGovernor {
this.provider = provider;
}
@Override
public boolean isRefreshRequired(ModuleContentEvent evt) {
return true;
}
@Override
public boolean isRefreshRequired(AbstractFile file) {
return true;
}
@Override
public Set<Integer> 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<String> 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<String> 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<String> 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<String> 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);
}
}

View File

@ -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

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019 - 2020 Basis Technology Corp.
* Copyright 2019 - 2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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<T> {
public interface ResultSetHandler<T> {
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<BlackboardArtifact> getArtifacts(SleuthkitCase skCase, BlackboardArtifact.Type artifactType, DataSource dataSource, BlackboardAttribute.Type attributeType, SortOrder sortOrder) throws TskCoreException {
public static List<BlackboardArtifact> 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<BlackboardArtifact> getArtifacts(SleuthkitCase skCase, BlackboardArtifact.Type artifactType, DataSource dataSource, BlackboardAttribute.Type attributeType, SortOrder sortOrder, int maxCount) throws TskCoreException {
public static List<BlackboardArtifact> 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,7 +398,7 @@ 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();
}
@ -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);
}
}

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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.
@ -142,8 +141,8 @@ 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.
*/
@ -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<MapWaypoint> mapWaypoints, List<Set<MapWaypoint>> tracks, List<Set<MapWaypoint>> 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<ARTIFACT_TYPE> getGeoTypes() {
public static List<ARTIFACT_TYPE> getGeoTypes() {
return GPS_ARTIFACT_TYPES;
}
@Override
public Set<Integer> getArtifactTypeIdsForRefresh() {
return GPS_ARTIFACT_TYPE_IDS;
public static Set<Integer> getArtifactTypeIdsForRefresh() {
return Collections.unmodifiableSet(GPS_ARTIFACT_TYPE_IDS);
}
/**
@ -294,10 +293,11 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
* 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.
*/
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 {
@ -312,10 +312,11 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
*
* @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.
*/
private Pair<Integer, Integer> getCounts(List<Long> points, Long minTime) {
private static Pair<Integer, Integer> getCounts(List<Long> points, Long minTime) {
if (points == null) {
return EMPTY_COUNT;
}
@ -333,6 +334,7 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
*
* @param cityMapper The means of mapping a point to the closest city.
* @param pt The geolocation point.
*
* @return A tuple of the closest city and timestamp in seconds from epoch.
*/
private Pair<CityRecord, Long> getClosestWithTime(ClosestCityMapper cityMapper, MapWaypoint pt) {
@ -353,8 +355,10 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
*
* @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<Pair<CityRecord, Long>> reduceGrouping(Set<MapWaypoint> points, ClosestCityMapper cityMapper) {
if (points == null) {
@ -387,8 +391,10 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
*
* @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.
*
* @throws IOException
*/
private Stream<Pair<CityRecord, Long>> processGeoResult(GeoResult geoResult, ClosestCityMapper cityMapper) {
@ -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.
* 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<GeoResult> asyncResult, GeoFilter filters) {
PointFetcher(BlockingQueue<GeoResult> 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

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -86,6 +86,7 @@ class LatLngMap<E extends KdTree.XYZPoint> {
* closest neighboring buckets.
*
* @param point The point to calculate the bucket location pair.
*
* @return The pair that was determined.
*/
private Pair<Double, Double> getBucketLocation(XYZPoint point) {
@ -106,6 +107,7 @@ class LatLngMap<E extends KdTree.XYZPoint> {
* 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) {
@ -135,6 +137,7 @@ class LatLngMap<E extends KdTree.XYZPoint> {
* @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) {

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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<IngestManager.IngestJobEvent> 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<IngestManager.IngestJobEvent> 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<String> 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<String> 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 = '') ");
}
/**

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019 Basis Technology Corp.
* Copyright 2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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<Pair<String, Long>> getSameIdsResults() {
return sameIdsResults;
return Collections.unmodifiableList(sameIdsResults);
}
/**
* @return Data for the tagged notable table.
*/
public List<Pair<String, Long>> getTaggedNotable() {
return taggedNotable;
return Collections.unmodifiableList(taggedNotable);
}
}
private static final Set<Integer> 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<Integer> 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<Pair<String, Long>> getCaseCounts(Stream<String> cases) {
private static List<Pair<String, Long>> getCaseCounts(Stream<String> cases) {
Collection<List<String>> groupedCases = cases
// group by case insensitive compare of cases
.collect(Collectors.groupingBy((caseStr) -> caseStr.toUpperCase().trim()))

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Copyright 2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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<Integer> 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<Integer> 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 <T extends RecentFileDetails> List<T> getSortedLimited(List<T> fileDetails, int limit) {
private static <T extends RecentFileDetails> List<T> getSortedLimited(List<T> fileDetails, int limit) {
Map<String, T> 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();

View File

@ -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();

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS = new HashSet<>(
Arrays.asList(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED));
private static final Set<TimelineEventType> FILE_SYSTEM_EVENTS
= new HashSet<>(Arrays.asList(
TimelineEventType.FILE_MODIFIED,
@ -105,26 +100,6 @@ 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<IngestManager.IngestJobEvent> getIngestJobEventUpdates() {
return INGEST_JOB_EVENTS;
}
/**
* Retrieves timeline summary data.
*
@ -132,12 +107,12 @@ public class TimelineSummary implements DefaultUpdateGovernor {
* retrieved.
* @param recentDaysNum The maximum number of most recent days' activity to
* 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;
*
* @return The most recent daily activity amounts.
*/
private List<DailyActivityAmount> getMostRecentActivityAmounts(Map<Long, DailyActivityAmount> dateCounts, long minRecentDay, long maxDay) {
@ -200,14 +176,15 @@ public class TimelineSummary implements DefaultUpdateGovernor {
* @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.
* @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<Long, DailyActivityAmount> 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.
* @param dataSource The data source for which this data
* applies. the latest usage date by day.
*/
TimelineSummaryData(Date minDate, Date maxDate, List<DailyActivityAmount> recentDaysActivity, DataSource dataSource) {
this.minDate = minDate;
@ -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);
}
}

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019 - 2020 Basis Technology Corp.
* Copyright 2019 - 2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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<IngestManager.IngestJobEvent> 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<IngestManager.IngestJobEvent> 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<String> 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<String> 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<String> getMimeTypes() {
return mimeTypes;
}
/**
* @return The color associated with this category.
*/
public Color getColor() {
return color;
}
}
}

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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<Integer> 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<String> DEVICE_EXCLUDE_LIST = new HashSet<>(Arrays.asList("ROOT_HUB", "ROOT_HUB20"));
private static final Set<String> DOMAIN_EXCLUDE_LIST = new HashSet<>(Arrays.asList("127.0.0.1", "LOCALHOST"));
@ -186,22 +175,50 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
this.logger = logger;
}
@Override
public Set<Integer> 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<String> 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<List<String>, 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.
*
@ -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
@ -372,7 +389,8 @@ 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.
@ -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,7 +499,8 @@ 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.
@ -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<String> 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<List<String>, 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.
*
@ -765,8 +752,8 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
*
* @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.
* @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.

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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 {
);
}// </editor-fold>//GEN-END:initComponents
@Override
List<ExcelSheetExport> 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
}

View File

@ -0,0 +1,105 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.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<Integer> 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<Integer> 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<Pair<String, Long>> 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<Pair<String, Long>> 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<Pair<String, Long>> getInterestingItemCounts(DataSource dataSource) throws SleuthkitCaseProviderException, TskCoreException {
return analysisSummary.getInterestingItemCounts(dataSource);
}
}

View File

@ -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<ExcelSheetExport> 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<T> {
/**
* 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 <T> ExcelSheetExport convertToExcel(ExcelExportFunction<T> 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 ? "<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 <T> ExcelSheetExport getExport(
DataFetcher<DataSource, T> dataFetcher, ExcelExportFunction<T> 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 <T, C extends ExcelCellModel> ExcelSheetExport getTableExport(List<ColumnModel<T, C>> columnsModel,
String sheetName, List<T> data) {
return convertToExcel((dataList) -> new ExcelTableExport<T, C>(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 <T, C extends ExcelCellModel> ExcelSheetExport getTableExport(
DataFetcher<DataSource, List<T>> dataFetcher, List<ColumnModel<T, C>> columnsModel,
String sheetName, DataSource ds) {
return getExport(dataFetcher,
(dataList) -> new ExcelTableExport<T, C>(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

View File

@ -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

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019 Basis Technology Corp.
* Copyright 2019-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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<String> 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<String> 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<String> 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.Events> 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<DataSource, ?>> dataFetchComponents;
private final DataFetcher<DataSource, ContainerViewModel> containerDataFetcher;
private final DataFetcher<DataSource, ContainerDetails> 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<O> {
/**
* 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> O retrieve(Retriever<O> 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<String> 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<? extends ExcelItemExportable> 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<ExcelSheetExport> 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<String> paths = result.getImageViewModel() == null ? Collections.singletonList(NA) : result.getImageViewModel().getPaths();
List<SingleCellExportable> 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

View File

@ -0,0 +1,140 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.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<Integer> 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<Integer> 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);
}
}

View File

@ -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;

View File

@ -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<DataSource> onDataSource;
private final Function<DataSource, List<ExcelSheetExport>> 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<DataSource> onDataSource,
Function<DataSource, List<ExcelSheetExport>> 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<ExcelSheetExport> 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<DataSourceTab> 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()));
}
/**

View File

@ -1,301 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.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<DataSource> {
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<ExcelSheetExport> getExcelExports(DataSource dataSource);
}
private final ExcelExport excelExport = ExcelExport.getInstance();
private final List<? extends ExportableTab> tabExports;
/**
* Main constructor.
*
* @param tabExports The different tabs that may have excel exports.
*/
ExcelExportAction(List<? extends ExportableTab> 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<Boolean, Void> 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<Boolean, Void> getWorker() {
return worker;
}
/**
* Sets the swing worker that could be cancelled.
*
* @param worker The swing worker that could be cancelled.
*/
void setWorker(SwingWorker<Boolean, Void> 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<Boolean, Void> worker = new SwingWorker<Boolean, Void>() {
@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<ExcelExport.ExcelSheetExport> 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<ExcelExport.ExcelSheetExport> 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);
}
}
}

View File

@ -1,114 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JDialogFormInfo">
<Properties>
<Property name="defaultCloseOperation" type="int" value="2"/>
</Properties>
<SyntheticProperties>
<SyntheticProperty name="formSizePolicy" type="int" value="1"/>
<SyntheticProperty name="generateCenter" type="boolean" value="false"/>
</SyntheticProperties>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="1" attributes="0">
<Component id="linkTextScrollPane" max="32767" attributes="0"/>
<Group type="102" alignment="1" attributes="0">
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
<Component id="okButton" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<Component id="titleLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="116" max="32767" attributes="0"/>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="titleLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="linkTextScrollPane" min="-2" pref="39" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="okButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JLabel" name="titleLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="ExcelExportDialog.titleLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
</Component>
<Component class="javax.swing.JButton" name="okButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="ExcelExportDialog.okButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="okButtonActionPerformed"/>
</Events>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
</Component>
<Container class="javax.swing.JScrollPane" name="linkTextScrollPane">
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JTextArea" name="linkText">
<Properties>
<Property name="editable" type="boolean" value="false"/>
<Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color type="null"/>
</Property>
<Property name="columns" type="int" value="20"/>
<Property name="foreground" type="java.awt.Color" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="java.awt.Color.BLUE" type="code"/>
</Property>
<Property name="lineWrap" type="boolean" value="true"/>
<Property name="rows" type="int" value="1"/>
<Property name="wrapStyleWord" type="boolean" value="true"/>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="null"/>
</Property>
<Property name="opaque" type="boolean" value="false"/>
</Properties>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Form>

View File

@ -1,143 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.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")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//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();
}// </editor-fold>//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
}

View File

@ -1,68 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="xlsxExportMessage" min="-2" max="-2" attributes="0"/>
<Component id="xlsxExportButton" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace pref="62" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="xlsxExportMessage" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="xlsxExportButton" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="250" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JButton" name="xlsxExportButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="ExportPanel.xlsxExportButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="xlsxExportButtonActionPerformed"/>
</Events>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
</Component>
<Component class="javax.swing.JLabel" name="xlsxExportMessage">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="ExportPanel.xlsxExportMessage.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
</Component>
</SubComponents>
</Form>

View File

@ -1,105 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.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")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//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))
);
}// </editor-fold>//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
}

View File

@ -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;
@ -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<DataSource, GeolocationViewModel> 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;
@ -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<String, Integer> formatRecord(CityRecordCount cityCount) {
@ -240,6 +240,7 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel {
*
* @param countsList The CityCountsList object representing the data to be
* displayed in the table.
*
* @return The list of city/count tuples to be displayed as a row.
*/
private List<Pair<String, Integer>> 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) {
@ -297,8 +299,8 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel {
*
* @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).
* 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<ExcelExport.ExcelSheetExport> 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();

View File

@ -0,0 +1,78 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.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<ARTIFACT_TYPE> getGeoTypes() {
return GeolocationSummary.getGeoTypes();
}
@Override
public Set<Integer> 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);
}
}

View File

@ -0,0 +1,150 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.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<IngestManager.IngestJobEvent> 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<IngestManager.IngestJobEvent> 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<String> 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<String> 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);
}
}

View File

@ -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;
@ -86,13 +81,13 @@ public class PastCasesPanel extends BaseDataSourceSummaryPanel {
private final DataFetcher<DataSource, PastCasesResult> 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);
@ -128,19 +123,6 @@ public class PastCasesPanel extends BaseDataSourceSummaryPanel {
onNewDataSource(dataFetchComponents, tables, dataSource);
}
@Override
List<ExcelExport.ExcelSheetExport> 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();

View File

@ -0,0 +1,71 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.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<Integer> 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<Integer> 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);
}
}

View File

@ -0,0 +1,116 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.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<Integer> 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<Integer> 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<RecentFileDetails> 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<RecentDownloadDetails> 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<RecentAttachmentDetails> getRecentAttachments(DataSource dataSource, int maxCount) throws SleuthkitCaseProviderException, TskCoreException {
return recentSummary.getRecentAttachments(dataSource, maxCount);
}
}

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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<DataSource, List<RecentAttachmentDetails>> attachmentsFetcher;
private final List<ColumnModel<RecentFileDetails, DefaultCellModel<?>>> 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<ColumnModel<RecentDownloadDetails, DefaultCellModel<?>>> 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<ColumnModel<RecentAttachmentDetails, DefaultCellModel<?>>> 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 <T extends RecentFileDetails> Function<T, DefaultCellModel<?>> getDateFunct() {
return (T lastAccessed) -> {
Function<Date, String> 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<List<MenuItem>> getPopupFunct(RecentFileDetails record) {
return () -> {
@ -190,16 +186,6 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
onNewDataSource(dataFetchComponents, tablePanelList, dataSource);
}
@Override
List<ExcelExport.ExcelSheetExport> 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.

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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<Long>(bytes, SizeRepresentationUtil::getSizeString, unit.getExcelFormatString());
return new DefaultCellModel<>(bytes, SizeRepresentationUtil::getSizeString);
}
}

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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<DataSource, ?>> 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.
*
* @return The data to be displayed in the BarChart.
*/
private List<BarChartSeries> parseChartData(List<DailyActivityAmount> 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<TimelineSummaryData> 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
@ -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<ExcelExport.ExcelSheetExport> 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

View File

@ -0,0 +1,88 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.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<IngestManager.IngestJobEvent> 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<IngestManager.IngestJobEvent> 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);
}
}

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019 Basis Technology Corp.
* Copyright 2019-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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<PieChartItem> pieSlices, boolean usefulContent) {
TypesPieChartData(List<PieChartItem> pieSlices, boolean usefulContent) {
this.pieSlices = pieSlices;
this.usefulContent = usefulContent;
}
@ -103,78 +95,20 @@ class TypesPanel extends BaseDataSourceSummaryPanel {
/**
* @return The pie chart data.
*/
public List<PieChartItem> getPieSlices() {
List<PieChartItem> 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<String> 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<String> 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<String> 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<TypesPieCategory> 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<FileTypeCategoryData> 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<DataSource, String> 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<PieChartItem> 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<DataSource, String> 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<DataSource, Long> fetcher, String key, DataSource dataSource) {
Long count = getFetchResult(fetcher, "Types", dataSource);
return (count == null) ? null : new KeyValueItemExportable(key,
new DefaultCellModel<Long>(count, COMMA_FORMATTER::format, COMMA_FORMAT_STR));
}
@Override
List<ExcelExport.ExcelSheetExport> 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

View File

@ -0,0 +1,154 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019 - 2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.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<IngestManager.IngestJobEvent> 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<IngestManager.IngestJobEvent> 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);
}
}

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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,13 +261,13 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel {
private final IngestRunningLabel ingestRunningLabel = new IngestRunningLabel();
private final List<DataFetchComponents<DataSource, ?>> dataFetchComponents;
private final UserActivitySummary userActivityData;
private final UserActivitySummaryGetter userActivityData;
/**
* Creates a new UserActivityPanel.
*/
public UserActivityPanel() {
this(new UserActivitySummary());
this(new UserActivitySummaryGetter());
}
/**
@ -280,7 +276,7 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel {
* @param userActivityData Class from which to obtain remaining user
* 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 <T extends LastAccessedArtifact> Function<T, DefaultCellModel<?>> getDateFunct() {
return (T lastAccessed) -> {
Function<Date, String> 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<MenuItem> getPopup(LastAccessedArtifact record) {
return record == null ? null : Arrays.asList(getArtifactNavigateItem(record.getArtifact()));
@ -346,8 +343,8 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel {
*
* @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<ExcelExport.ExcelSheetExport> 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

View File

@ -0,0 +1,162 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.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<Integer> 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<Integer> 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<TopDomainsResult> 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<TopWebSearchResult> 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<TopDeviceAttachedResult> 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<TopAccountResult> 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<TopProgramsResult> getTopPrograms(DataSource dataSource, int count) throws SleuthkitCaseProviderException, TskCoreException {
return userActivity.getTopPrograms(dataSource, count);
}
}

View File

@ -39,84 +39,6 @@ import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartSeries.BarChartIt
*/
public class BarChartPanel extends AbstractLoadableComponent<List<BarChartSeries>> {
/**
* 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<OrderedKey> {
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();

View File

@ -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<OrderedKey> {
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();
}
}
}

View File

@ -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

View File

@ -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;

View File

@ -27,7 +27,7 @@ import java.util.function.Supplier;
/**
* The default cell model.
*/
public class DefaultCellModel<T> implements GuiCellModel, ExcelCellModel {
public class DefaultCellModel<T> implements GuiCellModel {
private final T data;
private final String text;
@ -35,7 +35,6 @@ public class DefaultCellModel<T> implements GuiCellModel, ExcelCellModel {
private CellModel.HorizontalAlign horizontalAlignment;
private List<MenuItem> popupMenu;
private Supplier<List<MenuItem>> menuItemSupplier;
private final String excelFormatString;
/**
* Main constructor.
@ -43,7 +42,7 @@ public class DefaultCellModel<T> implements GuiCellModel, ExcelCellModel {
* @param data The data to be displayed in the cell.
*/
public DefaultCellModel(T data) {
this(data, null, null);
this(data, null);
}
/**
@ -54,24 +53,7 @@ public class DefaultCellModel<T> implements GuiCellModel, ExcelCellModel {
* null to use .toString method on object.
*/
public DefaultCellModel(T data, Function<T, String> 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.
*/
public DefaultCellModel(T data, Function<T, String> stringConverter, String excelFormatString) {
this.data = data;
this.excelFormatString = excelFormatString;
if (stringConverter == null) {
text = this.data == null ? "" : this.data.toString();
@ -86,11 +68,6 @@ public class DefaultCellModel<T> implements GuiCellModel, ExcelCellModel {
return this.data;
}
@Override
public String getExcelFormatString() {
return this.excelFormatString;
}
@Override
public String getText() {
return text;

View File

@ -1,32 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.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();
}

View File

@ -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<Pair<Object, List<Double>>, ? extends ExcelCellModel> getTableModel(
private static ExcelTableExport<Pair<Object, List<Double>>, ? extends CellModel> getTableModel(
List<BarChartSeries> 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<Pair<Object, List<Double>>, ? extends ExcelCellModel> tableExport;
private final ExcelTableExport<Pair<Object, List<Double>>, ? 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<BarChartSeries> 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<BarChartSeries> categories,
int colOffset, int rowPadding, int colSize, int rowSize) {

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,91 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.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();
}

View File

@ -0,0 +1,80 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.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<T, C extends CellModel> {
private final String headerTitle;
private final Function<T, ? extends C> 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<T, ? extends C> 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<T, ? extends C> 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<T, ? extends C> getCellRenderer() {
return cellRenderer;
}
/**
* @return The preferred width of the column (can be null).
*/
Integer getWidth() {
return width;
}
}

View File

@ -0,0 +1,162 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.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<Content> selectedDataSources = new ArrayList<>();
if(settings.getSelectedDataSources() == null) {
// Process all data sources if the list is null.
try {
selectedDataSources = currentCase.getDataSources();
List<Long> 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);
}
}

View File

@ -0,0 +1,111 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.report.modules.datasourcesummaryexport;
import java.util.function.Function;
/**
* The default cell model.
*/
class DefaultCellModel<T> 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<T, String> 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<T, String> 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<T> setHorizontalAlignment(CellModel.HorizontalAlign alignment) {
this.horizontalAlignment = alignment;
return this;
}
@Override
public String toString() {
return getText();
}
}

View File

@ -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<ExcelSheetExport> exports, File path) throws IOException, ExcelExportException {
static void writeExcel(List<ExcelSheetExport> 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> cellStyle) {
static Cell createCell(WorksheetEnv env, Row row, int colNum, CellModel cellModel, Optional<CellStyle> cellStyle) {
CellStyle cellStyleToUse = cellStyle.orElse(env.getDefaultCellStyle());
if (cellModel.getExcelFormatString() != null || cellModel.getHorizontalAlignment() != null) {

View File

@ -0,0 +1,332 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.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<ExcelExport.ExcelSheetExport> sheetExports = new ArrayList<>();
// Export file and MIME type data
progressPanel.updateStatusLabel(Bundle.ExcelExportAction_exportToXLSX_gatheringFileData());
progressPanel.setProgress(step);
List<ExcelExport.ExcelSheetExport> 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<T> {
/**
* 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> T getFetchResult(
DataFetcher<DataSource, T> 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 ? "<null>" : sheetName,
ds == null || ds.getName() == null ? "<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 <T> ExcelSheetExport convertToExcel(ExcelExportFunction<T> 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 ? "<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 <T> ExcelSheetExport getExport(
DataFetcher<DataSource, T> dataFetcher, ExcelExportFunction<T> 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 <T, C extends CellModel> ExcelSheetExport getTableExport(List<ColumnModel<T, C>> columnsModel,
String sheetName, List<T> 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 <T, C extends CellModel> ExcelSheetExport getTableExport(
DataFetcher<DataSource, List<T>> dataFetcher, List<ColumnModel<T, C>> columnsModel,
String sheetName, DataSource ds) {
return getExport(dataFetcher,
(dataList) -> new ExcelTableExport<>(sheetName, columnsModel, dataList),
sheetName,
ds);
}
}

View File

@ -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
* </pre>
*/
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<? extends ExcelItemExportable> children) {
TitledExportable(String title, List<? extends ExcelItemExportable> 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<ExcelItemExportable> exports) {
ExcelSpecialFormatExport(String sheetName, List<ExcelItemExportable> exports) {
this.sheetName = sheetName;
this.exports = exports == null ? Collections.emptyList() : exports;
}

View File

@ -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<T, C extends ExcelCellModel> implements ExcelSheetExport, ExcelItemExportable {
class ExcelTableExport<T, C extends CellModel> implements ExcelSheetExport, ExcelItemExportable {
private final String sheetName;
private final List<ColumnModel<T, C>> columns;
@ -47,7 +47,7 @@ public class ExcelTableExport<T, C extends ExcelCellModel> implements ExcelSheet
* @param columns The columns of the table.
* @param data The data to export.
*/
public ExcelTableExport(String sheetName, List<ColumnModel<T, C>> columns, List<T> data) {
ExcelTableExport(String sheetName, List<ColumnModel<T, C>> columns, List<T> data) {
this(sheetName, columns, data, 0);
}
@ -60,7 +60,7 @@ public class ExcelTableExport<T, C extends ExcelCellModel> implements ExcelSheet
* @param data The data to export.
* @param columnIndent The column indent.
*/
public ExcelTableExport(String sheetName, List<ColumnModel<T, C>> columns, List<T> data, int columnIndent) {
ExcelTableExport(String sheetName, List<ColumnModel<T, C>> columns, List<T> data, int columnIndent) {
this.sheetName = sheetName;
this.columns = columns;
this.data = data;
@ -104,7 +104,7 @@ public class ExcelTableExport<T, C extends ExcelCellModel> implements ExcelSheet
* @throws ExcelExportException
* @return The number of rows (including the header) written.
*/
private static <T, C extends ExcelCellModel> int renderSheet(
private static <T, C extends CellModel> int renderSheet(
Sheet sheet,
ExcelExport.WorksheetEnv worksheetEnv,
int rowStart,
@ -127,8 +127,8 @@ public class ExcelTableExport<T, C extends ExcelCellModel> implements ExcelSheet
T rowData = safeData.get(rowNum);
Row row = sheet.createRow(rowNum + rowStart + 1);
for (int colNum = 0; colNum < columns.size(); colNum++) {
ColumnModel<T, ? extends ExcelCellModel> colModel = columns.get(colNum);
ExcelCellModel cellModel = colModel.getCellRenderer().apply(rowData);
ColumnModel<T, ? extends CellModel> colModel = columns.get(colNum);
CellModel cellModel = colModel.getCellRenderer().apply(rowData);
ExcelExport.createCell(worksheetEnv, row, colNum + colStart, cellModel, Optional.empty());
}
}

View File

@ -0,0 +1,79 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.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<ColumnModel<Pair<String, Long>, 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<ExcelSheetExport> getExports(DataSource dataSource) {
DataFetcher<DataSource, List<Pair<String, Long>>> hashsetsFetcher = (ds) -> analysisSummary.getHashsetCounts(ds);
DataFetcher<DataSource, List<Pair<String, Long>>> keywordsFetcher = (ds) -> analysisSummary.getKeywordCounts(ds);
DataFetcher<DataSource, List<Pair<String, Long>>> 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());
}
}

View File

@ -0,0 +1,130 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.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<? extends ExcelItemExportable> 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<ExcelSheetExport> getExports(DataSource ds) {
DataFetcher<DataSource, ContainerDetails> 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<String> paths = containerDetails.getImageDetails() == null ? Collections.singletonList(NA) : containerDetails.getImageDetails().getPaths();
List<SingleCellExportable> 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)
)));
}
}

View File

@ -0,0 +1,227 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.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<Pair<String, Integer>> mostRecentData;
private final List<Pair<String, Integer>> 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<Pair<String, Integer>> mostRecentData, List<Pair<String, Integer>> 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<Pair<String, Integer>> 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<Pair<String, Integer>> 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<Pair<String, Integer>, 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<Pair<String, Integer>, DefaultCellModel<?>> COUNT_COL = new ColumnModel<>(
Bundle.ExportGeolocation_countColumn_title(),
(pair) -> new DefaultCellModel<>(pair.getRight()),
100
);
private static final List<ColumnModel<Pair<String, Integer>, 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<String> 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<String, Integer> 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<Pair<String, Integer>> formatList(CityCountsList countsList) {
if (countsList == null) {
return Collections.emptyList();
}
Stream<CityRecordCount> countsStream = ((countsList.getCounts() == null)
? new ArrayList<CityRecordCount>()
: countsList.getCounts()).stream();
Stream<Pair<String, Integer>> pairStream = countsStream.map((r) -> formatRecord(r));
Pair<String, Integer> 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<ExcelExport.ExcelSheetExport> getExports(DataSource dataSource) {
DataFetcher<DataSource, GeolocationData> 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())
);
}
}

View File

@ -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.
@ -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<ColumnModel<IngestJobEntry, DefaultCellModel<?>>> 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<IngestJobEntry> 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<IngestJobEntry> showFirstRowOnly(List<IngestJobEntry> 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<ExcelSheetExport> 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() {
}
}

View File

@ -0,0 +1,80 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.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<Pair<String, Long>, 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<Pair<String, Long>, 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<ColumnModel<Pair<String, Long>, DefaultCellModel<?>>> DEFAULT_TEMPLATE
= Arrays.asList(CASE_COL, COUNT_COL);
ExportPastCases() {
pastSummary = new PastCasesSummary();
}
List<ExcelExport.ExcelSheetExport> getExports(DataSource dataSource) {
DataFetcher<DataSource, PastCasesResult> 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())
);
}
}

View File

@ -0,0 +1,123 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.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<ColumnModel<RecentFileDetails, DefaultCellModel<?>>> 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<ColumnModel<RecentDownloadDetails, DefaultCellModel<?>>> 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<ColumnModel<RecentAttachmentDetails, DefaultCellModel<?>>> 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 <T extends RecentFileDetails> Function<T, DefaultCellModel<?>> getDateFunct() {
return (T lastAccessed) -> {
Function<Date, String> dateParser = (dt) -> dt == null ? "" : DATETIME_FORMAT.format(dt);
return new DefaultCellModel<>(new Date(lastAccessed.getDateAsLong() * 1000), dateParser, DATETIME_FORMAT_STR);
};
}
List<ExcelExport.ExcelSheetExport> getExports(DataSource dataSource) {
DataFetcher<DataSource, List<RecentFileDetails>> docsFetcher = (ds) -> recentSummary.getRecentlyOpenedDocuments(ds, 10);
DataFetcher<DataSource, List<RecentDownloadDetails>> downloadsFetcher = (ds) -> recentSummary.getRecentDownloads(ds, 10);
DataFetcher<DataSource, List<RecentAttachmentDetails>> 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());
}
}

View File

@ -0,0 +1,144 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.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<BarChartSeries> parseChartData(List<DailyActivityAmount> 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<BarChartItem> fileEvtCounts = new ArrayList<>();
List<BarChartItem> 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<ExcelExport.ExcelSheetExport> getExports(DataSource dataSource) {
DataFetcher<DataSource, TimelineSummaryData> 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)))));
}
}

View File

@ -0,0 +1,265 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.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<PieChartItem> 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<PieChartItem> pieSlices, boolean usefulContent) {
this.pieSlices = pieSlices;
this.usefulContent = usefulContent;
}
/**
* @return The pie chart data.
*/
List<PieChartItem> 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<FileTypeCategoryData> 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<PieChartItem> 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<PieChartItem> 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<DataSource, String> 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<DataSource, Long> fetcher, String key, DataSource dataSource) {
Long count = ExcelExportAction.getFetchResult(fetcher, "Types", dataSource);
return (count == null) ? null : new KeyValueItemExportable(key,
new DefaultCellModel<Long>(count, DataSourceInfoUtilities.COMMA_FORMATTER::format, DataSourceInfoUtilities.COMMA_FORMAT_STR));
}
List<ExcelExport.ExcelSheetExport> getExports(DataSource dataSource) {
if (dataSource == null) {
return Collections.emptyList();
}
DataFetcher<DataSource, String> usageFetcher = (ds) -> containerSummary.getDataSourceType(ds);
DataFetcher<DataSource, String> osFetcher = (ds) -> containerSummary.getOperatingSystems(ds);
DataFetcher<DataSource, Long> sizeFetcher = (ds) -> ds == null ? null : ds.getSize();
DataFetcher<DataSource, TypesPieChartData> typesFetcher = (ds) -> getMimeTypeCategoriesModel(ds);
DataFetcher<DataSource, Long> allocatedFetcher = (ds) -> typesSummary.getCountOfAllocatedFiles(ds);
DataFetcher<DataSource, Long> unallocatedFetcher = (ds) -> typesSummary.getCountOfUnallocatedFiles(ds);
DataFetcher<DataSource, Long> slackFetcher = (ds) -> typesSummary.getCountOfSlackFiles(ds);
DataFetcher<DataSource, Long> 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())
));
}
}

View File

@ -0,0 +1,240 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.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<ColumnModel<TopProgramsResult, DefaultCellModel<?>>> 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<ColumnModel<TopDomainsResult, DefaultCellModel<?>>> 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<ColumnModel<TopWebSearchResult, DefaultCellModel<?>>> 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<ColumnModel<TopDeviceAttachedResult, DefaultCellModel<?>>> 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<ColumnModel<TopAccountResult, DefaultCellModel<?>>> 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 <T extends LastAccessedArtifact> Function<T, DefaultCellModel<?>> getDateFunct() {
return (T lastAccessed) -> {
Function<Date, String> dateParser = (dt) -> dt == null ? "" : DATETIME_FORMAT.format(dt);
return new DefaultCellModel<>(lastAccessed.getLastAccessed(), dateParser, DATETIME_FORMAT_STR);
};
}
List<ExcelExport.ExcelSheetExport> getExports(DataSource dataSource) {
DataFetcher<DataSource, List<TopProgramsResult>> topProgramsFetcher = (ds) -> userSummary.getTopPrograms(ds, TOP_PROGS_COUNT);
DataFetcher<DataSource, List<TopDomainsResult>> topDomainsFetcher = (ds) -> userSummary.getRecentDomains(ds, TOP_DOMAINS_COUNT);
DataFetcher<DataSource, List<TopWebSearchResult>> topWebSearchesFetcher = (ds) -> userSummary.getMostRecentWebSearches(ds, TOP_SEARCHES_COUNT);
DataFetcher<DataSource, List<TopDeviceAttachedResult>> topDevicesAttachedFetcher = (ds) -> userSummary.getRecentDevices(ds, TOP_DEVICES_COUNT);
DataFetcher<DataSource, List<TopAccountResult>> 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());
}
}

View File

@ -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<PieChartItem, ? extends ExcelCellModel> tableExport;
private final ExcelTableExport<PieChartItem, ? extends CellModel> 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<PieChartItem> 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<PieChartItem> slices,

View File

@ -0,0 +1,178 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.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() {
}
}