From b5a07ed05dc29ec1eb4835c10e29dbd80304a22c Mon Sep 17 00:00:00 2001 From: Jeff Wallace Date: Fri, 20 Dec 2013 12:55:10 -0500 Subject: [PATCH] Add tagged images table to html report. --- .../autopsy/report/ReportGenerator.java | 49 +++++++++- .../sleuthkit/autopsy/report/ReportHTML.java | 95 ++++++++++++++++++- 2 files changed, 139 insertions(+), 5 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportGenerator.java b/Core/src/org/sleuthkit/autopsy/report/ReportGenerator.java index 3644148ad0..cef9ed6781 100644 --- a/Core/src/org/sleuthkit/autopsy/report/ReportGenerator.java +++ b/Core/src/org/sleuthkit/autopsy/report/ReportGenerator.java @@ -345,6 +345,8 @@ public class ReportGenerator { private List artifactTypes = new ArrayList<>(); private HashSet tagNamesFilter = new HashSet<>(); + private List images = new ArrayList<>(); + TableReportsWorker(Map artifactTypeSelections, Map tagNameSelections) { // Get the report modules selected by the user. for (Entry entry : tableProgress.entrySet()) { @@ -384,6 +386,7 @@ public class ReportGenerator { makeBlackboardArtifactTables(); makeContentTagsTables(); makeBlackboardArtifactTagsTables(); + makeThumbnailTable(); for (TableReportModule module : tableModules) { tableProgress.get(module).complete(); @@ -520,7 +523,8 @@ public class ReportGenerator { // Give the modules the rows for the content tags. for (ContentTag tag : tags) { - if (passesTagNamesFilter(tag.getName().getDisplayName())) { + if (passesTagNamesFilter(tag.getName().getDisplayName())) { + checkIfTagHasImage(tag); ArrayList rowData = new ArrayList<>(Arrays.asList(tag.getContent().getName(), tag.getName().getDisplayName(), tag.getComment())); for (TableReportModule module : tableModules) { // @@@ This casting is a tricky little workaround to allow the HTML report module to slip in a content hyperlink. @@ -580,9 +584,9 @@ public class ReportGenerator { // Give the modules the rows for the content tags. for (BlackboardArtifactTag tag : tags) { - if (passesTagNamesFilter(tag.getName().getDisplayName())) { + if (passesTagNamesFilter(tag.getName().getDisplayName())) { + checkIfTagHasImage(tag); List row; - File thumbFile; for (TableReportModule module : tableModules) { // We have specific behavior if the module is a ReportHTML. // We add a thumbnail if the artifact is associated with an @@ -618,6 +622,45 @@ public class ReportGenerator { } } } + + private void makeThumbnailTable() { + for (TableReportModule module : tableModules) { + if (module instanceof ReportHTML) { + ReportHTML htmlModule = (ReportHTML) module; + htmlModule.startDataType("Tagged Images", "Tagged Results and Contents that contain images."); + List emptyHeaders = new ArrayList<>(); + for (int i = 0; i < ReportHTML.THUMBNAIL_COLUMNS; i++) { + emptyHeaders.add(""); + } + htmlModule.startTable(emptyHeaders); + htmlModule.addThumbnailRows(images); + htmlModule.endTable(); + htmlModule.endDataType(); + } + } + } + + private void checkIfTagHasImage(BlackboardArtifactTag artifactTag) { + AbstractFile file; + try { + file = Case.getCurrentCase().getSleuthkitCase().getAbstractFileById(artifactTag.getArtifact().getObjectID()); + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Error while getting content from a blackboard artifact to report on.", ex); + return; + } + + // Only include content for images + if (ImageUtils.thumbnailSupported(file)) { + images.add(file); + } + } + + private void checkIfTagHasImage(ContentTag contentTag) { + Content c = contentTag.getContent(); + if (ImageUtils.thumbnailSupported(c)) { + images.add(c); + } + } } /// @@@ Should move the methods specific to TableReportsWorker into that scope. diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java b/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java index f678729622..f93812b7fa 100644 --- a/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java +++ b/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java @@ -34,6 +34,7 @@ import java.io.UnsupportedEncodingException; import java.io.Writer; import java.text.DateFormat; import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Map; @@ -53,6 +54,7 @@ import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.BlackboardArtifactTag; +import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.ContentTag; import org.sleuthkit.datamodel.TskData.TSK_DB_FILES_TYPE_ENUM; @@ -62,6 +64,7 @@ public class ReportHTML implements TableReportModule { private static ReportHTML instance; private Case currentCase; private SleuthkitCase skCase; + static Integer THUMBNAIL_COLUMNS = 5; private Map dataTypes; private String path; @@ -634,7 +637,95 @@ public class ReportHTML implements TableReportModule { logger.log(Level.SEVERE, "Output writer is null. Page was not initialized before writing.", ex); } } + + public void addThumbnailRows(List images) { + List currentRow = new ArrayList<>(); + int totalCount = 0; + for (Content content : images) { + if (currentRow.size() == THUMBNAIL_COLUMNS) { + addRow(currentRow); + currentRow.clear(); + } + + if (failsContentCheck(content)) { + continue; + } + + AbstractFile file = (AbstractFile) content; + + String thumbnailPath = saveContent(file); + StringBuilder linkToThumbnail = new StringBuilder(); + linkToThumbnail.append(""); + linkToThumbnail.append(""); + linkToThumbnail.append(""); + currentRow.add(linkToThumbnail.toString()); + + totalCount++; + } + if (currentRow.isEmpty() == false) { + int extraCells = THUMBNAIL_COLUMNS - currentRow.size(); + for (int i = 0; i < extraCells; i++) { + // Finish out the row. + currentRow.add(""); + } + addRow(currentRow); + } + + // manually set rowCount to be the total number of images. + rowCount = totalCount; + } + + private boolean failsContentCheck(Content c) { + if (c instanceof AbstractFile == false) { + return true; + } + AbstractFile file = (AbstractFile) c; + if (file.isDir() || + file.getType() == TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS || + file.getType() == TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS) { + return true; + } + return false; + } + + public String saveContent(AbstractFile file) { + // Make a folder for the local file with the same tagName as the tag. + StringBuilder localFilePath = new StringBuilder(); + localFilePath.append(path); + localFilePath.append("tagged_images"); + File localFileFolder = new File(localFilePath.toString()); + if (!localFileFolder.exists()) { + localFileFolder.mkdirs(); + } + + // Construct a file tagName for the local file that incorporates the file id to ensure uniqueness. + String fileName = file.getName(); + String objectIdSuffix = "_" + file.getId(); + int lastDotIndex = fileName.lastIndexOf("."); + if (lastDotIndex != -1 && lastDotIndex != 0) { + // The file tagName has a conventional extension. Insert the object id before the '.' of the extension. + fileName = fileName.substring(0, lastDotIndex) + objectIdSuffix + fileName.substring(lastDotIndex, fileName.length()); + } + else { + // The file has no extension or the only '.' in the file is an initial '.', as in a hidden file. + // Add the object id to the end of the file tagName. + fileName += objectIdSuffix; + } + localFilePath.append(File.separator); + localFilePath.append(fileName); + + // If the local file doesn't already exist, create it now. + // The existence check is necessary because it is possible to apply multiple tags with the same tagName to a file. + File localFile = new File(localFilePath.toString()); + if (!localFile.exists()) { + ExtractFscContentVisitor.extract(file, localFile, null, null); + } + return localFilePath.toString(); + } + /** * Return a String date for the long date given. * @param date date as a long @@ -949,12 +1040,12 @@ public class ReportHTML implements TableReportModule { } private String prepareThumbnail(AbstractFile file) { - File thumbFile = ImageUtils.getIconFile(file, ImageUtils.ICON_SIZE_SMALL); + File thumbFile = ImageUtils.getIconFile(file, ImageUtils.ICON_SIZE_MEDIUM); try { File to = new File(thumbsPath); FileUtil.copyFile(FileUtil.toFileObject(thumbFile), FileUtil.toFileObject(to), thumbFile.getName(), ""); } catch (IOException ex) { - logger.log(Level.SEVERE, "Failed to write thumb file to report directory."); + logger.log(Level.SEVERE, "Failed to write thumb file to report directory.", ex); } return THUMBS_REL_PATH + thumbFile.getName();