Merge pull request #398 from jawallace/thumbnail_reporting

Thumbnail reporting
This commit is contained in:
Richard Cordovano 2013-12-19 09:01:18 -08:00
commit cc1a7f7f47
7 changed files with 314 additions and 237 deletions

View File

@ -41,6 +41,7 @@ import org.openide.nodes.NodeListener;
import org.openide.nodes.NodeMemberEvent;
import org.openide.nodes.NodeReorderEvent;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer;
import org.sleuthkit.autopsy.coreutils.ImageUtils;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.TskCoreException;
@ -63,7 +64,7 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer {
private int curPage;
private int totalPages;
private int curPageImages;
private int iconSize = ThumbnailViewNode.ICON_SIZE_MEDIUM;
private int iconSize = ImageUtils.ICON_SIZE_MEDIUM;
private final PageUpdater pageUpdater = new PageUpdater();
/**
@ -240,13 +241,13 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer {
private void thumbnailSizeComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_thumbnailSizeComboBoxActionPerformed
iconSize = ThumbnailViewNode.ICON_SIZE_MEDIUM; //default size
iconSize = ImageUtils.ICON_SIZE_MEDIUM; //default size
switch(thumbnailSizeComboBox.getSelectedIndex()) {
case 0:
iconSize = ThumbnailViewNode.ICON_SIZE_SMALL;
iconSize = ImageUtils.ICON_SIZE_SMALL;
break;
case 2:
iconSize = ThumbnailViewNode.ICON_SIZE_LARGE;
iconSize = ImageUtils.ICON_SIZE_LARGE;
break;
}

View File

@ -21,21 +21,13 @@ package org.sleuthkit.autopsy.corecomponents;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import javax.imageio.ImageIO;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.contentviewers.Utilities;
import org.sleuthkit.autopsy.coreutils.ImageUtils;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.ContentVisitor;
import org.sleuthkit.datamodel.DerivedFile;
import org.sleuthkit.datamodel.File;
import org.sleuthkit.datamodel.LocalFile;
import org.sleuthkit.datamodel.LayoutFile;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Complementary class to ThumbnailViewNode. Children node factory. Wraps around
@ -49,13 +41,12 @@ import org.sleuthkit.datamodel.TskCoreException;
*/
class ThumbnailViewChildren extends Children.Keys<Integer> {
private static final IsSupportedContentVisitor isSupportedVisitor = new IsSupportedContentVisitor();
static final int IMAGES_PER_PAGE = 200;
private Node parent;
private final HashMap<Integer, List<Node>> pages = new HashMap<Integer, List<Node>>();
private int totalImages = 0;
private int totalPages = 0;
private int iconSize = ThumbnailViewNode.ICON_SIZE_MEDIUM;
private int iconSize = ImageUtils.ICON_SIZE_MEDIUM;
private static final Logger logger = Logger.getLogger(ThumbnailViewChildren.class.getName());
/**
@ -152,7 +143,7 @@ class ThumbnailViewChildren extends Children.Keys<Integer> {
if (node != null) {
Content content = node.getLookup().lookup(Content.class);
if (content != null) {
return content.accept(isSupportedVisitor);
return ImageUtils.thumbnailSupported(content);
}
}
return false;
@ -162,59 +153,6 @@ class ThumbnailViewChildren extends Children.Keys<Integer> {
this.iconSize = iconSize;
}
private static class IsSupportedContentVisitor extends ContentVisitor.Default<Boolean> {
private final List<String> SUPP_EXTENSIONS;
IsSupportedContentVisitor() {
String[] supportedImagesSuffixes = ImageIO.getReaderFileSuffixes();
SUPP_EXTENSIONS = new ArrayList<String>(supportedImagesSuffixes.length);
for (int i = 0; i < supportedImagesSuffixes.length; ++i) {
String suffix = supportedImagesSuffixes[i];
SUPP_EXTENSIONS.add("." + suffix);
}
}
@Override
public Boolean visit(DerivedFile f) {
return isSupported(f);
}
@Override
public Boolean visit(LocalFile f) {
return isSupported(f);
}
public Boolean visit(LayoutFile f) {
return isSupported(f);
}
@Override
public Boolean visit(File f) {
return isSupported(f);
}
public Boolean isSupported(AbstractFile f) {
final String fName = f.getName();
final int dotIdx = fName.lastIndexOf('.');
if (dotIdx == -1) {
return Utilities.isJpegFileHeader(f);
}
final String ext = fName.substring(dotIdx).toLowerCase();
// Note: thumbnail generator only supports JPG, GIF, and PNG for now
return (f.getSize() > 0
&& SUPP_EXTENSIONS.contains(ext));
}
@Override
protected Boolean defaultVisit(Content cntnt) {
return false;
}
}
/**
* Node representing page node, a parent of image nodes, with a name showing
* children range

View File

@ -18,30 +18,12 @@
*/
package org.sleuthkit.autopsy.corecomponents;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.SoftReference;
import java.util.logging.Level;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import org.openide.nodes.Children;
import org.openide.nodes.FilterNode;
import org.openide.nodes.Node;
import org.openide.util.Exceptions;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.corelibs.ScalrWrapper;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ImageUtils;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.ReadContentInputStream;
import org.sleuthkit.datamodel.TskException;
/**
* Node that wraps around original node and adds the bitmap icon representing
@ -50,12 +32,7 @@ import org.sleuthkit.datamodel.TskException;
class ThumbnailViewNode extends FilterNode {
private SoftReference<Image> iconCache = null;
private static final Image defaultIcon = new ImageIcon("/org/sleuthkit/autopsy/images/file-icon.png").getImage();
private static final Logger logger = Logger.getLogger(ThumbnailViewNode.class.getName());
static final int ICON_SIZE_SMALL = 50;
static final int ICON_SIZE_MEDIUM = 100;
static final int ICON_SIZE_LARGE = 200;
private int iconSize = ICON_SIZE_MEDIUM;
private int iconSize = ImageUtils.ICON_SIZE_MEDIUM;
//private final BufferedImage defaultIconBI;
/**
@ -87,91 +64,17 @@ class ThumbnailViewNode extends FilterNode {
Content content = this.getLookup().lookup(Content.class);
if (content != null) {
// If a thumbnail file is already saved locally
if (getFile(content.getId()).exists()) {
try {
BufferedImage bicon = ImageIO.read(getFile(content.getId()));
if (bicon == null) {
icon = ThumbnailViewNode.defaultIcon;
} else if (bicon.getWidth() != iconSize) {
icon = generateAndSaveIcon(content);
icon = ImageUtils.getIcon(content, iconSize);
} else {
icon = bicon;
}
} catch (IOException ex) {
icon = ThumbnailViewNode.defaultIcon;
}
} else { // Make a new icon
icon = generateAndSaveIcon(content);
}
} else {
icon = ThumbnailViewNode.defaultIcon;
icon = ImageUtils.getDefaultIcon();
}
iconCache = new SoftReference<Image>(icon);
iconCache = new SoftReference<>(icon);
}
return icon;
}
private Image generateAndSaveIcon(Content content) {
Image icon = null;
try {
icon = generateIcon(content);
if (icon == null) {
icon = ThumbnailViewNode.defaultIcon;
} else {
File f = getFile(content.getId());
if (f.exists()) {
f.delete();
}
ImageIO.write((BufferedImage) icon, "jpg", getFile(content.getId()));
}
} catch (IOException ex) {
logger.log(Level.WARNING, "Could not write cache thumbnail: " + content, ex);
}
return icon;
}
/*
* Generate a scaled image
*/
private BufferedImage generateIcon(Content content) {
InputStream inputStream = null;
try {
inputStream = new ReadContentInputStream(content);
BufferedImage bi = ImageIO.read(inputStream);
if (bi == null) {
logger.log(Level.WARNING, "No image reader for file: " + content.getName());
return null;
}
BufferedImage biScaled = ScalrWrapper.resizeFast(bi, iconSize);
return biScaled;
}catch (OutOfMemoryError e) {
logger.log(Level.WARNING, "Could not scale image (too large): " + content.getName(), e);
return null;
}
catch (Exception e) {
logger.log(Level.WARNING, "Could not scale image: " + content.getName(), e);
return null;
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException ex) {
logger.log(Level.WARNING, "Could not close input stream after resizing thumbnail: " + content.getName(), ex);
}
}
}
}
private static File getFile(long id) {
return new File(Case.getCurrentCase().getCacheDirectory() + File.separator + id + ".jpg");
}
public void setIconSize(int iconSize) {
this.iconSize = iconSize;
iconCache = null;

View File

@ -0,0 +1,196 @@
/*
*
* Autopsy Forensic Browser
*
* Copyright 2012 Basis Technology Corp.
*
* Copyright 2012 42six Solutions.
* Contact: aebadirad <at> 42six <dot> com
* Project Contact/Architect: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.coreutils;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.contentviewers.Utilities;
import org.sleuthkit.autopsy.corelibs.ScalrWrapper;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.ReadContentInputStream;
/**
* Utilities for creating and manipulating thumbnail and icon images.
* @author jwallace
*/
public class ImageUtils {
public static final int ICON_SIZE_SMALL = 50;
public static final int ICON_SIZE_MEDIUM = 100;
public static final int ICON_SIZE_LARGE = 200;
private static final Logger logger = Logger.getLogger(ImageUtils.class.getName());
private static final Image DEFAULT_ICON = new ImageIcon("/org/sleuthkit/autopsy/images/file-icon.png").getImage();
private static final List<String> SUPP_EXTENSIONS = Arrays.asList(ImageIO.getReaderFileSuffixes());
/**
* Get the default Icon, which is the icon for a file.
* @return
*/
public static Image getDefaultIcon() {
return DEFAULT_ICON;
}
/**
* Can a thumbnail be generated for the content?
*
* @param content
* @return
*/
public static boolean thumbnailSupported(Content content) {
if (content instanceof AbstractFile == false) {
return false;
}
AbstractFile f = (AbstractFile) content;
final String fName = f.getName();
final int dotIdx = fName.lastIndexOf('.');
if (dotIdx == -1 || dotIdx == (fName.length() - 1)) {
return Utilities.isJpegFileHeader(f);
}
final String ext = fName.substring(dotIdx + 1).toLowerCase();
// Note: thumbnail generator only supports JPG, GIF, and PNG for now
return (f.getSize() > 0
&& SUPP_EXTENSIONS.contains(ext));
}
/**
* Get an icon of a specified size.
*
* @param content
* @param iconSize
* @return
*/
public static Image getIcon(Content content, int iconSize) {
Image icon;
// If a thumbnail file is already saved locally
File file = getFile(content.getId());
if (file.exists()) {
try {
BufferedImage bicon = ImageIO.read(file);
if (bicon == null) {
icon = DEFAULT_ICON;
} else if (bicon.getWidth() != iconSize) {
icon = generateAndSaveIcon(content, iconSize);
} else {
icon = bicon;
}
} catch (IOException ex) {
logger.log(Level.WARNING, "Error while reading image.", ex);
icon = DEFAULT_ICON;
}
} else { // Make a new icon
icon = generateAndSaveIcon(content, iconSize);
}
return icon;
}
/**
* Get the cached file of the icon. Generates the icon and its file if it
* doesn't already exist, so this method guarantees to return a file that
* exists.
* @param content
* @param iconSize
* @return
*/
public static File getIconFile(Content content, int iconSize) {
getIcon(content, iconSize);
return getFile(content.getId());
}
/**
* Get the cached file of the content object with the given id.
*
* The returned file may not exist.
*
* @param id
* @return
*/
public static File getFile(long id) {
return new File(Case.getCurrentCase().getCacheDirectory() + File.separator + id + ".jpg");
}
private static Image generateAndSaveIcon(Content content, int iconSize) {
Image icon = null;
try {
icon = generateIcon(content, iconSize);
if (icon == null) {
return DEFAULT_ICON;
} else {
File f = getFile(content.getId());
if (f.exists()) {
f.delete();
}
ImageIO.write((BufferedImage) icon, "jpg", getFile(content.getId()));
}
} catch (IOException ex) {
logger.log(Level.WARNING, "Could not write cache thumbnail: " + content, ex);
}
return icon;
}
/*
* Generate a scaled image
*/
private static BufferedImage generateIcon(Content content, int iconSize) {
InputStream inputStream = null;
try {
inputStream = new ReadContentInputStream(content);
BufferedImage bi = ImageIO.read(inputStream);
if (bi == null) {
logger.log(Level.WARNING, "No image reader for file: " + content.getName());
return null;
}
BufferedImage biScaled = ScalrWrapper.resizeFast(bi, iconSize);
return biScaled;
} catch (OutOfMemoryError e) {
logger.log(Level.WARNING, "Could not scale image (too large): " + content.getName(), e);
return null;
} catch (Exception e) {
logger.log(Level.WARNING, "Could not scale image: " + content.getName(), e);
return null;
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException ex) {
logger.log(Level.WARNING, "Could not close input stream after resizing thumbnail: " + content.getName(), ex);
}
}
}
}
}

