19 package org.sleuthkit.autopsy.filequery;
21 import com.google.common.cache.Cache;
22 import com.google.common.cache.CacheBuilder;
23 import com.google.common.io.Files;
24 import java.awt.Image;
25 import java.awt.image.BufferedImage;
26 import java.awt.image.RenderedImage;
27 import java.io.IOException;
28 import java.nio.file.Paths;
29 import java.sql.ResultSet;
30 import java.sql.SQLException;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.Collections;
34 import java.util.HashMap;
35 import java.util.HashSet;
36 import java.util.Iterator;
37 import java.util.LinkedHashMap;
38 import java.util.List;
40 import java.util.Objects;
42 import java.util.logging.Level;
43 import javax.imageio.ImageIO;
44 import org.apache.commons.io.FileUtils;
45 import org.apache.commons.io.FilenameUtils;
46 import org.netbeans.api.progress.ProgressHandle;
47 import org.opencv.core.Mat;
48 import org.opencv.highgui.VideoCapture;
49 import org.openide.util.NbBundle;
80 private final static Logger logger = Logger.getLogger(FileSearch.class.getName());
81 private static final int MAXIMUM_CACHE_SIZE = 10;
82 private static final String THUMBNAIL_FORMAT =
"png";
83 private static final String VIDEO_THUMBNAIL_DIR =
"video-thumbnails";
84 private static final Cache<SearchKey, Map<GroupKey, List<ResultFile>>> searchCache = CacheBuilder.newBuilder()
85 .maximumSize(MAXIMUM_CACHE_SIZE)
106 static SearchResults runFileSearchDebug(String userName,
107 List<FileSearchFiltering.FileFilter> filters,
108 AttributeType groupAttributeType,
109 FileGroup.GroupSortingAlgorithm groupSortingType,
110 FileSorter.SortingMethod fileSortingMethod,
111 SleuthkitCase caseDb, EamDb centralRepoDb)
throws FileSearchException {
116 List<AttributeType> attributesNeededForGroupingOrSorting =
new ArrayList<>();
117 attributesNeededForGroupingOrSorting.add(groupAttributeType);
118 attributesNeededForGroupingOrSorting.addAll(fileSortingMethod.getRequiredAttributes());
121 List<ResultFile> resultFiles = FileSearchFiltering.runQueries(filters, caseDb, centralRepoDb);
124 addAttributes(attributesNeededForGroupingOrSorting, resultFiles, caseDb, centralRepoDb);
127 SearchResults searchResults =
new SearchResults(groupSortingType, groupAttributeType, fileSortingMethod);
128 searchResults.add(resultFiles);
131 searchResults.sortGroupsAndFiles();
132 Map<GroupKey, List<ResultFile>> resultHashMap = searchResults.toLinkedHashMap();
133 SearchKey searchKey =
new SearchKey(userName, filters, groupAttributeType, groupSortingType, fileSortingMethod);
134 synchronized (searchCache) {
135 searchCache.put(searchKey, resultHashMap);
137 return searchResults;
158 static Map<GroupKey, Integer> getGroupSizes(String userName,
159 List<FileSearchFiltering.FileFilter> filters,
160 AttributeType groupAttributeType,
161 FileGroup.GroupSortingAlgorithm groupSortingType,
162 FileSorter.SortingMethod fileSortingMethod,
163 SleuthkitCase caseDb, EamDb centralRepoDb)
throws FileSearchException {
164 Map<GroupKey, List<ResultFile>> searchResults = runFileSearch(userName, filters,
165 groupAttributeType, groupSortingType, fileSortingMethod, caseDb, centralRepoDb);
166 LinkedHashMap<GroupKey, Integer> groupSizes =
new LinkedHashMap<>();
167 for (GroupKey groupKey : searchResults.keySet()) {
168 groupSizes.put(groupKey, searchResults.get(groupKey).size());
195 static List<ResultFile> getFilesInGroup(String userName,
196 List<FileSearchFiltering.FileFilter> filters,
197 AttributeType groupAttributeType,
198 FileGroup.GroupSortingAlgorithm groupSortingType,
199 FileSorter.SortingMethod fileSortingMethod,
203 SleuthkitCase caseDb, EamDb centralRepoDb)
throws FileSearchException {
205 List<ResultFile> filesInGroup = null;
206 SearchKey searchKey =
new SearchKey(userName, filters, groupAttributeType, groupSortingType, fileSortingMethod);
207 Map<GroupKey, List<ResultFile>> resultsMap;
208 synchronized (searchCache) {
209 resultsMap = searchCache.getIfPresent(searchKey);
211 if (resultsMap != null) {
212 filesInGroup = resultsMap.get(groupKey);
214 List<ResultFile> page =
new ArrayList<>();
215 if (filesInGroup == null) {
216 logger.log(Level.INFO,
"Group {0} was not cached, performing search to cache all groups again", groupKey);
217 runFileSearch(userName, filters, groupAttributeType, groupSortingType, fileSortingMethod, caseDb, centralRepoDb);
218 synchronized (searchCache) {
219 resultsMap = searchCache.getIfPresent(searchKey.getKeyString());
221 if (resultsMap != null) {
222 filesInGroup = resultsMap.get(groupKey);
224 if (filesInGroup == null) {
225 logger.log(Level.WARNING,
"Group {0} did not exist in cache or new search results", groupKey);
230 if (filesInGroup.size() < startingEntry) {
231 logger.log(Level.WARNING,
"Group only contains {0} files, starting entry of {1} is too large.",
new Object[]{filesInGroup.size(), startingEntry});
235 for (
int i = startingEntry; (i < startingEntry + numberOfEntries)
236 && (i < filesInGroup.size()); i++) {
237 page.add(filesInGroup.get(i));
259 private static Map<GroupKey, List<ResultFile>> runFileSearch(String userName,
260 List<FileSearchFiltering.FileFilter> filters,
261 AttributeType groupAttributeType,
262 FileGroup.GroupSortingAlgorithm groupSortingType,
263 FileSorter.SortingMethod fileSortingMethod,
264 SleuthkitCase caseDb, EamDb centralRepoDb)
throws FileSearchException {
270 List<AttributeType> attributesNeededForGroupingOrSorting =
new ArrayList<>();
271 attributesNeededForGroupingOrSorting.add(groupAttributeType);
272 attributesNeededForGroupingOrSorting.addAll(fileSortingMethod.getRequiredAttributes());
275 List<ResultFile> resultFiles = FileSearchFiltering.runQueries(filters, caseDb, centralRepoDb);
278 addAttributes(attributesNeededForGroupingOrSorting, resultFiles, caseDb, centralRepoDb);
281 SearchResults searchResults =
new SearchResults(groupSortingType, groupAttributeType, fileSortingMethod);
282 searchResults.add(resultFiles);
283 Map<GroupKey, List<ResultFile>> resultHashMap = searchResults.toLinkedHashMap();
284 SearchKey searchKey =
new SearchKey(userName, filters, groupAttributeType, groupSortingType, fileSortingMethod);
285 synchronized (searchCache) {
286 searchCache.put(searchKey, resultHashMap);
289 return resultHashMap;
305 private static void addAttributes(List<AttributeType> attrs, List<ResultFile> resultFiles, SleuthkitCase caseDb, EamDb centralRepoDb)
306 throws FileSearchException {
307 for (AttributeType attr : attrs) {
308 attr.addAttributeToResultFiles(resultFiles, caseDb, centralRepoDb);
319 private static void computeFrequency(Set<String> hashesToLookUp, List<ResultFile> currentFiles, EamDb centralRepoDb) {
321 if (hashesToLookUp.isEmpty()) {
325 String hashes = String.join(
"','", hashesToLookUp);
326 hashes =
"'" + hashes +
"'";
328 CorrelationAttributeInstance.Type attributeType = centralRepoDb.getCorrelationTypeById(CorrelationAttributeInstance.FILES_TYPE_ID);
329 String tableName = EamDbUtil.correlationTypeToInstanceTableName(attributeType);
331 String selectClause =
" value, COUNT(value) FROM "
332 +
"(SELECT DISTINCT case_id, value FROM " + tableName
333 +
" WHERE value IN ("
335 +
")) AS foo GROUP BY value";
337 FrequencyCallback callback =
new FrequencyCallback(currentFiles);
338 centralRepoDb.processSelectClause(selectClause, callback);
340 }
catch (EamDbException ex) {
341 logger.log(Level.WARNING,
"Error getting frequency counts from Central Repository", ex);
346 private static String createSetNameClause(List<ResultFile> files,
347 int artifactTypeID,
int setNameAttrID)
throws FileSearchException {
350 String objIdList =
"";
351 for (ResultFile file : files) {
352 if (!objIdList.isEmpty()) {
355 objIdList +=
"\'" + file.getFirstInstance().getId() +
"\'";
360 return "blackboard_artifacts.obj_id AS object_id, blackboard_attributes.value_text AS set_name "
361 +
"FROM blackboard_artifacts "
362 +
"INNER JOIN blackboard_attributes ON blackboard_artifacts.artifact_id=blackboard_attributes.artifact_id "
363 +
"WHERE blackboard_attributes.artifact_type_id=\'" + artifactTypeID +
"\' "
364 +
"AND blackboard_attributes.attribute_type_id=\'" + setNameAttrID +
"\' "
365 +
"AND blackboard_artifacts.obj_id IN (" + objIdList +
") ";
377 @NbBundle.Messages({
"# {0} - file name",
378 "FileSearch.genVideoThumb.progress.text=extracting temporary file {0}"})
379 static void getVideoThumbnails(VideoThumbnailsWrapper thumbnailWrapper) {
380 AbstractFile file = thumbnailWrapper.getResultFile().getFirstInstance();
381 String cacheDirectory;
383 cacheDirectory = Case.getCurrentCaseThrows().getCacheDirectory();
384 }
catch (NoCurrentCaseException ex) {
385 cacheDirectory = null;
386 logger.log(Level.WARNING,
"Unable to get cache directory, video thumbnails will not be saved", ex);
389 if (cacheDirectory == null || file.getMd5Hash() == null || !Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, file.getMd5Hash()).toFile().exists()) {
390 java.io.File tempFile;
392 tempFile = getVideoFileInTempDir(file);
393 }
catch (NoCurrentCaseException ex) {
394 logger.log(Level.WARNING,
"Exception while getting open case.", ex);
395 int[] framePositions =
new int[]{
400 thumbnailWrapper.setThumbnails(createDefaultThumbnailList(), framePositions);
403 if (tempFile.exists() ==
false || tempFile.length() < file.getSize()) {
404 ProgressHandle progress = ProgressHandle.createHandle(Bundle.FileSearch_genVideoThumb_progress_text(file.getName()));
407 Files.createParentDirs(tempFile);
408 if (Thread.interrupted()) {
409 int[] framePositions =
new int[]{
414 thumbnailWrapper.setThumbnails(createDefaultThumbnailList(), framePositions);
417 ContentUtils.writeToFile(file, tempFile, progress, null,
true);
418 }
catch (IOException ex) {
419 logger.log(Level.WARNING,
"Error extracting temporary file for " + file.getParentPath() +
"/" + file.getName(), ex);
424 VideoCapture videoFile =
new VideoCapture();
425 BufferedImage bufferedImage = null;
428 if (!videoFile.open(tempFile.toString())) {
429 logger.log(Level.WARNING,
"Error opening {0} for preview generation.", file.getParentPath() +
"/" + file.getName());
430 int[] framePositions =
new int[]{
435 thumbnailWrapper.setThumbnails(createDefaultThumbnailList(), framePositions);
438 double fps = videoFile.get(5);
439 double totalFrames = videoFile.get(7);
440 if (fps <= 0 || totalFrames <= 0) {
441 logger.log(Level.WARNING,
"Error getting fps or total frames for {0}", file.getParentPath() +
"/" + file.getName());
442 int[] framePositions =
new int[]{
447 thumbnailWrapper.setThumbnails(createDefaultThumbnailList(), framePositions);
450 if (Thread.interrupted()) {
451 int[] framePositions =
new int[]{
456 thumbnailWrapper.setThumbnails(createDefaultThumbnailList(), framePositions);
460 double duration = 1000 * (totalFrames / fps);
462 int[] framePositions =
new int[]{
463 (int) (duration * .01),
464 (int) (duration * .25),
465 (int) (duration * .5),
466 (int) (duration * .75),};
468 Mat imageMatrix =
new Mat();
469 List<Image> videoThumbnails =
new ArrayList<>();
470 if (cacheDirectory == null || file.getMd5Hash() == null) {
471 cacheDirectory = null;
474 FileUtils.forceMkdir(Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, file.getMd5Hash()).toFile());
475 }
catch (IOException ex) {
476 cacheDirectory = null;
477 logger.log(Level.WARNING,
"Unable to make video thumbnails directory, thumbnails will not be saved", ex);
480 for (
int i = 0; i < framePositions.length; i++) {
481 if (!videoFile.set(0, framePositions[i])) {
482 logger.log(Level.WARNING,
"Error seeking to " + framePositions[i] +
"ms in {0}", file.getParentPath() +
"/" + file.getName());
485 videoThumbnails.add(ImageUtils.getDefaultThumbnail());
486 if (cacheDirectory != null) {
488 ImageIO.write((RenderedImage) ImageUtils.getDefaultThumbnail(), THUMBNAIL_FORMAT,
489 Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, file.getMd5Hash(), i +
"-" + framePositions[i] +
"." + THUMBNAIL_FORMAT).toFile());
490 }
catch (IOException ex) {
491 logger.log(Level.WARNING,
"Unable to save default video thumbnail for " + file.getMd5Hash() +
" at frame position " + framePositions[i], ex);
497 if (!videoFile.read(imageMatrix)) {
498 logger.log(Level.WARNING,
"Error reading frame at " + framePositions[i] +
"ms from {0}", file.getParentPath() +
"/" + file.getName());
500 videoThumbnails.add(ImageUtils.getDefaultThumbnail());
501 if (cacheDirectory != null) {
503 ImageIO.write((RenderedImage) ImageUtils.getDefaultThumbnail(), THUMBNAIL_FORMAT,
504 Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, file.getMd5Hash(), i +
"-" + framePositions[i] +
"." + THUMBNAIL_FORMAT).toFile());
505 }
catch (IOException ex) {
506 logger.log(Level.WARNING,
"Unable to save default video thumbnail for " + file.getMd5Hash() +
" at frame position " + framePositions[i], ex);
513 if (imageMatrix.empty()) {
514 videoThumbnails.add(ImageUtils.getDefaultThumbnail());
515 if (cacheDirectory != null) {
517 ImageIO.write((RenderedImage) ImageUtils.getDefaultThumbnail(), THUMBNAIL_FORMAT,
518 Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, file.getMd5Hash(), i +
"-" + framePositions[i] +
"." + THUMBNAIL_FORMAT).toFile());
519 }
catch (IOException ex) {
520 logger.log(Level.WARNING,
"Unable to save default video thumbnail for " + file.getMd5Hash() +
" at frame position " + framePositions[i], ex);
526 int matrixColumns = imageMatrix.cols();
527 int matrixRows = imageMatrix.rows();
530 if (bufferedImage == null) {
531 bufferedImage =
new BufferedImage(matrixColumns, matrixRows, BufferedImage.TYPE_3BYTE_BGR);
534 byte[] data =
new byte[matrixRows * matrixColumns * (int) (imageMatrix.elemSize())];
535 imageMatrix.get(0, 0, data);
537 if (imageMatrix.channels() == 3) {
538 for (
int k = 0; k < data.length; k += 3) {
540 data[k] = data[k + 2];
545 bufferedImage.getRaster().setDataElements(0, 0, matrixColumns, matrixRows, data);
546 if (Thread.interrupted()) {
547 thumbnailWrapper.setThumbnails(videoThumbnails, framePositions);
549 FileUtils.forceDelete(Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, file.getMd5Hash()).toFile());
550 }
catch (IOException ex) {
551 logger.log(Level.WARNING,
"Unable to delete directory for cancelled video thumbnail process", ex);
555 BufferedImage thumbnail = ScalrWrapper.resizeFast(bufferedImage, ImageUtils.ICON_SIZE_LARGE);
556 videoThumbnails.add(thumbnail);
557 if (cacheDirectory != null) {
559 ImageIO.write(thumbnail, THUMBNAIL_FORMAT,
560 Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, file.getMd5Hash(), i +
"-" + framePositions[i] +
"." + THUMBNAIL_FORMAT).toFile());
561 }
catch (IOException ex) {
562 logger.log(Level.WARNING,
"Unable to save video thumbnail for " + file.getMd5Hash() +
" at frame position " + framePositions[i], ex);
566 thumbnailWrapper.setThumbnails(videoThumbnails, framePositions);
571 loadSavedThumbnails(cacheDirectory, thumbnailWrapper);
585 private static void loadSavedThumbnails(String cacheDirectory, VideoThumbnailsWrapper thumbnailWrapper) {
586 int[] framePositions =
new int[4];
587 List<Image> videoThumbnails =
new ArrayList<>();
588 int thumbnailNumber = 0;
589 String md5 = thumbnailWrapper.getResultFile().getFirstInstance().getMd5Hash();
590 for (String fileName : Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, md5).toFile().list()) {
592 videoThumbnails.add(ImageIO.read(Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, md5, fileName).toFile()));
593 }
catch (IOException ex) {
594 videoThumbnails.add(ImageUtils.getDefaultThumbnail());
595 logger.log(Level.WARNING,
"Unable to read saved video thumbnail " + fileName +
" for " + md5, ex);
597 int framePos = Integer.valueOf(FilenameUtils.getBaseName(fileName).substring(2));
598 framePositions[thumbnailNumber] = framePos;
602 thumbnailWrapper.setThumbnails(videoThumbnails, framePositions);
611 private static List<Image> createDefaultThumbnailList() {
612 List<Image> videoThumbnails =
new ArrayList<>();
613 videoThumbnails.add(ImageUtils.getDefaultThumbnail());
614 videoThumbnails.add(ImageUtils.getDefaultThumbnail());
615 videoThumbnails.add(ImageUtils.getDefaultThumbnail());
616 videoThumbnails.add(ImageUtils.getDefaultThumbnail());
617 return videoThumbnails;
620 private FileSearch() {
627 abstract static class AttributeType {
637 abstract GroupKey getGroupKey(ResultFile file);
649 void addAttributeToResultFiles(List<ResultFile> files, SleuthkitCase caseDb, EamDb centralRepoDb)
throws FileSearchException {
657 abstract static class GroupKey
implements Comparable<GroupKey> {
665 abstract String getDisplayName();
675 abstract public boolean equals(Object otherKey);
683 abstract public int hashCode();
694 int compareClassNames(GroupKey otherGroupKey) {
695 return this.getClass().getName().compareTo(otherGroupKey.getClass().getName());
699 public String toString() {
700 return getDisplayName();
707 static class FileSizeAttribute
extends AttributeType {
710 GroupKey getGroupKey(ResultFile file) {
711 return new FileSizeGroupKey(file);
723 if (file.getFileType() == FileType.VIDEO) {
724 fileSize = FileSize.fromVideoSize(file.getFirstInstance().getSize());
726 fileSize = FileSize.fromImageSize(file.getFirstInstance().getSize());
731 String getDisplayName() {
732 return getFileSize().toString();
738 FileSizeGroupKey otherFileSizeGroupKey = (FileSizeGroupKey) otherGroupKey;
739 return Integer.compare(getFileSize().getRanking(), otherFileSizeGroupKey.getFileSize().getRanking());
741 return compareClassNames(otherGroupKey);
747 if (otherKey ==
this) {
755 FileSizeGroupKey otherFileSizeGroupKey = (FileSizeGroupKey) otherKey;
756 return getFileSize().equals(otherFileSizeGroupKey.getFileSize());
761 return Objects.hash(getFileSize().getRanking());
767 FileSize getFileSize() {
775 static class ParentPathAttribute
extends AttributeType {
778 GroupKey getGroupKey(ResultFile file) {
779 return new ParentPathGroupKey(file);
794 parent = file.getFirstInstance().getParent();
795 }
catch (TskCoreException ignored) {
799 while (parent != null && parent instanceof AbstractFile && ((AbstractFile) parent).isFile()) {
801 parent = parent.getParent();
802 }
catch (TskCoreException ignored) {
816 if (parent != null) {
818 parentPath = parent.getUniquePath();
819 parentID = parent.getId();
820 }
catch (TskCoreException ignored) {
825 if (parentPath == null) {
826 if (file.getFirstInstance().getParentPath() != null) {
827 parentPath = file.getFirstInstance().getParentPath();
836 String getDisplayName() {
837 return getParentPath();
843 ParentPathGroupKey otherParentPathGroupKey = (ParentPathGroupKey) otherGroupKey;
844 int comparisonResult = getParentPath().compareTo(otherParentPathGroupKey.getParentPath());
845 if (comparisonResult == 0) {
846 comparisonResult = getParentID().compareTo(otherParentPathGroupKey.getParentID());
848 return comparisonResult;
850 return compareClassNames(otherGroupKey);
856 if (otherKey ==
this) {
864 ParentPathGroupKey otherParentPathGroupKey = (ParentPathGroupKey) otherKey;
865 return getParentPath().equals(otherParentPathGroupKey.getParentPath()) && getParentID().equals(otherParentPathGroupKey.getParentID());
871 hashCode = 61 * hashCode + Objects.hash(getParentPath());
872 hashCode = 61 * hashCode + Objects.hash(getParentID());
879 String getParentPath() {
894 static class DataSourceAttribute
extends AttributeType {
897 GroupKey getGroupKey(ResultFile file) {
898 return new DataSourceGroupKey(file);
911 "# {0} - Data source name",
912 "# {1} - Data source ID",
913 "FileSearch.DataSourceGroupKey.datasourceAndID={0}(ID: {1})",
914 "# {0} - Data source ID",
915 "FileSearch.DataSourceGroupKey.idOnly=Data source (ID: {0})"})
917 dataSourceID = file.getFirstInstance().getDataSourceObjectId();
921 Content ds = file.getFirstInstance().getDataSource();
922 displayName = Bundle.FileSearch_DataSourceGroupKey_datasourceAndID(ds.getName(), ds.getId());
923 }
catch (TskCoreException ex) {
924 logger.log(Level.WARNING,
"Error looking up data source with ID " + dataSourceID, ex);
925 displayName = Bundle.FileSearch_DataSourceGroupKey_idOnly(dataSourceID);
930 String getDisplayName() {
937 DataSourceGroupKey otherDataSourceGroupKey = (DataSourceGroupKey) otherGroupKey;
938 return Long.compare(getDataSourceID(), otherDataSourceGroupKey.getDataSourceID());
940 return compareClassNames(otherGroupKey);
946 if (otherKey ==
this) {
954 DataSourceGroupKey otherDataSourceGroupKey = (DataSourceGroupKey) otherKey;
955 return getDataSourceID() == otherDataSourceGroupKey.getDataSourceID();
960 return Objects.hash(getDataSourceID());
966 long getDataSourceID() {
974 static class FileTypeAttribute
extends AttributeType {
977 GroupKey getGroupKey(ResultFile file) {
978 return new FileTypeGroupKey(file);
990 fileType = file.getFileType();
994 String getDisplayName() {
995 return getFileType().toString();
1001 FileTypeGroupKey otherFileTypeGroupKey = (FileTypeGroupKey) otherGroupKey;
1002 return Integer.compare(getFileType().getRanking(), otherFileTypeGroupKey.getFileType().getRanking());
1004 return compareClassNames(otherGroupKey);
1010 if (otherKey ==
this) {
1018 FileTypeGroupKey otherFileTypeGroupKey = (FileTypeGroupKey) otherKey;
1019 return getFileType().equals(otherFileTypeGroupKey.getFileType());
1024 return Objects.hash(getFileType().getRanking());
1030 FileType getFileType() {
1038 static class KeywordListAttribute
extends AttributeType {
1041 GroupKey getGroupKey(ResultFile file) {
1042 return new KeywordListGroupKey(file);
1046 void addAttributeToResultFiles(List<ResultFile> files, SleuthkitCase caseDb,
1047 EamDb centralRepoDb)
throws FileSearchException {
1051 String selectQuery = createSetNameClause(files, BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID(),
1052 BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID());
1054 SetKeywordListNamesCallback callback =
new SetKeywordListNamesCallback(files);
1056 caseDb.getCaseDbAccessManager().select(selectQuery, callback);
1057 }
catch (TskCoreException ex) {
1058 throw new FileSearchException(
"Error looking up keyword list attributes", ex);
1069 List<ResultFile> resultFiles;
1077 this.resultFiles = resultFiles;
1084 Map<Long, ResultFile> tempMap =
new HashMap<>();
1085 for (ResultFile file : resultFiles) {
1086 tempMap.put(file.getFirstInstance().getId(), file);
1091 Long objId = rs.getLong(
"object_id");
1092 String keywordListName = rs.getString(
"set_name");
1094 tempMap.get(objId).addKeywordListName(keywordListName);
1096 }
catch (SQLException ex) {
1097 logger.log(Level.SEVERE,
"Unable to get object_id or set_name from result set", ex);
1100 }
catch (SQLException ex) {
1101 logger.log(Level.SEVERE,
"Failed to get keyword list names", ex);
1115 @NbBundle.Messages({
1116 "FileSearch.KeywordListGroupKey.noKeywords=None"})
1118 keywordListNames = file.getKeywordListNames();
1120 if (keywordListNames.isEmpty()) {
1121 keywordListNamesString = Bundle.FileSearch_KeywordListGroupKey_noKeywords();
1123 keywordListNamesString = String.join(
",", keywordListNames);
1128 String getDisplayName() {
1129 return getKeywordListNamesString();
1135 KeywordListGroupKey otherKeywordListNamesGroupKey = (KeywordListGroupKey) otherGroupKey;
1138 if (getKeywordListNames().isEmpty()) {
1139 if (otherKeywordListNamesGroupKey.getKeywordListNames().isEmpty()) {
1144 }
else if (otherKeywordListNamesGroupKey.getKeywordListNames().isEmpty()) {
1148 return getKeywordListNamesString().compareTo(otherKeywordListNamesGroupKey.getKeywordListNamesString());
1150 return compareClassNames(otherGroupKey);
1156 if (otherKey ==
this) {
1164 KeywordListGroupKey otherKeywordListGroupKey = (KeywordListGroupKey) otherKey;
1165 return getKeywordListNamesString().equals(otherKeywordListGroupKey.getKeywordListNamesString());
1170 return Objects.hash(getKeywordListNamesString());
1176 List<String> getKeywordListNames() {
1177 return Collections.unmodifiableList(keywordListNames);
1183 String getKeywordListNamesString() {
1191 static class FrequencyAttribute
extends AttributeType {
1193 static final int BATCH_SIZE = 50;
1196 GroupKey getGroupKey(ResultFile file) {
1197 return new FrequencyGroupKey(file);
1201 void addAttributeToResultFiles(List<ResultFile> files, SleuthkitCase caseDb,
1202 EamDb centralRepoDb)
throws FileSearchException {
1203 if (centralRepoDb == null) {
1204 for (ResultFile file : files) {
1205 if (file.getFrequency() == Frequency.UNKNOWN && file.getFirstInstance().getKnown() == TskData.FileKnown.KNOWN) {
1206 file.setFrequency(Frequency.KNOWN);
1210 processResultFilesForCR(files, centralRepoDb);
1222 private void processResultFilesForCR(List<ResultFile> files,
1223 EamDb centralRepoDb) {
1224 List<ResultFile> currentFiles =
new ArrayList<>();
1225 Set<String> hashesToLookUp =
new HashSet<>();
1226 for (ResultFile file : files) {
1227 if (file.getFirstInstance().getKnown() == TskData.FileKnown.KNOWN) {
1228 file.setFrequency(Frequency.KNOWN);
1230 if (file.getFrequency() == Frequency.UNKNOWN
1231 && file.getFirstInstance().getMd5Hash() != null
1232 && !file.getFirstInstance().getMd5Hash().isEmpty()) {
1233 hashesToLookUp.add(file.getFirstInstance().getMd5Hash());
1234 currentFiles.add(file);
1236 if (hashesToLookUp.size() >= BATCH_SIZE) {
1237 computeFrequency(hashesToLookUp, currentFiles, centralRepoDb);
1239 hashesToLookUp.clear();
1240 currentFiles.clear();
1243 computeFrequency(hashesToLookUp, currentFiles, centralRepoDb);
1256 this.files =
new ArrayList<>(
files);
1263 while (resultSet.next()) {
1264 String hash = resultSet.getString(1);
1265 int count = resultSet.getInt(2);
1266 for (Iterator<ResultFile> iterator = files.iterator(); iterator.hasNext();) {
1267 ResultFile file = iterator.next();
1268 if (file.getFirstInstance().getMd5Hash().equalsIgnoreCase(hash)) {
1269 file.setFrequency(Frequency.fromCount(count));
1276 for (ResultFile file : files) {
1277 file.setFrequency(Frequency.UNIQUE);
1279 }
catch (SQLException ex) {
1280 logger.log(Level.WARNING,
"Error getting frequency counts from Central Repository", ex);
1293 frequency = file.getFrequency();
1297 String getDisplayName() {
1298 return getFrequency().toString();
1304 FrequencyGroupKey otherFrequencyGroupKey = (FrequencyGroupKey) otherGroupKey;
1305 return Integer.compare(getFrequency().getRanking(), otherFrequencyGroupKey.getFrequency().getRanking());
1307 return compareClassNames(otherGroupKey);
1313 if (otherKey ==
this) {
1321 FrequencyGroupKey otherFrequencyGroupKey = (FrequencyGroupKey) otherKey;
1322 return getFrequency().equals(otherFrequencyGroupKey.getFrequency());
1327 return Objects.hash(getFrequency().getRanking());
1333 Frequency getFrequency() {
1341 static class HashHitsAttribute
extends AttributeType {
1344 GroupKey getGroupKey(ResultFile file) {
1345 return new HashHitsGroupKey(file);
1349 void addAttributeToResultFiles(List<ResultFile> files, SleuthkitCase caseDb,
1350 EamDb centralRepoDb)
throws FileSearchException {
1354 String selectQuery = createSetNameClause(files, BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID(),
1355 BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID());
1357 HashSetNamesCallback callback =
new HashSetNamesCallback(files);
1359 caseDb.getCaseDbAccessManager().select(selectQuery, callback);
1360 }
catch (TskCoreException ex) {
1361 throw new FileSearchException(
"Error looking up hash set attributes", ex);
1371 List<ResultFile> resultFiles;
1379 this.resultFiles = resultFiles;
1386 Map<Long, ResultFile> tempMap =
new HashMap<>();
1387 for (ResultFile file : resultFiles) {
1388 tempMap.put(file.getFirstInstance().getId(), file);
1393 Long objId = rs.getLong(
"object_id");
1394 String hashSetName = rs.getString(
"set_name");
1396 tempMap.get(objId).addHashSetName(hashSetName);
1398 }
catch (SQLException ex) {
1399 logger.log(Level.SEVERE,
"Unable to get object_id or set_name from result set", ex);
1402 }
catch (SQLException ex) {
1403 logger.log(Level.SEVERE,
"Failed to get hash set names", ex);
1417 @NbBundle.Messages({
1418 "FileSearch.HashHitsGroupKey.noHashHits=None"})
1420 hashSetNames = file.getHashSetNames();
1422 if (hashSetNames.isEmpty()) {
1423 hashSetNamesString = Bundle.FileSearch_HashHitsGroupKey_noHashHits();
1425 hashSetNamesString = String.join(
",", hashSetNames);
1430 String getDisplayName() {
1431 return getHashSetNamesString();
1437 HashHitsGroupKey otherHashHitsGroupKey = (HashHitsGroupKey) otherGroupKey;
1440 if (getHashSetNames().isEmpty()) {
1441 if (otherHashHitsGroupKey.getHashSetNames().isEmpty()) {
1446 }
else if (otherHashHitsGroupKey.getHashSetNames().isEmpty()) {
1450 return getHashSetNamesString().compareTo(otherHashHitsGroupKey.getHashSetNamesString());
1452 return compareClassNames(otherGroupKey);
1458 if (otherKey ==
this) {
1466 HashHitsGroupKey otherHashHitsGroupKey = (HashHitsGroupKey) otherKey;
1467 return getHashSetNamesString().equals(otherHashHitsGroupKey.getHashSetNamesString());
1472 return Objects.hash(getHashSetNamesString());
1478 List<String> getHashSetNames() {
1479 return Collections.unmodifiableList(hashSetNames);
1485 String getHashSetNamesString() {
1493 static class InterestingItemAttribute
extends AttributeType {
1496 GroupKey getGroupKey(ResultFile file) {
1497 return new InterestingItemGroupKey(file);
1501 void addAttributeToResultFiles(List<ResultFile> files, SleuthkitCase caseDb,
1502 EamDb centralRepoDb)
throws FileSearchException {
1506 String selectQuery = createSetNameClause(files, BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID(),
1507 BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID());
1509 InterestingFileSetNamesCallback callback =
new InterestingFileSetNamesCallback(files);
1511 caseDb.getCaseDbAccessManager().select(selectQuery, callback);
1512 }
catch (TskCoreException ex) {
1513 throw new FileSearchException(
"Error looking up interesting file set attributes", ex);
1524 List<ResultFile> resultFiles;
1533 this.resultFiles = resultFiles;
1540 Map<Long, ResultFile> tempMap =
new HashMap<>();
1541 for (ResultFile file : resultFiles) {
1542 tempMap.put(file.getFirstInstance().getId(), file);
1547 Long objId = rs.getLong(
"object_id");
1548 String setName = rs.getString(
"set_name");
1550 tempMap.get(objId).addInterestingSetName(setName);
1552 }
catch (SQLException ex) {
1553 logger.log(Level.SEVERE,
"Unable to get object_id or set_name from result set", ex);
1556 }
catch (SQLException ex) {
1557 logger.log(Level.SEVERE,
"Failed to get interesting file set names", ex);
1571 @NbBundle.Messages({
1572 "FileSearch.InterestingItemGroupKey.noSets=None"})
1574 interestingItemSetNames = file.getInterestingSetNames();
1576 if (interestingItemSetNames.isEmpty()) {
1577 interestingItemSetNamesString = Bundle.FileSearch_InterestingItemGroupKey_noSets();
1579 interestingItemSetNamesString = String.join(
",", interestingItemSetNames);
1584 String getDisplayName() {
1585 return getInterestingItemSetNamesString();
1591 InterestingItemGroupKey otherInterestingItemGroupKey = (InterestingItemGroupKey) otherGroupKey;
1594 if (this.getInterestingItemSetNames().isEmpty()) {
1595 if (otherInterestingItemGroupKey.getInterestingItemSetNames().isEmpty()) {
1600 }
else if (otherInterestingItemGroupKey.getInterestingItemSetNames().isEmpty()) {
1604 return getInterestingItemSetNamesString().compareTo(otherInterestingItemGroupKey.getInterestingItemSetNamesString());
1606 return compareClassNames(otherGroupKey);
1612 if (otherKey ==
this) {
1620 InterestingItemGroupKey otherInterestingItemGroupKey = (InterestingItemGroupKey) otherKey;
1621 return getInterestingItemSetNamesString().equals(otherInterestingItemGroupKey.getInterestingItemSetNamesString());
1626 return Objects.hash(getInterestingItemSetNamesString());
1632 List<String> getInterestingItemSetNames() {
1633 return Collections.unmodifiableList(interestingItemSetNames);
1639 String getInterestingItemSetNamesString() {
1647 static class ObjectDetectedAttribute
extends AttributeType {
1650 GroupKey getGroupKey(ResultFile file) {
1651 return new ObjectDetectedGroupKey(file);
1655 void addAttributeToResultFiles(List<ResultFile> files, SleuthkitCase caseDb,
1656 EamDb centralRepoDb)
throws FileSearchException {
1660 String selectQuery = createSetNameClause(files, BlackboardArtifact.ARTIFACT_TYPE.TSK_OBJECT_DETECTED.getTypeID(),
1661 BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION.getTypeID());
1663 ObjectDetectedNamesCallback callback =
new ObjectDetectedNamesCallback(files);
1665 caseDb.getCaseDbAccessManager().select(selectQuery, callback);
1666 }
catch (TskCoreException ex) {
1667 throw new FileSearchException(
"Error looking up object detected attributes", ex);
1678 List<ResultFile> resultFiles;
1686 this.resultFiles = resultFiles;
1693 Map<Long, ResultFile> tempMap =
new HashMap<>();
1694 for (ResultFile file : resultFiles) {
1695 tempMap.put(file.getFirstInstance().getId(), file);
1700 Long objId = rs.getLong(
"object_id");
1701 String setName = rs.getString(
"set_name");
1703 tempMap.get(objId).addObjectDetectedName(setName);
1705 }
catch (SQLException ex) {
1706 logger.log(Level.SEVERE,
"Unable to get object_id or set_name from result set", ex);
1709 }
catch (SQLException ex) {
1710 logger.log(Level.SEVERE,
"Failed to get object detected names", ex);
1724 @NbBundle.Messages({
1725 "FileSearch.ObjectDetectedGroupKey.noSets=None"})
1727 objectDetectedNames = file.getObjectDetectedNames();
1729 if (objectDetectedNames.isEmpty()) {
1730 objectDetectedNamesString = Bundle.FileSearch_ObjectDetectedGroupKey_noSets();
1732 objectDetectedNamesString = String.join(
",", objectDetectedNames);
1737 String getDisplayName() {
1738 return getObjectDetectedNamesString();
1744 ObjectDetectedGroupKey otherObjectDetectedGroupKey = (ObjectDetectedGroupKey) otherGroupKey;
1747 if (this.getObjectDetectedNames().isEmpty()) {
1748 if (otherObjectDetectedGroupKey.getObjectDetectedNames().isEmpty()) {
1753 }
else if (otherObjectDetectedGroupKey.getObjectDetectedNames().isEmpty()) {
1757 return getObjectDetectedNamesString().compareTo(otherObjectDetectedGroupKey.getObjectDetectedNamesString());
1759 return compareClassNames(otherGroupKey);
1765 if (otherKey ==
this) {
1773 ObjectDetectedGroupKey otherObjectDetectedGroupKey = (ObjectDetectedGroupKey) otherKey;
1774 return getObjectDetectedNamesString().equals(otherObjectDetectedGroupKey.getObjectDetectedNamesString());
1779 return Objects.hash(getObjectDetectedNamesString());
1785 List<String> getObjectDetectedNames() {
1786 return Collections.unmodifiableList(objectDetectedNames);
1792 String getObjectDetectedNamesString() {
1800 static class FileTagAttribute
extends AttributeType {
1803 GroupKey getGroupKey(ResultFile file) {
1804 return new FileTagGroupKey(file);
1808 void addAttributeToResultFiles(List<ResultFile> files, SleuthkitCase caseDb,
1809 EamDb centralRepoDb)
throws FileSearchException {
1812 for (ResultFile resultFile : files) {
1813 List<ContentTag> contentTags = caseDb.getContentTagsByContent(resultFile.getFirstInstance());
1815 for (ContentTag tag : contentTags) {
1816 resultFile.addTagName(tag.getName().getDisplayName());
1819 }
catch (TskCoreException ex) {
1820 throw new FileSearchException(
"Error looking up file tag attributes", ex);
1828 private static class SearchKey implements Comparable<SearchKey> {
1841 SearchKey(String userName, List<FileSearchFiltering.FileFilter> filters,
1842 AttributeType groupAttributeType,
1843 FileGroup.GroupSortingAlgorithm groupSortingType,
1844 FileSorter.SortingMethod fileSortingMethod) {
1845 StringBuilder searchStringBuilder =
new StringBuilder();
1846 searchStringBuilder.append(userName);
1847 for (FileSearchFiltering.FileFilter filter : filters) {
1848 searchStringBuilder.append(filter.toString());
1850 searchStringBuilder.append(groupAttributeType).append(groupSortingType).append(fileSortingMethod);
1851 keyString = searchStringBuilder.toString();
1856 return getKeyString().compareTo(otherSearchKey.getKeyString());
1861 if (otherKey ==
this) {
1869 SearchKey otherSearchKey = (SearchKey) otherKey;
1870 return getKeyString().equals(otherSearchKey.getKeyString());
1876 hash = 79 * hash + Objects.hashCode(getKeyString());
1883 String getKeyString() {
1896 @NbBundle.Messages({
1897 "FileSearch.FileTagGroupKey.noSets=None"})
1899 tagNames = file.getTagNames();
1901 if (tagNames.isEmpty()) {
1902 tagNamesString = Bundle.FileSearch_FileTagGroupKey_noSets();
1904 tagNamesString = String.join(
",", tagNames);
1909 String getDisplayName() {
1910 return getTagNamesString();
1916 FileTagGroupKey otherFileTagGroupKey = (FileTagGroupKey) otherGroupKey;
1919 if (getTagNames().isEmpty()) {
1920 if (otherFileTagGroupKey.getTagNames().isEmpty()) {
1925 }
else if (otherFileTagGroupKey.getTagNames().isEmpty()) {
1929 return getTagNamesString().compareTo(otherFileTagGroupKey.getTagNamesString());
1931 return compareClassNames(otherGroupKey);
1937 if (otherKey ==
this) {
1945 FileTagGroupKey otherFileTagGroupKey = (FileTagGroupKey) otherKey;
1946 return getTagNamesString().equals(otherFileTagGroupKey.getTagNamesString());
1951 return Objects.hash(getTagNamesString());
1957 List<String> getTagNames() {
1958 return Collections.unmodifiableList(tagNames);
1964 String getTagNamesString() {
1972 static class NoGroupingAttribute
extends AttributeType {
1975 GroupKey getGroupKey(ResultFile file) {
1976 return new NoGroupingGroupKey();
1990 @NbBundle.Messages({
1991 "FileSearch.NoGroupingGroupKey.allFiles=All Files"})
1993 String getDisplayName() {
1994 return Bundle.FileSearch_NoGroupingGroupKey_allFiles();
2003 return compareClassNames(otherGroupKey);
2009 if (otherKey ==
this) {
2025 @NbBundle.Messages({
2026 "FileSearch.GroupingAttributeType.fileType.displayName=File Type",
2027 "FileSearch.GroupingAttributeType.frequency.displayName=Past Occurrences",
2028 "FileSearch.GroupingAttributeType.keywordList.displayName=Keyword",
2029 "FileSearch.GroupingAttributeType.size.displayName=File Size",
2030 "FileSearch.GroupingAttributeType.datasource.displayName=Data Source",
2031 "FileSearch.GroupingAttributeType.parent.displayName=Parent Folder",
2032 "FileSearch.GroupingAttributeType.hash.displayName=Hash Set",
2033 "FileSearch.GroupingAttributeType.interestingItem.displayName=Interesting Item",
2034 "FileSearch.GroupingAttributeType.tag.displayName=Tag",
2035 "FileSearch.GroupingAttributeType.object.displayName=Object Detected",
2036 "FileSearch.GroupingAttributeType.none.displayName=None"})
2037 enum GroupingAttributeType {
2038 FILE_SIZE(
new FileSizeAttribute(), Bundle.FileSearch_GroupingAttributeType_size_displayName()),
2039 FREQUENCY(
new FrequencyAttribute(), Bundle.FileSearch_GroupingAttributeType_frequency_displayName()),
2040 KEYWORD_LIST_NAME(
new KeywordListAttribute(), Bundle.FileSearch_GroupingAttributeType_keywordList_displayName()),
2041 DATA_SOURCE(
new DataSourceAttribute(), Bundle.FileSearch_GroupingAttributeType_datasource_displayName()),
2042 PARENT_PATH(
new ParentPathAttribute(), Bundle.FileSearch_GroupingAttributeType_parent_displayName()),
2043 HASH_LIST_NAME(
new HashHitsAttribute(), Bundle.FileSearch_GroupingAttributeType_hash_displayName()),
2044 INTERESTING_ITEM_SET(
new InterestingItemAttribute(), Bundle.FileSearch_GroupingAttributeType_interestingItem_displayName()),
2045 FILE_TAG(
new FileTagAttribute(), Bundle.FileSearch_GroupingAttributeType_tag_displayName()),
2046 OBJECT_DETECTED(
new ObjectDetectedAttribute(), Bundle.FileSearch_GroupingAttributeType_object_displayName()),
2047 NO_GROUPING(
new NoGroupingAttribute(), Bundle.FileSearch_GroupingAttributeType_none_displayName());
2049 private final AttributeType attributeType;
2050 private final String displayName;
2052 GroupingAttributeType(AttributeType attributeType, String displayName) {
2053 this.attributeType = attributeType;
2054 this.displayName = displayName;
2058 public String toString() {
2062 AttributeType getAttributeType() {
2063 return attributeType;
2071 static List<GroupingAttributeType> getOptionsForGrouping() {
2072 return Arrays.asList(FILE_SIZE, FREQUENCY, PARENT_PATH, OBJECT_DETECTED, HASH_LIST_NAME, INTERESTING_ITEM_SET);
int compareTo(GroupKey otherGroupKey)
final String tagNamesString
FrequencyCallback(List< ResultFile > files)
int compareTo(GroupKey otherGroupKey)
void process(ResultSet rs)
static File getVideoFileInTempDir(AbstractFile file)
boolean equals(Object otherKey)
int compareTo(GroupKey otherGroupKey)
final List< String > interestingItemSetNames
boolean equals(Object otherKey)
boolean equals(Object otherKey)
int compareTo(GroupKey otherGroupKey)
final List< String > tagNames
final Frequency frequency
final String interestingItemSetNamesString
void process(ResultSet rs)
final List< String > objectDetectedNames
int compareTo(GroupKey otherGroupKey)
final String keywordListNamesString
boolean equals(Object otherKey)
final List< ResultFile > files
void setParentPathAndID(Content parent, ResultFile file)
final List< String > keywordListNames
boolean equals(Object otherKey)
final String hashSetNamesString
int compareTo(GroupKey otherGroupKey)
final List< String > hashSetNames
boolean equals(Object otherKey)
boolean equals(Object otherKey)
final String objectDetectedNamesString
int compareTo(GroupKey otherGroupKey)
void process(ResultSet rs)
boolean equals(Object otherKey)
int compareTo(GroupKey otherGroupKey)
int compareTo(GroupKey otherGroupKey)
void process(ResultSet rs)
int compareTo(SearchKey otherSearchKey)
boolean equals(Object otherKey)
boolean equals(Object otherKey)
int compareTo(GroupKey otherGroupKey)
int compareTo(GroupKey otherGroupKey)
boolean equals(Object otherKey)
void process(ResultSet resultSet)
boolean equals(Object otherKey)