mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-11 23:46:15 +00:00
Merge pull request #7199 from eugene7646/ds_summary_report_7893
Data source summary report module (7893)
This commit is contained in:
commit
578a3d24bb
24
Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/AnalysisSummary.java
Normal file → Executable file
24
Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/AnalysisSummary.java
Normal file → Executable 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.
|
||||
*
|
||||
|
0
Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/Bundle.properties-MERGED
Normal file → Executable file
0
Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/Bundle.properties-MERGED
Normal file → Executable file
0
Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/Bundle_ja.properties
Normal file → Executable file
0
Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/Bundle_ja.properties
Normal file → Executable 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");
|
||||
|
@ -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 {
|
||||
@ -95,8 +96,9 @@ class ClosestCityMapper {
|
||||
* Main Constructor loading from an input stream.
|
||||
*
|
||||
* @param citiesInputStream The input stream for the csv text file
|
||||
* containing the cities.
|
||||
* @param logger The logger to be used with this.
|
||||
* containing the cities.
|
||||
* @param logger The logger to be used with this.
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
private ClosestCityMapper(InputStream citiesInputStream, java.util.logging.Logger logger) throws IOException {
|
||||
@ -109,6 +111,7 @@ class ClosestCityMapper {
|
||||
* city can be determined.
|
||||
*
|
||||
* @param point The point to locate.
|
||||
*
|
||||
* @return The closest city or null if no close city can be found.
|
||||
*/
|
||||
CityRecord findClosest(CityRecord point) {
|
||||
@ -120,6 +123,7 @@ class ClosestCityMapper {
|
||||
* returned.
|
||||
*
|
||||
* @param s The string to parse.
|
||||
*
|
||||
* @return The double value or null if value cannot be parsed.
|
||||
*/
|
||||
private Double tryParse(String s) {
|
||||
@ -138,8 +142,9 @@ class ClosestCityMapper {
|
||||
* Parses a country name and transforms values like "last, first" to "first
|
||||
* last" (i.e. "Korea, South" becomes "South Korea").
|
||||
*
|
||||
* @param orig The original string value.
|
||||
* @param orig The original string value.
|
||||
* @param lineNum The line number that this country was found.
|
||||
*
|
||||
* @return The country name.
|
||||
*/
|
||||
private String parseCountryName(String orig, int lineNum) {
|
||||
@ -159,9 +164,10 @@ class ClosestCityMapper {
|
||||
/**
|
||||
* Parses a row from the csv creating a city record.
|
||||
*
|
||||
* @param csvRow The row of data where each item in the list is each column
|
||||
* in the row.
|
||||
* @param csvRow The row of data where each item in the list is each column
|
||||
* in the row.
|
||||
* @param lineNum The line number for this csv row.
|
||||
*
|
||||
* @return The parsed CityRecord or null if none can be determined.
|
||||
*/
|
||||
private CityRecord getCsvCityRecord(List<String> csvRow, int lineNum) {
|
||||
@ -199,8 +205,9 @@ class ClosestCityMapper {
|
||||
/**
|
||||
* Parses a row of the csv into individual column values.
|
||||
*
|
||||
* @param line The line to parse.
|
||||
* @param line The line to parse.
|
||||
* @param lineNum The line number in the csv where this line is.
|
||||
*
|
||||
* @return The list of column values.
|
||||
*/
|
||||
private List<String> parseCsvLine(String line, int lineNum) {
|
||||
@ -222,10 +229,12 @@ class ClosestCityMapper {
|
||||
* Parses all lines in the csv file input stream into a list of city
|
||||
* records.
|
||||
*
|
||||
* @param csvInputStream The csv file input stream.
|
||||
* @param csvInputStream The csv file input stream.
|
||||
* @param ignoreHeaderRow Whether or not there is a header row in the csv
|
||||
* file.
|
||||
* file.
|
||||
*
|
||||
* @return The list of city records.
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
private List<CityRecord> parseCsvLines(InputStream csvInputStream, boolean ignoreHeaderRow) throws IOException {
|
||||
|
259
Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/ContainerSummary.java
Normal file → Executable file
259
Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/ContainerSummary.java
Normal file → Executable 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;
|
||||
|
||||
@ -59,22 +55,7 @@ public class ContainerSummary implements DefaultArtifactUpdateGovernor {
|
||||
public ContainerSummary(SleuthkitCaseProvider provider) {
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRefreshRequired(ModuleContentEvent evt) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRefreshRequired(AbstractFile file) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
@ -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,11 +398,11 @@ final class DataSourceInfoUtilities {
|
||||
* @return The 'getValueLong()' value or null if the attribute could not be
|
||||
* retrieved.
|
||||
*/
|
||||
static Long getLongOrNull(BlackboardArtifact artifact, Type attributeType) {
|
||||
public static Long getLongOrNull(BlackboardArtifact artifact, Type attributeType) {
|
||||
BlackboardAttribute attr = getAttributeOrNull(artifact, attributeType);
|
||||
return (attr == null) ? null : attr.getValueLong();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves the int value of a certain attribute type from an artifact.
|
||||
*
|
||||
@ -408,7 +412,7 @@ final class DataSourceInfoUtilities {
|
||||
* @return The 'getValueInt()' value or null if the attribute could not be
|
||||
* retrieved.
|
||||
*/
|
||||
static Integer getIntOrNull(BlackboardArtifact artifact, Type attributeType) {
|
||||
public static Integer getIntOrNull(BlackboardArtifact artifact, Type attributeType) {
|
||||
BlackboardAttribute attr = getAttributeOrNull(artifact, attributeType);
|
||||
return (attr == null) ? null : attr.getValueInt();
|
||||
}
|
||||
@ -423,8 +427,31 @@ final class DataSourceInfoUtilities {
|
||||
* @return The date determined from the 'getValueLong()' as seconds from
|
||||
* epoch or null if the attribute could not be retrieved or is 0.
|
||||
*/
|
||||
static Date getDateOrNull(BlackboardArtifact artifact, Type attributeType) {
|
||||
public static Date getDateOrNull(BlackboardArtifact artifact, Type attributeType) {
|
||||
Long longVal = getLongOrNull(artifact, attributeType);
|
||||
return (longVal == null || longVal == 0) ? null : new Date(longVal * 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the long value or zero if longVal is null.
|
||||
*
|
||||
* @param longVal The long value.
|
||||
*
|
||||
* @return The long value or 0 if provided value is null.
|
||||
*/
|
||||
public static long getLongOrZero(Long longVal) {
|
||||
return longVal == null ? 0 : longVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns string value of long with comma separators. If null returns a
|
||||
* string of '0'.
|
||||
*
|
||||
* @param longVal The long value.
|
||||
*
|
||||
* @return The string value of the long.
|
||||
*/
|
||||
public static String getStringOrZero(Long longVal) {
|
||||
return longVal == null ? "0" : COMMA_FORMATTER.format(longVal);
|
||||
}
|
||||
}
|
||||
|
98
Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/GeolocationSummary.java
Normal file → Executable file
98
Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/GeolocationSummary.java
Normal file → Executable 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.
|
||||
@ -59,8 +58,8 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
|
||||
* Main constructor.
|
||||
*
|
||||
* @param cityRecord The record for the city including name, country,
|
||||
* and location.
|
||||
* @param count The number of hits in proximity to that city.
|
||||
* and location.
|
||||
* @param count The number of hits in proximity to that city.
|
||||
*/
|
||||
CityRecordCount(CityRecord cityRecord, int count) {
|
||||
this.cityRecord = cityRecord;
|
||||
@ -69,7 +68,7 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
|
||||
|
||||
/**
|
||||
* @return The record for the city including name, country, and
|
||||
* location.
|
||||
* location.
|
||||
*/
|
||||
public CityRecord getCityRecord() {
|
||||
return cityRecord;
|
||||
@ -96,8 +95,8 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param mostCommon The list of most common cities seen.
|
||||
* @param mostRecent The list of most recent cities seen.
|
||||
* @param mostCommon The list of most common cities seen.
|
||||
* @param mostRecent The list of most recent cities seen.
|
||||
* @param mostRecentSeen
|
||||
*/
|
||||
CityData(CityCountsList mostCommon, CityCountsList mostRecent, Long mostRecentSeen) {
|
||||
@ -122,7 +121,7 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
|
||||
|
||||
/**
|
||||
* @return The time stamp in seconds from epoch of the most recently
|
||||
* seen city
|
||||
* seen city
|
||||
*/
|
||||
public Long getMostRecentSeen() {
|
||||
return mostRecentSeen;
|
||||
@ -142,10 +141,10 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param counts The list of cities and the count of how many points are
|
||||
* closest to that city.
|
||||
* @param counts The list of cities and the count of how many points
|
||||
* are closest to that city.
|
||||
* @param otherCount The count of points where no closest city was
|
||||
* determined due to not being close enough.
|
||||
* determined due to not being close enough.
|
||||
*/
|
||||
CityCountsList(List<CityRecordCount> counts, int otherCount) {
|
||||
this.counts = Collections.unmodifiableList(new ArrayList<>(counts));
|
||||
@ -154,7 +153,7 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
|
||||
|
||||
/**
|
||||
* @return The list of cities and the count of how many points are
|
||||
* closest to that city.
|
||||
* closest to that city.
|
||||
*/
|
||||
public List<CityRecordCount> getCounts() {
|
||||
return counts;
|
||||
@ -162,7 +161,7 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
|
||||
|
||||
/**
|
||||
* @return The count of points where no closest city was determined due
|
||||
* to not being close enough.
|
||||
* to not being close enough.
|
||||
*/
|
||||
public int getOtherCount() {
|
||||
return otherCount;
|
||||
@ -183,10 +182,10 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
|
||||
* Main constructor.
|
||||
*
|
||||
* @param mapWaypoints The way points found for the data source.
|
||||
* @param tracks A list of sets where each set is a track in the data
|
||||
* source.
|
||||
* @param areas A list of areas where each set is an area in the data
|
||||
* source.
|
||||
* @param tracks A list of sets where each set is a track in the
|
||||
* data source.
|
||||
* @param areas A list of areas where each set is an area in the
|
||||
* data source.
|
||||
*/
|
||||
private GeoResult(Set<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);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -291,13 +290,14 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
|
||||
* the event where either time is null.
|
||||
*
|
||||
* @param minTime The minimum time. If null is provided, this function will
|
||||
* return false.
|
||||
* @param time The time to check. If null is provided and the min time is
|
||||
* non-null, then this function will return false.
|
||||
* return false.
|
||||
* @param time The time to check. If null is provided and the min time is
|
||||
* non-null, then this function will return false.
|
||||
*
|
||||
* @return If minTime == null then false. If minTime != null && time == null
|
||||
* then false. Otherwise time >= minTime.
|
||||
* then false. Otherwise time >= minTime.
|
||||
*/
|
||||
private boolean greaterThanOrEqual(Long minTime, Long time) {
|
||||
private static boolean greaterThanOrEqual(Long minTime, Long time) {
|
||||
if (minTime != null && time != null && time >= minTime) {
|
||||
return true;
|
||||
} else {
|
||||
@ -310,12 +310,13 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
|
||||
* a total of waypoints whose time stamp is greater than or equal to
|
||||
* minTime.
|
||||
*
|
||||
* @param points The list of way points.
|
||||
* @param points The list of way points.
|
||||
* @param minTime The minimum time for most recent points count.
|
||||
*
|
||||
* @return A pair where the left value is the total count of way points and
|
||||
* the right is the total list of way points that are >= minTime.
|
||||
* the right is the total list of way points that are >= minTime.
|
||||
*/
|
||||
private Pair<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;
|
||||
}
|
||||
@ -332,7 +333,8 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
|
||||
* the point is null, null is returned.
|
||||
*
|
||||
* @param cityMapper The means of mapping a point to the closest city.
|
||||
* @param pt The geolocation point.
|
||||
* @param pt The geolocation point.
|
||||
*
|
||||
* @return A tuple of the closest city and timestamp in seconds from epoch.
|
||||
*/
|
||||
private Pair<CityRecord, Long> getClosestWithTime(ClosestCityMapper cityMapper, MapWaypoint pt) {
|
||||
@ -351,10 +353,12 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
|
||||
* into a stream of the unique cities identified in this grouping and the
|
||||
* latest time stamp for each grouping.
|
||||
*
|
||||
* @param points The points in the grouping.
|
||||
* @param points The points in the grouping.
|
||||
* @param cityMapper The means of mapping a point to the closest city.
|
||||
*
|
||||
* @return A stream of tuples where each tuple will be a unique city (or
|
||||
* null if a closest is not determined) and the latest timestamp for each.
|
||||
* null if a closest is not determined) and the latest timestamp for
|
||||
* each.
|
||||
*/
|
||||
private Stream<Pair<CityRecord, Long>> reduceGrouping(Set<MapWaypoint> points, ClosestCityMapper cityMapper) {
|
||||
if (points == null) {
|
||||
@ -367,7 +371,7 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
|
||||
if (pair == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
CityRecord city = pair.getLeft();
|
||||
Long prevTime = timeMapping.get(city);
|
||||
Long curTime = pair.getRight();
|
||||
@ -375,7 +379,7 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
|
||||
timeMapping.put(city, curTime);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return timeMapping.entrySet().stream()
|
||||
.map(e -> Pair.of(e.getKey(), e.getValue()));
|
||||
}
|
||||
@ -385,10 +389,12 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
|
||||
* of tuples where each tuple represents a point with the closest city and
|
||||
* the time stamp in seconds from epoch.
|
||||
*
|
||||
* @param geoResult The result from the Geolocation API.
|
||||
* @param geoResult The result from the Geolocation API.
|
||||
* @param cityMapper The means of mapping a point to the closest city.
|
||||
*
|
||||
* @return A list of tuples where each tuple represents a point to be
|
||||
* counted with a combination of the closest city and the timestamp.
|
||||
* counted with a combination of the closest city and the timestamp.
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
private Stream<Pair<CityRecord, Long>> processGeoResult(GeoResult geoResult, ClosestCityMapper cityMapper) {
|
||||
@ -398,7 +404,7 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
|
||||
|
||||
List<Set<MapWaypoint>> areas = (geoResult.getAreas() == null) ? Collections.emptyList() : geoResult.getAreas();
|
||||
List<Set<MapWaypoint>> tracks = (geoResult.getTracks() == null) ? Collections.emptyList() : geoResult.getTracks();
|
||||
|
||||
|
||||
Stream<Pair<CityRecord, Long>> reducedGroupings = Stream.of(areas, tracks)
|
||||
.flatMap((groupingList) -> groupingList.stream())
|
||||
.flatMap((grouping) -> reduceGrouping(grouping, cityMapper));
|
||||
@ -407,7 +413,7 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
|
||||
.flatMap((groupingList) -> groupingList.stream())
|
||||
.flatMap((group) -> group.stream())
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
|
||||
Set<MapWaypoint> pointSet = geoResult.getMapWaypoints() == null ? Collections.emptySet() : geoResult.getMapWaypoints();
|
||||
Stream<Pair<CityRecord, Long>> citiesForPoints = pointSet.stream()
|
||||
// it appears that AbstractWaypointFetcher.handleFilteredWaypointSet returns all points
|
||||
@ -423,8 +429,8 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
|
||||
* number of found hits (i.e. most hits is first index).
|
||||
*
|
||||
* @param dataSource The data source.
|
||||
* @param daysCount Number of days to go back.
|
||||
* @param maxCount Maximum number of results.
|
||||
* @param daysCount Number of days to go back.
|
||||
* @param maxCount Maximum number of results.
|
||||
*
|
||||
* @return The sorted list.
|
||||
*
|
||||
@ -507,11 +513,12 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
|
||||
* Main constructor.
|
||||
*
|
||||
* @param asyncResult Geolocation fetches results in a callback which is
|
||||
* already handled by other mechanisms in data source summary. The
|
||||
* BlockingQueue blocks until a result is received from geolocation.
|
||||
* @param filters The applicable filters for geolocation.
|
||||
* already handled by other mechanisms in data source
|
||||
* summary. The BlockingQueue blocks until a result
|
||||
* is received from geolocation.
|
||||
* @param filters The applicable filters for geolocation.
|
||||
*/
|
||||
public PointFetcher(BlockingQueue<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
|
||||
|
@ -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,8 +64,8 @@ class LatLngMap<E extends KdTree.XYZPoint> {
|
||||
* Main contructor.
|
||||
*
|
||||
* @param pointsToAdd The points to be added to the data structure.
|
||||
* @param bucketSize The size of a grid square in kilometers. So, if this
|
||||
* value is 100, each sqaure will be a 100 x 100 km.
|
||||
* @param bucketSize The size of a grid square in kilometers. So, if this
|
||||
* value is 100, each sqaure will be a 100 x 100 km.
|
||||
*/
|
||||
LatLngMap(List<E> pointsToAdd, double bucketSize) {
|
||||
this.bucketSize = bucketSize;
|
||||
@ -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) {
|
||||
@ -132,9 +134,10 @@ class LatLngMap<E extends KdTree.XYZPoint> {
|
||||
/**
|
||||
* Within the specific bucket, finds the closest point if any exists.
|
||||
*
|
||||
* @param x The x axis bucket.
|
||||
* @param y The y axis bucket.
|
||||
* @param x The x axis bucket.
|
||||
* @param y The y axis bucket.
|
||||
* @param point The point to search for.
|
||||
*
|
||||
* @return The point, if any, that was found.
|
||||
*/
|
||||
private E findClosestInBucket(int x, int y, E point) {
|
||||
|
59
Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/MimeTypeSummary.java
Normal file → Executable file
59
Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/MimeTypeSummary.java
Normal file → Executable 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 = '') ");
|
||||
}
|
||||
|
||||
/**
|
||||
|
21
Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/PastCasesSummary.java
Normal file → Executable file
21
Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/PastCasesSummary.java
Normal file → Executable 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()))
|
||||
|
@ -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();
|
||||
|
0
Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/SleuthkitCaseProvider.java
Normal file → Executable file
0
Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/SleuthkitCaseProvider.java
Normal file → Executable file
7
Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TimelineDataSourceUtils.java
Normal file → Executable file
7
Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TimelineDataSourceUtils.java
Normal file → Executable 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();
|
||||
|
||||
|
116
Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TimelineSummary.java
Normal file → Executable file
116
Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TimelineSummary.java
Normal file → Executable 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,39 +100,19 @@ public class TimelineSummary implements DefaultUpdateGovernor {
|
||||
this.filterFunction = filterFunction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRefreshRequired(ModuleContentEvent evt) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRefreshRequired(AbstractFile file) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRefreshRequired(IngestManager.IngestJobEvent evt) {
|
||||
return (evt != null && INGEST_JOB_EVENTS.contains(evt));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<IngestManager.IngestJobEvent> getIngestJobEventUpdates() {
|
||||
return INGEST_JOB_EVENTS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves timeline summary data.
|
||||
*
|
||||
* @param dataSource The data source for which timeline data will be
|
||||
* retrieved.
|
||||
* @param dataSource The data source for which timeline data will be
|
||||
* retrieved.
|
||||
* @param recentDaysNum The maximum number of most recent days' activity to
|
||||
* include.
|
||||
* include.
|
||||
*
|
||||
* @return The retrieved data.
|
||||
* @throws SleuthkitCaseProviderException
|
||||
* @throws TskCoreException
|
||||
* @throws NoCurrentCaseException
|
||||
*/
|
||||
public TimelineSummaryData getData(DataSource dataSource, int recentDaysNum) throws SleuthkitCaseProviderException, TskCoreException, NoCurrentCaseException {
|
||||
public TimelineSummaryData getTimelineSummaryData(DataSource dataSource, int recentDaysNum) throws SleuthkitCaseProviderException, TskCoreException {
|
||||
TimeZone timeZone = this.timeZoneProvider.get();
|
||||
TimelineManager timelineManager = this.caseProvider.get().getTimelineManager();
|
||||
|
||||
@ -174,10 +149,11 @@ public class TimelineSummary implements DefaultUpdateGovernor {
|
||||
* Given activity by day, converts to most recent days' activity handling
|
||||
* empty values.
|
||||
*
|
||||
* @param dateCounts The day from epoch mapped to activity amounts for that
|
||||
* day.
|
||||
* @param dateCounts The day from epoch mapped to activity amounts for
|
||||
* that day.
|
||||
* @param minRecentDay The minimum recent day in days from epoch.
|
||||
* @param maxDay The maximum recent day in days from epoch;
|
||||
* @param maxDay The maximum recent day in days from epoch;
|
||||
*
|
||||
* @return The most recent daily activity amounts.
|
||||
*/
|
||||
private List<DailyActivityAmount> getMostRecentActivityAmounts(Map<Long, DailyActivityAmount> dateCounts, long minRecentDay, long maxDay) {
|
||||
@ -197,17 +173,18 @@ public class TimelineSummary implements DefaultUpdateGovernor {
|
||||
/**
|
||||
* Fetches timeline events per day for a particular data source.
|
||||
*
|
||||
* @param dataSource The data source.
|
||||
* @param dataSource The data source.
|
||||
* @param timelineManager The timeline manager to use while fetching the
|
||||
* data.
|
||||
* @param timeZone The time zone to use to determine which day activity
|
||||
* belongs.
|
||||
* data.
|
||||
* @param timeZone The time zone to use to determine which day
|
||||
* activity belongs.
|
||||
*
|
||||
* @return A Map mapping days from epoch to the activity for that day.
|
||||
*
|
||||
* @throws TskCoreException
|
||||
* @throws NoCurrentCaseException
|
||||
*/
|
||||
private Map<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.
|
||||
* max date sorted by min to max date.
|
||||
* @param dataSource The data source for which this data
|
||||
* applies. the latest usage date by day.
|
||||
*/
|
||||
TimelineSummaryData(Date minDate, Date maxDate, List<DailyActivityAmount> recentDaysActivity, DataSource dataSource) {
|
||||
this.minDate = minDate;
|
||||
@ -281,7 +260,7 @@ public class TimelineSummary implements DefaultUpdateGovernor {
|
||||
|
||||
/**
|
||||
* @return A list of activity prior to and including the latest usage
|
||||
* date by day sorted min to max date.
|
||||
* date by day sorted min to max date.
|
||||
*/
|
||||
public List<DailyActivityAmount> getMostRecentDaysActivity() {
|
||||
return histogramActivity;
|
||||
@ -307,8 +286,10 @@ public class TimelineSummary implements DefaultUpdateGovernor {
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param day The day for which activity is being measured.
|
||||
* @param fileActivityCount The amount of file activity timeline events.
|
||||
* @param day The day for which activity is being
|
||||
* measured.
|
||||
* @param fileActivityCount The amount of file activity timeline
|
||||
* events.
|
||||
* @param artifactActivityCount The amount of artifact timeline events.
|
||||
*/
|
||||
DailyActivityAmount(Date day, long fileActivityCount, long artifactActivityCount) {
|
||||
@ -337,6 +318,29 @@ public class TimelineSummary implements DefaultUpdateGovernor {
|
||||
public long getArtifactActivityCount() {
|
||||
return artifactActivityCount;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a DateFormat formatter that uses UTC for time zone.
|
||||
*
|
||||
* @param formatString The date format string.
|
||||
* @return The data format.
|
||||
*/
|
||||
public static DateFormat getUtcFormat(String formatString) {
|
||||
return new SimpleDateFormat(formatString, Locale.getDefault());
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a date using a DateFormat. In the event that the date is null,
|
||||
* returns a null string.
|
||||
*
|
||||
* @param date The date to format.
|
||||
* @param formatter The DateFormat to use to format the date.
|
||||
*
|
||||
* @return The formatted string generated from the formatter or null if the
|
||||
* date is null.
|
||||
*/
|
||||
public static String formatDate(Date date, DateFormat formatter) {
|
||||
return date == null ? null : formatter.format(date);
|
||||
}
|
||||
}
|
||||
|
93
Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TypesSummary.java
Normal file → Executable file
93
Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TypesSummary.java
Normal file → Executable 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
159
Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/UserActivitySummary.java
Normal file → Executable file
159
Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/UserActivitySummary.java
Normal file → Executable 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,27 +175,55 @@ 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.
|
||||
*
|
||||
* @param dataSource The datasource to query for recent domains.
|
||||
* @param count The max count of items to return.
|
||||
* @param count The max count of items to return.
|
||||
*
|
||||
* @return The list of items retrieved from the database.
|
||||
*
|
||||
@ -242,13 +259,13 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
||||
* Creates a TopDomainsResult from data or null if no visit date exists
|
||||
* within DOMAIN_WINDOW_MS of mostRecentMs.
|
||||
*
|
||||
* @param domain The domain.
|
||||
* @param visits The list of the artifact and its associated time in
|
||||
* milliseconds.
|
||||
* @param domain The domain.
|
||||
* @param visits The list of the artifact and its associated time in
|
||||
* milliseconds.
|
||||
* @param mostRecentMs The most recent visit of any domain.
|
||||
*
|
||||
* @return The TopDomainsResult or null if no visits to this domain within
|
||||
* 30 days of mostRecentMs.
|
||||
* 30 days of mostRecentMs.
|
||||
*/
|
||||
private TopDomainsResult getDomainsResult(String domain, List<Pair<BlackboardArtifact, Long>> visits, long mostRecentMs) {
|
||||
long visitCount = 0;
|
||||
@ -288,9 +305,9 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
||||
* @param dataSource The datasource.
|
||||
*
|
||||
* @return A tuple where the first value is the latest web history accessed
|
||||
* date in milliseconds and the second value maps normalized (lowercase;
|
||||
* trimmed) domain names to when those domains were visited and the relevant
|
||||
* artifact.
|
||||
* date in milliseconds and the second value maps normalized
|
||||
* (lowercase; trimmed) domain names to when those domains were
|
||||
* visited and the relevant artifact.
|
||||
*
|
||||
* @throws TskCoreException
|
||||
* @throws SleuthkitCaseProviderException
|
||||
@ -357,7 +374,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
||||
* @param artifact The artifact.
|
||||
*
|
||||
* @return The TopWebSearchResult or null if the search string or date
|
||||
* accessed cannot be determined.
|
||||
* accessed cannot be determined.
|
||||
*/
|
||||
private static TopWebSearchResult getWebSearchResult(BlackboardArtifact artifact) {
|
||||
String searchString = DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_TEXT);
|
||||
@ -372,10 +389,11 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
||||
* term.
|
||||
*
|
||||
* @param dataSource The data source.
|
||||
* @param count The maximum number of records to be shown (must be > 0).
|
||||
* @param count The maximum number of records to be shown (must be >
|
||||
* 0).
|
||||
*
|
||||
* @return The list of most recent web searches where most recent search
|
||||
* appears first.
|
||||
* appears first.
|
||||
*
|
||||
* @throws
|
||||
* org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException
|
||||
@ -462,6 +480,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
||||
*
|
||||
* @param r1 A result.
|
||||
* @param r2 Another result.
|
||||
*
|
||||
* @return The most recent one with a non-null date.
|
||||
*/
|
||||
private TopDeviceAttachedResult getMostRecentDevice(TopDeviceAttachedResult r1, TopDeviceAttachedResult r2) {
|
||||
@ -480,10 +499,11 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
||||
* Retrieves most recent devices used by most recent date attached.
|
||||
*
|
||||
* @param dataSource The data source.
|
||||
* @param count The maximum number of records to be shown (must be > 0).
|
||||
* @param count The maximum number of records to be shown (must be >
|
||||
* 0).
|
||||
*
|
||||
* @return The list of most recent devices attached where most recent device
|
||||
* attached appears first.
|
||||
* attached appears first.
|
||||
*
|
||||
* @throws
|
||||
* org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException
|
||||
@ -528,7 +548,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
||||
* @param artifact The artifact.
|
||||
*
|
||||
* @return The TopAccountResult or null if the account type or message date
|
||||
* cannot be determined.
|
||||
* cannot be determined.
|
||||
*/
|
||||
private static TopAccountResult getMessageAccountResult(BlackboardArtifact artifact) {
|
||||
String type = DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_MESSAGE_TYPE);
|
||||
@ -542,12 +562,12 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
||||
* Obtains a TopAccountResult from a blackboard artifact. The date is
|
||||
* maximum of any found dates for attribute types provided.
|
||||
*
|
||||
* @param artifact The artifact.
|
||||
* @param artifact The artifact.
|
||||
* @param messageType The type of message this is.
|
||||
* @param dateAttrs The date attribute types.
|
||||
* @param dateAttrs The date attribute types.
|
||||
*
|
||||
* @return The TopAccountResult or null if the account type or max date are
|
||||
* not provided.
|
||||
* not provided.
|
||||
*/
|
||||
private static TopAccountResult getAccountResult(BlackboardArtifact artifact, String messageType, BlackboardAttribute.Type... dateAttrs) {
|
||||
String type = messageType;
|
||||
@ -638,39 +658,6 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines a short folder name if any. Otherwise, returns empty string.
|
||||
*
|
||||
* @param strPath The string path.
|
||||
* @param applicationName The application name.
|
||||
*
|
||||
* @return The short folder name or empty string if not found.
|
||||
*/
|
||||
public String getShortFolderName(String strPath, String applicationName) {
|
||||
if (strPath == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
List<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.
|
||||
*
|
||||
@ -764,12 +751,12 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
||||
* be ignored and all items will be returned.
|
||||
*
|
||||
* @param dataSource The datasource. If the datasource is null, an empty
|
||||
* list will be returned.
|
||||
* @param count The number of results to return. This value must be > 0 or
|
||||
* an IllegalArgumentException will be thrown.
|
||||
* list will be returned.
|
||||
* @param count The number of results to return. This value must be > 0
|
||||
* or an IllegalArgumentException will be thrown.
|
||||
*
|
||||
* @return The sorted list and limited to the count if last run or run count
|
||||
* information is available on any item.
|
||||
* information is available on any item.
|
||||
*
|
||||
* @throws SleuthkitCaseProviderException
|
||||
* @throws TskCoreException
|
||||
@ -840,7 +827,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
||||
* Main constructor.
|
||||
*
|
||||
* @param lastAccessed The date of last access.
|
||||
* @param artifact The relevant blackboard artifact.
|
||||
* @param artifact The relevant blackboard artifact.
|
||||
*/
|
||||
public LastAccessedArtifact(Date lastAccessed, BlackboardArtifact artifact) {
|
||||
this.lastAccessed = lastAccessed;
|
||||
@ -875,7 +862,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
||||
*
|
||||
* @param searchString The search string.
|
||||
* @param dateAccessed The latest date searched.
|
||||
* @param artifact The relevant blackboard artifact.
|
||||
* @param artifact The relevant blackboard artifact.
|
||||
*/
|
||||
public TopWebSearchResult(String searchString, Date dateAccessed, BlackboardArtifact artifact) {
|
||||
super(dateAccessed, artifact);
|
||||
@ -918,11 +905,11 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param deviceId The device id.
|
||||
* @param deviceId The device id.
|
||||
* @param dateAccessed The date last attached.
|
||||
* @param deviceMake The device make.
|
||||
* @param deviceModel The device model.
|
||||
* @param artifact The relevant blackboard artifact.
|
||||
* @param deviceMake The device make.
|
||||
* @param deviceModel The device model.
|
||||
* @param artifact The relevant blackboard artifact.
|
||||
*/
|
||||
public TopDeviceAttachedResult(String deviceId, Date dateAccessed, String deviceMake, String deviceModel, BlackboardArtifact artifact) {
|
||||
super(dateAccessed, artifact);
|
||||
@ -965,8 +952,8 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
||||
* Main constructor.
|
||||
*
|
||||
* @param accountType The account type.
|
||||
* @param lastAccess The date the account was last accessed.
|
||||
* @param artifact The artifact indicating last access.
|
||||
* @param lastAccess The date the account was last accessed.
|
||||
* @param artifact The artifact indicating last access.
|
||||
*/
|
||||
public TopAccountResult(String accountType, Date lastAccess, BlackboardArtifact artifact) {
|
||||
super(lastAccess, artifact);
|
||||
@ -992,10 +979,10 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
||||
/**
|
||||
* Describes a top domain result.
|
||||
*
|
||||
* @param domain The domain.
|
||||
* @param domain The domain.
|
||||
* @param visitTimes The number of times it was visited.
|
||||
* @param lastVisit The date of the last visit.
|
||||
* @param artifact The relevant blackboard artifact.
|
||||
* @param lastVisit The date of the last visit.
|
||||
* @param artifact The relevant blackboard artifact.
|
||||
*/
|
||||
public TopDomainsResult(String domain, Long visitTimes, Date lastVisit, BlackboardArtifact artifact) {
|
||||
super(lastVisit, artifact);
|
||||
@ -1032,8 +1019,8 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
||||
*
|
||||
* @param programName The name of the program.
|
||||
* @param programPath The path of the program.
|
||||
* @param runTimes The number of runs.
|
||||
* @param artifact The relevant blackboard artifact.
|
||||
* @param runTimes The number of runs.
|
||||
* @param artifact The relevant blackboard artifact.
|
||||
*/
|
||||
TopProgramsResult(String programName, String programPath, Long runTimes, Date lastRun, BlackboardArtifact artifact) {
|
||||
super(lastRun, artifact);
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</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>
|
@ -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
|
||||
}
|
@ -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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</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>
|
@ -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
|
||||
}
|
@ -34,7 +34,6 @@ import org.openide.util.NbBundle.Messages;
|
||||
import org.openide.util.actions.CallableSystemAction;
|
||||
import org.openide.windows.TopComponent;
|
||||
import org.openide.windows.WindowManager;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.GeolocationSummary;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.GeolocationSummary.CityCountsList;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.GeolocationSummary.CityData;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.GeolocationSummary.CityRecordCount;
|
||||
@ -43,9 +42,8 @@ import org.sleuthkit.autopsy.datasourcesummary.uiutils.ColumnModel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetcher;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataFetcher;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultCellModel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel;
|
||||
import org.sleuthkit.autopsy.geolocation.GeoFilter;
|
||||
@ -79,9 +77,9 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel {
|
||||
* Main constructor.
|
||||
*
|
||||
* @param mostRecentData The data to be displayed in the most recent
|
||||
* table.
|
||||
* table.
|
||||
* @param mostCommonData The data to be displayed in the most common
|
||||
* table.
|
||||
* table.
|
||||
*/
|
||||
GeolocationViewModel(List<Pair<String, Integer>> mostRecentData, List<Pair<String, Integer>> mostCommonData) {
|
||||
this.mostRecentData = mostRecentData;
|
||||
@ -147,7 +145,7 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel {
|
||||
|
||||
private final IngestRunningLabel ingestRunningLabel = new IngestRunningLabel();
|
||||
|
||||
private final GeolocationSummary whereUsedData;
|
||||
private final GeolocationSummaryGetter whereUsedData;
|
||||
|
||||
private final DataFetcher<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;
|
||||
@ -183,7 +181,7 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel {
|
||||
* Means of rendering data to be shown in the tables.
|
||||
*
|
||||
* @param result The result of fetching data for a data source and
|
||||
* processing into view model data.
|
||||
* processing into view model data.
|
||||
*/
|
||||
private void handleData(DataFetchResult<GeolocationViewModel> result) {
|
||||
showCityContent(DataFetchResult.getSubResult(result, (dr) -> dr.getMostCommonData()), mostCommonTable, commonViewInGeolocationBtn);
|
||||
@ -194,6 +192,7 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel {
|
||||
* Retrieves the city name to display from the record.
|
||||
*
|
||||
* @param record The record for the city to display.
|
||||
*
|
||||
* @return The display name (city, country).
|
||||
*/
|
||||
private static String getCityName(CityRecord record) {
|
||||
@ -221,6 +220,7 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel {
|
||||
* formats the city name).
|
||||
*
|
||||
* @param cityCount The CityRecordCount representing a row.
|
||||
*
|
||||
* @return The city/count pair to be displayed as a row.
|
||||
*/
|
||||
private Pair<String, Integer> formatRecord(CityRecordCount cityCount) {
|
||||
@ -239,7 +239,8 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel {
|
||||
* 'unknown').
|
||||
*
|
||||
* @param countsList The CityCountsList object representing the data to be
|
||||
* displayed in the table.
|
||||
* displayed in the table.
|
||||
*
|
||||
* @return The list of city/count tuples to be displayed as a row.
|
||||
*/
|
||||
private List<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) {
|
||||
@ -280,8 +282,8 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel {
|
||||
/**
|
||||
* Shows data in a particular table.
|
||||
*
|
||||
* @param result The result to be displayed in the table.
|
||||
* @param table The table where the data will be displayed.
|
||||
* @param result The result to be displayed in the table.
|
||||
* @param table The table where the data will be displayed.
|
||||
* @param goToGeolocation The corresponding geolocation navigation button.
|
||||
*/
|
||||
private void showCityContent(DataFetchResult<List<Pair<String, Integer>>> result, JTablePanel<Pair<String, Integer>> table, JButton goToGeolocation) {
|
||||
@ -296,9 +298,9 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel {
|
||||
* Action to open the geolocation window.
|
||||
*
|
||||
* @param dataSource The data source for which the window should filter.
|
||||
* @param daysLimit The limit for how recently the waypoints should be (for
|
||||
* most recent table) or null for most recent filter to not be set (for most
|
||||
* common table).
|
||||
* @param daysLimit The limit for how recently the waypoints should be (for
|
||||
* most recent table) or null for most recent filter to
|
||||
* not be set (for most common table).
|
||||
*/
|
||||
private void openGeolocationWindow(DataSource dataSource, Integer daysLimit) {
|
||||
// notify dialog (if in dialog) should close.
|
||||
@ -349,19 +351,6 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel {
|
||||
onNewDataSource(dataFetchComponents, tables, dataSource);
|
||||
}
|
||||
|
||||
@Override
|
||||
List<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();
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -19,21 +19,16 @@
|
||||
package org.sleuthkit.autopsy.datasourcesummary.ui;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.PastCasesSummary;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.PastCasesSummary.PastCasesResult;
|
||||
import static org.sleuthkit.autopsy.datasourcesummary.ui.BaseDataSourceSummaryPanel.getFetchResult;
|
||||
import static org.sleuthkit.autopsy.datasourcesummary.ui.BaseDataSourceSummaryPanel.getTableExport;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ColumnModel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetcher;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataFetcher;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultCellModel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
@ -84,19 +79,19 @@ public class PastCasesPanel extends BaseDataSourceSummaryPanel {
|
||||
private final IngestRunningLabel ingestRunningLabel = new IngestRunningLabel();
|
||||
|
||||
private final DataFetcher<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);
|
||||
|
||||
|
||||
// set up data acquisition methods
|
||||
dataFetchComponents = Arrays.asList(
|
||||
new DataFetchWorker.DataFetchComponents<>(
|
||||
@ -128,19 +123,6 @@ public class PastCasesPanel extends BaseDataSourceSummaryPanel {
|
||||
onNewDataSource(dataFetchComponents, tables, dataSource);
|
||||
}
|
||||
|
||||
@Override
|
||||
List<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();
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
116
Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RecentFilesGetter.java
Executable file
116
Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RecentFilesGetter.java
Executable 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);
|
||||
}
|
||||
}
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
* 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
|
||||
@ -242,8 +211,8 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel {
|
||||
* Action that occurs when 'View in Timeline' button is pressed.
|
||||
*
|
||||
* @param dataSource The data source to filter to.
|
||||
* @param minDate The min date for the zoom of the window.
|
||||
* @param maxDate The max date for the zoom of the window.
|
||||
* @param minDate The min date for the zoom of the window.
|
||||
* @param maxDate The max date for the zoom of the window.
|
||||
*/
|
||||
private void openFilteredChart(DataSource dataSource, Date minDate, Date maxDate) {
|
||||
OpenTimelineAction openTimelineAction = CallableSystemAction.get(OpenTimelineAction.class);
|
||||
@ -266,7 +235,7 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel {
|
||||
if (minDate != null && maxDate != null) {
|
||||
timeSpan = new Interval(new DateTime(minDate), new DateTime(maxDate));
|
||||
}
|
||||
} catch (NoCurrentCaseException | TskCoreException ex) {
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.WARNING, "Unable to view time range in Timeline view", ex);
|
||||
}
|
||||
|
||||
@ -293,43 +262,6 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel {
|
||||
super.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a default cell model to be use with excel export in the earliest /
|
||||
* latest date format.
|
||||
*
|
||||
* @param date The date.
|
||||
* @return The cell model.
|
||||
*/
|
||||
private static DefaultCellModel<?> getEarliestLatestCell(Date date) {
|
||||
return new DefaultCellModel<>(date, (dt) -> dt == null ? "" : EARLIEST_LATEST_FORMAT.format(dt), EARLIEST_LATEST_FORMAT_STR);
|
||||
}
|
||||
|
||||
@Messages({
|
||||
"TimelinePanel_getExports_sheetName=Timeline",
|
||||
"TimelinePanel_getExports_activityRange=Activity Range",
|
||||
"TimelinePanel_getExports_earliest=Earliest:",
|
||||
"TimelinePanel_getExports_latest=Latest:",
|
||||
"TimelinePanel_getExports_dateColumnHeader=Date",
|
||||
"TimelinePanel_getExports_chartName=Last 30 Days",})
|
||||
@Override
|
||||
List<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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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,22 +261,22 @@ 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());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new UserActivityPanel.
|
||||
*
|
||||
* @param userActivityData Class from which to obtain remaining user
|
||||
* activity data.
|
||||
* activity data.
|
||||
*/
|
||||
public UserActivityPanel(UserActivitySummary userActivityData) {
|
||||
public UserActivityPanel(UserActivitySummaryGetter userActivityData) {
|
||||
super(userActivityData);
|
||||
this.userActivityData = userActivityData;
|
||||
|
||||
@ -320,7 +316,7 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel {
|
||||
private <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()));
|
||||
@ -341,13 +338,13 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel {
|
||||
/**
|
||||
* Queries DataSourceTopProgramsSummary instance for short folder name.
|
||||
*
|
||||
* @param path The path for the application.
|
||||
* @param path The path for the application.
|
||||
* @param appName The application name.
|
||||
*
|
||||
* @return The underlying short folder name if one exists.
|
||||
*/
|
||||
private String getShortFolderName(String path, String appName) {
|
||||
return this.userActivityData.getShortFolderName(path, appName);
|
||||
private static String getShortFolderName(String path, String appName) {
|
||||
return UserActivitySummary.getShortFolderName(path, appName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -366,18 +363,6 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel {
|
||||
super.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
List<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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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();
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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,18 +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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param data The data to be displayed in the cell.
|
||||
* @param stringConverter The means of converting that data to a string or
|
||||
* null to use .toString method on object.
|
||||
*/
|
||||
public DefaultCellModel(T data, Function<T, String> stringConverter) {
|
||||
this(data, stringConverter, null);
|
||||
this(data, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -63,15 +51,9 @@ public class DefaultCellModel<T> implements GuiCellModel, ExcelCellModel {
|
||||
* @param data The data to be displayed in the cell.
|
||||
* @param stringConverter The means of converting that data to a string or
|
||||
* null to use .toString method on object.
|
||||
* @param excelFormatString The apache poi excel format string to use with
|
||||
* the data.
|
||||
*
|
||||
* NOTE: Only certain data types can be exported. See
|
||||
* ExcelTableExport.createCell() for types.
|
||||
*/
|
||||
public DefaultCellModel(T data, Function<T, String> stringConverter, String excelFormatString) {
|
||||
public DefaultCellModel(T data, Function<T, String> stringConverter) {
|
||||
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;
|
||||
|
@ -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();
|
||||
|
||||
}
|
@ -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) {
|
@ -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
|
@ -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
|
@ -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();
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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) {
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
@ -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)
|
||||
)));
|
||||
|
||||
}
|
||||
}
|
@ -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())
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@ -16,7 +16,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.datasourcesummary.ui;
|
||||
package org.sleuthkit.autopsy.report.modules.datasourcesummaryexport;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
@ -34,27 +34,24 @@ import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ColumnModel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultCellModel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport.ExcelSheetExport;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelTableExport;
|
||||
import org.sleuthkit.autopsy.report.modules.datasourcesummaryexport.ExcelExport.ExcelSheetExport;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
import org.sleuthkit.datamodel.IngestJobInfo;
|
||||
import org.sleuthkit.datamodel.IngestModuleInfo;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* Class that handles exporting information in IngestJobInfoPanel to excel.
|
||||
* Class that handles exporting ingest job information to excel.
|
||||
*/
|
||||
@Messages({
|
||||
"IngestJobExcelExport_startTimeColumn=Start Time",
|
||||
"IngestJobExcelExport_endTimeColumn=End Time",
|
||||
"IngestJobExcelExport_ingestStatusTimeColumn=Ingest Status",
|
||||
"IngestJobExcelExport_moduleNameTimeColumn=Module Name",
|
||||
"IngestJobExcelExport_versionColumn=Module Version",
|
||||
"IngestJobExcelExport_sheetName=Ingest History"
|
||||
"ExportIngestHistory_startTimeColumn=Start Time",
|
||||
"ExportIngestHistory_endTimeColumn=End Time",
|
||||
"ExportIngestHistory_ingestStatusTimeColumn=Ingest Status",
|
||||
"ExportIngestHistory_moduleNameTimeColumn=Module Name",
|
||||
"ExportIngestHistory_versionColumn=Module Version",
|
||||
"ExportIngestHistory_sheetName=Ingest History"
|
||||
})
|
||||
class IngestJobExcelExport {
|
||||
class ExportIngestHistory {
|
||||
|
||||
/**
|
||||
* An entry to display in an excel export.
|
||||
@ -70,10 +67,10 @@ class IngestJobExcelExport {
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param startTime The ingest start time.
|
||||
* @param endTime The ingest stop time.
|
||||
* @param status The ingest status.
|
||||
* @param ingestModule The ingest module.
|
||||
* @param startTime The ingest start time.
|
||||
* @param endTime The ingest stop time.
|
||||
* @param status The ingest status.
|
||||
* @param ingestModule The ingest module.
|
||||
* @param ingestModuleVersion The ingest module version.
|
||||
*/
|
||||
IngestJobEntry(Date startTime, Date endTime, String status, String ingestModule, String ingestModuleVersion) {
|
||||
@ -120,26 +117,26 @@ class IngestJobExcelExport {
|
||||
}
|
||||
}
|
||||
|
||||
private static final Logger logger = Logger.getLogger(IngestJobExcelExport.class.getName());
|
||||
private static final Logger logger = Logger.getLogger(ExportIngestHistory.class.getName());
|
||||
private static final String DATETIME_FORMAT_STR = "yyyy/MM/dd HH:mm:ss";
|
||||
private static final DateFormat DATETIME_FORMAT = new SimpleDateFormat(DATETIME_FORMAT_STR, Locale.getDefault());
|
||||
|
||||
// columns in the excel export table to be created.
|
||||
private static final List<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() {
|
||||
}
|
||||
}
|
@ -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())
|
||||
);
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
@ -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)))));
|
||||
}
|
||||
}
|
@ -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())
|
||||
));
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
@ -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,
|
@ -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() {
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user