19 package org.sleuthkit.autopsy.modules.pictureanalyzer.impls;
21 import com.drew.imaging.ImageMetadataReader;
22 import com.drew.imaging.ImageProcessingException;
23 import com.drew.lang.GeoLocation;
24 import com.drew.lang.Rational;
25 import com.drew.metadata.Metadata;
26 import com.drew.metadata.exif.ExifIFD0Directory;
27 import com.drew.metadata.exif.ExifSubIFDDirectory;
28 import com.drew.metadata.exif.GpsDirectory;
29 import java.io.BufferedInputStream;
30 import java.io.IOException;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.Collection;
34 import java.util.Date;
36 import java.util.HashSet;
37 import java.util.List;
38 import java.util.TimeZone;
39 import java.util.logging.Level;
40 import org.apache.commons.lang3.StringUtils;
41 import org.openide.util.NbBundle;
42 import org.openide.util.lookup.ServiceProvider;
51 import static org.
sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF;
53 import static org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
71 private static final BlackboardArtifact.Type EXIF_METADATA =
new BlackboardArtifact.Type(TSK_METADATA_EXIF);
75 "ExifProcessor.userContent.description=EXIF metadata data exists for this file."
80 try (BufferedInputStream bin =
new BufferedInputStream(
new ReadContentInputStream(file));) {
82 final Collection<BlackboardAttribute> attributes =
new ArrayList<>();
83 final Metadata metadata = ImageMetadataReader.readMetadata(bin);
86 final ExifSubIFDDirectory exifDir = metadata.getFirstDirectoryOfType(ExifSubIFDDirectory.class);
87 if (exifDir != null) {
90 TimeZone timeZone = null;
92 Content dataSource = file.getDataSource();
93 if ((dataSource != null) && (dataSource instanceof Image)) {
94 Image image = (Image) dataSource;
95 timeZone = TimeZone.getTimeZone(image.getTimeZone());
97 }
catch (TskCoreException ex) {
98 logger.log(Level.INFO,
"Error getting time zones", ex);
101 final Date date = exifDir.getDate(ExifSubIFDDirectory.TAG_DATETIME_ORIGINAL, timeZone);
103 attributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED, MODULE_NAME, date.getTime() / 1000));
112 final GpsDirectory gpsDir = metadata.getFirstDirectoryOfType(GpsDirectory.class);
113 if (gpsDir != null) {
114 final GeoLocation loc = gpsDir.getGeoLocation();
116 attributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_GEO_LATITUDE, MODULE_NAME, loc.getLatitude()));
117 attributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE, MODULE_NAME, loc.getLongitude()));
120 final Rational altitude = gpsDir.getRational(GpsDirectory.TAG_ALTITUDE);
121 if (altitude != null) {
122 attributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE, MODULE_NAME, altitude.doubleValue()));
131 final ExifIFD0Directory devDir = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);
132 if (devDir != null) {
133 final String model = devDir.getString(ExifIFD0Directory.TAG_MODEL);
134 if (StringUtils.isNotBlank(model)) {
135 attributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DEVICE_MODEL, MODULE_NAME, model));
138 final String make = devDir.getString(ExifIFD0Directory.TAG_MAKE);
139 if (StringUtils.isNotBlank(make)) {
140 attributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DEVICE_MAKE, MODULE_NAME, make));
150 if (!attributes.isEmpty() && !blackboard.artifactExists(file, TSK_METADATA_EXIF, attributes)) {
151 List<BlackboardArtifact> artifacts =
new ArrayList<>();
152 final BlackboardArtifact exifArtifact = (file.newAnalysisResult(
153 BlackboardArtifact.Type.TSK_METADATA_EXIF,
156 attributes)).getAnalysisResult();
157 artifacts.add(exifArtifact);
159 final BlackboardArtifact userSuspectedArtifact = file.newAnalysisResult(
160 BlackboardArtifact.Type.TSK_USER_CONTENT_SUSPECTED,
163 Arrays.asList(
new BlackboardAttribute(
164 BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT,
166 Bundle.ExifProcessor_userContent_description())))
167 .getAnalysisResult();
168 artifacts.add(userSuspectedArtifact);
171 blackboard.postArtifacts(artifacts, MODULE_NAME);
172 }
catch (Blackboard.BlackboardException ex) {
173 logger.log(Level.SEVERE, String.format(
"Error posting TSK_METADATA_EXIF and TSK_USER_CONTENT_SUSPECTED artifacts for %s (object ID = %d)", file.getName(), file.getId()), ex);
176 }
catch (TskCoreException ex) {
177 logger.log(Level.SEVERE, String.format(
"Error creating TSK_METADATA_EXIF and TSK_USER_CONTENT_SUSPECTED artifacts for %s (object ID = %d)", file.getName(), file.getId()), ex);
178 }
catch (IOException | ImageProcessingException ex) {
179 logger.log(Level.WARNING, String.format(
"Error parsing %s (object ID = %d), presumed corrupt", file.getName(), file.getId()), ex);
181 logger.log(Level.SEVERE, String.format(
"Error processing %s (object ID = %d)", file.getName(), file.getId()), ex);
187 return new HashSet<String>() {
void process(IngestJobContext context, AbstractFile file)
static String getModuleName()
SleuthkitCase getSleuthkitCase()
boolean fileIngestIsCancelled()
synchronized static Logger getLogger(String name)
static Case getCurrentCaseThrows()
Set< String > mimeTypes()