Clean up naming, formatting, and comments for Discovery package

This commit is contained in:
William Schaefer 2020-09-17 12:27:06 -04:00
parent 17af08456a
commit 1f99be6537
29 changed files with 881 additions and 444 deletions

View File

@ -29,8 +29,7 @@ import org.sleuthkit.datamodel.SleuthkitCase;
public abstract class AbstractFilter { public abstract class AbstractFilter {
/** /**
* Returns part of a query on the table that can be AND-ed with * Returns part of a query on the table that can be AND-ed with other pieces
* other pieces
* *
* @return the SQL query or an empty string if there is no SQL query for * @return the SQL query or an empty string if there is no SQL query for
* this filter. * this filter.

View File

@ -61,20 +61,20 @@ public class DiscoveryAttributes {
public abstract static class AttributeType { public abstract static class AttributeType {
/** /**
* For a given file, return the key for the group it belongs to for this * For a given Result, return the key for the group it belongs to for
* attribute type. * this attribute type.
* *
* @param file the result file to be grouped * @param result The result to be grouped.
* *
* @return the key for the group this file goes in * @return The key for the group this result goes in.
*/ */
public abstract DiscoveryKeyUtils.GroupKey getGroupKey(Result file); public abstract DiscoveryKeyUtils.GroupKey getGroupKey(Result result);
/** /**
* Add any extra data to the ResultFile object from this attribute. * Add any extra data to the ResultFile object from this attribute.
* *
* @param files The list of results to enhance * @param files The list of results to enhance.
* @param caseDb The case database * @param caseDb The case database.
* @param centralRepoDb The central repository database. Can be null if * @param centralRepoDb The central repository database. Can be null if
* not needed. * not needed.
* *
@ -86,7 +86,7 @@ public class DiscoveryAttributes {
} }
/** /**
* Attribute for grouping/sorting by file size * Attribute for grouping/sorting by file size.
*/ */
public static class FileSizeAttribute extends AttributeType { public static class FileSizeAttribute extends AttributeType {
@ -97,7 +97,7 @@ public class DiscoveryAttributes {
} }
/** /**
* Attribute for grouping/sorting by parent path * Attribute for grouping/sorting by parent path.
*/ */
public static class ParentPathAttribute extends AttributeType { public static class ParentPathAttribute extends AttributeType {
@ -108,7 +108,7 @@ public class DiscoveryAttributes {
} }
/** /**
* Default attribute used to make one group * Default attribute used to make one group.
*/ */
static class NoGroupingAttribute extends AttributeType { static class NoGroupingAttribute extends AttributeType {
@ -119,7 +119,7 @@ public class DiscoveryAttributes {
} }
/** /**
* Attribute for grouping/sorting by data source * Attribute for grouping/sorting by data source.
*/ */
static class DataSourceAttribute extends AttributeType { static class DataSourceAttribute extends AttributeType {
@ -130,7 +130,7 @@ public class DiscoveryAttributes {
} }
/** /**
* Attribute for grouping/sorting by file type * Attribute for grouping/sorting by file type.
*/ */
static class FileTypeAttribute extends AttributeType { static class FileTypeAttribute extends AttributeType {
@ -141,7 +141,7 @@ public class DiscoveryAttributes {
} }
/** /**
* Attribute for grouping/sorting by keyword lists * Attribute for grouping/sorting by keyword lists.
*/ */
static class KeywordListAttribute extends AttributeType { static class KeywordListAttribute extends AttributeType {
@ -179,7 +179,7 @@ public class DiscoveryAttributes {
/** /**
* Create the callback. * Create the callback.
* *
* @param resultFiles List of files to add keyword list names to * @param resultFiles List of files to add keyword list names to.
*/ */
SetKeywordListNamesCallback(List<Result> resultFiles) { SetKeywordListNamesCallback(List<Result> resultFiles) {
this.resultFiles = resultFiles; this.resultFiles = resultFiles;
@ -217,7 +217,7 @@ public class DiscoveryAttributes {
} }
/** /**
* Attribute for grouping/sorting by frequency in the central repository * Attribute for grouping/sorting by frequency in the central repository.
*/ */
static class FrequencyAttribute extends AttributeType { static class FrequencyAttribute extends AttributeType {
@ -294,11 +294,18 @@ public class DiscoveryAttributes {
} }
} }
/**
* Query to get the frequency of a domain.
*
* @param domainsToQuery List of domains to check the frequency of.
* @param centralRepository The central repository to query.
*
* @throws DiscoveryException
*/
private static void queryDomainFrequency(List<ResultDomain> domainsToQuery, CentralRepository centralRepository) throws DiscoveryException { private static void queryDomainFrequency(List<ResultDomain> domainsToQuery, CentralRepository centralRepository) throws DiscoveryException {
if (domainsToQuery.isEmpty()) { if (domainsToQuery.isEmpty()) {
return; return;
} }
try { try {
final Map<String, List<ResultDomain>> resultDomainTable = new HashMap<>(); final Map<String, List<ResultDomain>> resultDomainTable = new HashMap<>();
final StringJoiner joiner = new StringJoiner(", "); final StringJoiner joiner = new StringJoiner(", ");
@ -334,11 +341,19 @@ public class DiscoveryAttributes {
} }
} }
/**
* Callback to get the frequency of domain.
*/
private static class DomainFrequencyCallback implements InstanceTableCallback { private static class DomainFrequencyCallback implements InstanceTableCallback {
private final Map<String, List<ResultDomain>> domainLookup; private final Map<String, List<ResultDomain>> domainLookup;
private SQLException sqlCause; private SQLException sqlCause;
/**
* Construct a new DomainFrequencyCallback.
*
* @param domainLookup The map to get domain from.
*/
private DomainFrequencyCallback(Map<String, List<ResultDomain>> domainLookup) { private DomainFrequencyCallback(Map<String, List<ResultDomain>> domainLookup) {
this.domainLookup = domainLookup; this.domainLookup = domainLookup;
} }
@ -360,6 +375,11 @@ public class DiscoveryAttributes {
} }
} }
/**
* Get the SQL exception if one occurred during this callback.
*
* @return
*/
SQLException getCause() { SQLException getCause() {
return this.sqlCause; return this.sqlCause;
} }
@ -373,6 +393,11 @@ public class DiscoveryAttributes {
private final List<ResultFile> files; private final List<ResultFile> files;
/**
* Construct a new FrequencyCallback.
*
* @param resultFiles List of files to add hash set names to.
*/
private FrequencyCallback(List<ResultFile> files) { private FrequencyCallback(List<ResultFile> files) {
this.files = new ArrayList<>(files); this.files = new ArrayList<>(files);
} }
@ -404,7 +429,7 @@ public class DiscoveryAttributes {
} }
/** /**
* Attribute for grouping/sorting by hash set lists * Attribute for grouping/sorting by hash set lists.
*/ */
static class HashHitsAttribute extends AttributeType { static class HashHitsAttribute extends AttributeType {
@ -444,7 +469,7 @@ public class DiscoveryAttributes {
/** /**
* Create the callback. * Create the callback.
* *
* @param resultFiles List of files to add hash set names to * @param resultFiles List of files to add hash set names to.
*/ */
HashSetNamesCallback(List<Result> results) { HashSetNamesCallback(List<Result> results) {
this.results = results; this.results = results;
@ -482,7 +507,7 @@ public class DiscoveryAttributes {
} }
/** /**
* Attribute for grouping/sorting by interesting item set lists * Attribute for grouping/sorting by interesting item set lists.
*/ */
static class InterestingItemAttribute extends AttributeType { static class InterestingItemAttribute extends AttributeType {
@ -521,7 +546,7 @@ public class DiscoveryAttributes {
* Create the callback. * Create the callback.
* *
* @param resultFiles List of files to add interesting file set * @param resultFiles List of files to add interesting file set
* names to * names to.
*/ */
InterestingFileSetNamesCallback(List<Result> results) { InterestingFileSetNamesCallback(List<Result> results) {
this.results = results; this.results = results;
@ -581,12 +606,12 @@ public class DiscoveryAttributes {
} }
} }
/** /**
* Attribute for grouping/sorting by number of visits. * Attribute for grouping/sorting by number of visits.
*/ */
static class NumberOfVisitsAttribute extends AttributeType { static class NumberOfVisitsAttribute extends AttributeType {
@Override @Override
public DiscoveryKeyUtils.GroupKey getGroupKey(Result result) { public DiscoveryKeyUtils.GroupKey getGroupKey(Result result) {
return new DiscoveryKeyUtils.NumberOfVisitsGroupKey(result); return new DiscoveryKeyUtils.NumberOfVisitsGroupKey(result);
@ -594,7 +619,7 @@ public class DiscoveryAttributes {
} }
/** /**
* Attribute for grouping/sorting by objects detected * Attribute for grouping/sorting by objects detected.
*/ */
static class ObjectDetectedAttribute extends AttributeType { static class ObjectDetectedAttribute extends AttributeType {
@ -632,7 +657,7 @@ public class DiscoveryAttributes {
/** /**
* Create the callback. * Create the callback.
* *
* @param resultFiles List of files to add object detected names to * @param resultFiles List of files to add object detected names to.
*/ */
ObjectDetectedNamesCallback(List<Result> results) { ObjectDetectedNamesCallback(List<Result> results) {
this.results = results; this.results = results;
@ -670,7 +695,7 @@ public class DiscoveryAttributes {
} }
/** /**
* Attribute for grouping/sorting by tag name * Attribute for grouping/sorting by tag name.
*/ */
static class FileTagAttribute extends AttributeType { static class FileTagAttribute extends AttributeType {
@ -737,6 +762,14 @@ public class DiscoveryAttributes {
private final AttributeType attributeType; private final AttributeType attributeType;
private final String displayName; private final String displayName;
/**
* Construct a new GroupingAttributeType enum value.
*
* @param attributeType The type of attribute this enum value was
* constructed for.
* @param displayName The display name for this grouping attribute
* type.
*/
GroupingAttributeType(AttributeType attributeType, String displayName) { GroupingAttributeType(AttributeType attributeType, String displayName) {
this.attributeType = attributeType; this.attributeType = attributeType;
this.displayName = displayName; this.displayName = displayName;
@ -747,6 +780,11 @@ public class DiscoveryAttributes {
return displayName; return displayName;
} }
/**
* Get the type of attribute this enum value was constructed for.
*
* @return The type of attribute this enum value was constructed for.
*/
public AttributeType getAttributeType() { public AttributeType getAttributeType() {
return attributeType; return attributeType;
} }
@ -774,8 +812,9 @@ public class DiscoveryAttributes {
* Computes the CR frequency of all the given hashes and updates the list of * Computes the CR frequency of all the given hashes and updates the list of
* files. * files.
* *
* @param hashesToLookUp Hashes to find the frequency of * @param hashesToLookUp Hashes to find the frequency of.
* @param currentFiles List of files to update with frequencies * @param currentFiles List of files to update with frequencies.
* @param centralRepoDb The central repository being used.
*/ */
private static void computeFrequency(Set<String> hashesToLookUp, List<ResultFile> currentFiles, CentralRepository centralRepoDb) { private static void computeFrequency(Set<String> hashesToLookUp, List<ResultFile> currentFiles, CentralRepository centralRepoDb) {
@ -804,6 +843,19 @@ public class DiscoveryAttributes {
} }
/**
* Private helper method to create a set name clause to be used in queries.
*
* @param results The list of results to create the set name clause
* for.
* @param artifactTypeID The Blackboard Artifact type ID for the artifact
* type.
* @param setNameAttrID The set name attribute id.
*
* @return The String to use as a set name clause in queries.
*
* @throws DiscoveryException
*/
private static String createSetNameClause(List<Result> results, private static String createSetNameClause(List<Result> results,
int artifactTypeID, int setNameAttrID) throws DiscoveryException { int artifactTypeID, int setNameAttrID) throws DiscoveryException {

View File

@ -57,19 +57,18 @@ public final class DiscoveryEventUtils {
private final Type type; private final Type type;
/** /**
* Construct a new SearchStartedEvent * Construct a new SearchStartedEvent.
* *
* @param type The type of file the search event is for. * @param type The type of result the search event is for.
*/ */
public SearchStartedEvent(Type type) { public SearchStartedEvent(Type type) {
this.type = type; this.type = type;
} }
/** /**
* Get the type of file the search is being performed for. * Get the type of result the search is being performed for.
* *
* @return The type of files being searched for. * @return The type of results being searched for.
*/ */
public Type getType() { public Type getType() {
return type; return type;
@ -105,7 +104,9 @@ public final class DiscoveryEventUtils {
} }
/** /**
* @return the instances * Get the list of AbstractFiles for the instances list.
*
* @return The list of AbstractFiles for the instances list.
*/ */
public List<AbstractFile> getInstances() { public List<AbstractFile> getInstances() {
return Collections.unmodifiableList(instances); return Collections.unmodifiableList(instances);
@ -121,7 +122,7 @@ public final class DiscoveryEventUtils {
private final List<AbstractFilter> searchFilters; private final List<AbstractFilter> searchFilters;
private final DiscoveryAttributes.AttributeType groupingAttribute; private final DiscoveryAttributes.AttributeType groupingAttribute;
private final Group.GroupSortingAlgorithm groupSort; private final Group.GroupSortingAlgorithm groupSort;
private final ResultsSorter.SortingMethod fileSortMethod; private final ResultsSorter.SortingMethod sortMethod;
/** /**
* Construct a new SearchCompleteEvent, * Construct a new SearchCompleteEvent,
@ -132,16 +133,16 @@ public final class DiscoveryEventUtils {
* search. * search.
* @param groupingAttribute The grouping attribute used by the search. * @param groupingAttribute The grouping attribute used by the search.
* @param groupSort The sorting algorithm used for groups. * @param groupSort The sorting algorithm used for groups.
* @param fileSortMethod The sorting method used for files. * @param sortMethod The sorting method used for results.
*/ */
public SearchCompleteEvent(Map<GroupKey, Integer> groupMap, List<AbstractFilter> searchfilters, public SearchCompleteEvent(Map<GroupKey, Integer> groupMap, List<AbstractFilter> searchfilters,
DiscoveryAttributes.AttributeType groupingAttribute, Group.GroupSortingAlgorithm groupSort, DiscoveryAttributes.AttributeType groupingAttribute, Group.GroupSortingAlgorithm groupSort,
ResultsSorter.SortingMethod fileSortMethod) { ResultsSorter.SortingMethod sortMethod) {
this.groupMap = groupMap; this.groupMap = groupMap;
this.searchFilters = searchfilters; this.searchFilters = searchfilters;
this.groupingAttribute = groupingAttribute; this.groupingAttribute = groupingAttribute;
this.groupSort = groupSort; this.groupSort = groupSort;
this.fileSortMethod = fileSortMethod; this.sortMethod = sortMethod;
} }
/** /**
@ -154,7 +155,7 @@ public final class DiscoveryEventUtils {
} }
/** /**
* Get the file filters used by the search. * Get the filters used by the search.
* *
* @return The search filters which were used by the search. * @return The search filters which were used by the search.
*/ */
@ -181,12 +182,12 @@ public final class DiscoveryEventUtils {
} }
/** /**
* Get the sorting method used for files. * Get the sorting method used for results.
* *
* @return The sorting method used for files. * @return The sorting method used for results.
*/ */
public ResultsSorter.SortingMethod getFileSort() { public ResultsSorter.SortingMethod getResultSort() {
return fileSortMethod; return sortMethod;
} }
} }
@ -204,9 +205,9 @@ public final class DiscoveryEventUtils {
/** /**
* Construct a new PageRetrievedEvent. * Construct a new PageRetrievedEvent.
* *
* @param resultType The type of files which exist in the page. * @param resultType The type of results which exist in the page.
* @param page The number of the page which was retrieved. * @param page The number of the page which was retrieved.
* @param results The list of files in the page retrieved. * @param results The list of results in the page retrieved.
*/ */
public PageRetrievedEvent(Type resultType, int page, List<Result> results) { public PageRetrievedEvent(Type resultType, int page, List<Result> results) {
this.results = results; this.results = results;
@ -215,9 +216,9 @@ public final class DiscoveryEventUtils {
} }
/** /**
* Get the list of files in the page retrieved. * Get the list of results in the page retrieved.
* *
* @return The list of files in the page retrieved. * @return The list of results in the page retrieved.
*/ */
public List<Result> getSearchResults() { public List<Result> getSearchResults() {
return Collections.unmodifiableList(results); return Collections.unmodifiableList(results);
@ -233,9 +234,9 @@ public final class DiscoveryEventUtils {
} }
/** /**
* Get the type of files which exist in the page. * Get the type of results which exist in the page.
* *
* @return The type of files which exist in the page. * @return The type of results which exist in the page.
*/ */
public Type getType() { public Type getType() {
return resultType; return resultType;
@ -256,7 +257,7 @@ public final class DiscoveryEventUtils {
} }
/** /**
* Event to signal that a search has been cancelled * Event to signal that a search has been cancelled.
*/ */
public static final class SearchCancelledEvent { public static final class SearchCancelledEvent {
@ -280,7 +281,7 @@ public final class DiscoveryEventUtils {
private final List<AbstractFilter> searchfilters; private final List<AbstractFilter> searchfilters;
private final DiscoveryAttributes.AttributeType groupingAttribute; private final DiscoveryAttributes.AttributeType groupingAttribute;
private final Group.GroupSortingAlgorithm groupSort; private final Group.GroupSortingAlgorithm groupSort;
private final ResultsSorter.SortingMethod fileSortMethod; private final ResultsSorter.SortingMethod sortMethod;
/** /**
* Construct a new GroupSelectedEvent. * Construct a new GroupSelectedEvent.
@ -289,29 +290,30 @@ public final class DiscoveryEventUtils {
* search. * search.
* @param groupingAttribute The grouping attribute used by the search. * @param groupingAttribute The grouping attribute used by the search.
* @param groupSort The sorting algorithm used for groups. * @param groupSort The sorting algorithm used for groups.
* @param fileSortMethod The sorting method used for files. * @param sortMethod The sorting method used for results.
* @param groupKey The key associated with the group which was * @param groupKey The key associated with the group which was
* selected. * selected.
* @param groupSize The number of files in the group which was * @param groupSize The number of results in the group which was
* selected. * selected.
* @param resultType The type of files which exist in the group. * @param resultType The type of results which exist in the
* group.
*/ */
public GroupSelectedEvent(List<AbstractFilter> searchfilters, public GroupSelectedEvent(List<AbstractFilter> searchfilters,
DiscoveryAttributes.AttributeType groupingAttribute, Group.GroupSortingAlgorithm groupSort, DiscoveryAttributes.AttributeType groupingAttribute, Group.GroupSortingAlgorithm groupSort,
ResultsSorter.SortingMethod fileSortMethod, GroupKey groupKey, int groupSize, Type resultType) { ResultsSorter.SortingMethod sortMethod, GroupKey groupKey, int groupSize, Type resultType) {
this.searchfilters = searchfilters; this.searchfilters = searchfilters;
this.groupingAttribute = groupingAttribute; this.groupingAttribute = groupingAttribute;
this.groupSort = groupSort; this.groupSort = groupSort;
this.fileSortMethod = fileSortMethod; this.sortMethod = sortMethod;
this.groupKey = groupKey; this.groupKey = groupKey;
this.groupSize = groupSize; this.groupSize = groupSize;
this.resultType = resultType; this.resultType = resultType;
} }
/** /**
* Get the type of files which exist in the group. * Get the type of results which exist in the group.
* *
* @return The type of files which exist in the group. * @return The type of results which exist in the group.
*/ */
public Type getResultType() { public Type getResultType() {
return resultType; return resultType;
@ -329,9 +331,9 @@ public final class DiscoveryEventUtils {
} }
/** /**
* Get the number of files in the group which was selected. * Get the number of results in the group which was selected.
* *
* @return The number of files in the group which was selected. * @return The number of results in the group which was selected.
*/ */
public int getGroupSize() { public int getGroupSize() {
return groupSize; return groupSize;
@ -347,16 +349,16 @@ public final class DiscoveryEventUtils {
} }
/** /**
* Get the sorting method used for files in the group. * Get the sorting method used for results in the group.
* *
* @return The sorting method used for files. * @return The sorting method used for results.
*/ */
public ResultsSorter.SortingMethod getFileSort() { public ResultsSorter.SortingMethod getResultSort() {
return fileSortMethod; return sortMethod;
} }
/** /**
* Get the file filters which were used by the search * Get the result filters which were used by the search.
* *
* @return The search filters which were used by the search. * @return The search filters which were used by the search.
*/ */

View File

@ -19,7 +19,7 @@
package org.sleuthkit.autopsy.discovery.search; package org.sleuthkit.autopsy.discovery.search;
/** /**
* Exception type used for FileSearch. * Exception type used for Discovery search.
*/ */
final public class DiscoveryException extends Exception { final public class DiscoveryException extends Exception {

View File

@ -49,7 +49,7 @@ public class DiscoveryKeyUtils {
private final String keyString; private final String keyString;
private final Group.GroupSortingAlgorithm groupSortingType; private final Group.GroupSortingAlgorithm groupSortingType;
private final DiscoveryAttributes.AttributeType groupAttributeType; private final DiscoveryAttributes.AttributeType groupAttributeType;
private final ResultsSorter.SortingMethod fileSortingMethod; private final ResultsSorter.SortingMethod sortingMethod;
private final List<AbstractFilter> filters; private final List<AbstractFilter> filters;
private final SleuthkitCase sleuthkitCase; private final SleuthkitCase sleuthkitCase;
private final CentralRepository centralRepository; private final CentralRepository centralRepository;
@ -58,21 +58,21 @@ public class DiscoveryKeyUtils {
* Construct a new SearchKey with all information that defines a search. * Construct a new SearchKey with all information that defines a search.
* *
* @param userName The name of the user performing the search. * @param userName The name of the user performing the search.
* @param filters The FileFilters being used for the search. * @param filters The Filters being used for the search.
* @param groupAttributeType The AttributeType to group by. * @param groupAttributeType The AttributeType to group by.
* @param groupSortingType The algorithm to sort the groups by. * @param groupSortingType The algorithm to sort the groups by.
* @param fileSortingMethod The method to sort the files by. * @param sortingMethod The method to sort the results by.
* @param sleuthkitCase The SleuthkitCase being searched. * @param sleuthkitCase The SleuthkitCase being searched.
* @param centralRepository The Central Repository being searched. * @param centralRepository The Central Repository being searched.
*/ */
SearchKey(String userName, List<AbstractFilter> filters, SearchKey(String userName, List<AbstractFilter> filters,
DiscoveryAttributes.AttributeType groupAttributeType, DiscoveryAttributes.AttributeType groupAttributeType,
Group.GroupSortingAlgorithm groupSortingType, Group.GroupSortingAlgorithm groupSortingType,
ResultsSorter.SortingMethod fileSortingMethod, ResultsSorter.SortingMethod sortingMethod,
SleuthkitCase sleuthkitCase, CentralRepository centralRepository) { SleuthkitCase sleuthkitCase, CentralRepository centralRepository) {
this.groupAttributeType = groupAttributeType; this.groupAttributeType = groupAttributeType;
this.groupSortingType = groupSortingType; this.groupSortingType = groupSortingType;
this.fileSortingMethod = fileSortingMethod; this.sortingMethod = sortingMethod;
this.filters = filters; this.filters = filters;
StringBuilder searchStringBuilder = new StringBuilder(); StringBuilder searchStringBuilder = new StringBuilder();
@ -80,7 +80,7 @@ public class DiscoveryKeyUtils {
for (AbstractFilter filter : filters) { for (AbstractFilter filter : filters) {
searchStringBuilder.append(filter.toString()); searchStringBuilder.append(filter.toString());
} }
searchStringBuilder.append(groupAttributeType).append(groupSortingType).append(fileSortingMethod); searchStringBuilder.append(groupAttributeType).append(groupSortingType).append(sortingMethod);
keyString = searchStringBuilder.toString(); keyString = searchStringBuilder.toString();
this.sleuthkitCase = sleuthkitCase; this.sleuthkitCase = sleuthkitCase;
this.centralRepository = centralRepository; this.centralRepository = centralRepository;
@ -89,13 +89,19 @@ public class DiscoveryKeyUtils {
/** /**
* Construct a SearchKey without a SleuthkitCase or CentralRepositry * Construct a SearchKey without a SleuthkitCase or CentralRepositry
* instance. * instance.
*
* @param userName The name of the user performing the search.
* @param filters The Filters being used for the search.
* @param groupAttributeType The AttributeType to group by.
* @param groupSortingType The algorithm to sort the groups by.
* @param sortingMethod The method to sort the results by.
*/ */
SearchKey(String userName, List<AbstractFilter> filters, SearchKey(String userName, List<AbstractFilter> filters,
DiscoveryAttributes.AttributeType groupAttributeType, DiscoveryAttributes.AttributeType groupAttributeType,
Group.GroupSortingAlgorithm groupSortingType, Group.GroupSortingAlgorithm groupSortingType,
ResultsSorter.SortingMethod fileSortingMethod) { ResultsSorter.SortingMethod sortingMethod) {
this(userName, filters, groupAttributeType, groupSortingType, this(userName, filters, groupAttributeType, groupSortingType,
fileSortingMethod, null, null); sortingMethod, null, null);
} }
@Override @Override
@ -166,18 +172,28 @@ public class DiscoveryKeyUtils {
} }
/** /**
* Get the fileSorting * Get the SortingMethod for this key.
* *
* @return * @return The SortingMethod for this key.
*/ */
ResultsSorter.SortingMethod getFileSortingMethod() { ResultsSorter.SortingMethod getFileSortingMethod() {
return fileSortingMethod; return sortingMethod;
} }
/**
* Get the case database for this key.
*
* @return The case database for this key.
*/
SleuthkitCase getSleuthkitCase() { SleuthkitCase getSleuthkitCase() {
return this.sleuthkitCase; return this.sleuthkitCase;
} }
/**
* Get the central repository for this key.
*
* @return The central repository for this key.
*/
CentralRepository getCentralRepository() { CentralRepository getCentralRepository() {
return this.centralRepository; return this.centralRepository;
} }
@ -199,7 +215,7 @@ public class DiscoveryKeyUtils {
/** /**
* Subclasses must implement equals(). * Subclasses must implement equals().
* *
* @param otherKey * @param otherKey The GroupKey to compare to this key.
* *
* @return true if the keys are equal, false otherwise * @return true if the keys are equal, false otherwise
*/ */
@ -209,7 +225,7 @@ public class DiscoveryKeyUtils {
/** /**
* Subclasses must implement hashCode(). * Subclasses must implement hashCode().
* *
* @return the hash code * @return The hash code for the GroupKey.
*/ */
@Override @Override
abstract public int hashCode(); abstract public int hashCode();
@ -219,9 +235,9 @@ public class DiscoveryKeyUtils {
* case where two different GroupKey subclasses are compared against * case where two different GroupKey subclasses are compared against
* each other. Use a lexicographic comparison on the class names. * each other. Use a lexicographic comparison on the class names.
* *
* @param otherGroupKey The other group key * @param otherGroupKey The other group key.
* *
* @return result of alphabetical comparison on the class name * @return Result of alphabetical comparison on the class name.
*/ */
int compareClassNames(GroupKey otherGroupKey) { int compareClassNames(GroupKey otherGroupKey) {
return this.getClass().getName().compareTo(otherGroupKey.getClass().getName()); return this.getClass().getName().compareTo(otherGroupKey.getClass().getName());
@ -234,12 +250,17 @@ public class DiscoveryKeyUtils {
} }
/** /**
* Key representing a file size group * Key representing a file size group.
*/ */
static class FileSizeGroupKey extends GroupKey { static class FileSizeGroupKey extends GroupKey {
private final SearchData.FileSize fileSize; private final SearchData.FileSize fileSize;
/**
* Construct a new FileSizeGroupKey.
*
* @param file The file to create the group key for.
*/
FileSizeGroupKey(Result file) { FileSizeGroupKey(Result file) {
ResultFile resultFile = (ResultFile) file; ResultFile resultFile = (ResultFile) file;
if (resultFile.getFileType() == SearchData.Type.VIDEO) { if (resultFile.getFileType() == SearchData.Type.VIDEO) {
@ -284,7 +305,9 @@ public class DiscoveryKeyUtils {
} }
/** /**
* @return the fileSize * The size of the file.
*
* @return The size of the file.
*/ */
SearchData.FileSize getFileSize() { SearchData.FileSize getFileSize() {
return fileSize; return fileSize;
@ -292,12 +315,17 @@ public class DiscoveryKeyUtils {
} }
/** /**
* Key representing a file type group * Key representing a file type group.
*/ */
static class FileTypeGroupKey extends GroupKey { static class FileTypeGroupKey extends GroupKey {
private final SearchData.Type fileType; private final SearchData.Type fileType;
/**
* Construct a new FileTypeGroupKey.
*
* @param file The file to create the group key for.
*/
FileTypeGroupKey(Result file) { FileTypeGroupKey(Result file) {
fileType = ((ResultFile) file).getFileType(); fileType = ((ResultFile) file).getFileType();
} }
@ -337,7 +365,9 @@ public class DiscoveryKeyUtils {
} }
/** /**
* @return the fileType * Get the type of file the group exists for.
*
* @return The type of file the group exists for.
*/ */
SearchData.Type getFileType() { SearchData.Type getFileType() {
return fileType; return fileType;
@ -345,18 +375,22 @@ public class DiscoveryKeyUtils {
} }
/** /**
* Key representing a keyword list group * Key representing a keyword list group.
*/ */
static class KeywordListGroupKey extends GroupKey { static class KeywordListGroupKey extends GroupKey {
private final List<String> keywordListNames; private final List<String> keywordListNames;
private final String keywordListNamesString; private final String keywordListNamesString;
/**
* Construct a new KeywordListGroupKey.
*
* @param file The file to create the group key for.
*/
@NbBundle.Messages({ @NbBundle.Messages({
"DiscoveryKeyUtils.KeywordListGroupKey.noKeywords=None"}) "DiscoveryKeyUtils.KeywordListGroupKey.noKeywords=None"})
KeywordListGroupKey(ResultFile file) { KeywordListGroupKey(ResultFile file) {
keywordListNames = file.getKeywordListNames(); keywordListNames = file.getKeywordListNames();
if (keywordListNames.isEmpty()) { if (keywordListNames.isEmpty()) {
keywordListNamesString = Bundle.DiscoveryKeyUtils_KeywordListGroupKey_noKeywords(); keywordListNamesString = Bundle.DiscoveryKeyUtils_KeywordListGroupKey_noKeywords();
} else { } else {
@ -411,14 +445,20 @@ public class DiscoveryKeyUtils {
} }
/** /**
* @return the keywordListNames * Get the list of keywords this group is for.
*
* @return The list of keywords this group is for.
*/ */
List<String> getKeywordListNames() { List<String> getKeywordListNames() {
return Collections.unmodifiableList(keywordListNames); return Collections.unmodifiableList(keywordListNames);
} }
/** /**
* @return the keywordListNamesString * Get the string which represents the keyword names represented by this
* group key.
*
* @return The string which represents the keyword names represented by
* this group key.
*/ */
String getKeywordListNamesString() { String getKeywordListNamesString() {
return keywordListNamesString; return keywordListNamesString;
@ -426,13 +466,18 @@ public class DiscoveryKeyUtils {
} }
/** /**
* Key representing a file tag group * Key representing a file tag group.
*/ */
static class FileTagGroupKey extends GroupKey { static class FileTagGroupKey extends GroupKey {
private final List<String> tagNames; private final List<String> tagNames;
private final String tagNamesString; private final String tagNamesString;
/**
* Construct a new FileTagGroupKey.
*
* @param file The file to create the group key for.
*/
@NbBundle.Messages({ @NbBundle.Messages({
"DiscoveryKeyUtils.FileTagGroupKey.noSets=None"}) "DiscoveryKeyUtils.FileTagGroupKey.noSets=None"})
FileTagGroupKey(ResultFile file) { FileTagGroupKey(ResultFile file) {
@ -477,11 +522,9 @@ public class DiscoveryKeyUtils {
if (otherKey == this) { if (otherKey == this) {
return true; return true;
} }
if (!(otherKey instanceof FileTagGroupKey)) { if (!(otherKey instanceof FileTagGroupKey)) {
return false; return false;
} }
FileTagGroupKey otherFileTagGroupKey = (FileTagGroupKey) otherKey; FileTagGroupKey otherFileTagGroupKey = (FileTagGroupKey) otherKey;
return getTagNamesString().equals(otherFileTagGroupKey.getTagNamesString()); return getTagNamesString().equals(otherFileTagGroupKey.getTagNamesString());
} }
@ -492,14 +535,20 @@ public class DiscoveryKeyUtils {
} }
/** /**
* @return the tagNames * Get the list of tag names which are represented by this group.
*
* @return The list of tag names which are represented by this group.
*/ */
List<String> getTagNames() { List<String> getTagNames() {
return Collections.unmodifiableList(tagNames); return Collections.unmodifiableList(tagNames);
} }
/** /**
* @return the tagNamesString * Get the String representation of the tags which are represented by
* this group.
*
* @return The String representation of the tags which are represented
* by this group.
*/ */
String getTagNamesString() { String getTagNamesString() {
return tagNamesString; return tagNamesString;
@ -507,13 +556,18 @@ public class DiscoveryKeyUtils {
} }
/** /**
* Key representing a parent path group * Key representing a parent path group.
*/ */
static class ParentPathGroupKey extends GroupKey { static class ParentPathGroupKey extends GroupKey {
private String parentPath; private String parentPath;
private Long parentID; private Long parentID;
/**
* Construct a new ParentPathGroupKey.
*
* @param file The file to create the group key for.
*/
ParentPathGroupKey(ResultFile file) { ParentPathGroupKey(ResultFile file) {
Content parent; Content parent;
try { try {
@ -600,14 +654,18 @@ public class DiscoveryKeyUtils {
} }
/** /**
* @return the parentPath * Get the parent path this group is for.
*
* @return The parent path this group is for as a String.
*/ */
String getParentPath() { String getParentPath() {
return parentPath; return parentPath;
} }
/** /**
* @return the parentID * Get the object ID of the parent object.
*
* @return The object ID of the parent object.
*/ */
Long getParentID() { Long getParentID() {
return parentID; return parentID;
@ -615,13 +673,18 @@ public class DiscoveryKeyUtils {
} }
/** /**
* Key representing a data source group * Key representing a data source group.
*/ */
static class DataSourceGroupKey extends GroupKey { static class DataSourceGroupKey extends GroupKey {
private final long dataSourceID; private final long dataSourceID;
private String displayName; private String displayName;
/**
* Construct a new DataSourceGroupKey.
*
* @param result The Result to create the group key for.
*/
@NbBundle.Messages({ @NbBundle.Messages({
"# {0} - Data source name", "# {0} - Data source name",
"# {1} - Data source ID", "# {1} - Data source ID",
@ -676,7 +739,9 @@ public class DiscoveryKeyUtils {
} }
/** /**
* @return the dataSourceID * Get the object ID of the data source.
*
* @return The object ID of the data source.
*/ */
long getDataSourceID() { long getDataSourceID() {
return dataSourceID; return dataSourceID;
@ -689,6 +754,9 @@ public class DiscoveryKeyUtils {
*/ */
static class NoGroupingGroupKey extends GroupKey { static class NoGroupingGroupKey extends GroupKey {
/**
* Constructor for dummy group which puts all files together.
*/
NoGroupingGroupKey() { NoGroupingGroupKey() {
// Nothing to save - all files will get the same GroupKey // Nothing to save - all files will get the same GroupKey
} }
@ -726,14 +794,19 @@ public class DiscoveryKeyUtils {
} }
/** /**
* Key representing a central repository frequency group * Key representing a central repository frequency group.
*/ */
static class FrequencyGroupKey extends GroupKey { static class FrequencyGroupKey extends GroupKey {
private final SearchData.Frequency frequency; private final SearchData.Frequency frequency;
FrequencyGroupKey(Result file) { /**
frequency = file.getFrequency(); * Construct a new FrequencyGroupKey.
*
* @param result The Result to create the group key for.
*/
FrequencyGroupKey(Result result) {
frequency = result.getFrequency();
} }
@Override @Override
@ -771,7 +844,9 @@ public class DiscoveryKeyUtils {
} }
/** /**
* @return the frequency * Get the frequency which the group is for.
*
* @return The frequency which the group is for.
*/ */
SearchData.Frequency getFrequency() { SearchData.Frequency getFrequency() {
return frequency; return frequency;
@ -779,13 +854,18 @@ public class DiscoveryKeyUtils {
} }
/** /**
* Key representing a hash hits group * Key representing a hash hits group.
*/ */
static class HashHitsGroupKey extends GroupKey { static class HashHitsGroupKey extends GroupKey {
private final List<String> hashSetNames; private final List<String> hashSetNames;
private final String hashSetNamesString; private final String hashSetNamesString;
/**
* Construct a new HashHitsGroupKey.
*
* @param file The file to create the group key for.
*/
@NbBundle.Messages({ @NbBundle.Messages({
"DiscoveryKeyUtils.HashHitsGroupKey.noHashHits=None"}) "DiscoveryKeyUtils.HashHitsGroupKey.noHashHits=None"})
HashHitsGroupKey(ResultFile file) { HashHitsGroupKey(ResultFile file) {
@ -845,14 +925,18 @@ public class DiscoveryKeyUtils {
} }
/** /**
* @return the hashSetNames * Get the list of hash set names the group is for.
*
* @return The list of hash set names the group is for.
*/ */
List<String> getHashSetNames() { List<String> getHashSetNames() {
return Collections.unmodifiableList(hashSetNames); return Collections.unmodifiableList(hashSetNames);
} }
/** /**
* @return the hashSetNamesString * Get the String representation of the list of hash set names.
*
* @return The String representation of the list of hash set names.
*/ */
String getHashSetNamesString() { String getHashSetNamesString() {
return hashSetNamesString; return hashSetNamesString;
@ -860,13 +944,18 @@ public class DiscoveryKeyUtils {
} }
/** /**
* Key representing a interesting item set group * Key representing a interesting item set group.
*/ */
static class InterestingItemGroupKey extends GroupKey { static class InterestingItemGroupKey extends GroupKey {
private final List<String> interestingItemSetNames; private final List<String> interestingItemSetNames;
private final String interestingItemSetNamesString; private final String interestingItemSetNamesString;
/**
* Construct a new InterestingItemGroupKey.
*
* @param file The file to create the group key for.
*/
@NbBundle.Messages({ @NbBundle.Messages({
"DiscoveryKeyUtils.InterestingItemGroupKey.noSets=None"}) "DiscoveryKeyUtils.InterestingItemGroupKey.noSets=None"})
InterestingItemGroupKey(ResultFile file) { InterestingItemGroupKey(ResultFile file) {
@ -926,14 +1015,20 @@ public class DiscoveryKeyUtils {
} }
/** /**
* @return the interestingItemSetNames * Get the list of interesting item set names the group is for.
*
* @return The list of interesting item set names the group is for.
*/ */
List<String> getInterestingItemSetNames() { List<String> getInterestingItemSetNames() {
return Collections.unmodifiableList(interestingItemSetNames); return Collections.unmodifiableList(interestingItemSetNames);
} }
/** /**
* @return the interestingItemSetNamesString * Get the String representation of the interesting item set names the
* group is for.
*
* @return The String representation of the interesting item set names
* the group is for.
*/ */
String getInterestingItemSetNamesString() { String getInterestingItemSetNamesString() {
return interestingItemSetNamesString; return interestingItemSetNamesString;
@ -948,6 +1043,11 @@ public class DiscoveryKeyUtils {
private final Long epochDate; private final Long epochDate;
private final String dateNameString; private final String dateNameString;
/**
* Construct a new MostRecentActivityDateGroupKey.
*
* @param result The Result to create the group key for.
*/
@NbBundle.Messages({ @NbBundle.Messages({
"DiscoveryKeyUtils.MostRecentActivityDateGroupKey.noDate=No Date Available"}) "DiscoveryKeyUtils.MostRecentActivityDateGroupKey.noDate=No Date Available"})
MostRecentActivityDateGroupKey(Result result) { MostRecentActivityDateGroupKey(Result result) {
@ -1018,7 +1118,7 @@ public class DiscoveryKeyUtils {
/** /**
* Get the name which identifies this group. * Get the name which identifies this group.
* *
* @return The dateNameString * @return The dateNameString.
*/ */
String getDateNameString() { String getDateNameString() {
return dateNameString; return dateNameString;
@ -1033,6 +1133,11 @@ public class DiscoveryKeyUtils {
private final Long epochDate; private final Long epochDate;
private final String dateNameString; private final String dateNameString;
/**
* Construct a new FirstActivityDateGroupKey.
*
* @param result The Result to create the group key for.
*/
@NbBundle.Messages({ @NbBundle.Messages({
"DiscoveryKeyUtils.FirstActivityDateGroupKey.noDate=No Date Available"}) "DiscoveryKeyUtils.FirstActivityDateGroupKey.noDate=No Date Available"})
FirstActivityDateGroupKey(Result result) { FirstActivityDateGroupKey(Result result) {
@ -1103,21 +1208,26 @@ public class DiscoveryKeyUtils {
/** /**
* Get the name which identifies this group. * Get the name which identifies this group.
* *
* @return The dateNameString * @return The dateNameString.
*/ */
String getDateNameString() { String getDateNameString() {
return dateNameString; return dateNameString;
} }
} }
/** /**
* Key representing the number of visits. * Key representing the number of visits.
*/ */
static class NumberOfVisitsGroupKey extends GroupKey { static class NumberOfVisitsGroupKey extends GroupKey {
private final String displayName; private final String displayName;
private final Long visits; private final Long visits;
/**
* Construct a new NumberOfVisitsGroupKey.
*
* @param result The Result to create the group key for.
*/
@NbBundle.Messages({ @NbBundle.Messages({
"# {0} - totalVisits", "# {0} - totalVisits",
"DiscoveryKeyUtils.NumberOfVisitsGroupKey.displayName={0} visits", "DiscoveryKeyUtils.NumberOfVisitsGroupKey.displayName={0} visits",
@ -1135,21 +1245,26 @@ public class DiscoveryKeyUtils {
visits = -1L; visits = -1L;
} }
} }
@Override @Override
String getDisplayName() { String getDisplayName() {
return displayName; return displayName;
} }
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hash(displayName); return Objects.hash(displayName);
} }
/**
* Get the number of visits this group is for.
*
* @return The number of visits this group is for.
*/
Long getVisits() { Long getVisits() {
return visits; return visits;
} }
@Override @Override
public boolean equals(Object otherKey) { public boolean equals(Object otherKey) {
if (otherKey == this) { if (otherKey == this) {
@ -1163,7 +1278,7 @@ public class DiscoveryKeyUtils {
NumberOfVisitsGroupKey visitsKey = (NumberOfVisitsGroupKey) otherKey; NumberOfVisitsGroupKey visitsKey = (NumberOfVisitsGroupKey) otherKey;
return visits.equals(visitsKey.getVisits()); return visits.equals(visitsKey.getVisits());
} }
@Override @Override
public int compareTo(GroupKey otherGroupKey) { public int compareTo(GroupKey otherGroupKey) {
if (otherGroupKey instanceof NumberOfVisitsGroupKey) { if (otherGroupKey instanceof NumberOfVisitsGroupKey) {
@ -1176,18 +1291,22 @@ public class DiscoveryKeyUtils {
} }
/** /**
* Key representing an object detected group * Key representing an object detected group.
*/ */
static class ObjectDetectedGroupKey extends GroupKey { static class ObjectDetectedGroupKey extends GroupKey {
private final List<String> objectDetectedNames; private final List<String> objectDetectedNames;
private final String objectDetectedNamesString; private final String objectDetectedNamesString;
/**
* Construct a new ObjectDetectedGroupKey.
*
* @param file The file to create the group key for.
*/
@NbBundle.Messages({ @NbBundle.Messages({
"DiscoveryKeyUtils.ObjectDetectedGroupKey.noSets=None"}) "DiscoveryKeyUtils.ObjectDetectedGroupKey.noSets=None"})
ObjectDetectedGroupKey(ResultFile file) { ObjectDetectedGroupKey(ResultFile file) {
objectDetectedNames = file.getObjectDetectedNames(); objectDetectedNames = file.getObjectDetectedNames();
if (objectDetectedNames.isEmpty()) { if (objectDetectedNames.isEmpty()) {
objectDetectedNamesString = Bundle.DiscoveryKeyUtils_ObjectDetectedGroupKey_noSets(); objectDetectedNamesString = Bundle.DiscoveryKeyUtils_ObjectDetectedGroupKey_noSets();
} else { } else {
@ -1242,14 +1361,20 @@ public class DiscoveryKeyUtils {
} }
/** /**
* @return the objectDetectedNames * Get the list of object detected names for this group.
*
* @return The list of object detected names for this group.
*/ */
List<String> getObjectDetectedNames() { List<String> getObjectDetectedNames() {
return Collections.unmodifiableList(objectDetectedNames); return Collections.unmodifiableList(objectDetectedNames);
} }
/** /**
* @return the objectDetectedNamesString * Get the String representation of the object detected names for this
* group.
*
* @return The String representation of the object detected names for
* this group.
*/ */
String getObjectDetectedNamesString() { String getObjectDetectedNamesString() {
return objectDetectedNamesString; return objectDetectedNamesString;

View File

@ -32,34 +32,45 @@ import org.sleuthkit.datamodel.SleuthkitCase;
* Main class to perform the domain search. * Main class to perform the domain search.
*/ */
public class DomainSearch { public class DomainSearch {
private final DomainSearchCache searchCache; private final DomainSearchCache searchCache;
private final DomainSearchThumbnailCache thumbnailCache; private final DomainSearchThumbnailCache thumbnailCache;
/**
* Construct a new DomainSearch object.
*/
public DomainSearch() { public DomainSearch() {
this(new DomainSearchCache(), new DomainSearchThumbnailCache()); this(new DomainSearchCache(), new DomainSearchThumbnailCache());
} }
/**
* Construct a new DomainSearch object with an existing DomainSearchCache
* and DomainSearchThumbnailCache.
*
* @param cache The DomainSearchCache to use for this DomainSearch.
* @param thumbnailCache The DomainSearchThumnailCache to use for this
* DomainSearch.
*/
DomainSearch(DomainSearchCache cache, DomainSearchThumbnailCache thumbnailCache) { DomainSearch(DomainSearchCache cache, DomainSearchThumbnailCache thumbnailCache) {
this.searchCache = cache; this.searchCache = cache;
this.thumbnailCache = thumbnailCache; this.thumbnailCache = thumbnailCache;
} }
/** /**
* Run the domain search to get the group keys and sizes. Clears cache of * Run the domain search to get the group keys and sizes. Clears cache of
* search results, caching new results for access at later time. * search results, caching new results for access at later time.
* *
* @param userName The name of the user performing the search. * @param userName The name of the user performing the search.
* @param filters The filters to apply * @param filters The filters to apply.
* @param groupAttributeType The attribute to use for grouping * @param groupAttributeType The attribute to use for grouping.
* @param groupSortingType The method to use to sort the groups * @param groupSortingType The method to use to sort the groups.
* @param domainSortingMethod The method to use to sort the domains within * @param domainSortingMethod The method to use to sort the domains within
* the groups * the groups.
* @param caseDb The case database * @param caseDb The case database.
* @param centralRepoDb The central repository database. Can be null * @param centralRepoDb The central repository database. Can be null
* if not needed. * if not needed.
* *
* @return A LinkedHashMap grouped and sorted according to the parameters * @return A LinkedHashMap grouped and sorted according to the parameters.
* *
* @throws DiscoveryException * @throws DiscoveryException
*/ */
@ -69,9 +80,9 @@ public class DomainSearch {
Group.GroupSortingAlgorithm groupSortingType, Group.GroupSortingAlgorithm groupSortingType,
ResultsSorter.SortingMethod domainSortingMethod, ResultsSorter.SortingMethod domainSortingMethod,
SleuthkitCase caseDb, CentralRepository centralRepoDb) throws DiscoveryException { SleuthkitCase caseDb, CentralRepository centralRepoDb) throws DiscoveryException {
final Map<GroupKey, List<Result>> searchResults = searchCache.get( final Map<GroupKey, List<Result>> searchResults = searchCache.get(
userName, filters, groupAttributeType, groupSortingType, userName, filters, groupAttributeType, groupSortingType,
domainSortingMethod, caseDb, centralRepoDb); domainSortingMethod, caseDb, centralRepoDb);
// Transform the cached results into a map of group key to group size. // Transform the cached results into a map of group key to group size.
@ -88,20 +99,20 @@ public class DomainSearch {
* was not cached perform a search caching the groups. * was not cached perform a search caching the groups.
* *
* @param userName The name of the user performing the search. * @param userName The name of the user performing the search.
* @param filters The filters to apply * @param filters The filters to apply.
* @param groupAttributeType The attribute to use for grouping * @param groupAttributeType The attribute to use for grouping.
* @param groupSortingType The method to use to sort the groups * @param groupSortingType The method to use to sort the groups.
* @param domainSortingMethod The method to use to sort the Domains within * @param domainSortingMethod The method to use to sort the Domains within
* the groups * the groups.
* @param groupKey The key which uniquely identifies the group to * @param groupKey The key which uniquely identifies the group to
* get entries from * get entries from.
* @param startingEntry The first entry to return * @param startingEntry The first entry to return.
* @param numberOfEntries The number of entries to return * @param numberOfEntries The number of entries to return.
* @param caseDb The case database * @param caseDb The case database.
* @param centralRepoDb The central repository database. Can be null * @param centralRepoDb The central repository database. Can be null
* if not needed. * if not needed.
* *
* @return A LinkedHashMap grouped and sorted according to the parameters * @return A LinkedHashMap grouped and sorted according to the parameters.
* *
* @throws DiscoveryException * @throws DiscoveryException
*/ */
@ -112,9 +123,9 @@ public class DomainSearch {
ResultsSorter.SortingMethod domainSortingMethod, ResultsSorter.SortingMethod domainSortingMethod,
GroupKey groupKey, int startingEntry, int numberOfEntries, GroupKey groupKey, int startingEntry, int numberOfEntries,
SleuthkitCase caseDb, CentralRepository centralRepoDb) throws DiscoveryException { SleuthkitCase caseDb, CentralRepository centralRepoDb) throws DiscoveryException {
final Map<GroupKey, List<Result>> searchResults = searchCache.get( final Map<GroupKey, List<Result>> searchResults = searchCache.get(
userName, filters, groupAttributeType, groupSortingType, userName, filters, groupAttributeType, groupSortingType,
domainSortingMethod, caseDb, centralRepoDb); domainSortingMethod, caseDb, centralRepoDb);
final List<Result> domainsInGroup = searchResults.get(groupKey); final List<Result> domainsInGroup = searchResults.get(groupKey);
@ -131,11 +142,12 @@ public class DomainSearch {
* Get a thumbnail representation of a domain name. See * Get a thumbnail representation of a domain name. See
* DomainSearchThumbnailRequest for more details. * DomainSearchThumbnailRequest for more details.
* *
* @param thumbnailRequest Thumbnail request for domain * @param thumbnailRequest Thumbnail request for domain.
*
* @return An Image instance or null if no thumbnail is available. * @return An Image instance or null if no thumbnail is available.
* *
* @throws DiscoveryException If there is an error with Discovery related * @throws DiscoveryException If there is an error with Discovery related
* processing * processing.
*/ */
public Image getThumbnail(DomainSearchThumbnailRequest thumbnailRequest) throws DiscoveryException { public Image getThumbnail(DomainSearchThumbnailRequest thumbnailRequest) throws DiscoveryException {
return thumbnailCache.get(thumbnailRequest); return thumbnailCache.get(thumbnailRequest);

View File

@ -40,10 +40,11 @@ public class DomainSearchArtifactsCache {
* is new, the results will be automatically loaded. * is new, the results will be automatically loaded.
* *
* @param request Artifact request, specifies type, Case, and domain name. * @param request Artifact request, specifies type, Case, and domain name.
* @return A list of matching artifacts *
* @return A list of matching artifacts.
* *
* @throws DiscoveryException Any error that occurs during the loading * @throws DiscoveryException Any error that occurs during the loading
* process. * process.
*/ */
public List<BlackboardArtifact> get(DomainSearchArtifactsRequest request) throws DiscoveryException { public List<BlackboardArtifact> get(DomainSearchArtifactsRequest request) throws DiscoveryException {
try { try {

View File

@ -31,6 +31,13 @@ public class DomainSearchArtifactsRequest {
private final String domain; private final String domain;
private final ARTIFACT_TYPE artifactType; private final ARTIFACT_TYPE artifactType;
/**
* Construct a new DomainSearchArtifactsRequest object.
*
* @param sleuthkitCase The case database for the search.
* @param domain The domain that artifacts are being requested for.
* @param artifactType The type of artifact being requested.
*/
public DomainSearchArtifactsRequest(SleuthkitCase sleuthkitCase, public DomainSearchArtifactsRequest(SleuthkitCase sleuthkitCase,
String domain, ARTIFACT_TYPE artifactType) { String domain, ARTIFACT_TYPE artifactType) {
this.sleuthkitCase = sleuthkitCase; this.sleuthkitCase = sleuthkitCase;
@ -38,14 +45,29 @@ public class DomainSearchArtifactsRequest {
this.artifactType = artifactType; this.artifactType = artifactType;
} }
/**
* Get the case database for the search.
*
* @return The case database for the search.
*/
public SleuthkitCase getSleuthkitCase() { public SleuthkitCase getSleuthkitCase() {
return sleuthkitCase; return sleuthkitCase;
} }
/**
* Get the domain that artifacts are being requested for.
*
* @return The domain that artifacts are being requested for.
*/
public String getDomain() { public String getDomain() {
return domain; return domain;
} }
/**
* Get the type of artifact being requested.
*
* @return The type of artifact being requested.
*/
public ARTIFACT_TYPE getArtifactType() { public ARTIFACT_TYPE getArtifactType() {
return artifactType; return artifactType;
} }
@ -55,11 +77,9 @@ public class DomainSearchArtifactsRequest {
if (other == this) { if (other == this) {
return true; return true;
} }
if (!(other instanceof DomainSearchArtifactsRequest)) { if (!(other instanceof DomainSearchArtifactsRequest)) {
return false; return false;
} }
DomainSearchArtifactsRequest otherRequest = (DomainSearchArtifactsRequest) other; DomainSearchArtifactsRequest otherRequest = (DomainSearchArtifactsRequest) other;
return this.sleuthkitCase == otherRequest.getSleuthkitCase() return this.sleuthkitCase == otherRequest.getSleuthkitCase()
&& this.domain.equals(otherRequest.getDomain()) && this.domain.equals(otherRequest.getDomain())

View File

@ -44,6 +44,21 @@ class DomainSearchCache {
/** /**
* Get domain search results matching the given parameters. If no results * Get domain search results matching the given parameters. If no results
* are found, the cache will automatically load them. * are found, the cache will automatically load them.
*
*
* @param userName The name of the user performing the search.
* @param filters The filters to apply.
* @param groupAttributeType The attribute to use for grouping.
* @param groupSortingType The method to use to sort the groups.
* @param fileSortingMethod The method to use to sort the domains within
* the groups.
* @param caseDb The case database.
* @param centralRepoDb The central repository database. Can be null if
* not needed.
*
* @return Domain search results matching the given parameters.
*
* @throws DiscoveryException
*/ */
Map<GroupKey, List<Result>> get(String userName, Map<GroupKey, List<Result>> get(String userName,
List<AbstractFilter> filters, List<AbstractFilter> filters,
@ -51,11 +66,9 @@ class DomainSearchCache {
Group.GroupSortingAlgorithm groupSortingType, Group.GroupSortingAlgorithm groupSortingType,
ResultsSorter.SortingMethod domainSortingMethod, ResultsSorter.SortingMethod domainSortingMethod,
SleuthkitCase caseDb, CentralRepository centralRepoDb) throws DiscoveryException { SleuthkitCase caseDb, CentralRepository centralRepoDb) throws DiscoveryException {
try { try {
final SearchKey searchKey = new SearchKey(userName, filters, groupAttributeType, final SearchKey searchKey = new SearchKey(userName, filters, groupAttributeType,
groupSortingType, domainSortingMethod, caseDb, centralRepoDb); groupSortingType, domainSortingMethod, caseDb, centralRepoDb);
return cache.get(searchKey); return cache.get(searchKey);
} catch (ExecutionException ex) { } catch (ExecutionException ex) {
throw new DiscoveryException("Error fetching results from cache", ex.getCause()); throw new DiscoveryException("Error fetching results from cache", ex.getCause());

View File

@ -47,17 +47,17 @@ import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
/** /**
* Loads domain search results for cache misses. This loader is a Guava cache loader, * Loads domain search results for cache misses. This loader is a Guava cache
* which will be used in tandem with the DomainSearchCache, which is backed by a * loader, which will be used in tandem with the DomainSearchCache, which is
* Guava LoadingCache. * backed by a Guava LoadingCache.
*/ */
class DomainSearchCacheLoader extends CacheLoader<SearchKey, Map<GroupKey, List<Result>>> { class DomainSearchCacheLoader extends CacheLoader<SearchKey, Map<GroupKey, List<Result>>> {
@Override @Override
public Map<GroupKey, List<Result>> load(SearchKey key) throws DiscoveryException, SQLException, TskCoreException { public Map<GroupKey, List<Result>> load(SearchKey key) throws DiscoveryException, SQLException, TskCoreException {
List<Result> domainResults = getResultDomainsFromDatabase(key); List<Result> domainResults = getResultDomainsFromDatabase(key);
// Apply secondary in memory filters // Apply secondary in memory filters
for (AbstractFilter filter : key.getFilters()) { for (AbstractFilter filter : key.getFilters()) {
if (filter.useAlternateFilter()) { if (filter.useAlternateFilter()) {
@ -76,95 +76,93 @@ class DomainSearchCacheLoader extends CacheLoader<SearchKey, Map<GroupKey, List<
/** /**
* Queries for domain names from the case database. * Queries for domain names from the case database.
* *
* @param key The SearchKey passed to the cache. * @param key The SearchKey passed to the cache.
* @return A list of results corresponding to the domains found in the *
* case database. * @return A list of results corresponding to the domains found in the case
* database.
*/ */
List<Result> getResultDomainsFromDatabase(SearchKey key) throws TskCoreException, SQLException, DiscoveryException { List<Result> getResultDomainsFromDatabase(SearchKey key) throws TskCoreException, SQLException, DiscoveryException {
// Filters chosen in the UI are aggregated into SQL statements to be used in // Filters chosen in the UI are aggregated into SQL statements to be used in
// the queries that follow. // the queries that follow.
final Pair<String, String> filterClauses = createWhereAndHavingClause(key.getFilters()); final Pair<String, String> filterClauses = createWhereAndHavingClause(key.getFilters());
final String whereClause = filterClauses.getLeft(); final String whereClause = filterClauses.getLeft();
final String havingClause = filterClauses.getRight(); final String havingClause = filterClauses.getRight();
// You may think of each row of this result as a TSK_DOMAIN attribute, where the parent // You may think of each row of this result as a TSK_DOMAIN attribute, where the parent
// artifact type is within the (optional) filter and the parent artifact // artifact type is within the (optional) filter and the parent artifact
// had a date time attribute that was within the (optional) filter. With this // had a date time attribute that was within the (optional) filter. With this
// table in hand, we can simply group by domain and apply aggregate functions // table in hand, we can simply group by domain and apply aggregate functions
// to get, for example, # of downloads, # of visits in last 60, etc. // to get, for example, # of downloads, # of visits in last 60, etc.
final String domainsTable = final String domainsTable
"SELECT LOWER(MAX(value_text)) AS domain," + = "SELECT LOWER(MAX(value_text)) AS domain,"
" MAX(value_int64) AS date," + + " MAX(value_int64) AS date,"
" artifact_id AS parent_artifact_id," + + " artifact_id AS parent_artifact_id,"
" MAX(artifact_type_id) AS parent_artifact_type_id " + + " MAX(artifact_type_id) AS parent_artifact_type_id "
+ "FROM blackboard_attributes "
"FROM blackboard_attributes " + + "WHERE " + whereClause + " "
"WHERE " + whereClause + " " + + "GROUP BY artifact_id "
+ "HAVING " + havingClause;
"GROUP BY artifact_id " +
"HAVING " + havingClause;
// Needed to populate the visitsInLast60 data. // Needed to populate the visitsInLast60 data.
final Instant currentTime = Instant.now(); final Instant currentTime = Instant.now();
final Instant sixtyDaysAgo = currentTime.minus(60, ChronoUnit.DAYS); final Instant sixtyDaysAgo = currentTime.minus(60, ChronoUnit.DAYS);
// Check the group attribute, if by data source then the GROUP BY clause // Check the group attribute, if by data source then the GROUP BY clause
// should group by data source id before grouping by domain. // should group by data source id before grouping by domain.
final AttributeType groupAttribute = key.getGroupAttributeType(); final AttributeType groupAttribute = key.getGroupAttributeType();
final String groupByClause = (groupAttribute instanceof DataSourceAttribute) ? final String groupByClause = (groupAttribute instanceof DataSourceAttribute)
"data_source_obj_id, domain" : "domain"; ? "data_source_obj_id, domain" : "domain";
final Optional<AbstractFilter> dataSourceFilter = key.getFilters().stream() final Optional<AbstractFilter> dataSourceFilter = key.getFilters().stream()
.filter(filter -> filter instanceof DataSourceFilter) .filter(filter -> filter instanceof DataSourceFilter)
.findFirst(); .findFirst();
String dataSourceWhereClause = null; String dataSourceWhereClause = null;
if (dataSourceFilter.isPresent()) { if (dataSourceFilter.isPresent()) {
dataSourceWhereClause = dataSourceFilter.get().getWhereClause(); dataSourceWhereClause = dataSourceFilter.get().getWhereClause();
} }
// This query just processes the domains table, performing additional // This query just processes the domains table, performing additional
// groupings and applying aggregate functions to calculate discovery data. // groupings and applying aggregate functions to calculate discovery data.
final String domainsQuery = final String domainsQuery
/*SELECT */" domain," + = /*
" MIN(date) AS activity_start," + * SELECT
" MAX(date) AS activity_end," + */ " domain,"
" SUM(CASE " + + " MIN(date) AS activity_start,"
" WHEN artifact_type_id = " + TSK_WEB_DOWNLOAD.getTypeID() + " THEN 1 " + + " MAX(date) AS activity_end,"
" ELSE 0 " + + " SUM(CASE "
" END) AS fileDownloads," + + " WHEN artifact_type_id = " + TSK_WEB_DOWNLOAD.getTypeID() + " THEN 1 "
" SUM(CASE " + + " ELSE 0 "
" WHEN artifact_type_id = " + TSK_WEB_HISTORY.getTypeID() + " THEN 1 " + + " END) AS fileDownloads,"
" ELSE 0 " + + " SUM(CASE "
" END) AS totalVisits," + + " WHEN artifact_type_id = " + TSK_WEB_HISTORY.getTypeID() + " THEN 1 "
" SUM(CASE " + + " ELSE 0 "
" WHEN artifact_type_id = " + TSK_WEB_HISTORY.getTypeID() + " AND" + + " END) AS totalVisits,"
" date BETWEEN " + sixtyDaysAgo.getEpochSecond() + " AND " + currentTime.getEpochSecond() + " THEN 1 " + + " SUM(CASE "
" ELSE 0 " + + " WHEN artifact_type_id = " + TSK_WEB_HISTORY.getTypeID() + " AND"
" END) AS last60," + + " date BETWEEN " + sixtyDaysAgo.getEpochSecond() + " AND " + currentTime.getEpochSecond() + " THEN 1 "
" data_source_obj_id AS dataSource " + + " ELSE 0 "
+ " END) AS last60,"
"FROM blackboard_artifacts" + + " data_source_obj_id AS dataSource "
" JOIN (" + domainsTable + ") AS domains_table" + + "FROM blackboard_artifacts"
" ON artifact_id = parent_artifact_id " + + " JOIN (" + domainsTable + ") AS domains_table"
+ " ON artifact_id = parent_artifact_id "
// Add the data source where clause here if present. + // Add the data source where clause here if present.
((dataSourceWhereClause != null) ? "WHERE " + dataSourceWhereClause + " " : "") + ((dataSourceWhereClause != null) ? "WHERE " + dataSourceWhereClause + " " : "")
+ "GROUP BY " + groupByClause;
"GROUP BY " + groupByClause;
final SleuthkitCase caseDb = key.getSleuthkitCase(); final SleuthkitCase caseDb = key.getSleuthkitCase();
final CaseDbAccessManager dbManager = caseDb.getCaseDbAccessManager(); final CaseDbAccessManager dbManager = caseDb.getCaseDbAccessManager();
final DomainCallback domainCallback = new DomainCallback(caseDb); final DomainCallback domainCallback = new DomainCallback(caseDb);
dbManager.select(domainsQuery, domainCallback); dbManager.select(domainsQuery, domainCallback);
if (domainCallback.getSQLException() != null) { if (domainCallback.getSQLException() != null) {
throw domainCallback.getSQLException(); throw domainCallback.getSQLException();
} }
if (domainCallback.getTskCoreException() != null) { if (domainCallback.getTskCoreException() != null) {
throw domainCallback.getTskCoreException(); throw domainCallback.getTskCoreException();
} }
@ -180,49 +178,51 @@ class DomainSearchCacheLoader extends CacheLoader<SearchKey, Map<GroupKey, List<
* below. If no dateTime filter is supplied, then in order for the query to * below. If no dateTime filter is supplied, then in order for the query to
* be correct, an additional clause needs to be added in. * be correct, an additional clause needs to be added in.
* *
* @param filters The list of filters to apply create the where clause from.
*
* @return The whereClause and havingClause as a pair. These methods are one * @return The whereClause and havingClause as a pair. These methods are one
* to stress that these clauses are tightly coupled. * to stress that these clauses are tightly coupled.
*/ */
Pair<String, String> createWhereAndHavingClause(List<AbstractFilter> filters) { Pair<String, String> createWhereAndHavingClause(List<AbstractFilter> filters) {
final StringJoiner whereClause = new StringJoiner(" OR "); final StringJoiner whereClause = new StringJoiner(" OR ");
final StringJoiner havingClause = new StringJoiner(" AND "); final StringJoiner havingClause = new StringJoiner(" AND ");
String artifactTypeFilter = null; String artifactTypeFilter = null;
boolean hasDateTimeFilter = false; boolean hasDateTimeFilter = false;
for (AbstractFilter filter : filters) { for (AbstractFilter filter : filters) {
if (filter instanceof ArtifactTypeFilter) { if (filter instanceof ArtifactTypeFilter) {
artifactTypeFilter = filter.getWhereClause(); artifactTypeFilter = filter.getWhereClause();
} else if (!(filter instanceof DataSourceFilter) && !filter.useAlternateFilter()) { } else if (!(filter instanceof DataSourceFilter) && !filter.useAlternateFilter()) {
if (filter instanceof ArtifactDateRangeFilter) { if (filter instanceof ArtifactDateRangeFilter) {
hasDateTimeFilter = true; hasDateTimeFilter = true;
} }
whereClause.add("(" + filter.getWhereClause() + ")"); whereClause.add("(" + filter.getWhereClause() + ")");
havingClause.add("SUM(CASE WHEN " + filter.getWhereClause() + " THEN 1 ELSE 0 END) > 0"); havingClause.add("SUM(CASE WHEN " + filter.getWhereClause() + " THEN 1 ELSE 0 END) > 0");
} }
} }
if (!hasDateTimeFilter) { if (!hasDateTimeFilter) {
whereClause.add(ArtifactDateRangeFilter.createAttributeTypeClause()); whereClause.add(ArtifactDateRangeFilter.createAttributeTypeClause());
} }
String domainAttributeFilter = "attribute_type_id = " + TSK_DOMAIN.getTypeID() + String domainAttributeFilter = "attribute_type_id = " + TSK_DOMAIN.getTypeID()
" AND value_text <> ''"; + " AND value_text <> ''";
whereClause.add("(" + domainAttributeFilter + ")"); whereClause.add("(" + domainAttributeFilter + ")");
havingClause.add("SUM(CASE WHEN " + domainAttributeFilter + " THEN 1 ELSE 0 END) > 0"); havingClause.add("SUM(CASE WHEN " + domainAttributeFilter + " THEN 1 ELSE 0 END) > 0");
return Pair.of( return Pair.of(
whereClause.toString() + ((artifactTypeFilter != null) ? " AND (" + artifactTypeFilter + ")" : ""), whereClause.toString() + ((artifactTypeFilter != null) ? " AND (" + artifactTypeFilter + ")" : ""),
havingClause.toString() havingClause.toString()
); );
} }
/** /**
* Callback to handle the result set of the domain query. This callback * Callback to handle the result set of the domain query. This callback is
* is responsible for mapping result set rows into ResultDomain objects * responsible for mapping result set rows into ResultDomain objects for
* for display. * display.
*/ */
private class DomainCallback implements CaseDbAccessQueryCallback { private class DomainCallback implements CaseDbAccessQueryCallback {
@ -230,17 +230,22 @@ class DomainSearchCacheLoader extends CacheLoader<SearchKey, Map<GroupKey, List<
private final SleuthkitCase skc; private final SleuthkitCase skc;
private SQLException sqlCause; private SQLException sqlCause;
private TskCoreException coreCause; private TskCoreException coreCause;
/**
* Construct a new DomainCallback object.
*
* @param skc The case database for the query being performed.
*/
private DomainCallback(SleuthkitCase skc) { private DomainCallback(SleuthkitCase skc) {
this.resultDomains = new ArrayList<>(); this.resultDomains = new ArrayList<>();
this.skc = skc; this.skc = skc;
} }
@Override @Override
public void process(ResultSet resultSet) { public void process(ResultSet resultSet) {
try { try {
resultSet.setFetchSize(500); resultSet.setFetchSize(500);
while (resultSet.next()) { while (resultSet.next()) {
String domain = resultSet.getString("domain"); String domain = resultSet.getString("domain");
Long activityStart = resultSet.getLong("activity_start"); Long activityStart = resultSet.getLong("activity_start");
@ -259,15 +264,15 @@ class DomainSearchCacheLoader extends CacheLoader<SearchKey, Map<GroupKey, List<
if (resultSet.wasNull()) { if (resultSet.wasNull()) {
totalVisits = null; totalVisits = null;
} }
Long visitsInLast60 = resultSet.getLong("last60"); Long visitsInLast60 = resultSet.getLong("last60");
if (resultSet.wasNull()) { if (resultSet.wasNull()) {
visitsInLast60 = null; visitsInLast60 = null;
} }
Long dataSourceID = resultSet.getLong("dataSource"); Long dataSourceID = resultSet.getLong("dataSource");
Content dataSource = skc.getContentById(dataSourceID); Content dataSource = skc.getContentById(dataSourceID);
resultDomains.add(new ResultDomain(domain, activityStart, resultDomains.add(new ResultDomain(domain, activityStart,
activityEnd, totalVisits, visitsInLast60, filesDownloaded, dataSource)); activityEnd, totalVisits, visitsInLast60, filesDownloaded, dataSource));
} }
@ -277,15 +282,32 @@ class DomainSearchCacheLoader extends CacheLoader<SearchKey, Map<GroupKey, List<
this.coreCause = ex; this.coreCause = ex;
} }
} }
/**
* Get the list of Result object for the domains which were in the
* search results.
*
* @return The list of Result object for the domains which were in the
* search results.
*/
private List<Result> getResultDomains() { private List<Result> getResultDomains() {
return Collections.unmodifiableList(this.resultDomains); return Collections.unmodifiableList(this.resultDomains);
} }
/**
* Get the SQLEception in an exception occurred.
*
* @return The SQLEception in an exception occurred.
*/
private SQLException getSQLException() { private SQLException getSQLException() {
return this.sqlCause; return this.sqlCause;
} }
/**
* Get the TskCoreException if a SQL exception occurred.
*
* @return The TskCoreException if a tsk core exception occurred.
*/
private TskCoreException getTskCoreException() { private TskCoreException getTskCoreException() {
return this.coreCause; return this.coreCause;
} }

View File

@ -27,7 +27,7 @@ import java.util.concurrent.ExecutionException;
* Caches thumbnail requests. * Caches thumbnail requests.
*/ */
public class DomainSearchThumbnailCache { public class DomainSearchThumbnailCache {
private static final int MAXIMUM_CACHE_SIZE = 500; private static final int MAXIMUM_CACHE_SIZE = 500;
private static final LoadingCache<DomainSearchThumbnailRequest, Image> cache private static final LoadingCache<DomainSearchThumbnailRequest, Image> cache
= CacheBuilder.newBuilder() = CacheBuilder.newBuilder()
@ -37,11 +37,14 @@ public class DomainSearchThumbnailCache {
/** /**
* Get a thumbnail for the requested domain. If the request is new, the * Get a thumbnail for the requested domain. If the request is new, the
* thumbnail will be automatically loaded. * thumbnail will be automatically loaded.
* *
* @param request Requested domain to thumbnail * @param request Requested domain to thumbnail.
* @return The thumbnail Image instance, or null if no thumbnail is available *
* * @return The thumbnail Image instance, or null if no thumbnail is
* @throws DiscoveryException If any error occurs during thumbnail generation. * available.
*
* @throws DiscoveryException If any error occurs during thumbnail
* generation.
*/ */
public Image get(DomainSearchThumbnailRequest request) throws DiscoveryException { public Image get(DomainSearchThumbnailRequest request) throws DiscoveryException {
try { try {

View File

@ -45,33 +45,38 @@ import org.openide.util.ImageUtilities;
* loaded from the DomainSearchArtifactsCache and then further analyzed. * loaded from the DomainSearchArtifactsCache and then further analyzed.
*/ */
public class DomainSearchThumbnailLoader extends CacheLoader<DomainSearchThumbnailRequest, Image> { public class DomainSearchThumbnailLoader extends CacheLoader<DomainSearchThumbnailRequest, Image> {
private static final String UNSUPPORTED_IMAGE = "org/sleuthkit/autopsy/images/image-extraction-not-supported.png";
private static final String UNSUPPORTED_IMAGE = "org/sleuthkit/autopsy/images/image-extraction-not-supported.png";
private static final String JPG_EXTENSION = "jpg"; private static final String JPG_EXTENSION = "jpg";
private static final String JPG_MIME_TYPE = "image/jpeg"; private static final String JPG_MIME_TYPE = "image/jpeg";
private final DomainSearchArtifactsCache artifactsCache; private final DomainSearchArtifactsCache artifactsCache;
/**
* Construct a new DomainSearchThumbnailLoader.
*/
public DomainSearchThumbnailLoader() { public DomainSearchThumbnailLoader() {
this(new DomainSearchArtifactsCache()); this(new DomainSearchArtifactsCache());
} }
/**
* Construct a new DomainSearchThumbnailLoader with an existing
* DomainSearchArtifactsCache.
*
* @param artifactsCache The DomainSearchArtifactsCache to use for this
* DomainSearchThumnailLoader.
*/
DomainSearchThumbnailLoader(DomainSearchArtifactsCache artifactsCache) { DomainSearchThumbnailLoader(DomainSearchArtifactsCache artifactsCache) {
this.artifactsCache = artifactsCache; this.artifactsCache = artifactsCache;
} }
@Override @Override
public Image load(DomainSearchThumbnailRequest thumbnailRequest) throws TskCoreException, DiscoveryException { public Image load(DomainSearchThumbnailRequest thumbnailRequest) throws TskCoreException, DiscoveryException {
final SleuthkitCase caseDb = thumbnailRequest.getSleuthkitCase(); final SleuthkitCase caseDb = thumbnailRequest.getSleuthkitCase();
final DomainSearchArtifactsRequest webDownloadsRequest = new DomainSearchArtifactsRequest( final DomainSearchArtifactsRequest webDownloadsRequest = new DomainSearchArtifactsRequest(
caseDb, thumbnailRequest.getDomain(), TSK_WEB_DOWNLOAD); caseDb, thumbnailRequest.getDomain(), TSK_WEB_DOWNLOAD);
final List<BlackboardArtifact> webDownloads = artifactsCache.get(webDownloadsRequest); final List<BlackboardArtifact> webDownloads = artifactsCache.get(webDownloadsRequest);
final List<AbstractFile> webDownloadPictures = getJpegsFromWebDownload(caseDb, webDownloads); final List<AbstractFile> webDownloadPictures = getJpegsFromWebDownload(caseDb, webDownloads);
Collections.sort(webDownloadPictures, (file1, file2) -> Long.compare(file1.getCrtime(), file2.getCrtime())); Collections.sort(webDownloadPictures, (file1, file2) -> Long.compare(file1.getCrtime(), file2.getCrtime()));
for (int i = webDownloadPictures.size() - 1; i >= 0; i--) { for (int i = webDownloadPictures.size() - 1; i >= 0; i--) {
// Get the most recent image, according to creation time. // Get the most recent image, according to creation time.
final AbstractFile mostRecent = webDownloadPictures.get(i); final AbstractFile mostRecent = webDownloadPictures.get(i);
@ -81,47 +86,53 @@ public class DomainSearchThumbnailLoader extends CacheLoader<DomainSearchThumbna
return candidateThumbnail; return candidateThumbnail;
} }
} }
final DomainSearchArtifactsRequest webCacheRequest = new DomainSearchArtifactsRequest( final DomainSearchArtifactsRequest webCacheRequest = new DomainSearchArtifactsRequest(
caseDb, thumbnailRequest.getDomain(), TSK_WEB_CACHE); caseDb, thumbnailRequest.getDomain(), TSK_WEB_CACHE);
final List<BlackboardArtifact> webCacheArtifacts = artifactsCache.get(webCacheRequest); final List<BlackboardArtifact> webCacheArtifacts = artifactsCache.get(webCacheRequest);
final List<AbstractFile> webCachePictures = getJpegsFromWebCache(caseDb, webCacheArtifacts); final List<AbstractFile> webCachePictures = getJpegsFromWebCache(caseDb, webCacheArtifacts);
Collections.sort(webCachePictures, (file1, file2) -> Long.compare(file1.getSize(), file2.getSize())); Collections.sort(webCachePictures, (file1, file2) -> Long.compare(file1.getSize(), file2.getSize()));
for (int i = webCachePictures.size() - 1; i >= 0; i--) { for (int i = webCachePictures.size() - 1; i >= 0; i--) {
// Get the largest image, according to file size. // Get the largest image, according to file size.
final AbstractFile largest = webCachePictures.get(i); final AbstractFile largest = webCachePictures.get(i);
final Image candidateThumbnail = ImageUtils.getThumbnail(largest, thumbnailRequest.getIconSize()); final Image candidateThumbnail = ImageUtils.getThumbnail(largest, thumbnailRequest.getIconSize());
if (candidateThumbnail != ImageUtils.getDefaultThumbnail()) { if (candidateThumbnail != ImageUtils.getDefaultThumbnail()) {
return candidateThumbnail; return candidateThumbnail;
} }
} }
return ImageUtilities.loadImage(UNSUPPORTED_IMAGE, false); return ImageUtilities.loadImage(UNSUPPORTED_IMAGE, false);
} }
/** /**
* Finds all JPEG source files from TSK_WEB_DOWNLOAD instances. * Finds all JPEG source files from TSK_WEB_DOWNLOAD instances.
*
* @param caseDb The case database being searched.
* @param artifacts The list of artifacts to get jpegs from.
*
* @return The list of AbstractFiles representing jpegs which were
* associated with the artifacts.
*
* @throws TskCoreException
*/ */
private List<AbstractFile> getJpegsFromWebDownload(SleuthkitCase caseDb, List<BlackboardArtifact> artifacts) throws TskCoreException { private List<AbstractFile> getJpegsFromWebDownload(SleuthkitCase caseDb, List<BlackboardArtifact> artifacts) throws TskCoreException {
final List<AbstractFile> jpegs = new ArrayList<>(); final List<AbstractFile> jpegs = new ArrayList<>();
for (BlackboardArtifact artifact : artifacts) { for (BlackboardArtifact artifact : artifacts) {
final Content sourceContent = caseDb.getContentById(artifact.getObjectID()); final Content sourceContent = caseDb.getContentById(artifact.getObjectID());
addIfJpeg(jpegs, sourceContent); addIfJpeg(jpegs, sourceContent);
} }
return jpegs; return jpegs;
} }
/** /**
* Finds all JPEG source files from TSK_WEB_CACHE instances. * Finds all JPEG source files from TSK_WEB_CACHE instances.
*
* @param caseDb The case database being searched.
* @param artifacts The list of artifacts to get jpegs from.
*
* @return The list of AbstractFiles representing jpegs which were
* associated with the artifacts.
*/ */
private List<AbstractFile> getJpegsFromWebCache(SleuthkitCase caseDb, List<BlackboardArtifact> artifacts) throws TskCoreException { private List<AbstractFile> getJpegsFromWebCache(SleuthkitCase caseDb, List<BlackboardArtifact> artifacts) throws TskCoreException {
final BlackboardAttribute.Type TSK_PATH_ID = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_PATH_ID); final BlackboardAttribute.Type TSK_PATH_ID = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_PATH_ID);
final List<AbstractFile> jpegs = new ArrayList<>(); final List<AbstractFile> jpegs = new ArrayList<>();
for (BlackboardArtifact artifact : artifacts) { for (BlackboardArtifact artifact : artifacts) {
final BlackboardAttribute tskPathId = artifact.getAttribute(TSK_PATH_ID); final BlackboardAttribute tskPathId = artifact.getAttribute(TSK_PATH_ID);
@ -130,12 +141,15 @@ public class DomainSearchThumbnailLoader extends CacheLoader<DomainSearchThumbna
addIfJpeg(jpegs, sourceContent); addIfJpeg(jpegs, sourceContent);
} }
} }
return jpegs; return jpegs;
} }
/** /**
* Checks if the candidate source content is indeed a JPEG file. * Checks if the candidate source content is indeed a JPEG file.
*
* @param files The list of source content files which are jpegs to
* add to.
* @param sourceContent The source content to check and possibly add.
*/ */
private void addIfJpeg(List<AbstractFile> files, Content sourceContent) { private void addIfJpeg(List<AbstractFile> files, Content sourceContent) {
if ((sourceContent instanceof AbstractFile) && !(sourceContent instanceof DataSource)) { if ((sourceContent instanceof AbstractFile) && !(sourceContent instanceof DataSource)) {

View File

@ -22,34 +22,57 @@ import java.util.Objects;
import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.SleuthkitCase;
/** /**
* Requests a thumbnail to be generated for a given Case, domain and * Requests a thumbnail to be generated for a given Case, domain and size.
* size. IconSize should be a value obtained from ImageUtils. * IconSize should be a value obtained from ImageUtils.
*/ */
public class DomainSearchThumbnailRequest { public class DomainSearchThumbnailRequest {
private final SleuthkitCase sleuthkitCase; private final SleuthkitCase sleuthkitCase;
private final String domain; private final String domain;
private final int iconSize; private final int iconSize;
public DomainSearchThumbnailRequest(SleuthkitCase sleuthkitCase, /**
* Construct a new DomainSearchThumbnailRequest.
*
* @param sleuthkitCase The case database for this thumbnail request.
* @param domain The domain name for this thumbnail request.
* @param iconSize The size of icon that this thumbnail request should
* retrieve.
*/
public DomainSearchThumbnailRequest(SleuthkitCase sleuthkitCase,
String domain, int iconSize) { String domain, int iconSize) {
this.sleuthkitCase = sleuthkitCase; this.sleuthkitCase = sleuthkitCase;
this.domain = domain; this.domain = domain;
this.iconSize = iconSize; this.iconSize = iconSize;
} }
/**
* Get the case database for this thumbnail request.
*
* @return The case database for this thumbnail request.
*/
public SleuthkitCase getSleuthkitCase() { public SleuthkitCase getSleuthkitCase() {
return sleuthkitCase; return sleuthkitCase;
} }
/**
* Get the domain name for this thumbnail request.
*
* @return The domain name for this thumbnail request.
*/
public String getDomain() { public String getDomain() {
return domain; return domain;
} }
/**
* Get the size of icon that this thumbnail request should retrieve.
*
* @return The size of icon that this thumbnail request should retrieve.
*/
public int getIconSize() { public int getIconSize() {
return iconSize; return iconSize;
} }
@Override @Override
public boolean equals(Object other) { public boolean equals(Object other) {
if (other == this) { if (other == this) {

View File

@ -24,7 +24,7 @@ import java.util.List;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
/** /**
* Class for storing files that belong to a particular group. * Class for storing results that belong to a particular group.
*/ */
public class Group implements Comparable<Group> { public class Group implements Comparable<Group> {
@ -34,7 +34,7 @@ public class Group implements Comparable<Group> {
private final String displayName; private final String displayName;
/** /**
* Create a FileGroup object with its first file. * Create a Group object with its first result.
* *
* @param groupSortingType The method for sorting the group * @param groupSortingType The method for sorting the group
* @param groupKey The GroupKey for this group * @param groupKey The GroupKey for this group
@ -49,13 +49,13 @@ public class Group implements Comparable<Group> {
/** /**
* Add a Result to the group. Will not be sorted at this time. * Add a Result to the group. Will not be sorted at this time.
* *
* @param result The Result to add to the FileGroup * @param result The Result to add to the Group.
*/ */
void addResult(Result result) { void addResult(Result result) {
if (result.getType() != SearchData.Type.DOMAIN && results.contains(result)) { if (result.getType() != SearchData.Type.DOMAIN && results.contains(result)) {
//dedupe files and show instances //dedupe files and show instances
ResultFile existingCopy = (ResultFile)results.get(results.indexOf(result)); //get the copy of this which exists in the list ResultFile existingCopy = (ResultFile) results.get(results.indexOf(result)); //get the copy of this which exists in the list
existingCopy.addDuplicate(((ResultFile)result).getFirstInstance()); existingCopy.addDuplicate(((ResultFile) result).getFirstInstance());
} else { } else {
//Domains and non files are not being deduped currently //Domains and non files are not being deduped currently
results.add(result); results.add(result);
@ -81,9 +81,9 @@ public class Group implements Comparable<Group> {
} }
/** /**
* Sort all the files in the group * Sort all the results in the group
*/ */
public void sortFiles(ResultsSorter sorter) { public void sortResults(ResultsSorter sorter) {
Collections.sort(results, sorter); Collections.sort(results, sorter);
} }
@ -91,10 +91,10 @@ public class Group implements Comparable<Group> {
* Compare this group to another group for sorting. Uses the algorithm * Compare this group to another group for sorting. Uses the algorithm
* specified in groupSortingType. * specified in groupSortingType.
* *
* @param otherGroup the group to compare this one to * @param otherGroup The group to compare this one to.
* *
* @return -1 if this group should be displayed before the other group, 1 * @return -1 if this group should be displayed before the other group, 1
* otherwise * otherwise.
*/ */
@Override @Override
public int compareTo(Group otherGroup) { public int compareTo(Group otherGroup) {
@ -109,12 +109,12 @@ public class Group implements Comparable<Group> {
} }
/** /**
* Compare two groups based on the group key * Compare two groups based on the group key.
* *
* @param group1 * @param group1 The first group to be compared.
* @param group2 * @param group2 The second group to be compared.
* *
* @return -1 if group1 should be displayed before group2, 1 otherwise * @return -1 if group1 should be displayed before group2, 1 otherwise.
*/ */
private static int compareGroupsByGroupKey(Group group1, Group group2) { private static int compareGroupsByGroupKey(Group group1, Group group2) {
return group1.getGroupKey().compareTo(group2.getGroupKey()); return group1.getGroupKey().compareTo(group2.getGroupKey());
@ -124,10 +124,10 @@ public class Group implements Comparable<Group> {
* Compare two groups based on the group size. Falls back on the group key * Compare two groups based on the group size. Falls back on the group key
* if the groups are the same size. * if the groups are the same size.
* *
* @param group1 * @param group1 The first group to be compared.
* @param group2 * @param group2 The second group to be compared.
* *
* @return -1 if group1 should be displayed before group2, 1 otherwise * @return -1 if group1 should be displayed before group2, 1 otherwise.
*/ */
private static int compareGroupsBySize(Group group1, Group group2) { private static int compareGroupsBySize(Group group1, Group group2) {
if (group1.getResults().size() != group2.getResults().size()) { if (group1.getResults().size() != group2.getResults().size()) {
@ -166,9 +166,9 @@ public class Group implements Comparable<Group> {
} }
/** /**
* Get the list of ResultFile objects in the group * Get the list of Result objects in the group.
* *
* @return List of ResultFile objects * @return The list of Result objects.
*/ */
public List<Result> getResults() { public List<Result> getResults() {
return Collections.unmodifiableList(results); return Collections.unmodifiableList(results);

View File

@ -26,16 +26,17 @@ import org.sleuthkit.datamodel.TskData;
* Container for domains that holds all necessary data for grouping and sorting. * Container for domains that holds all necessary data for grouping and sorting.
*/ */
public class ResultDomain extends Result { public class ResultDomain extends Result {
private final String domain; private final String domain;
private final Long activityStart; private final Long activityStart;
private final Long activityEnd; private final Long activityEnd;
private final Long totalVisits; private final Long totalVisits;
private final Long visitsInLast60; private final Long visitsInLast60;
private final Long filesDownloaded; private final Long filesDownloaded;
private final Content dataSource; private final Content dataSource;
private final long dataSourceId; private final long dataSourceId;
/** /**
* Create a ResultDomain from a String. * Create a ResultDomain from a String.
* *
@ -51,30 +52,61 @@ public class ResultDomain extends Result {
this.totalVisits = totalVisits; this.totalVisits = totalVisits;
this.visitsInLast60 = visitsInLast60; this.visitsInLast60 = visitsInLast60;
this.filesDownloaded = filesDownloaded; this.filesDownloaded = filesDownloaded;
this.setFrequency(SearchData.Frequency.UNKNOWN); this.setFrequency(SearchData.Frequency.UNKNOWN);
} }
/**
* Get the domain represented as a String.
*
* @return The String representation of the domain this result is for.
*/
public String getDomain() { public String getDomain() {
return this.domain; return this.domain;
} }
/**
* Get the date of first activity for this domain.
*
* @return The date of first activity for this domain.
*/
public Long getActivityStart() { public Long getActivityStart() {
return activityStart; return activityStart;
} }
/**
* Get the date of most recent activity for this domain.
*
* @return The date of most recent activity for this domain.
*/
public Long getActivityEnd() { public Long getActivityEnd() {
return activityEnd; return activityEnd;
} }
/**
* Get the total number of visits that this domain has had.
*
* @return The total number of visits that this domain has had.
*/
public Long getTotalVisits() { public Long getTotalVisits() {
return totalVisits; return totalVisits;
} }
/**
* Get the number of visits that this domain has had in the last 60 days.
*
* @return The number of visits that this domain has had in the last 60
* days.
*/
public Long getVisitsInLast60() { public Long getVisitsInLast60() {
return visitsInLast60; return visitsInLast60;
} }
/**
* Get the number of files downloaded associated with this domain.
*
* @return The number of files downloaded associated with this domain.
*/
public Long getFilesDownloaded() { public Long getFilesDownloaded() {
return filesDownloaded; return filesDownloaded;
} }
@ -98,12 +130,12 @@ public class ResultDomain extends Result {
public SearchData.Type getType() { public SearchData.Type getType() {
return SearchData.Type.DOMAIN; return SearchData.Type.DOMAIN;
} }
@Override @Override
public String toString() { public String toString() {
return "[domain=" + this.domain + ", data_source=" + this.dataSourceId + ", start=" return "[domain=" + this.domain + ", data_source=" + this.dataSourceId + ", start="
+ this.activityStart + ", end=" + this.activityEnd + ", totalVisits=" + this.totalVisits + ", visitsLast60=" + this.activityStart + ", end=" + this.activityEnd + ", totalVisits=" + this.totalVisits + ", visitsLast60="
+ this.visitsInLast60 + ", downloads=" + this.filesDownloaded + ", frequency=" + this.visitsInLast60 + ", downloads=" + this.filesDownloaded + ", frequency="
+ this.getFrequency() + "]"; + this.getFrequency() + "]";
} }
} }

View File

@ -27,7 +27,7 @@ import org.openide.util.NbBundle;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
/** /**
* Class used to sort ResultFiles using the supplied method. * Class used to sort Results using the supplied method.
*/ */
public class ResultsSorter implements Comparator<Result> { public class ResultsSorter implements Comparator<Result> {
@ -35,14 +35,14 @@ public class ResultsSorter implements Comparator<Result> {
/** /**
* Set up the sorter using the supplied sorting method. The sorting is * Set up the sorter using the supplied sorting method. The sorting is
* defined by a list of ResultFile comparators. These comparators will be * defined by a list of Result comparators. These comparators will be run in
* run in order until one returns a non-zero result. * order until one returns a non-zero result.
* *
* @param method The method that should be used to sort the files * @param method The method that should be used to sort the results.
*/ */
public ResultsSorter(SortingMethod method) { public ResultsSorter(SortingMethod method) {
// Set up the primary comparators that should applied to the files // Set up the primary comparators that should applied to the results
switch (method) { switch (method) {
case BY_DATA_SOURCE: case BY_DATA_SOURCE:
comparators.add(getDataSourceComparator()); comparators.add(getDataSourceComparator());
@ -75,7 +75,7 @@ public class ResultsSorter implements Comparator<Result> {
} }
// Add the default comparator to the end. This will ensure a consistent sort // Add the default comparator to the end. This will ensure a consistent sort
// order regardless of the order the files were added to the list. // order regardless of the order the results were added to the list.
comparators.add(getDefaultComparator()); comparators.add(getDefaultComparator());
} }
@ -90,36 +90,37 @@ public class ResultsSorter implements Comparator<Result> {
} }
} }
// The files are the same // The results are the same
return result; return result;
} }
/** /**
* Compare files using data source ID. Will order smallest to largest. * Compare results using data source ID. Will order smallest to largest.
* *
* @return -1 if file1 has the lower data source ID, 0 if equal, 1 otherwise * @return -1 if result1 has the lower data source ID, 0 if equal, 1
* otherwise.
*/ */
private static Comparator<Result> getDataSourceComparator() { private static Comparator<Result> getDataSourceComparator() {
return (Result result1, Result result2) -> Long.compare(result1.getDataSourceObjectId(), result2.getDataSourceObjectId()); return (Result result1, Result result2) -> Long.compare(result1.getDataSourceObjectId(), result2.getDataSourceObjectId());
} }
/** /**
* Compare files using their FileType enum. Orders based on the ranking in * Compare results using their Type enum. Orders based on the ranking in the
* the FileType enum. * Type enum.
* *
* @return -1 if file1 has the lower FileType value, 0 if equal, 1 otherwise * @return -1 if result1 has the lower Type value, 0 if equal, 1 otherwise.
*/ */
private static Comparator<Result> getTypeComparator() { private static Comparator<Result> getTypeComparator() {
return (Result result1, Result result2) -> Integer.compare(result1.getType().getRanking(), result2.getType().getRanking()); return (Result result1, Result result2) -> Integer.compare(result1.getType().getRanking(), result2.getType().getRanking());
} }
/** /**
* Compare files using a concatenated version of keyword list names. * Compare files using a concatenated version of keyword list names.
* Alphabetical by the list names with files with no keyword list hits going * Alphabetical by the list names with files with no keyword list hits going
* last. * last.
* *
* @return -1 if file1 has the earliest combined keyword list name, 0 if * @return -1 if result1 has the earliest combined keyword list name, 0 if
* equal, 1 otherwise * equal, 1 otherwise.
*/ */
private static Comparator<Result> getKeywordListNameComparator() { private static Comparator<Result> getKeywordListNameComparator() {
return (Result result1, Result result2) -> { return (Result result1, Result result2) -> {
@ -147,8 +148,8 @@ public class ResultsSorter implements Comparator<Result> {
/** /**
* Compare files based on parent path. Order alphabetically. * Compare files based on parent path. Order alphabetically.
* *
* @return -1 if file1's path comes first alphabetically, 0 if equal, 1 * @return -1 if result1's path comes first alphabetically, 0 if equal, 1
* otherwise * otherwise.
*/ */
private static Comparator<Result> getParentPathComparator() { private static Comparator<Result> getParentPathComparator() {
@ -178,10 +179,11 @@ public class ResultsSorter implements Comparator<Result> {
} }
/** /**
* Compare files based on number of occurrences in the central repository. * Compare results based on number of occurrences in the central repository.
* Order from most rare to least rare Frequency enum. * Order from most rare to least rare Frequency enum.
* *
* @return -1 if file1's rarity is lower than file2, 0 if equal, 1 otherwise * @return -1 if result1's rarity is lower than result2, 0 if equal, 1
* otherwise.
*/ */
private static Comparator<Result> getFrequencyComparator() { private static Comparator<Result> getFrequencyComparator() {
return (Result result1, Result result2) -> Integer.compare(result1.getFrequency().getRanking(), result2.getFrequency().getRanking()); return (Result result1, Result result2) -> Integer.compare(result1.getFrequency().getRanking(), result2.getFrequency().getRanking());
@ -190,8 +192,8 @@ public class ResultsSorter implements Comparator<Result> {
/** /**
* Compare files based on MIME type. Order is alphabetical. * Compare files based on MIME type. Order is alphabetical.
* *
* @return -1 if file1's MIME type comes before file2's, 0 if equal, 1 * @return -1 if result1's MIME type comes before result2's, 0 if equal, 1
* otherwise * otherwise.
*/ */
private static Comparator<Result> getMIMETypeComparator() { private static Comparator<Result> getMIMETypeComparator() {
return (Result result1, Result result2) -> { return (Result result1, Result result2) -> {
@ -205,7 +207,7 @@ public class ResultsSorter implements Comparator<Result> {
/** /**
* Compare files based on size. Order large to small. * Compare files based on size. Order large to small.
* *
* @return -1 if file1 is larger than file2, 0 if equal, 1 otherwise * @return -1 if result1 is larger than result2, 0 if equal, 1 otherwise.
*/ */
private static Comparator<Result> getFileSizeComparator() { private static Comparator<Result> getFileSizeComparator() {
return (Result result1, Result result2) -> { return (Result result1, Result result2) -> {
@ -219,7 +221,7 @@ public class ResultsSorter implements Comparator<Result> {
/** /**
* Compare files based on file name. Order alphabetically. * Compare files based on file name. Order alphabetically.
* *
* @return -1 if file1 comes before file2, 0 if equal, 1 otherwise * @return -1 if result1 comes before result2, 0 if equal, 1 otherwise.
*/ */
private static Comparator<Result> getFileNameComparator() { private static Comparator<Result> getFileNameComparator() {
return (Result result1, Result result2) -> { return (Result result1, Result result2) -> {
@ -232,6 +234,8 @@ public class ResultsSorter implements Comparator<Result> {
/** /**
* Sorts domain names in lexographical order, ignoring case. * Sorts domain names in lexographical order, ignoring case.
*
* @return -1 if domain1 comes before domain2, 0 if equal, 1 otherwise.
*/ */
private static Comparator<Result> getDomainNameComparator() { private static Comparator<Result> getDomainNameComparator() {
return (Result domain1, Result domain2) -> { return (Result domain1, Result domain2) -> {
@ -244,16 +248,18 @@ public class ResultsSorter implements Comparator<Result> {
return compareStrings(first.getDomain().toLowerCase(), second.getDomain().toLowerCase()); return compareStrings(first.getDomain().toLowerCase(), second.getDomain().toLowerCase());
}; };
} }
/** /**
* Sorts results by most recent date time * Sorts results by most recent date time.
*
* @return -1 if domain1 comes before domain2, 0 if equal, 1 otherwise.
*/ */
private static Comparator<Result> getMostRecentDateTimeComparator() { private static Comparator<Result> getMostRecentDateTimeComparator() {
return (Result result1, Result result2) -> { return (Result result1, Result result2) -> {
if(result1.getType() != SearchData.Type.DOMAIN) { if (result1.getType() != SearchData.Type.DOMAIN) {
return 0; return 0;
} }
ResultDomain first = (ResultDomain) result1; ResultDomain first = (ResultDomain) result1;
ResultDomain second = (ResultDomain) result2; ResultDomain second = (ResultDomain) result2;
return Long.compare(second.getActivityEnd(), first.getActivityEnd()); return Long.compare(second.getActivityEnd(), first.getActivityEnd());
@ -266,7 +272,7 @@ public class ResultsSorter implements Comparator<Result> {
* include something like the object ID to ensure a consistent sorting when * include something like the object ID to ensure a consistent sorting when
* the rest of the compared fields are the same. * the rest of the compared fields are the same.
* *
* @return -1 if file1 comes before file2, 0 if equal, 1 otherwise * @return -1 if file1 comes before file2, 0 if equal, 1 otherwise.
*/ */
private static Comparator<Result> getDefaultComparator() { private static Comparator<Result> getDefaultComparator() {
return (Result result1, Result result2) -> { return (Result result1, Result result2) -> {
@ -292,7 +298,7 @@ public class ResultsSorter implements Comparator<Result> {
* @param s1 * @param s1
* @param s2 * @param s2
* *
* @return -1 if s1 comes before s2, 0 if equal, 1 otherwise * @return -1 if s1 comes before s2, 0 if equal, 1 otherwise.
*/ */
private static int compareStrings(String s1, String s2) { private static int compareStrings(String s1, String s2) {
String string1 = s1 == null ? "" : s1; String string1 = s1 == null ? "" : s1;
@ -334,6 +340,13 @@ public class ResultsSorter implements Comparator<Result> {
private final String displayName; private final String displayName;
private final List<DiscoveryAttributes.AttributeType> requiredAttributes; private final List<DiscoveryAttributes.AttributeType> requiredAttributes;
/**
* Construct a new SortingMethod enum value.
*
* @param attributes The list of DiscoveryAttributes required by this
* enum value.
* @param displayName The display name for this enum value.
*/
SortingMethod(List<DiscoveryAttributes.AttributeType> attributes, String displayName) { SortingMethod(List<DiscoveryAttributes.AttributeType> attributes, String displayName) {
this.requiredAttributes = attributes; this.requiredAttributes = attributes;
this.displayName = displayName; this.displayName = displayName;
@ -344,23 +357,28 @@ public class ResultsSorter implements Comparator<Result> {
return displayName; return displayName;
} }
/**
* Get the list of DiscoveryAttributes required by this enum value.
*
* @return The list of DiscoveryAttributes required by this enum value.
*/
public List<DiscoveryAttributes.AttributeType> getRequiredAttributes() { public List<DiscoveryAttributes.AttributeType> getRequiredAttributes() {
return Collections.unmodifiableList(requiredAttributes); return Collections.unmodifiableList(requiredAttributes);
} }
/** /**
* Get the list of enums that are valid for ordering files. * Get the list of enum values that are valid for ordering files.
* *
* @return Enums that can be used to ordering files. * @return Enum values that can be used to ordering files.
*/ */
public static List<SortingMethod> getOptionsForOrderingFiles() { public static List<SortingMethod> getOptionsForOrderingFiles() {
return Arrays.asList(BY_FILE_SIZE, BY_FULL_PATH, BY_FILE_NAME, BY_DATA_SOURCE); return Arrays.asList(BY_FILE_SIZE, BY_FULL_PATH, BY_FILE_NAME, BY_DATA_SOURCE);
} }
/** /**
* Get the list of enums that are valid for ordering files. * Get the list of enum values that are valid for ordering files.
* *
* @return Enums that can be used to ordering files. * @return Enum values that can be used to ordering files.
*/ */
public static List<SortingMethod> getOptionsForOrderingDomains() { public static List<SortingMethod> getOptionsForOrderingDomains() {
return Arrays.asList(BY_DOMAIN_NAME, BY_DATA_SOURCE); return Arrays.asList(BY_DOMAIN_NAME, BY_DATA_SOURCE);

View File

@ -39,7 +39,7 @@ public final class SearchData {
private static final Set<BlackboardArtifact.ARTIFACT_TYPE> DOMAIN_ARTIFACT_TYPES = EnumSet.of(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_BOOKMARK, BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CACHE, BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_COOKIE, BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY, BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_SEARCH_QUERY); private static final Set<BlackboardArtifact.ARTIFACT_TYPE> DOMAIN_ARTIFACT_TYPES = EnumSet.of(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_BOOKMARK, BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CACHE, BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_COOKIE, BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY, BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_SEARCH_QUERY);
/** /**
* Enum representing how often the file occurs in the Central Repository. * Enum representing how often the result occurs in the Central Repository.
*/ */
@NbBundle.Messages({ @NbBundle.Messages({
"SearchData.Frequency.unique.displayName=Unique (1)", "SearchData.Frequency.unique.displayName=Unique (1)",
@ -60,6 +60,13 @@ public final class SearchData {
private final String displayName; private final String displayName;
private final int maxOccur; private final int maxOccur;
/**
* Construct a new frequency enum value.
*
* @param ranking The rank for sorting.
* @param maxOccur The max occurrences this enum value is for.
* @param displayName The display name for this enum value.
*/
Frequency(int ranking, int maxOccur, String displayName) { Frequency(int ranking, int maxOccur, String displayName) {
this.ranking = ranking; this.ranking = ranking;
this.maxOccur = maxOccur; this.maxOccur = maxOccur;
@ -69,7 +76,7 @@ public final class SearchData {
/** /**
* Get the rank for sorting. * Get the rank for sorting.
* *
* @return the rank (lower should be displayed first) * @return The rank (lower should be displayed first).
*/ */
public int getRanking() { public int getRanking() {
return ranking; return ranking;
@ -78,9 +85,9 @@ public final class SearchData {
/** /**
* Get the enum matching the given occurrence count. * Get the enum matching the given occurrence count.
* *
* @param count Number of times a file is in the Central Repository. * @param count Number of times a result is in the Central Repository.
* *
* @return the corresponding enum * @return The corresponding enum.
*/ */
public static Frequency fromCount(long count) { public static Frequency fromCount(long count) {
if (count <= UNIQUE.getMaxOccur()) { if (count <= UNIQUE.getMaxOccur()) {
@ -119,7 +126,9 @@ public final class SearchData {
} }
/** /**
* @return the maxOccur * Get the maximum number of occurrences this enum value is for.
*
* @return The maximum number of occurrences this enum value is for.
*/ */
public int getMaxOccur() { public int getMaxOccur() {
return maxOccur; return maxOccur;
@ -127,7 +136,7 @@ public final class SearchData {
} }
/** /**
* Enum representing the file size * Enum representing the file size.
*/ */
@NbBundle.Messages({ @NbBundle.Messages({
"SearchData.FileSize.XXLARGE.displayName=XXLarge", "SearchData.FileSize.XXLARGE.displayName=XXLarge",
@ -169,6 +178,16 @@ public final class SearchData {
private final String displaySize; private final String displaySize;
final static long NO_MAXIMUM = -1; final static long NO_MAXIMUM = -1;
/**
* Construct a new FileSize enum value.
*
* @param ranking The rank for sorting.
* @param minB The minimum size included in this enum value.
* @param maxB The maximum size included in this enum value.
* @param displayName The display name for this enum value.
* @param displaySize The size to display in association with this enum
* value.
*/
FileSize(int ranking, long minB, long maxB, String displayName, String displaySize) { FileSize(int ranking, long minB, long maxB, String displayName, String displaySize) {
this.ranking = ranking; this.ranking = ranking;
this.minBytes = minB; this.minBytes = minB;
@ -185,9 +204,9 @@ public final class SearchData {
* Get the enum corresponding to the given file size for image files. * Get the enum corresponding to the given file size for image files.
* The file size must be strictly greater than minBytes. * The file size must be strictly greater than minBytes.
* *
* @param size the file size * @param size The file size.
* *
* @return the enum whose range contains the file size * @return The enum whose range contains the file size.
*/ */
public static FileSize fromImageSize(long size) { public static FileSize fromImageSize(long size) {
if (size > XXLARGE_IMAGE.getMinBytes()) { if (size > XXLARGE_IMAGE.getMinBytes()) {
@ -209,9 +228,9 @@ public final class SearchData {
* Get the enum corresponding to the given file size for video files. * Get the enum corresponding to the given file size for video files.
* The file size must be strictly greater than minBytes. * The file size must be strictly greater than minBytes.
* *
* @param size the file size * @param size The file size.
* *
* @return the enum whose range contains the file size * @return The enum whose range contains the file size.
*/ */
public static FileSize fromVideoSize(long size) { public static FileSize fromVideoSize(long size) {
if (size > XXLARGE_VIDEO.getMinBytes()) { if (size > XXLARGE_VIDEO.getMinBytes()) {
@ -232,7 +251,7 @@ public final class SearchData {
/** /**
* Get the upper limit of the range. * Get the upper limit of the range.
* *
* @return the maximum file size that will fit in this range. * @return The maximum file size that will fit in this range.
*/ */
public long getMaxBytes() { public long getMaxBytes() {
return maxBytes; return maxBytes;
@ -241,7 +260,7 @@ public final class SearchData {
/** /**
* Get the lower limit of the range. * Get the lower limit of the range.
* *
* @return the maximum file size that is not part of this range * @return The maximum file size that is not part of this range.
*/ */
public long getMinBytes() { public long getMinBytes() {
return minBytes; return minBytes;
@ -250,7 +269,7 @@ public final class SearchData {
/** /**
* Get the rank for sorting. * Get the rank for sorting.
* *
* @return the rank (lower should be displayed first) * @return The rank (lower should be displayed first).
*/ */
public int getRanking() { public int getRanking() {
return ranking; return ranking;
@ -261,6 +280,11 @@ public final class SearchData {
return sizeGroup + displaySize; return sizeGroup + displaySize;
} }
/**
* Get the name of the size group. For example Small.
*
* @return The name of the size group. For example Small.
*/
public String getSizeGroup() { public String getSizeGroup() {
return sizeGroup; return sizeGroup;
} }
@ -313,6 +337,13 @@ public final class SearchData {
.add("application/pdf", //NON-NLS .add("application/pdf", //NON-NLS
"application/xhtml+xml").build(); //NON-NLS "application/xhtml+xml").build(); //NON-NLS
/**
* Get the list of document types for which image extraction is not
* supported.
*
* @return The list of document types for which image extraction is not
* supported.
*/
public static Collection<String> getDocTypesWithoutImageExtraction() { public static Collection<String> getDocTypesWithoutImageExtraction() {
return Collections.unmodifiableCollection(IMAGE_UNSUPPORTED_DOC_TYPES); return Collections.unmodifiableCollection(IMAGE_UNSUPPORTED_DOC_TYPES);
} }
@ -333,17 +364,26 @@ public final class SearchData {
IMAGE(0, Bundle.SearchData_FileType_Image_displayName(), FileTypeUtils.FileTypeCategory.IMAGE.getMediaTypes(), new ArrayList<>()), IMAGE(0, Bundle.SearchData_FileType_Image_displayName(), FileTypeUtils.FileTypeCategory.IMAGE.getMediaTypes(), new ArrayList<>()),
AUDIO(1, Bundle.SearchData_FileType_Audio_displayName(), FileTypeUtils.FileTypeCategory.AUDIO.getMediaTypes(), new ArrayList<>()), AUDIO(1, Bundle.SearchData_FileType_Audio_displayName(), FileTypeUtils.FileTypeCategory.AUDIO.getMediaTypes(), new ArrayList<>()),
VIDEO(2, Bundle.SearchData_FileType_Video_displayName(), FileTypeUtils.FileTypeCategory.VIDEO.getMediaTypes(), new ArrayList<>()), VIDEO(2, Bundle.SearchData_FileType_Video_displayName(), FileTypeUtils.FileTypeCategory.VIDEO.getMediaTypes(), new ArrayList<>()),
EXECUTABLE(3, Bundle.SearchData_FileType_Executables_displayName(), FileTypeUtils.FileTypeCategory.EXECUTABLE.getMediaTypes(),new ArrayList<>()), EXECUTABLE(3, Bundle.SearchData_FileType_Executables_displayName(), FileTypeUtils.FileTypeCategory.EXECUTABLE.getMediaTypes(), new ArrayList<>()),
DOCUMENT(4, Bundle.SearchData_FileType_Documents_displayName(), DOCUMENT_MIME_TYPES, new ArrayList<>()), DOCUMENT(4, Bundle.SearchData_FileType_Documents_displayName(), DOCUMENT_MIME_TYPES, new ArrayList<>()),
DOMAIN(6, Bundle.SearchData_AttributeType_Domain_displayName(), new ArrayList<>(), DOMAIN_ARTIFACT_TYPES), DOMAIN(6, Bundle.SearchData_AttributeType_Domain_displayName(), new ArrayList<>(), DOMAIN_ARTIFACT_TYPES),
OTHER(5, Bundle.SearchData_FileType_Other_displayName(), new ArrayList<>(), new ArrayList<>()); OTHER(5, Bundle.SearchData_FileType_Other_displayName(), new ArrayList<>(), new ArrayList<>());
private final int ranking; // For ordering in the UI private final int ranking; // For ordering in the UI
private final String displayName; private final String displayName;
private final Collection<String> mediaTypes; private final Collection<String> mediaTypes;
private final Collection<BlackboardArtifact.ARTIFACT_TYPE> artifactTypes; private final Collection<BlackboardArtifact.ARTIFACT_TYPE> artifactTypes;
/**
* Construct a new Type enum value.
*
* @param value Integer value for comparison.
* @param displayName The display name for this type.
* @param mediaTypes The list of mime types this type is defined by
* if it is file type.
* @param artifactTypes The list of artifact types this type is defined
* by if it is an attribute type.
*/
Type(int value, String displayName, Collection<String> mediaTypes, Collection<BlackboardArtifact.ARTIFACT_TYPE> artifactTypes) { Type(int value, String displayName, Collection<String> mediaTypes, Collection<BlackboardArtifact.ARTIFACT_TYPE> artifactTypes) {
this.ranking = value; this.ranking = value;
this.displayName = displayName; this.displayName = displayName;
@ -359,7 +399,12 @@ public final class SearchData {
public Collection<String> getMediaTypes() { public Collection<String> getMediaTypes() {
return Collections.unmodifiableCollection(mediaTypes); return Collections.unmodifiableCollection(mediaTypes);
} }
/**
* Get the BlackboardArtifact types matching this category.
*
* @return Collection of BlackboardArtifact.ARTIFACT_TYPE objects.
*/
public Collection<BlackboardArtifact.ARTIFACT_TYPE> getArtifactTypes() { public Collection<BlackboardArtifact.ARTIFACT_TYPE> getArtifactTypes() {
return Collections.unmodifiableCollection(artifactTypes); return Collections.unmodifiableCollection(artifactTypes);
} }
@ -395,6 +440,12 @@ public final class SearchData {
private final int ranking; private final int ranking;
private final String displayName; private final String displayName;
/**
* Construct a new Score enum value.
*
* @param ranking The rank for sorting.
* @param displayName The display name for this enum value.
*/
Score(int ranking, String displayName) { Score(int ranking, String displayName) {
this.ranking = ranking; this.ranking = ranking;
this.displayName = displayName; this.displayName = displayName;
@ -403,7 +454,7 @@ public final class SearchData {
/** /**
* Get the rank for sorting. * Get the rank for sorting.
* *
* @return the rank (lower should be displayed first) * @return The rank (lower should be displayed first).
*/ */
public int getRanking() { public int getRanking() {
return ranking; return ranking;
@ -412,7 +463,7 @@ public final class SearchData {
/** /**
* Get the list of enums that are valid for filtering. * Get the list of enums that are valid for filtering.
* *
* @return enums that can be used to filter * @return Enums that can be used to filter.
*/ */
public static List<Score> getOptionsForFiltering() { public static List<Score> getOptionsForFiltering() {
return Arrays.asList(NOTABLE, INTERESTING); return Arrays.asList(NOTABLE, INTERESTING);
@ -424,6 +475,9 @@ public final class SearchData {
} }
} }
/**
* Private constructor for SearchData class.
*/
private SearchData() { private SearchData() {
// Class should not be instantiated // Class should not be instantiated
} }

View File

@ -43,19 +43,19 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
/** /**
* Run various filters to return a subset of files from the current case. * Run various filters to return a subset of Results from the current case.
*/ */
public class SearchFiltering { public class SearchFiltering {
/** /**
* Run the given filters to get a list of matching files. * Run the given filters to get a list of matching files.
* *
* @param filters The filters to run * @param filters The filters to run.
* @param caseDb The case database * @param caseDb The case database.
* @param crDb The central repo. Can be null as long as no filters need * @param centralRepoDb The central repo. Can be null as long as no filters
* it. * need it.
* *
* @return * @return List of Results from the search performed.
*/ */
static List<Result> runQueries(List<AbstractFilter> filters, SleuthkitCase caseDb, CentralRepository centralRepoDb) throws DiscoveryException { static List<Result> runQueries(List<AbstractFilter> filters, SleuthkitCase caseDb, CentralRepository centralRepoDb) throws DiscoveryException {
if (caseDb == null) { if (caseDb == null) {
@ -89,10 +89,10 @@ public class SearchFiltering {
* @param filters The filters to run. * @param filters The filters to run.
* @param combinedQuery The query to get results files for. * @param combinedQuery The query to get results files for.
* @param caseDb The case database. * @param caseDb The case database.
* @param crDb The central repo. Can be null as long as no filters * @param centralRepoDb The central repo. Can be null as long as no filters
* need it. * need it.
* *
* @return An ArrayList of ResultFiles returned by the query. * @return An ArrayList of Results returned by the query.
* *
* @throws TskCoreException * @throws TskCoreException
* @throws DiscoveryException * @throws DiscoveryException
@ -124,36 +124,42 @@ public class SearchFiltering {
} }
return resultList; return resultList;
} }
/** /**
* A filter to specify date range for artifacts, start and end times should * A filter to specify date range for artifacts, start and end times should
* be in epoch seconds. * be in epoch seconds.
*/ */
public static class ArtifactDateRangeFilter extends AbstractFilter { public static class ArtifactDateRangeFilter extends AbstractFilter {
private final Long startDate; private final Long startDate;
private final Long endDate; private final Long endDate;
// Attributes to search for date // Attributes to search for date
private static List<BlackboardAttribute.ATTRIBUTE_TYPE> dateAttributes = private static List<BlackboardAttribute.ATTRIBUTE_TYPE> dateAttributes
Arrays.asList( = Arrays.asList(
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME,
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED
); );
/**
* Construct a new ArtifactDateRangeFilter.
*
* @param startDate The first date to include results for in the search.
* @param endDate The last date to include results for in the search.
*/
public ArtifactDateRangeFilter(Long startDate, Long endDate) { public ArtifactDateRangeFilter(Long startDate, Long endDate) {
this.startDate = startDate; this.startDate = startDate;
this.endDate = endDate; this.endDate = endDate;
} }
/** /**
* Create a SQL clause containing the date time attribute types * Create a SQL clause containing the date time attribute types to
* to search. * search.
*/ */
static String createAttributeTypeClause() { static String createAttributeTypeClause() {
StringJoiner joiner = new StringJoiner(","); StringJoiner joiner = new StringJoiner(",");
for(BlackboardAttribute.ATTRIBUTE_TYPE type : dateAttributes) { for (BlackboardAttribute.ATTRIBUTE_TYPE type : dateAttributes) {
joiner.add("\'" + type.getTypeID() + "\'"); joiner.add("\'" + type.getTypeID() + "\'");
} }
return "attribute_type_id IN (" + joiner.toString() + ")"; return "attribute_type_id IN (" + joiner.toString() + ")";
@ -161,8 +167,8 @@ public class SearchFiltering {
@Override @Override
public String getWhereClause() { public String getWhereClause() {
return createAttributeTypeClause() + return createAttributeTypeClause()
" AND (value_int64 BETWEEN " + startDate + " AND " + endDate + ")"; + " AND (value_int64 BETWEEN " + startDate + " AND " + endDate + ")";
} }
@Override @Override
@ -170,14 +176,20 @@ public class SearchFiltering {
return "ArtifactDateRangeFilter Stub"; return "ArtifactDateRangeFilter Stub";
} }
} }
/** /**
* A filter to specify artifact types * A filter to specify artifact types.
*/ */
public static class ArtifactTypeFilter extends AbstractFilter { public static class ArtifactTypeFilter extends AbstractFilter {
private final List<ARTIFACT_TYPE> types; private final List<ARTIFACT_TYPE> types;
/**
* Construct a new ArtifactTypeFilter.
*
* @param types The list of BlackboardArtifact types to include in
* results from.
*/
public ArtifactTypeFilter(List<ARTIFACT_TYPE> types) { public ArtifactTypeFilter(List<ARTIFACT_TYPE> types) {
this.types = types; this.types = types;
} }
@ -185,10 +197,10 @@ public class SearchFiltering {
@Override @Override
public String getWhereClause() { public String getWhereClause() {
StringJoiner joiner = new StringJoiner(","); StringJoiner joiner = new StringJoiner(",");
for(ARTIFACT_TYPE type : types) { for (ARTIFACT_TYPE type : types) {
joiner.add("\'" + type.getTypeID() + "\'"); joiner.add("\'" + type.getTypeID() + "\'");
} }
return "artifact_type_id IN (" + joiner + ")"; return "artifact_type_id IN (" + joiner + ")";
} }
@ -196,20 +208,20 @@ public class SearchFiltering {
public String getDesc() { public String getDesc() {
return "ArtifactTypeFilter Stub"; return "ArtifactTypeFilter Stub";
} }
} }
/** /**
* A filter for specifying the file size * A filter for specifying the file size.
*/ */
public static class SizeFilter extends AbstractFilter { public static class SizeFilter extends AbstractFilter {
private final List<FileSize> fileSizes; private final List<FileSize> fileSizes;
/** /**
* Create the SizeFilter * Create the SizeFilter.
* *
* @param fileSizes the file sizes that should match * @param fileSizes The file sizes that should match.
*/ */
public SizeFilter(List<FileSize> fileSizes) { public SizeFilter(List<FileSize> fileSizes) {
this.fileSizes = fileSizes; this.fileSizes = fileSizes;
@ -251,7 +263,7 @@ public class SearchFiltering {
/** /**
* A utility class for the ParentFilter to store the search string and * A utility class for the ParentFilter to store the search string and
* whether it is a full path or a substring. * whether it is a full path or a sub-string.
*/ */
public static class ParentSearchTerm { public static class ParentSearchTerm {
@ -260,11 +272,11 @@ public class SearchFiltering {
private final boolean included; private final boolean included;
/** /**
* Create the ParentSearchTerm object * Create the ParentSearchTerm object.
* *
* @param searchStr The string to search for in the file path * @param searchStr The string to search for in the file path.
* @param isFullPath True if the path should exactly match the given * @param isFullPath True if the path should exactly match the given
* string, false to do a substring search * string, false to do a sub-string search.
* @param isIncluded True if the results must include the path, false if * @param isIncluded True if the results must include the path, false if
* the path should be excluded from the results. * the path should be excluded from the results.
*/ */
@ -275,9 +287,9 @@ public class SearchFiltering {
} }
/** /**
* Get the SQL term to search for * Get the SQL term to search for.
* *
* @return The SQL for a where clause to search for a matching path * @return The SQL for a where clause to search for a matching path.
*/ */
public String getSQLForTerm() { public String getSQLForTerm() {
// TODO - these should really be prepared statements // TODO - these should really be prepared statements
@ -318,21 +330,31 @@ public class SearchFiltering {
} }
/** /**
* @return the fullPath * Is the search string the full path of the of the parent or is it a
* sub-string in the parent path?
*
* @return True if the search string is the full path of the parent,
* false if it is a sub-string.
*/ */
public boolean isFullPath() { public boolean isFullPath() {
return fullPath; return fullPath;
} }
/** /**
* @return the included * Should the search string be included in the path, or excluded from
* the path?
*
* @return True if the search string should be included, false if it
* should be excluded.
*/ */
public boolean isIncluded() { public boolean isIncluded() {
return included; return included;
} }
/** /**
* @return the searchStr * Get the string being searched for by this filter.
*
* @return The string being searched for by this filter.
*/ */
public String getSearchStr() { public String getSearchStr() {
return searchStr; return searchStr;
@ -340,16 +362,16 @@ public class SearchFiltering {
} }
/** /**
* A filter for specifying parent path (either full path or substring) * A filter for specifying parent path (either full path or substring).
*/ */
public static class ParentFilter extends AbstractFilter { public static class ParentFilter extends AbstractFilter {
private final List<ParentSearchTerm> parentSearchTerms; private final List<ParentSearchTerm> parentSearchTerms;
/** /**
* Create the ParentFilter * Create the ParentFilter.
* *
* @param parentSearchTerms Full paths or substrings to filter on * @param parentSearchTerms Full paths or substrings to filter on.
*/ */
public ParentFilter(List<ParentSearchTerm> parentSearchTerms) { public ParentFilter(List<ParentSearchTerm> parentSearchTerms) {
this.parentSearchTerms = parentSearchTerms; this.parentSearchTerms = parentSearchTerms;
@ -417,16 +439,16 @@ public class SearchFiltering {
} }
/** /**
* A filter for specifying data sources * A filter for specifying data sources.
*/ */
public static class DataSourceFilter extends AbstractFilter { public static class DataSourceFilter extends AbstractFilter {
private final List<DataSource> dataSources; private final List<DataSource> dataSources;
/** /**
* Create the DataSourceFilter * Create the DataSourceFilter.
* *
* @param dataSources the data sources to filter on * @param dataSources The data sources to filter on.
*/ */
public DataSourceFilter(List<DataSource> dataSources) { public DataSourceFilter(List<DataSource> dataSources) {
this.dataSources = dataSources; this.dataSources = dataSources;
@ -475,9 +497,9 @@ public class SearchFiltering {
private final List<String> listNames; private final List<String> listNames;
/** /**
* Create the KeywordListFilter * Create the KeywordListFilter.
* *
* @param listNames * @param listNames The list of keywords for this filter.
*/ */
public KeywordListFilter(List<String> listNames) { public KeywordListFilter(List<String> listNames) {
this.listNames = listNames; this.listNames = listNames;
@ -511,7 +533,7 @@ public class SearchFiltering {
private final List<Type> categories; private final List<Type> categories;
/** /**
* Create the FileTypeFilter * Create the FileTypeFilter.
* *
* @param categories List of file types to filter on * @param categories List of file types to filter on
*/ */
@ -520,9 +542,9 @@ public class SearchFiltering {
} }
/** /**
* Create the FileTypeFilter * Create the FileTypeFilter.
* *
* @param category the file type to filter on * @param category The file type to filter on.
*/ */
public FileTypeFilter(Type category) { public FileTypeFilter(Type category) {
this.categories = new ArrayList<>(); this.categories = new ArrayList<>();
@ -570,9 +592,9 @@ public class SearchFiltering {
private final List<Frequency> frequencies; private final List<Frequency> frequencies;
/** /**
* Create the FrequencyFilter * Create the FrequencyFilter.
* *
* @param frequencies List of frequencies that will pass the filter * @param frequencies List of frequencies that will pass the filter.
*/ */
public FrequencyFilter(List<Frequency> frequencies) { public FrequencyFilter(List<Frequency> frequencies) {
this.frequencies = frequencies; this.frequencies = frequencies;
@ -640,9 +662,9 @@ public class SearchFiltering {
private final List<String> setNames; private final List<String> setNames;
/** /**
* Create the HashSetFilter * Create the HashSetFilter.
* *
* @param setNames * @param setNames The hash set names for this filter.
*/ */
public HashSetFilter(List<String> setNames) { public HashSetFilter(List<String> setNames) {
this.setNames = setNames; this.setNames = setNames;
@ -678,9 +700,9 @@ public class SearchFiltering {
private final List<String> setNames; private final List<String> setNames;
/** /**
* Create the InterestingFileSetFilter * Create the InterestingFileSetFilter.
* *
* @param setNames * @param setNames The interesting file set names for this filter.
*/ */
public InterestingFileSetFilter(List<String> setNames) { public InterestingFileSetFilter(List<String> setNames) {
this.setNames = setNames; this.setNames = setNames;
@ -716,9 +738,9 @@ public class SearchFiltering {
private final List<String> typeNames; private final List<String> typeNames;
/** /**
* Create the ObjectDetectionFilter * Create the ObjectDetectionFilter.
* *
* @param typeNames * @param typeNames The type names for this filter.
*/ */
public ObjectDetectionFilter(List<String> typeNames) { public ObjectDetectionFilter(List<String> typeNames) {
this.typeNames = typeNames; this.typeNames = typeNames;
@ -747,16 +769,16 @@ public class SearchFiltering {
/** /**
* A filter for specifying the score. A file must have one of the given * A filter for specifying the score. A file must have one of the given
* scores to pass * scores to pass.
*/ */
public static class ScoreFilter extends AbstractFilter { public static class ScoreFilter extends AbstractFilter {
private final List<Score> scores; private final List<Score> scores;
/** /**
* Create the ObjectDetectionFilter * Create the ScoreFilter.
* *
* @param typeNames * @param scores The list of scores for this filter.
*/ */
public ScoreFilter(List<Score> scores) { public ScoreFilter(List<Score> scores) {
this.scores = scores; this.scores = scores;
@ -831,9 +853,9 @@ public class SearchFiltering {
private final List<TagName> tagNames; private final List<TagName> tagNames;
/** /**
* Create the TagsFilter * Create the TagsFilter.
* *
* @param tagNames * @param tagNames The list of tag names for this filter.
*/ */
public TagsFilter(List<TagName> tagNames) { public TagsFilter(List<TagName> tagNames) {
this.tagNames = tagNames; this.tagNames = tagNames;
@ -975,6 +997,13 @@ public class SearchFiltering {
} }
} }
/**
* Concatenate the set names into a "," separated list.
*
* @param setNames The List of setNames to concatenate.
*
* @return The concatenated list for display.
*/
@NbBundle.Messages({ @NbBundle.Messages({
"FileSearchFiltering.concatenateSetNamesForDisplay.comma=, ",}) "FileSearchFiltering.concatenateSetNamesForDisplay.comma=, ",})
private static String concatenateSetNamesForDisplay(List<String> setNames) { private static String concatenateSetNamesForDisplay(List<String> setNames) {
@ -992,9 +1021,9 @@ public class SearchFiltering {
* Concatenate the set names into an "OR" separated list. This does not do * Concatenate the set names into an "OR" separated list. This does not do
* any SQL-escaping. * any SQL-escaping.
* *
* @param setNames * @param setNames The List of setNames to concatenate.
* *
* @return the list to use in the SQL query * @return The concatenated list to use in the SQL query.
*/ */
private static String concatenateNamesForSQL(List<String> setNames) { private static String concatenateNamesForSQL(List<String> setNames) {
String result = ""; // NON-NLS String result = ""; // NON-NLS
@ -1007,6 +1036,9 @@ public class SearchFiltering {
return result; return result;
} }
/**
* Private constructor for SearchFiltering class.
*/
private SearchFiltering() { private SearchFiltering() {
// Class should not be instantiated // Class should not be instantiated
} }

View File

@ -76,7 +76,7 @@ class SearchResults {
for (Result result : results) { for (Result result : results) {
// Add the file to the appropriate group, creating it if necessary // Add the file to the appropriate group, creating it if necessary
GroupKey groupKey = attrType.getGroupKey(result); GroupKey groupKey = attrType.getGroupKey(result);
if (!groupMap.containsKey(groupKey)) { if (!groupMap.containsKey(groupKey)) {
groupMap.put(groupKey, new Group(groupSortingType, groupKey)); groupMap.put(groupKey, new Group(groupSortingType, groupKey));
} }
@ -92,7 +92,7 @@ class SearchResults {
// First sortGroupsAndFiles the files // First sortGroupsAndFiles the files
for (Group group : groupMap.values()) { for (Group group : groupMap.values()) {
group.sortFiles(fileSorter); group.sortResults(fileSorter);
} }
// Now put the groups in a list and sortGroupsAndFiles them // Now put the groups in a list and sortGroupsAndFiles them

View File

@ -59,6 +59,13 @@ class SummaryHelpers {
// Class should not be instantiated // Class should not be instantiated
} }
/**
* Get the default text summary for the document.
*
* @param file The file to summarize.
*
* @return The TextSummary object which is a default summary for the file.
*/
static TextSummary getDefaultSummary(AbstractFile file) { static TextSummary getDefaultSummary(AbstractFile file) {
Image image = null; Image image = null;
int countOfImages = 0; int countOfImages = 0;

View File

@ -70,7 +70,8 @@ abstract class AbstractDiscoveryFilterPanel extends javax.swing.JPanel {
/** /**
* Check if this filter is configured to valid settings. * Check if this filter is configured to valid settings.
* *
* @return If the settings are invalid returns the error that has occurred, otherwise returns empty string. * @return If the settings are invalid returns the error that has occurred,
* otherwise returns empty string.
*/ */
abstract String checkForError(); abstract String checkForError();
@ -93,8 +94,8 @@ abstract class AbstractDiscoveryFilterPanel extends javax.swing.JPanel {
/** /**
* Get the AbstractFilter which is represented by this Panel. * Get the AbstractFilter which is represented by this Panel.
* *
* @return The AbstractFilter for the selected settings, null if the settings * @return The AbstractFilter for the selected settings, null if the
* are not in use. * settings are not in use.
*/ */
abstract AbstractFilter getFilter(); abstract AbstractFilter getFilter();

View File

@ -70,7 +70,6 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
secondColumnPanel.setLayout(new GridBagLayout()); secondColumnPanel.setLayout(new GridBagLayout());
} }
/** /**
* Get the type of results this filters panel is for. * Get the type of results this filters panel is for.
* *
@ -127,7 +126,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
secondColumnY += constraints.gridheight; secondColumnY += constraints.gridheight;
} }
} }
/** /**
* Add the panels representing the two columns to the specified JSplitPane. * Add the panels representing the two columns to the specified JSplitPane.
* *
@ -279,42 +278,56 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
} }
/** /**
* @return the lastSortingMethod * Get the most recently used sorting method.
*
* @return The most recently used sorting method.
*/ */
SortingMethod getLastSortingMethod() { SortingMethod getLastSortingMethod() {
return lastSortingMethod; return lastSortingMethod;
} }
/** /**
* @param lastSortingMethod the lastSortingMethod to set * Set the most recently used sorting method.
*
* @param lastSortingMethod The most recently used sorting method.
*/ */
final void setLastSortingMethod(SortingMethod lastSortingMethod) { final void setLastSortingMethod(SortingMethod lastSortingMethod) {
this.lastSortingMethod = lastSortingMethod; this.lastSortingMethod = lastSortingMethod;
} }
/** /**
* @return the lastGroupingAttributeType * Get the most recently used grouping attribute.
*
* @return The most recently used grouping attribute.
*/ */
GroupingAttributeType getLastGroupingAttributeType() { GroupingAttributeType getLastGroupingAttributeType() {
return lastGroupingAttributeType; return lastGroupingAttributeType;
} }
/** /**
* @param lastGroupingAttributeType the lastGroupingAttributeType to set * Set the most recently used grouping attribute.
*
* @param lastGroupingAttributeType The most recently used grouping
* attribute.
*/ */
final void setLastGroupingAttributeType(GroupingAttributeType lastGroupingAttributeType) { final void setLastGroupingAttributeType(GroupingAttributeType lastGroupingAttributeType) {
this.lastGroupingAttributeType = lastGroupingAttributeType; this.lastGroupingAttributeType = lastGroupingAttributeType;
} }
/** /**
* @return the lastGroupSortingAlg * Get the most recently used group sorting algorithm.
*
* @return The most recently used group sorting algorithm.
*/ */
Group.GroupSortingAlgorithm getLastGroupSortingAlg() { Group.GroupSortingAlgorithm getLastGroupSortingAlg() {
return lastGroupSortingAlg; return lastGroupSortingAlg;
} }
/** /**
* @param lastGroupSortingAlg the lastGroupSortingAlg to set * Set the group sorting algorithm that was used most recently.
*
* @param lastGroupSortingAlg The most recently used group sorting
* algorithm.
*/ */
final void setLastGroupSortingAlg(Group.GroupSortingAlgorithm lastGroupSortingAlg) { final void setLastGroupSortingAlg(Group.GroupSortingAlgorithm lastGroupSortingAlg) {
this.lastGroupSortingAlg = lastGroupSortingAlg; this.lastGroupSortingAlg = lastGroupSortingAlg;

View File

@ -6,9 +6,6 @@
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[600, 300]"/> <Dimension value="[600, 300]"/>
</Property> </Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[1000, 650]"/>
</Property>
</Properties> </Properties>
<SyntheticProperties> <SyntheticProperties>
<SyntheticProperty name="formSizePolicy" type="int" value="1"/> <SyntheticProperty name="formSizePolicy" type="int" value="1"/>

View File

@ -318,7 +318,6 @@ final class DiscoveryDialog extends javax.swing.JDialog {
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
setMinimumSize(new java.awt.Dimension(600, 300)); setMinimumSize(new java.awt.Dimension(600, 300));
setPreferredSize(new java.awt.Dimension(1000, 650));
imagesButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/pictures-icon.png"))); // NOI18N imagesButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/pictures-icon.png"))); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(imagesButton, org.openide.util.NbBundle.getMessage(DiscoveryDialog.class, "DiscoveryDialog.imagesButton.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(imagesButton, org.openide.util.NbBundle.getMessage(DiscoveryDialog.class, "DiscoveryDialog.imagesButton.text")); // NOI18N

View File

@ -81,7 +81,7 @@ public final class DiscoveryTopComponent extends TopComponent {
}); });
rightSplitPane.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, new PropertyChangeListener() { rightSplitPane.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, new PropertyChangeListener() {
@Override @Override
public void propertyChange(PropertyChangeEvent evt) { public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equalsIgnoreCase(JSplitPane.DIVIDER_LOCATION_PROPERTY)) { if (evt.getPropertyName().equalsIgnoreCase(JSplitPane.DIVIDER_LOCATION_PROPERTY)) {
//Only change the saved location when it was a manual change by the user and not the animation or the window opening initially //Only change the saved location when it was a manual change by the user and not the animation or the window opening initially
if ((animator == null || !animator.isRunning()) && evt.getNewValue() instanceof Integer if ((animator == null || !animator.isRunning()) && evt.getNewValue() instanceof Integer
@ -91,7 +91,7 @@ public final class DiscoveryTopComponent extends TopComponent {
} }
} }
} }
}); });
} }
/** /**

View File

@ -98,7 +98,6 @@ public class DomainFilterPanel extends AbstractFiltersPanel {
} }
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JSplitPane domainFiltersSplitPane; private javax.swing.JSplitPane domainFiltersSplitPane;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables

View File

@ -88,7 +88,7 @@ final class GroupListPanel extends javax.swing.JPanel {
searchfilters = searchCompleteEvent.getFilters(); searchfilters = searchCompleteEvent.getFilters();
groupingAttribute = searchCompleteEvent.getGroupingAttr(); groupingAttribute = searchCompleteEvent.getGroupingAttr();
groupSort = searchCompleteEvent.getGroupSort(); groupSort = searchCompleteEvent.getGroupSort();
resultSortMethod = searchCompleteEvent.getFileSort(); resultSortMethod = searchCompleteEvent.getResultSort();
groupKeyList.setListData(groupMap.keySet().toArray(new GroupKey[groupMap.keySet().size()])); groupKeyList.setListData(groupMap.keySet().toArray(new GroupKey[groupMap.keySet().size()]));
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(() -> {
if (groupKeyList.getModel().getSize() > 0) { if (groupKeyList.getModel().getSize() > 0) {

View File

@ -38,8 +38,7 @@ import org.sleuthkit.autopsy.casemodule.Case;
*/ */
@ActionID(category = "Tools", id = "org.sleuthkit.autopsy.newpackage.OpenDiscoveryAction") @ActionID(category = "Tools", id = "org.sleuthkit.autopsy.newpackage.OpenDiscoveryAction")
@ActionReferences(value = { @ActionReferences(value = {
@ActionReference(path = "Menu/Tools", position = 105) @ActionReference(path = "Menu/Tools", position = 105),
,
@ActionReference(path = "Toolbars/Case", position = 105)}) @ActionReference(path = "Toolbars/Case", position = 105)})
@ActionRegistration(displayName = "#CTL_OpenDiscoveryAction", lazy = false) @ActionRegistration(displayName = "#CTL_OpenDiscoveryAction", lazy = false)
@NbBundle.Messages({"CTL_OpenDiscoveryAction=Discovery"}) @NbBundle.Messages({"CTL_OpenDiscoveryAction=Discovery"})
@ -68,7 +67,7 @@ public final class OpenDiscoveryAction extends CallableSystemAction implements P
final DiscoveryDialog discDialog = DiscoveryDialog.getDiscoveryDialogInstance(); final DiscoveryDialog discDialog = DiscoveryDialog.getDiscoveryDialogInstance();
discDialog.cancelSearch(); discDialog.cancelSearch();
DiscoveryUiUtils.displayErrorMessage(discDialog); DiscoveryUiUtils.displayErrorMessage(discDialog);
discDialog.setVisible(true); discDialog.setVisible(true);
}); });
} }

View File

@ -127,7 +127,7 @@ final class ResultsPanel extends javax.swing.JPanel {
}); });
//JIRA-TODO 6307 Add listener for domainSummaryViewer when 6782, 6773, and the other details area related stories are done //JIRA-TODO 6307 Add listener for domainSummaryViewer when 6782, 6773, and the other details area related stories are done
} }
SearchData.Type getActiveType() { SearchData.Type getActiveType() {
return resultType; return resultType;
} }
@ -294,7 +294,7 @@ final class ResultsPanel extends javax.swing.JPanel {
// Do nothing, case has been closed. // Do nothing, case has been closed.
return; return;
} }
for (Result result : results) { for (Result result : results) {
DomainThumbnailWorker domainWorker = new DomainThumbnailWorker( DomainThumbnailWorker domainWorker = new DomainThumbnailWorker(
currentCase, (ResultDomain) result); currentCase, (ResultDomain) result);
@ -315,7 +315,7 @@ final class ResultsPanel extends javax.swing.JPanel {
searchFilters = groupSelectedEvent.getFilters(); searchFilters = groupSelectedEvent.getFilters();
groupingAttribute = groupSelectedEvent.getGroupingAttr(); groupingAttribute = groupSelectedEvent.getGroupingAttr();
groupSort = groupSelectedEvent.getGroupSort(); groupSort = groupSelectedEvent.getGroupSort();
fileSortMethod = groupSelectedEvent.getFileSort(); fileSortMethod = groupSelectedEvent.getResultSort();
selectedGroupKey = groupSelectedEvent.getGroupKey(); selectedGroupKey = groupSelectedEvent.getGroupKey();
resultType = groupSelectedEvent.getResultType(); resultType = groupSelectedEvent.getResultType();
groupSize = groupSelectedEvent.getGroupSize(); groupSize = groupSelectedEvent.getGroupSize();
@ -839,7 +839,7 @@ final class ResultsPanel extends javax.swing.JPanel {
domainWrapper.getResultDomain().getDomain(), domainWrapper.getResultDomain().getDomain(),
ImageUtils.ICON_SIZE_LARGE ImageUtils.ICON_SIZE_LARGE
); );
Image thumbnail = domainSearch.getThumbnail(request); Image thumbnail = domainSearch.getThumbnail(request);
domainWrapper.setThumbnail(thumbnail); domainWrapper.setThumbnail(thumbnail);
return null; return null;