View File

@ -48,6 +48,7 @@ import javax.swing.SwingWorker;
import org.openide.filesystems.FileUtil;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.EscapeUtil;
import org.sleuthkit.autopsy.coreutils.ImageUtils;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.report.ReportProgressPanel.ReportStatus;
@ -57,9 +58,11 @@ import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
import org.sleuthkit.datamodel.BlackboardArtifactTag;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.ContentTag;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskException;
/**
* Instances of this class use GeneralReportModules, TableReportModules and
@ -566,14 +569,30 @@ public class ReportGenerator {
comment.append(makeCommaSeparatedList(tagNamesFilter));
}
module.startDataType(ARTIFACT_TYPE.TSK_TAG_ARTIFACT.getDisplayName(), comment.toString());
module.startTable(new ArrayList<>(Arrays.asList("Result Type", "Tag", "Comment", "Source File")));
String[] tableHeaders;
if (module instanceof ReportHTML) {
tableHeaders = new String[] {"Result Type", "Tag", "Comment", "Source File", "Thumbnail"};
} else {
tableHeaders = new String[] {"Result Type", "Tag", "Comment", "Source File"};
}
module.startTable(new ArrayList<>(Arrays.asList(tableHeaders)));
}
// Give the modules the rows for the content tags.
for (BlackboardArtifactTag tag : tags) {
if (passesTagNamesFilter(tag.getName().getDisplayName())) {
List<String> row;
File thumbFile;
for (TableReportModule module : tableModules) {
module.addRow(new ArrayList<>(Arrays.asList(tag.getArtifact().getArtifactTypeName(), tag.getName().getDisplayName(), tag.getComment(), tag.getContent().getName())));
// We have specific behavior if the module is a ReportHTML.
// We add a thumbnail if the artifact is associated with an
// image file.
row = new ArrayList<>(Arrays.asList(tag.getArtifact().getArtifactTypeName(), tag.getName().getDisplayName(), tag.getComment(), tag.getContent().getName()));
if (module instanceof ReportHTML) {
((ReportHTML) module).addRowWithTaggedContentHyperlink(row, tag);
} else {
module.addRow(row);
}
}
}
}
@ -1334,21 +1353,6 @@ public class ReportGenerator {
private Map<Integer,String> getMappedAttributes() {
return ReportGenerator.this.getMappedAttributes(attributes);
}
/**
* Get a BlackboardArtifact.
*
* @param long artifactId An artifact id
* @return The BlackboardArtifact associated with the artifact id
*/
private BlackboardArtifact getArtifactByID(long artifactId) {
try {
return skCase.getBlackboardArtifact(artifactId);
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Failed to get blackboard artifact by ID.", ex);
}
return null;
}
}
}

