diff --git a/Core/src/org/sleuthkit/autopsy/discovery/search/DomainSearchThumbnailLoader.java b/Core/src/org/sleuthkit/autopsy/discovery/search/DomainSearchThumbnailLoader.java index 4a17ad6434..888cc5379b 100755 --- a/Core/src/org/sleuthkit/autopsy/discovery/search/DomainSearchThumbnailLoader.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/search/DomainSearchThumbnailLoader.java @@ -22,6 +22,7 @@ import com.google.common.cache.CacheLoader; import java.awt.Image; import java.util.List; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import org.sleuthkit.autopsy.coreutils.ImageUtils; import org.sleuthkit.datamodel.BlackboardArtifact; @@ -38,17 +39,22 @@ import org.sleuthkit.datamodel.TskCoreException; import org.openide.util.ImageUtilities; /** - * Loads a thumbnail for the given request. Thumbnail candidates are JPEG files - * that have either TSK_WEB_DOWNLOAD or TSK_WEB_CACHE artifacts that match the - * domain name (see the DomainSearch getArtifacts() API). JPEG files are sorted + * Loads a thumbnail for the given request. Thumbnail candidates types are defined below. + * These candidates types must be the source of either TSK_WEB_DOWNLOAD or + * TSK_WEB_CACHE artifacts that match the + * domain name (see the DomainSearch getArtifacts() API). Candidate files are sorted * by most recent if sourced from TSK_WEB_DOWNLOADs and by size if sourced from * TSK_WEB_CACHE artifacts. The first suitable thumbnail is selected. */ public class DomainSearchThumbnailLoader extends CacheLoader { private static final String UNSUPPORTED_IMAGE = "org/sleuthkit/autopsy/images/image-extraction-not-supported.png"; - private static final String JPG_EXTENSION = "jpg"; - private static final String JPG_MIME_TYPE = "image/jpeg"; + private static final List SUPPORTED_EXTENSIONS = Arrays.asList("jpg", "svg", "png", "webp", "ico", "gif"); + private static final List SUPPORTED_MIMETYPES = Arrays.asList( + "image/gif", "image/jpeg", "image/png", "image/webp", + "image/svg+xml", "image/vnd.microsoft.icon", "image/x-icon"); + private static final String ICO_EXTENSION = "ico"; + private static final List ICO_MIMETYPES = Arrays.asList("image/vnd.microsoft.icon", "image/x-icon"); private final DomainSearchArtifactsCache artifactsCache; /** @@ -75,8 +81,21 @@ public class DomainSearchThumbnailLoader extends CacheLoader webDownloads = artifactsCache.get(webDownloadsRequest); - final List webDownloadPictures = getJpegsFromWebDownload(caseDb, webDownloads); - Collections.sort(webDownloadPictures, (file1, file2) -> Long.compare(file1.getCrtime(), file2.getCrtime())); + final List webDownloadPictures = getCandidatesFromWebDownloads(caseDb, webDownloads); + Collections.sort(webDownloadPictures, (file1, file2) -> { + // Push ICO to the back of the sorted collection, so that ICO + // is a last resort matching type. + if (isIco(file1) && isIco(file2)) { + return Long.compare(file1.getCrtime(), file2.getCrtime()); + } else if (isIco(file1)) { + return 1; + } else if (isIco(file2)) { + return -1; + } else { + return Long.compare(file1.getCrtime(), file2.getCrtime()); + } + }); + for (int i = webDownloadPictures.size() - 1; i >= 0; i--) { if(Thread.currentThread().isInterrupted()) { throw new InterruptedException(); @@ -92,8 +111,20 @@ public class DomainSearchThumbnailLoader extends CacheLoader webCacheArtifacts = artifactsCache.get(webCacheRequest); - final List webCachePictures = getJpegsFromWebCache(caseDb, webCacheArtifacts); - Collections.sort(webCachePictures, (file1, file2) -> Long.compare(file1.getSize(), file2.getSize())); + final List webCachePictures = getCandidatesFromWebCache(caseDb, webCacheArtifacts); + Collections.sort(webCachePictures, (file1, file2) -> { + // Push ICO to the back of the sorted collection, so that ICO + // is a last resort matching type. + if (isIco(file1) && isIco(file2)) { + return Long.compare(file1.getSize(), file2.getSize()); + } else if (isIco(file1)) { + return 1; + } else if (isIco(file2)) { + return -1; + } else { + return Long.compare(file1.getSize(), file2.getSize()); + } + }); for (int i = webCachePictures.size() - 1; i >= 0; i--) { if(Thread.currentThread().isInterrupted()) { throw new InterruptedException(); @@ -109,40 +140,45 @@ public class DomainSearchThumbnailLoader extends CacheLoader getJpegsFromWebDownload(SleuthkitCase caseDb, List artifacts) throws TskCoreException, InterruptedException { - final List jpegs = new ArrayList<>(); + private List getCandidatesFromWebDownloads(SleuthkitCase caseDb, List artifacts) throws TskCoreException, InterruptedException { + final List candidates = new ArrayList<>(); for (BlackboardArtifact artifact : artifacts) { if(Thread.currentThread().isInterrupted()) { throw new InterruptedException(); } final Content sourceContent = caseDb.getContentById(artifact.getObjectID()); - addIfJpeg(jpegs, sourceContent); + addIfSupported(candidates, sourceContent); } - return jpegs; + return candidates; + } + + private boolean isIco(AbstractFile file) { + return ICO_EXTENSION.equals(file.getNameExtension()) + || ICO_MIMETYPES.contains(file.getMIMEType()); } /** - * Finds all JPEG source files from TSK_WEB_CACHE instances. + * Finds all supported image files from TSK_WEB_CACHE instances. * * @param caseDb The case database being searched. - * @param artifacts The list of artifacts to get jpegs from. + * @param artifacts The list of artifacts to get images from. * - * @return The list of AbstractFiles representing jpegs which were + * @return The list of AbstractFiles representing supported images which were * associated with the artifacts. */ - private List getJpegsFromWebCache(SleuthkitCase caseDb, List artifacts) throws TskCoreException, InterruptedException { + private List getCandidatesFromWebCache(SleuthkitCase caseDb, List artifacts) throws TskCoreException, InterruptedException { final BlackboardAttribute.Type TSK_PATH_ID = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_PATH_ID); - final List jpegs = new ArrayList<>(); + final List candidates = new ArrayList<>(); for (BlackboardArtifact artifact : artifacts) { if(Thread.currentThread().isInterrupted()) { throw new InterruptedException(); @@ -150,24 +186,23 @@ public class DomainSearchThumbnailLoader extends CacheLoader files, Content sourceContent) { + private void addIfSupported(List files, Content sourceContent) { if ((sourceContent instanceof AbstractFile) && !(sourceContent instanceof DataSource)) { final AbstractFile file = (AbstractFile) sourceContent; - if (JPG_EXTENSION.equals(file.getNameExtension()) - || JPG_MIME_TYPE.equals(file.getMIMEType())) { + if (SUPPORTED_EXTENSIONS.contains(file.getNameExtension()) + || SUPPORTED_MIMETYPES.contains(file.getMIMEType())) { files.add(file); } }