Autopsy  4.13.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
FileSearch.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2019 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.filequery;
20 
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;
39 import java.util.Map;
40 import java.util.Objects;
41 import java.util.Set;
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;
65 import org.sleuthkit.datamodel.AbstractFile;
66 import org.sleuthkit.datamodel.BlackboardArtifact;
67 import org.sleuthkit.datamodel.BlackboardAttribute;
68 import org.sleuthkit.datamodel.CaseDbAccessManager;
69 import org.sleuthkit.datamodel.Content;
70 import org.sleuthkit.datamodel.ContentTag;
71 import org.sleuthkit.datamodel.SleuthkitCase;
72 import org.sleuthkit.datamodel.TskCoreException;
73 import org.sleuthkit.datamodel.TskData;
74 
78 class FileSearch {
79 
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"; //NON-NLS
83  private static final String VIDEO_THUMBNAIL_DIR = "video-thumbnails"; //NON-NLS
84  private static final Cache<SearchKey, Map<GroupKey, List<ResultFile>>> searchCache = CacheBuilder.newBuilder()
85  .maximumSize(MAXIMUM_CACHE_SIZE)
86  .build();
87 
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 {
112  // Make a list of attributes that we want to add values for. This ensures the
113  // ResultFile objects will have all needed fields set when it's time to group
114  // and sort them. For example, if we're grouping by central repo frequency, we need
115  // to make sure we've loaded those values before grouping.
116  List<AttributeType> attributesNeededForGroupingOrSorting = new ArrayList<>();
117  attributesNeededForGroupingOrSorting.add(groupAttributeType);
118  attributesNeededForGroupingOrSorting.addAll(fileSortingMethod.getRequiredAttributes());
119 
120  // Run the queries for each filter
121  List<ResultFile> resultFiles = FileSearchFiltering.runQueries(filters, caseDb, centralRepoDb);
122 
123  // Add the data to resultFiles for any attributes needed for sorting and grouping
124  addAttributes(attributesNeededForGroupingOrSorting, resultFiles, caseDb, centralRepoDb);
125 
126  // Collect everything in the search results
127  SearchResults searchResults = new SearchResults(groupSortingType, groupAttributeType, fileSortingMethod);
128  searchResults.add(resultFiles);
129 
130  // Sort and group the results
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);
136  }
137  return searchResults;
138  }
139 
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());
169  }
170  return groupSizes;
171  }
172 
195  static List<ResultFile> getFilesInGroup(String userName,
196  List<FileSearchFiltering.FileFilter> filters,
197  AttributeType groupAttributeType,
198  FileGroup.GroupSortingAlgorithm groupSortingType,
199  FileSorter.SortingMethod fileSortingMethod,
200  GroupKey groupKey,
201  int startingEntry,
202  int numberOfEntries,
203  SleuthkitCase caseDb, EamDb centralRepoDb) throws FileSearchException {
204  //the group should be in the cache at this point
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);
210  }
211  if (resultsMap != null) {
212  filesInGroup = resultsMap.get(groupKey);
213  }
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());
220  }
221  if (resultsMap != null) {
222  filesInGroup = resultsMap.get(groupKey);
223  }
224  if (filesInGroup == null) {
225  logger.log(Level.WARNING, "Group {0} did not exist in cache or new search results", groupKey);
226  return page; //group does not exist
227  }
228  }
229  // Check that there is data after the starting point
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});
232  return page;
233  }
234  // Add files to the page
235  for (int i = startingEntry; (i < startingEntry + numberOfEntries)
236  && (i < filesInGroup.size()); i++) {
237  page.add(filesInGroup.get(i));
238  }
239  return page;
240  }
241 
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 {
265 
266  // Make a list of attributes that we want to add values for. This ensures the
267  // ResultFile objects will have all needed fields set when it's time to group
268  // and sort them. For example, if we're grouping by central repo frequency, we need
269  // to make sure we've loaded those values before grouping.
270  List<AttributeType> attributesNeededForGroupingOrSorting = new ArrayList<>();
271  attributesNeededForGroupingOrSorting.add(groupAttributeType);
272  attributesNeededForGroupingOrSorting.addAll(fileSortingMethod.getRequiredAttributes());
273 
274  // Run the queries for each filter
275  List<ResultFile> resultFiles = FileSearchFiltering.runQueries(filters, caseDb, centralRepoDb);
276 
277  // Add the data to resultFiles for any attributes needed for sorting and grouping
278  addAttributes(attributesNeededForGroupingOrSorting, resultFiles, caseDb, centralRepoDb);
279 
280  // Collect everything in the search results
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);
287  }
288  // Return a version of the results in general Java objects
289  return resultHashMap;
290  }
291 
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);
309  }
310  }
311 
319  private static void computeFrequency(Set<String> hashesToLookUp, List<ResultFile> currentFiles, EamDb centralRepoDb) {
320 
321  if (hashesToLookUp.isEmpty()) {
322  return;
323  }
324 
325  String hashes = String.join("','", hashesToLookUp);
326  hashes = "'" + hashes + "'";
327  try {
328  CorrelationAttributeInstance.Type attributeType = centralRepoDb.getCorrelationTypeById(CorrelationAttributeInstance.FILES_TYPE_ID);
329  String tableName = EamDbUtil.correlationTypeToInstanceTableName(attributeType);
330 
331  String selectClause = " value, COUNT(value) FROM "
332  + "(SELECT DISTINCT case_id, value FROM " + tableName
333  + " WHERE value IN ("
334  + hashes
335  + ")) AS foo GROUP BY value";
336 
337  FrequencyCallback callback = new FrequencyCallback(currentFiles);
338  centralRepoDb.processSelectClause(selectClause, callback);
339 
340  } catch (EamDbException ex) {
341  logger.log(Level.WARNING, "Error getting frequency counts from Central Repository", ex); // NON-NLS
342  }
343 
344  }
345 
346  private static String createSetNameClause(List<ResultFile> files,
347  int artifactTypeID, int setNameAttrID) throws FileSearchException {
348 
349  // Concatenate the object IDs in the list of files
350  String objIdList = ""; // NON-NLS
351  for (ResultFile file : files) {
352  if (!objIdList.isEmpty()) {
353  objIdList += ","; // NON-NLS
354  }
355  objIdList += "\'" + file.getFirstInstance().getId() + "\'"; // NON-NLS
356  }
357 
358  // Get pairs of (object ID, set name) for all files in the list of files that have
359  // the given artifact type.
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 + ") "; // NON-NLS
366  }
367 
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;
382  try {
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);
387  }
388 
389  if (cacheDirectory == null || file.getMd5Hash() == null || !Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, file.getMd5Hash()).toFile().exists()) {
390  java.io.File tempFile;
391  try {
392  tempFile = getVideoFileInTempDir(file);
393  } catch (NoCurrentCaseException ex) {
394  logger.log(Level.WARNING, "Exception while getting open case.", ex); //NON-NLS
395  int[] framePositions = new int[]{
396  0,
397  0,
398  0,
399  0};
400  thumbnailWrapper.setThumbnails(createDefaultThumbnailList(), framePositions);
401  return;
402  }
403  if (tempFile.exists() == false || tempFile.length() < file.getSize()) {
404  ProgressHandle progress = ProgressHandle.createHandle(Bundle.FileSearch_genVideoThumb_progress_text(file.getName()));
405  progress.start(100);
406  try {
407  Files.createParentDirs(tempFile);
408  if (Thread.interrupted()) {
409  int[] framePositions = new int[]{
410  0,
411  0,
412  0,
413  0};
414  thumbnailWrapper.setThumbnails(createDefaultThumbnailList(), framePositions);
415  return;
416  }
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); //NON-NLS
420  } finally {
421  progress.finish();
422  }
423  }
424  VideoCapture videoFile = new VideoCapture(); // will contain the video
425  BufferedImage bufferedImage = null;
426 
427  try {
428  if (!videoFile.open(tempFile.toString())) {
429  logger.log(Level.WARNING, "Error opening {0} for preview generation.", file.getParentPath() + "/" + file.getName()); //NON-NLS
430  int[] framePositions = new int[]{
431  0,
432  0,
433  0,
434  0};
435  thumbnailWrapper.setThumbnails(createDefaultThumbnailList(), framePositions);
436  return;
437  }
438  double fps = videoFile.get(5); // gets frame per second
439  double totalFrames = videoFile.get(7); // gets total frames
440  if (fps <= 0 || totalFrames <= 0) {
441  logger.log(Level.WARNING, "Error getting fps or total frames for {0}", file.getParentPath() + "/" + file.getName()); //NON-NLS
442  int[] framePositions = new int[]{
443  0,
444  0,
445  0,
446  0};
447  thumbnailWrapper.setThumbnails(createDefaultThumbnailList(), framePositions);
448  return;
449  }
450  if (Thread.interrupted()) {
451  int[] framePositions = new int[]{
452  0,
453  0,
454  0,
455  0};
456  thumbnailWrapper.setThumbnails(createDefaultThumbnailList(), framePositions);
457  return;
458  }
459 
460  double duration = 1000 * (totalFrames / fps); //total milliseconds
461 
462  int[] framePositions = new int[]{
463  (int) (duration * .01),
464  (int) (duration * .25),
465  (int) (duration * .5),
466  (int) (duration * .75),};
467 
468  Mat imageMatrix = new Mat();
469  List<Image> videoThumbnails = new ArrayList<>();
470  if (cacheDirectory == null || file.getMd5Hash() == null) {
471  cacheDirectory = null;
472  } else {
473  try {
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);
478  }
479  }
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()); //NON-NLS
483  // If we can't set the time, continue to the next frame position and try again.
484 
485  videoThumbnails.add(ImageUtils.getDefaultThumbnail());
486  if (cacheDirectory != null) {
487  try {
488  ImageIO.write((RenderedImage) ImageUtils.getDefaultThumbnail(), THUMBNAIL_FORMAT,
489  Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, file.getMd5Hash(), i + "-" + framePositions[i] + "." + THUMBNAIL_FORMAT).toFile()); //NON-NLS)
490  } catch (IOException ex) {
491  logger.log(Level.WARNING, "Unable to save default video thumbnail for " + file.getMd5Hash() + " at frame position " + framePositions[i], ex);
492  }
493  }
494  continue;
495  }
496  // Read the frame into the image/matrix.
497  if (!videoFile.read(imageMatrix)) {
498  logger.log(Level.WARNING, "Error reading frame at " + framePositions[i] + "ms from {0}", file.getParentPath() + "/" + file.getName()); //NON-NLS
499  // If the image is bad for some reason, continue to the next frame position and try again.
500  videoThumbnails.add(ImageUtils.getDefaultThumbnail());
501  if (cacheDirectory != null) {
502  try {
503  ImageIO.write((RenderedImage) ImageUtils.getDefaultThumbnail(), THUMBNAIL_FORMAT,
504  Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, file.getMd5Hash(), i + "-" + framePositions[i] + "." + THUMBNAIL_FORMAT).toFile()); //NON-NLS)
505  } catch (IOException ex) {
506  logger.log(Level.WARNING, "Unable to save default video thumbnail for " + file.getMd5Hash() + " at frame position " + framePositions[i], ex);
507  }
508  }
509 
510  continue;
511  }
512  // If the image is empty, return since no buffered image can be created.
513  if (imageMatrix.empty()) {
514  videoThumbnails.add(ImageUtils.getDefaultThumbnail());
515  if (cacheDirectory != null) {
516  try {
517  ImageIO.write((RenderedImage) ImageUtils.getDefaultThumbnail(), THUMBNAIL_FORMAT,
518  Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, file.getMd5Hash(), i + "-" + framePositions[i] + "." + THUMBNAIL_FORMAT).toFile()); //NON-NLS)
519  } catch (IOException ex) {
520  logger.log(Level.WARNING, "Unable to save default video thumbnail for " + file.getMd5Hash() + " at frame position " + framePositions[i], ex);
521  }
522  }
523  continue;
524  }
525 
526  int matrixColumns = imageMatrix.cols();
527  int matrixRows = imageMatrix.rows();
528 
529  // Convert the matrix that contains the frame to a buffered image.
530  if (bufferedImage == null) {
531  bufferedImage = new BufferedImage(matrixColumns, matrixRows, BufferedImage.TYPE_3BYTE_BGR);
532  }
533 
534  byte[] data = new byte[matrixRows * matrixColumns * (int) (imageMatrix.elemSize())];
535  imageMatrix.get(0, 0, data); //copy the image to data
536 
537  if (imageMatrix.channels() == 3) {
538  for (int k = 0; k < data.length; k += 3) {
539  byte temp = data[k];
540  data[k] = data[k + 2];
541  data[k + 2] = temp;
542  }
543  }
544 
545  bufferedImage.getRaster().setDataElements(0, 0, matrixColumns, matrixRows, data);
546  if (Thread.interrupted()) {
547  thumbnailWrapper.setThumbnails(videoThumbnails, framePositions);
548  try {
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);
552  }
553  return;
554  }
555  BufferedImage thumbnail = ScalrWrapper.resizeFast(bufferedImage, ImageUtils.ICON_SIZE_LARGE);
556  videoThumbnails.add(thumbnail);
557  if (cacheDirectory != null) {
558  try {
559  ImageIO.write(thumbnail, THUMBNAIL_FORMAT,
560  Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, file.getMd5Hash(), i + "-" + framePositions[i] + "." + THUMBNAIL_FORMAT).toFile()); //NON-NLS)
561  } catch (IOException ex) {
562  logger.log(Level.WARNING, "Unable to save video thumbnail for " + file.getMd5Hash() + " at frame position " + framePositions[i], ex);
563  }
564  }
565  }
566  thumbnailWrapper.setThumbnails(videoThumbnails, framePositions);
567  } finally {
568  videoFile.release(); // close the file}
569  }
570  } else {
571  loadSavedThumbnails(cacheDirectory, thumbnailWrapper);
572  }
573  }
574 
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()) {
591  try {
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);
596  }
597  int framePos = Integer.valueOf(FilenameUtils.getBaseName(fileName).substring(2));
598  framePositions[thumbnailNumber] = framePos;
599  thumbnailNumber++;
600 
601  }
602  thumbnailWrapper.setThumbnails(videoThumbnails, framePositions);
603  }
604 
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;
618  }
619 
620  private FileSearch() {
621  // Class should not be instantiated
622  }
623 
627  abstract static class AttributeType {
628 
637  abstract GroupKey getGroupKey(ResultFile file);
638 
649  void addAttributeToResultFiles(List<ResultFile> files, SleuthkitCase caseDb, EamDb centralRepoDb) throws FileSearchException {
650  // Default is to do nothing
651  }
652  }
653 
657  abstract static class GroupKey implements Comparable<GroupKey> {
658 
665  abstract String getDisplayName();
666 
674  @Override
675  abstract public boolean equals(Object otherKey);
676 
682  @Override
683  abstract public int hashCode();
684 
694  int compareClassNames(GroupKey otherGroupKey) {
695  return this.getClass().getName().compareTo(otherGroupKey.getClass().getName());
696  }
697 
698  @Override
699  public String toString() {
700  return getDisplayName();
701  }
702  }
703 
707  static class FileSizeAttribute extends AttributeType {
708 
709  @Override
710  GroupKey getGroupKey(ResultFile file) {
711  return new FileSizeGroupKey(file);
712  }
713  }
714 
718  private static class FileSizeGroupKey extends GroupKey {
719 
720  private final FileSize fileSize;
721 
722  FileSizeGroupKey(ResultFile file) {
723  if (file.getFileType() == FileType.VIDEO) {
724  fileSize = FileSize.fromVideoSize(file.getFirstInstance().getSize());
725  } else {
726  fileSize = FileSize.fromImageSize(file.getFirstInstance().getSize());
727  }
728  }
729 
730  @Override
731  String getDisplayName() {
732  return getFileSize().toString();
733  }
734 
735  @Override
736  public int compareTo(GroupKey otherGroupKey) {
737  if (otherGroupKey instanceof FileSizeGroupKey) {
738  FileSizeGroupKey otherFileSizeGroupKey = (FileSizeGroupKey) otherGroupKey;
739  return Integer.compare(getFileSize().getRanking(), otherFileSizeGroupKey.getFileSize().getRanking());
740  } else {
741  return compareClassNames(otherGroupKey);
742  }
743  }
744 
745  @Override
746  public boolean equals(Object otherKey) {
747  if (otherKey == this) {
748  return true;
749  }
750 
751  if (!(otherKey instanceof FileSizeGroupKey)) {
752  return false;
753  }
754 
755  FileSizeGroupKey otherFileSizeGroupKey = (FileSizeGroupKey) otherKey;
756  return getFileSize().equals(otherFileSizeGroupKey.getFileSize());
757  }
758 
759  @Override
760  public int hashCode() {
761  return Objects.hash(getFileSize().getRanking());
762  }
763 
767  FileSize getFileSize() {
768  return fileSize;
769  }
770  }
771 
775  static class ParentPathAttribute extends AttributeType {
776 
777  @Override
778  GroupKey getGroupKey(ResultFile file) {
779  return new ParentPathGroupKey(file);
780  }
781  }
782 
786  private static class ParentPathGroupKey extends GroupKey {
787 
788  private String parentPath;
789  private Long parentID;
790 
791  ParentPathGroupKey(ResultFile file) {
792  Content parent;
793  try {
794  parent = file.getFirstInstance().getParent();
795  } catch (TskCoreException ignored) {
796  parent = null;
797  }
798  //Find the directory this file is in if it is an embedded file
799  while (parent != null && parent instanceof AbstractFile && ((AbstractFile) parent).isFile()) {
800  try {
801  parent = parent.getParent();
802  } catch (TskCoreException ignored) {
803  parent = null;
804  }
805  }
806  setParentPathAndID(parent, file);
807  }
808 
815  private void setParentPathAndID(Content parent, ResultFile file) {
816  if (parent != null) {
817  try {
818  parentPath = parent.getUniquePath();
819  parentID = parent.getId();
820  } catch (TskCoreException ignored) {
821  //catch block left blank purposefully next if statement will handle case when exception takes place as well as when parent is null
822  }
823 
824  }
825  if (parentPath == null) {
826  if (file.getFirstInstance().getParentPath() != null) {
827  parentPath = file.getFirstInstance().getParentPath();
828  } else {
829  parentPath = ""; // NON-NLS
830  }
831  parentID = -1L;
832  }
833  }
834 
835  @Override
836  String getDisplayName() {
837  return getParentPath();
838  }
839 
840  @Override
841  public int compareTo(GroupKey otherGroupKey) {
842  if (otherGroupKey instanceof ParentPathGroupKey) {
843  ParentPathGroupKey otherParentPathGroupKey = (ParentPathGroupKey) otherGroupKey;
844  int comparisonResult = getParentPath().compareTo(otherParentPathGroupKey.getParentPath());
845  if (comparisonResult == 0) {
846  comparisonResult = getParentID().compareTo(otherParentPathGroupKey.getParentID());
847  }
848  return comparisonResult;
849  } else {
850  return compareClassNames(otherGroupKey);
851  }
852  }
853 
854  @Override
855  public boolean equals(Object otherKey) {
856  if (otherKey == this) {
857  return true;
858  }
859 
860  if (!(otherKey instanceof ParentPathGroupKey)) {
861  return false;
862  }
863 
864  ParentPathGroupKey otherParentPathGroupKey = (ParentPathGroupKey) otherKey;
865  return getParentPath().equals(otherParentPathGroupKey.getParentPath()) && getParentID().equals(otherParentPathGroupKey.getParentID());
866  }
867 
868  @Override
869  public int hashCode() {
870  int hashCode = 11;
871  hashCode = 61 * hashCode + Objects.hash(getParentPath());
872  hashCode = 61 * hashCode + Objects.hash(getParentID());
873  return hashCode;
874  }
875 
879  String getParentPath() {
880  return parentPath;
881  }
882 
886  Long getParentID() {
887  return parentID;
888  }
889  }
890 
894  static class DataSourceAttribute extends AttributeType {
895 
896  @Override
897  GroupKey getGroupKey(ResultFile file) {
898  return new DataSourceGroupKey(file);
899  }
900  }
901 
905  private static class DataSourceGroupKey extends GroupKey {
906 
907  private final long dataSourceID;
908  private String displayName;
909 
910  @NbBundle.Messages({
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})"})
916  DataSourceGroupKey(ResultFile file) {
917  dataSourceID = file.getFirstInstance().getDataSourceObjectId();
918 
919  try {
920  // The data source should be cached so this won't actually be a database query.
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); // NON-NLS
925  displayName = Bundle.FileSearch_DataSourceGroupKey_idOnly(dataSourceID);
926  }
927  }
928 
929  @Override
930  String getDisplayName() {
931  return displayName;
932  }
933 
934  @Override
935  public int compareTo(GroupKey otherGroupKey) {
936  if (otherGroupKey instanceof DataSourceGroupKey) {
937  DataSourceGroupKey otherDataSourceGroupKey = (DataSourceGroupKey) otherGroupKey;
938  return Long.compare(getDataSourceID(), otherDataSourceGroupKey.getDataSourceID());
939  } else {
940  return compareClassNames(otherGroupKey);
941  }
942  }
943 
944  @Override
945  public boolean equals(Object otherKey) {
946  if (otherKey == this) {
947  return true;
948  }
949 
950  if (!(otherKey instanceof DataSourceGroupKey)) {
951  return false;
952  }
953 
954  DataSourceGroupKey otherDataSourceGroupKey = (DataSourceGroupKey) otherKey;
955  return getDataSourceID() == otherDataSourceGroupKey.getDataSourceID();
956  }
957 
958  @Override
959  public int hashCode() {
960  return Objects.hash(getDataSourceID());
961  }
962 
966  long getDataSourceID() {
967  return dataSourceID;
968  }
969  }
970 
974  static class FileTypeAttribute extends AttributeType {
975 
976  @Override
977  GroupKey getGroupKey(ResultFile file) {
978  return new FileTypeGroupKey(file);
979  }
980  }
981 
985  private static class FileTypeGroupKey extends GroupKey {
986 
987  private final FileType fileType;
988 
989  FileTypeGroupKey(ResultFile file) {
990  fileType = file.getFileType();
991  }
992 
993  @Override
994  String getDisplayName() {
995  return getFileType().toString();
996  }
997 
998  @Override
999  public int compareTo(GroupKey otherGroupKey) {
1000  if (otherGroupKey instanceof FileTypeGroupKey) {
1001  FileTypeGroupKey otherFileTypeGroupKey = (FileTypeGroupKey) otherGroupKey;
1002  return Integer.compare(getFileType().getRanking(), otherFileTypeGroupKey.getFileType().getRanking());
1003  } else {
1004  return compareClassNames(otherGroupKey);
1005  }
1006  }
1007 
1008  @Override
1009  public boolean equals(Object otherKey) {
1010  if (otherKey == this) {
1011  return true;
1012  }
1013 
1014  if (!(otherKey instanceof FileTypeGroupKey)) {
1015  return false;
1016  }
1017 
1018  FileTypeGroupKey otherFileTypeGroupKey = (FileTypeGroupKey) otherKey;
1019  return getFileType().equals(otherFileTypeGroupKey.getFileType());
1020  }
1021 
1022  @Override
1023  public int hashCode() {
1024  return Objects.hash(getFileType().getRanking());
1025  }
1026 
1030  FileType getFileType() {
1031  return fileType;
1032  }
1033  }
1034 
1038  static class KeywordListAttribute extends AttributeType {
1039 
1040  @Override
1041  GroupKey getGroupKey(ResultFile file) {
1042  return new KeywordListGroupKey(file);
1043  }
1044 
1045  @Override
1046  void addAttributeToResultFiles(List<ResultFile> files, SleuthkitCase caseDb,
1047  EamDb centralRepoDb) throws FileSearchException {
1048 
1049  // Get pairs of (object ID, keyword list name) for all files in the list of files that have
1050  // keyword list hits.
1051  String selectQuery = createSetNameClause(files, BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID(),
1052  BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID());
1053 
1054  SetKeywordListNamesCallback callback = new SetKeywordListNamesCallback(files);
1055  try {
1056  caseDb.getCaseDbAccessManager().select(selectQuery, callback);
1057  } catch (TskCoreException ex) {
1058  throw new FileSearchException("Error looking up keyword list attributes", ex); // NON-NLS
1059  }
1060  }
1061 
1067  private static class SetKeywordListNamesCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback {
1068 
1069  List<ResultFile> resultFiles;
1070 
1076  SetKeywordListNamesCallback(List<ResultFile> resultFiles) {
1077  this.resultFiles = resultFiles;
1078  }
1079 
1080  @Override
1081  public void process(ResultSet rs) {
1082  try {
1083  // Create a temporary map of object ID to ResultFile
1084  Map<Long, ResultFile> tempMap = new HashMap<>();
1085  for (ResultFile file : resultFiles) {
1086  tempMap.put(file.getFirstInstance().getId(), file);
1087  }
1088 
1089  while (rs.next()) {
1090  try {
1091  Long objId = rs.getLong("object_id"); // NON-NLS
1092  String keywordListName = rs.getString("set_name"); // NON-NLS
1093 
1094  tempMap.get(objId).addKeywordListName(keywordListName);
1095 
1096  } catch (SQLException ex) {
1097  logger.log(Level.SEVERE, "Unable to get object_id or set_name from result set", ex); // NON-NLS
1098  }
1099  }
1100  } catch (SQLException ex) {
1101  logger.log(Level.SEVERE, "Failed to get keyword list names", ex); // NON-NLS
1102  }
1103  }
1104  }
1105  }
1106 
1110  private static class KeywordListGroupKey extends GroupKey {
1111 
1112  private final List<String> keywordListNames;
1113  private final String keywordListNamesString;
1114 
1115  @NbBundle.Messages({
1116  "FileSearch.KeywordListGroupKey.noKeywords=None"})
1117  KeywordListGroupKey(ResultFile file) {
1118  keywordListNames = file.getKeywordListNames();
1119 
1120  if (keywordListNames.isEmpty()) {
1121  keywordListNamesString = Bundle.FileSearch_KeywordListGroupKey_noKeywords();
1122  } else {
1123  keywordListNamesString = String.join(",", keywordListNames); // NON-NLS
1124  }
1125  }
1126 
1127  @Override
1128  String getDisplayName() {
1129  return getKeywordListNamesString();
1130  }
1131 
1132  @Override
1133  public int compareTo(GroupKey otherGroupKey) {
1134  if (otherGroupKey instanceof KeywordListGroupKey) {
1135  KeywordListGroupKey otherKeywordListNamesGroupKey = (KeywordListGroupKey) otherGroupKey;
1136 
1137  // Put the empty list at the end
1138  if (getKeywordListNames().isEmpty()) {
1139  if (otherKeywordListNamesGroupKey.getKeywordListNames().isEmpty()) {
1140  return 0;
1141  } else {
1142  return 1;
1143  }
1144  } else if (otherKeywordListNamesGroupKey.getKeywordListNames().isEmpty()) {
1145  return -1;
1146  }
1147 
1148  return getKeywordListNamesString().compareTo(otherKeywordListNamesGroupKey.getKeywordListNamesString());
1149  } else {
1150  return compareClassNames(otherGroupKey);
1151  }
1152  }
1153 
1154  @Override
1155  public boolean equals(Object otherKey) {
1156  if (otherKey == this) {
1157  return true;
1158  }
1159 
1160  if (!(otherKey instanceof KeywordListGroupKey)) {
1161  return false;
1162  }
1163 
1164  KeywordListGroupKey otherKeywordListGroupKey = (KeywordListGroupKey) otherKey;
1165  return getKeywordListNamesString().equals(otherKeywordListGroupKey.getKeywordListNamesString());
1166  }
1167 
1168  @Override
1169  public int hashCode() {
1170  return Objects.hash(getKeywordListNamesString());
1171  }
1172 
1176  List<String> getKeywordListNames() {
1177  return Collections.unmodifiableList(keywordListNames);
1178  }
1179 
1183  String getKeywordListNamesString() {
1184  return keywordListNamesString;
1185  }
1186  }
1187 
1191  static class FrequencyAttribute extends AttributeType {
1192 
1193  static final int BATCH_SIZE = 50; // Number of hashes to look up at one time
1194 
1195  @Override
1196  GroupKey getGroupKey(ResultFile file) {
1197  return new FrequencyGroupKey(file);
1198  }
1199 
1200  @Override
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);
1207  }
1208  }
1209  } else {
1210  processResultFilesForCR(files, centralRepoDb);
1211  }
1212  }
1213 
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);
1229  }
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);
1235  }
1236  if (hashesToLookUp.size() >= BATCH_SIZE) {
1237  computeFrequency(hashesToLookUp, currentFiles, centralRepoDb);
1238 
1239  hashesToLookUp.clear();
1240  currentFiles.clear();
1241  }
1242  }
1243  computeFrequency(hashesToLookUp, currentFiles, centralRepoDb);
1244  }
1245  }
1246 
1251  private static class FrequencyCallback implements InstanceTableCallback {
1252 
1253  private final List<ResultFile> files;
1254 
1255  private FrequencyCallback(List<ResultFile> files) {
1256  this.files = new ArrayList<>(files);
1257  }
1258 
1259  @Override
1260  public void process(ResultSet resultSet) {
1261  try {
1262 
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));
1270  iterator.remove();
1271  }
1272  }
1273  }
1274 
1275  // The files left had no matching entries in the CR, so mark them as unique
1276  for (ResultFile file : files) {
1277  file.setFrequency(Frequency.UNIQUE);
1278  }
1279  } catch (SQLException ex) {
1280  logger.log(Level.WARNING, "Error getting frequency counts from Central Repository", ex); // NON-NLS
1281  }
1282  }
1283  }
1284 
1288  private static class FrequencyGroupKey extends GroupKey {
1289 
1290  private final Frequency frequency;
1291 
1292  FrequencyGroupKey(ResultFile file) {
1293  frequency = file.getFrequency();
1294  }
1295 
1296  @Override
1297  String getDisplayName() {
1298  return getFrequency().toString();
1299  }
1300 
1301  @Override
1302  public int compareTo(GroupKey otherGroupKey) {
1303  if (otherGroupKey instanceof FrequencyGroupKey) {
1304  FrequencyGroupKey otherFrequencyGroupKey = (FrequencyGroupKey) otherGroupKey;
1305  return Integer.compare(getFrequency().getRanking(), otherFrequencyGroupKey.getFrequency().getRanking());
1306  } else {
1307  return compareClassNames(otherGroupKey);
1308  }
1309  }
1310 
1311  @Override
1312  public boolean equals(Object otherKey) {
1313  if (otherKey == this) {
1314  return true;
1315  }
1316 
1317  if (!(otherKey instanceof FrequencyGroupKey)) {
1318  return false;
1319  }
1320 
1321  FrequencyGroupKey otherFrequencyGroupKey = (FrequencyGroupKey) otherKey;
1322  return getFrequency().equals(otherFrequencyGroupKey.getFrequency());
1323  }
1324 
1325  @Override
1326  public int hashCode() {
1327  return Objects.hash(getFrequency().getRanking());
1328  }
1329 
1333  Frequency getFrequency() {
1334  return frequency;
1335  }
1336  }
1337 
1341  static class HashHitsAttribute extends AttributeType {
1342 
1343  @Override
1344  GroupKey getGroupKey(ResultFile file) {
1345  return new HashHitsGroupKey(file);
1346  }
1347 
1348  @Override
1349  void addAttributeToResultFiles(List<ResultFile> files, SleuthkitCase caseDb,
1350  EamDb centralRepoDb) throws FileSearchException {
1351 
1352  // Get pairs of (object ID, hash set name) for all files in the list of files that have
1353  // hash set hits.
1354  String selectQuery = createSetNameClause(files, BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID(),
1355  BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID());
1356 
1357  HashSetNamesCallback callback = new HashSetNamesCallback(files);
1358  try {
1359  caseDb.getCaseDbAccessManager().select(selectQuery, callback);
1360  } catch (TskCoreException ex) {
1361  throw new FileSearchException("Error looking up hash set attributes", ex); // NON-NLS
1362  }
1363  }
1364 
1369  private static class HashSetNamesCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback {
1370 
1371  List<ResultFile> resultFiles;
1372 
1378  HashSetNamesCallback(List<ResultFile> resultFiles) {
1379  this.resultFiles = resultFiles;
1380  }
1381 
1382  @Override
1383  public void process(ResultSet rs) {
1384  try {
1385  // Create a temporary map of object ID to ResultFile
1386  Map<Long, ResultFile> tempMap = new HashMap<>();
1387  for (ResultFile file : resultFiles) {
1388  tempMap.put(file.getFirstInstance().getId(), file);
1389  }
1390 
1391  while (rs.next()) {
1392  try {
1393  Long objId = rs.getLong("object_id"); // NON-NLS
1394  String hashSetName = rs.getString("set_name"); // NON-NLS
1395 
1396  tempMap.get(objId).addHashSetName(hashSetName);
1397 
1398  } catch (SQLException ex) {
1399  logger.log(Level.SEVERE, "Unable to get object_id or set_name from result set", ex); // NON-NLS
1400  }
1401  }
1402  } catch (SQLException ex) {
1403  logger.log(Level.SEVERE, "Failed to get hash set names", ex); // NON-NLS
1404  }
1405  }
1406  }
1407  }
1408 
1412  private static class HashHitsGroupKey extends GroupKey {
1413 
1414  private final List<String> hashSetNames;
1415  private final String hashSetNamesString;
1416 
1417  @NbBundle.Messages({
1418  "FileSearch.HashHitsGroupKey.noHashHits=None"})
1419  HashHitsGroupKey(ResultFile file) {
1420  hashSetNames = file.getHashSetNames();
1421 
1422  if (hashSetNames.isEmpty()) {
1423  hashSetNamesString = Bundle.FileSearch_HashHitsGroupKey_noHashHits();
1424  } else {
1425  hashSetNamesString = String.join(",", hashSetNames); // NON-NLS
1426  }
1427  }
1428 
1429  @Override
1430  String getDisplayName() {
1431  return getHashSetNamesString();
1432  }
1433 
1434  @Override
1435  public int compareTo(GroupKey otherGroupKey) {
1436  if (otherGroupKey instanceof HashHitsGroupKey) {
1437  HashHitsGroupKey otherHashHitsGroupKey = (HashHitsGroupKey) otherGroupKey;
1438 
1439  // Put the empty list at the end
1440  if (getHashSetNames().isEmpty()) {
1441  if (otherHashHitsGroupKey.getHashSetNames().isEmpty()) {
1442  return 0;
1443  } else {
1444  return 1;
1445  }
1446  } else if (otherHashHitsGroupKey.getHashSetNames().isEmpty()) {
1447  return -1;
1448  }
1449 
1450  return getHashSetNamesString().compareTo(otherHashHitsGroupKey.getHashSetNamesString());
1451  } else {
1452  return compareClassNames(otherGroupKey);
1453  }
1454  }
1455 
1456  @Override
1457  public boolean equals(Object otherKey) {
1458  if (otherKey == this) {
1459  return true;
1460  }
1461 
1462  if (!(otherKey instanceof HashHitsGroupKey)) {
1463  return false;
1464  }
1465 
1466  HashHitsGroupKey otherHashHitsGroupKey = (HashHitsGroupKey) otherKey;
1467  return getHashSetNamesString().equals(otherHashHitsGroupKey.getHashSetNamesString());
1468  }
1469 
1470  @Override
1471  public int hashCode() {
1472  return Objects.hash(getHashSetNamesString());
1473  }
1474 
1478  List<String> getHashSetNames() {
1479  return Collections.unmodifiableList(hashSetNames);
1480  }
1481 
1485  String getHashSetNamesString() {
1486  return hashSetNamesString;
1487  }
1488  }
1489 
1493  static class InterestingItemAttribute extends AttributeType {
1494 
1495  @Override
1496  GroupKey getGroupKey(ResultFile file) {
1497  return new InterestingItemGroupKey(file);
1498  }
1499 
1500  @Override
1501  void addAttributeToResultFiles(List<ResultFile> files, SleuthkitCase caseDb,
1502  EamDb centralRepoDb) throws FileSearchException {
1503 
1504  // Get pairs of (object ID, interesting item set name) for all files in the list of files that have
1505  // interesting file set hits.
1506  String selectQuery = createSetNameClause(files, BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID(),
1507  BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID());
1508 
1509  InterestingFileSetNamesCallback callback = new InterestingFileSetNamesCallback(files);
1510  try {
1511  caseDb.getCaseDbAccessManager().select(selectQuery, callback);
1512  } catch (TskCoreException ex) {
1513  throw new FileSearchException("Error looking up interesting file set attributes", ex); // NON-NLS
1514  }
1515  }
1516 
1522  private static class InterestingFileSetNamesCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback {
1523 
1524  List<ResultFile> resultFiles;
1525 
1532  InterestingFileSetNamesCallback(List<ResultFile> resultFiles) {
1533  this.resultFiles = resultFiles;
1534  }
1535 
1536  @Override
1537  public void process(ResultSet rs) {
1538  try {
1539  // Create a temporary map of object ID to ResultFile
1540  Map<Long, ResultFile> tempMap = new HashMap<>();
1541  for (ResultFile file : resultFiles) {
1542  tempMap.put(file.getFirstInstance().getId(), file);
1543  }
1544 
1545  while (rs.next()) {
1546  try {
1547  Long objId = rs.getLong("object_id"); // NON-NLS
1548  String setName = rs.getString("set_name"); // NON-NLS
1549 
1550  tempMap.get(objId).addInterestingSetName(setName);
1551 
1552  } catch (SQLException ex) {
1553  logger.log(Level.SEVERE, "Unable to get object_id or set_name from result set", ex); // NON-NLS
1554  }
1555  }
1556  } catch (SQLException ex) {
1557  logger.log(Level.SEVERE, "Failed to get interesting file set names", ex); // NON-NLS
1558  }
1559  }
1560  }
1561  }
1562 
1566  private static class InterestingItemGroupKey extends GroupKey {
1567 
1568  private final List<String> interestingItemSetNames;
1569  private final String interestingItemSetNamesString;
1570 
1571  @NbBundle.Messages({
1572  "FileSearch.InterestingItemGroupKey.noSets=None"})
1573  InterestingItemGroupKey(ResultFile file) {
1574  interestingItemSetNames = file.getInterestingSetNames();
1575 
1576  if (interestingItemSetNames.isEmpty()) {
1577  interestingItemSetNamesString = Bundle.FileSearch_InterestingItemGroupKey_noSets();
1578  } else {
1579  interestingItemSetNamesString = String.join(",", interestingItemSetNames); // NON-NLS
1580  }
1581  }
1582 
1583  @Override
1584  String getDisplayName() {
1585  return getInterestingItemSetNamesString();
1586  }
1587 
1588  @Override
1589  public int compareTo(GroupKey otherGroupKey) {
1590  if (otherGroupKey instanceof InterestingItemGroupKey) {
1591  InterestingItemGroupKey otherInterestingItemGroupKey = (InterestingItemGroupKey) otherGroupKey;
1592 
1593  // Put the empty list at the end
1594  if (this.getInterestingItemSetNames().isEmpty()) {
1595  if (otherInterestingItemGroupKey.getInterestingItemSetNames().isEmpty()) {
1596  return 0;
1597  } else {
1598  return 1;
1599  }
1600  } else if (otherInterestingItemGroupKey.getInterestingItemSetNames().isEmpty()) {
1601  return -1;
1602  }
1603 
1604  return getInterestingItemSetNamesString().compareTo(otherInterestingItemGroupKey.getInterestingItemSetNamesString());
1605  } else {
1606  return compareClassNames(otherGroupKey);
1607  }
1608  }
1609 
1610  @Override
1611  public boolean equals(Object otherKey) {
1612  if (otherKey == this) {
1613  return true;
1614  }
1615 
1616  if (!(otherKey instanceof InterestingItemGroupKey)) {
1617  return false;
1618  }
1619 
1620  InterestingItemGroupKey otherInterestingItemGroupKey = (InterestingItemGroupKey) otherKey;
1621  return getInterestingItemSetNamesString().equals(otherInterestingItemGroupKey.getInterestingItemSetNamesString());
1622  }
1623 
1624  @Override
1625  public int hashCode() {
1626  return Objects.hash(getInterestingItemSetNamesString());
1627  }
1628 
1632  List<String> getInterestingItemSetNames() {
1633  return Collections.unmodifiableList(interestingItemSetNames);
1634  }
1635 
1639  String getInterestingItemSetNamesString() {
1641  }
1642  }
1643 
1647  static class ObjectDetectedAttribute extends AttributeType {
1648 
1649  @Override
1650  GroupKey getGroupKey(ResultFile file) {
1651  return new ObjectDetectedGroupKey(file);
1652  }
1653 
1654  @Override
1655  void addAttributeToResultFiles(List<ResultFile> files, SleuthkitCase caseDb,
1656  EamDb centralRepoDb) throws FileSearchException {
1657 
1658  // Get pairs of (object ID, object type name) for all files in the list of files that have
1659  // objects detected
1660  String selectQuery = createSetNameClause(files, BlackboardArtifact.ARTIFACT_TYPE.TSK_OBJECT_DETECTED.getTypeID(),
1661  BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION.getTypeID());
1662 
1663  ObjectDetectedNamesCallback callback = new ObjectDetectedNamesCallback(files);
1664  try {
1665  caseDb.getCaseDbAccessManager().select(selectQuery, callback);
1666  } catch (TskCoreException ex) {
1667  throw new FileSearchException("Error looking up object detected attributes", ex); // NON-NLS
1668  }
1669  }
1670 
1676  private static class ObjectDetectedNamesCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback {
1677 
1678  List<ResultFile> resultFiles;
1679 
1685  ObjectDetectedNamesCallback(List<ResultFile> resultFiles) {
1686  this.resultFiles = resultFiles;
1687  }
1688 
1689  @Override
1690  public void process(ResultSet rs) {
1691  try {
1692  // Create a temporary map of object ID to ResultFile
1693  Map<Long, ResultFile> tempMap = new HashMap<>();
1694  for (ResultFile file : resultFiles) {
1695  tempMap.put(file.getFirstInstance().getId(), file);
1696  }
1697 
1698  while (rs.next()) {
1699  try {
1700  Long objId = rs.getLong("object_id"); // NON-NLS
1701  String setName = rs.getString("set_name"); // NON-NLS
1702 
1703  tempMap.get(objId).addObjectDetectedName(setName);
1704 
1705  } catch (SQLException ex) {
1706  logger.log(Level.SEVERE, "Unable to get object_id or set_name from result set", ex); // NON-NLS
1707  }
1708  }
1709  } catch (SQLException ex) {
1710  logger.log(Level.SEVERE, "Failed to get object detected names", ex); // NON-NLS
1711  }
1712  }
1713  }
1714  }
1715 
1719  private static class ObjectDetectedGroupKey extends GroupKey {
1720 
1721  private final List<String> objectDetectedNames;
1722  private final String objectDetectedNamesString;
1723 
1724  @NbBundle.Messages({
1725  "FileSearch.ObjectDetectedGroupKey.noSets=None"})
1726  ObjectDetectedGroupKey(ResultFile file) {
1727  objectDetectedNames = file.getObjectDetectedNames();
1728 
1729  if (objectDetectedNames.isEmpty()) {
1730  objectDetectedNamesString = Bundle.FileSearch_ObjectDetectedGroupKey_noSets();
1731  } else {
1732  objectDetectedNamesString = String.join(",", objectDetectedNames); // NON-NLS
1733  }
1734  }
1735 
1736  @Override
1737  String getDisplayName() {
1738  return getObjectDetectedNamesString();
1739  }
1740 
1741  @Override
1742  public int compareTo(GroupKey otherGroupKey) {
1743  if (otherGroupKey instanceof ObjectDetectedGroupKey) {
1744  ObjectDetectedGroupKey otherObjectDetectedGroupKey = (ObjectDetectedGroupKey) otherGroupKey;
1745 
1746  // Put the empty list at the end
1747  if (this.getObjectDetectedNames().isEmpty()) {
1748  if (otherObjectDetectedGroupKey.getObjectDetectedNames().isEmpty()) {
1749  return 0;
1750  } else {
1751  return 1;
1752  }
1753  } else if (otherObjectDetectedGroupKey.getObjectDetectedNames().isEmpty()) {
1754  return -1;
1755  }
1756 
1757  return getObjectDetectedNamesString().compareTo(otherObjectDetectedGroupKey.getObjectDetectedNamesString());
1758  } else {
1759  return compareClassNames(otherGroupKey);
1760  }
1761  }
1762 
1763  @Override
1764  public boolean equals(Object otherKey) {
1765  if (otherKey == this) {
1766  return true;
1767  }
1768 
1769  if (!(otherKey instanceof ObjectDetectedGroupKey)) {
1770  return false;
1771  }
1772 
1773  ObjectDetectedGroupKey otherObjectDetectedGroupKey = (ObjectDetectedGroupKey) otherKey;
1774  return getObjectDetectedNamesString().equals(otherObjectDetectedGroupKey.getObjectDetectedNamesString());
1775  }
1776 
1777  @Override
1778  public int hashCode() {
1779  return Objects.hash(getObjectDetectedNamesString());
1780  }
1781 
1785  List<String> getObjectDetectedNames() {
1786  return Collections.unmodifiableList(objectDetectedNames);
1787  }
1788 
1792  String getObjectDetectedNamesString() {
1794  }
1795  }
1796 
1800  static class FileTagAttribute extends AttributeType {
1801 
1802  @Override
1803  GroupKey getGroupKey(ResultFile file) {
1804  return new FileTagGroupKey(file);
1805  }
1806 
1807  @Override
1808  void addAttributeToResultFiles(List<ResultFile> files, SleuthkitCase caseDb,
1809  EamDb centralRepoDb) throws FileSearchException {
1810 
1811  try {
1812  for (ResultFile resultFile : files) {
1813  List<ContentTag> contentTags = caseDb.getContentTagsByContent(resultFile.getFirstInstance());
1814 
1815  for (ContentTag tag : contentTags) {
1816  resultFile.addTagName(tag.getName().getDisplayName());
1817  }
1818  }
1819  } catch (TskCoreException ex) {
1820  throw new FileSearchException("Error looking up file tag attributes", ex); // NON-NLS
1821  }
1822  }
1823  }
1824 
1828  private static class SearchKey implements Comparable<SearchKey> {
1829 
1830  private final String keyString;
1831 
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());
1849  }
1850  searchStringBuilder.append(groupAttributeType).append(groupSortingType).append(fileSortingMethod);
1851  keyString = searchStringBuilder.toString();
1852  }
1853 
1854  @Override
1855  public int compareTo(SearchKey otherSearchKey) {
1856  return getKeyString().compareTo(otherSearchKey.getKeyString());
1857  }
1858 
1859  @Override
1860  public boolean equals(Object otherKey) {
1861  if (otherKey == this) {
1862  return true;
1863  }
1864 
1865  if (!(otherKey instanceof SearchKey)) {
1866  return false;
1867  }
1868 
1869  SearchKey otherSearchKey = (SearchKey) otherKey;
1870  return getKeyString().equals(otherSearchKey.getKeyString());
1871  }
1872 
1873  @Override
1874  public int hashCode() {
1875  int hash = 5;
1876  hash = 79 * hash + Objects.hashCode(getKeyString());
1877  return hash;
1878  }
1879 
1883  String getKeyString() {
1884  return keyString;
1885  }
1886  }
1887 
1891  private static class FileTagGroupKey extends GroupKey {
1892 
1893  private final List<String> tagNames;
1894  private final String tagNamesString;
1895 
1896  @NbBundle.Messages({
1897  "FileSearch.FileTagGroupKey.noSets=None"})
1898  FileTagGroupKey(ResultFile file) {
1899  tagNames = file.getTagNames();
1900 
1901  if (tagNames.isEmpty()) {
1902  tagNamesString = Bundle.FileSearch_FileTagGroupKey_noSets();
1903  } else {
1904  tagNamesString = String.join(",", tagNames); // NON-NLS
1905  }
1906  }
1907 
1908  @Override
1909  String getDisplayName() {
1910  return getTagNamesString();
1911  }
1912 
1913  @Override
1914  public int compareTo(GroupKey otherGroupKey) {
1915  if (otherGroupKey instanceof FileTagGroupKey) {
1916  FileTagGroupKey otherFileTagGroupKey = (FileTagGroupKey) otherGroupKey;
1917 
1918  // Put the empty list at the end
1919  if (getTagNames().isEmpty()) {
1920  if (otherFileTagGroupKey.getTagNames().isEmpty()) {
1921  return 0;
1922  } else {
1923  return 1;
1924  }
1925  } else if (otherFileTagGroupKey.getTagNames().isEmpty()) {
1926  return -1;
1927  }
1928 
1929  return getTagNamesString().compareTo(otherFileTagGroupKey.getTagNamesString());
1930  } else {
1931  return compareClassNames(otherGroupKey);
1932  }
1933  }
1934 
1935  @Override
1936  public boolean equals(Object otherKey) {
1937  if (otherKey == this) {
1938  return true;
1939  }
1940 
1941  if (!(otherKey instanceof FileTagGroupKey)) {
1942  return false;
1943  }
1944 
1945  FileTagGroupKey otherFileTagGroupKey = (FileTagGroupKey) otherKey;
1946  return getTagNamesString().equals(otherFileTagGroupKey.getTagNamesString());
1947  }
1948 
1949  @Override
1950  public int hashCode() {
1951  return Objects.hash(getTagNamesString());
1952  }
1953 
1957  List<String> getTagNames() {
1958  return Collections.unmodifiableList(tagNames);
1959  }
1960 
1964  String getTagNamesString() {
1965  return tagNamesString;
1966  }
1967  }
1968 
1972  static class NoGroupingAttribute extends AttributeType {
1973 
1974  @Override
1975  GroupKey getGroupKey(ResultFile file) {
1976  return new NoGroupingGroupKey();
1977  }
1978  }
1979 
1984  private static class NoGroupingGroupKey extends GroupKey {
1985 
1986  NoGroupingGroupKey() {
1987  // Nothing to save - all files will get the same GroupKey
1988  }
1989 
1990  @NbBundle.Messages({
1991  "FileSearch.NoGroupingGroupKey.allFiles=All Files"})
1992  @Override
1993  String getDisplayName() {
1994  return Bundle.FileSearch_NoGroupingGroupKey_allFiles();
1995  }
1996 
1997  @Override
1998  public int compareTo(GroupKey otherGroupKey) {
1999  // As long as the other key is the same type, they are equal
2000  if (otherGroupKey instanceof NoGroupingGroupKey) {
2001  return 0;
2002  } else {
2003  return compareClassNames(otherGroupKey);
2004  }
2005  }
2006 
2007  @Override
2008  public boolean equals(Object otherKey) {
2009  if (otherKey == this) {
2010  return true;
2011  }
2012  // As long as the other key is the same type, they are equal
2013  return otherKey instanceof NoGroupingGroupKey;
2014  }
2015 
2016  @Override
2017  public int hashCode() {
2018  return 0;
2019  }
2020  }
2021 
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());
2048 
2049  private final AttributeType attributeType;
2050  private final String displayName;
2051 
2052  GroupingAttributeType(AttributeType attributeType, String displayName) {
2053  this.attributeType = attributeType;
2054  this.displayName = displayName;
2055  }
2056 
2057  @Override
2058  public String toString() {
2059  return displayName;
2060  }
2061 
2062  AttributeType getAttributeType() {
2063  return attributeType;
2064  }
2065 
2071  static List<GroupingAttributeType> getOptionsForGrouping() {
2072  return Arrays.asList(FILE_SIZE, FREQUENCY, PARENT_PATH, OBJECT_DETECTED, HASH_LIST_NAME, INTERESTING_ITEM_SET);
2073  }
2074  }
2075 }
static File getVideoFileInTempDir(AbstractFile file)
void setParentPathAndID(Content parent, ResultFile file)

Copyright © 2012-2019 Basis Technology. Generated on: Tue Jan 7 2020
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.