View File

@ -39,9 +39,10 @@ import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.logging.Level;
import org.openide.filesystems.FileUtil;
import org.openide.util.Exceptions;
import org.sleuthkit.autopsy.casemodule.Case;
import org.openide.filesystems.FileUtil;
import org.sleuthkit.autopsy.coreutils.ImageUtils;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.ContentUtils.ExtractFscContentVisitor;
import org.sleuthkit.autopsy.ingest.IngestManager;
@ -51,17 +52,20 @@ import org.sleuthkit.datamodel.SleuthkitCase;
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.ContentTag;
import org.sleuthkit.datamodel.TskData.TSK_DB_FILES_TYPE_ENUM;
public class ReportHTML implements TableReportModule {
private static final Logger logger = Logger.getLogger(ReportHTML.class.getName());
private static final String THUMBS_REL_PATH = "thumbs" + File.separator;
private static ReportHTML instance;
private Case currentCase;
private SleuthkitCase skCase;
private Map<String, Integer> dataTypes;
private String path;
private String thumbsPath;
private String currentDataType; // name of current data type
private Integer rowCount; // number of rows (aka artifacts or tags) for the current data type
private Writer out;
@ -90,6 +94,7 @@ public class ReportHTML implements TableReportModule {
dataTypes = new TreeMap<>();
path = "";
thumbsPath = "";
currentDataType = "";
rowCount = 0;
@ -266,8 +271,10 @@ public class ReportHTML implements TableReportModule {
refresh();
// Setup the path for the HTML report
this.path = path + "HTML Report" + File.separator;
this.thumbsPath = this.path + "thumbs" + File.separator;
try {
FileUtil.createFolder(new File(this.path));
FileUtil.createFolder(new File(this.thumbsPath));
} catch (IOException ex) {
logger.log(Level.SEVERE, "Unable to make HTML report folder.");
}
@ -499,21 +506,67 @@ public class ReportHTML implements TableReportModule {
/**
* Saves a local copy of a tagged file and adds a row with a hyper link to
* the file.
* the file. The hyper link will be a thumbnail if the Content associated
* with the given ContentTag is an image.
*
* @param row Values for each data cell in the row.
* @param contentTag A content tag to use to make the hyper link.
*/
public void addRowWithTaggedContentHyperlink(List<String> row, ContentTag contentTag) {
// Only handling AbstractFiles at present.
String tagName = contentTag.getName().getDisplayName();
AbstractFile file;
if (contentTag.getContent() instanceof AbstractFile) {
file = (AbstractFile)contentTag.getContent();
StringBuilder linkContent = new StringBuilder();
if (ImageUtils.thumbnailSupported(file)) {
linkContent.append("<img src=\"").append(prepareThumbnail(file)).append("\" />");
} else {
linkContent.append("View File");
}
else {
addRowWithTaggedContentHyperlink(row, file, tagName, linkContent.toString());
}
}
/**
* Saves a local copy of a tagged file and adds a row with a hyper link to
* the file. The hyper link will be a thumbnail if the Content associated
* with the given BlackboardArtifactTag is an image.
*
* @param row Values for each data cell in the row.
* @param artifactTag An artifact tag to use to make the hyper link.
*/
public void addRowWithTaggedContentHyperlink(List<String> row, BlackboardArtifactTag artifactTag) {
String tagName = artifactTag.getName().getDisplayName();
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)) {
StringBuilder linkContent = new StringBuilder();
linkContent.append("<img src=\"").append(prepareThumbnail(file)).append("\" />");
addRowWithTaggedContentHyperlink(row, file, tagName, linkContent.toString());
} else {
row.add("");
this.addRow(row);
}
}
/**
* Saves a local copy of a tagged file and adds a row with a hyper link to
* the file. The content of the hyperlink is provided in linkHTMLContent.
*
* @param row Values for each data cell in the row
* @param file The file to link to in the report.
* @param tagName the name of the tag that the content was flagged by
* @param linkHTMLContent the html that will be the body of the link
*/
private void addRowWithTaggedContentHyperlink(List<String> row, AbstractFile file, String tagName, String linkHTMLContent) {
// Don't make a local copy of the file if it is a directory or unallocated space.
if (file.isDir() ||
file.getType() == TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS ||
@ -522,33 +575,33 @@ public class ReportHTML implements TableReportModule {
return;
}
// Make a folder for the local file with the same name as the tag.
// Make a folder for the local file with the same tagName as the tag.
StringBuilder localFilePath = new StringBuilder();
localFilePath.append(path);
localFilePath.append(contentTag.getName().getDisplayName());
localFilePath.append(tagName);
File localFileFolder = new File(localFilePath.toString());
if (!localFileFolder.exists()) {
localFileFolder.mkdirs();
}
// Construct a file name for the local file that incorporates the file id to ensure uniqueness.
// 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 name has a conventional extension. Insert the object id before the '.' of the extension.
// 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 name.
// 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 name to a file.
// 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);
@ -558,7 +611,9 @@ public class ReportHTML implements TableReportModule {
StringBuilder localFileLink = new StringBuilder();
localFileLink.append("<a href=\"file:///");
localFileLink.append(localFilePath.toString());
localFileLink.append("\">View File</a>");
localFileLink.append("\">");
localFileLink.append(linkHTMLContent);
localFileLink.append("</a>");
row.add(localFileLink.toString());
StringBuilder builder = new StringBuilder();
@ -892,4 +947,17 @@ public class ReportHTML implements TableReportModule {
}
}
}
private String prepareThumbnail(AbstractFile file) {
File thumbFile = ImageUtils.getIconFile(file, ImageUtils.ICON_SIZE_SMALL);
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.");
}
return THUMBS_REL_PATH + thumbFile.getName();
}
}

View File

@ -33,6 +33,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.logging.Level;
import org.sleuthkit.autopsy.contentviewers.Utilities;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.Version;
import org.sleuthkit.autopsy.ingest.PipelineContext;
@ -197,41 +198,7 @@ public final class ExifParserFileIngestModule extends IngestModuleAbstractFile {
* @return true if to be processed
*/
private boolean parsableFormat(AbstractFile f) {
return isJpegFileHeader(f);
}
/**
* Check if is jpeg file based on header
*
* @param file
*
* @return true if jpeg file, false otherwise
*/
public static boolean isJpegFileHeader(AbstractFile file) {
if (file.getSize() < 100) {
return false;
}
byte[] fileHeaderBuffer = new byte[2];
int bytesRead;
try {
bytesRead = file.read(fileHeaderBuffer, 0, 2);
} catch (TskCoreException ex) {
//ignore if can't read the first few bytes, not a JPEG
return false;
}
if (bytesRead != 2) {
return false;
}
/*
* Check for the JPEG header. Since Java bytes are signed, we cast them
* to an int first.
*/
if (((int) (fileHeaderBuffer[0] & 0xff) == 0xff) && ((int) (fileHeaderBuffer[1] & 0xff) == 0xd8)) {
return true;
}
return false;
return Utilities.isJpegFileHeader(f);
}
@Override