19 package org.sleuthkit.autopsy.modules.exif;
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.io.InputStream;
32 import java.util.ArrayList;
33 import java.util.Collection;
34 import java.util.Date;
35 import java.util.HashSet;
36 import java.util.List;
37 import java.util.TimeZone;
38 import java.util.concurrent.atomic.AtomicInteger;
39 import java.util.logging.Level;
40 import org.openide.util.NbBundle;
41 import org.openide.util.NbBundle.Messages;
69 "CannotRunFileTypeDetection=Cannot run file type detection."
75 private final AtomicInteger filesProcessed =
new AtomicInteger(0);
76 private volatile boolean filesToFire =
false;
77 private final List<BlackboardArtifact> listOfFacesDetectedArtifacts =
new ArrayList<>();
81 private final HashSet<String> supportedMimeTypes =
new HashSet<>();
82 private TimeZone timeZone = null;
86 supportedMimeTypes.add(
"audio/x-wav");
87 supportedMimeTypes.add(
"image/jpeg");
88 supportedMimeTypes.add(
"image/tiff");
93 jobId = context.getJobId();
112 if (content.
isFile() ==
false) {
122 final int filesProcessedValue = filesProcessed.incrementAndGet();
123 if ((filesProcessedValue % 1000 == 0)) {
131 if (!parsableFormat(content)) {
135 return processFile(content);
138 @Messages({
"ExifParserFileIngestModule.indexError.message=Failed to index EXIF Metadata artifact for keyword search."})
139 ProcessResult processFile(AbstractFile f) {
140 InputStream in = null;
141 BufferedInputStream bin = null;
144 in =
new ReadContentInputStream(f);
145 bin =
new BufferedInputStream(in);
147 Collection<BlackboardAttribute> attributes =
new ArrayList<>();
148 Metadata metadata = ImageMetadataReader.readMetadata(bin);
151 ExifSubIFDDirectory exifDir = metadata.getFirstDirectoryOfType(ExifSubIFDDirectory.class);
152 if (exifDir != null) {
155 if (timeZone == null) {
157 Content dataSource = f.getDataSource();
158 if ((dataSource != null) && (dataSource instanceof Image)) {
159 Image image = (Image) dataSource;
160 timeZone = TimeZone.getTimeZone(image.getTimeZone());
162 }
catch (TskCoreException ex) {
163 logger.log(Level.INFO,
"Error getting time zones", ex);
166 Date date = exifDir.getDate(ExifSubIFDDirectory.TAG_DATETIME_ORIGINAL, timeZone);
168 attributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED, ExifParserModuleFactory.getModuleName(), date.getTime() / 1000));
173 GpsDirectory gpsDir = metadata.getFirstDirectoryOfType(GpsDirectory.class);
174 if (gpsDir != null) {
175 GeoLocation loc = gpsDir.getGeoLocation();
177 double latitude = loc.getLatitude();
178 double longitude = loc.getLongitude();
179 attributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_GEO_LATITUDE, ExifParserModuleFactory.getModuleName(), latitude));
180 attributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE, ExifParserModuleFactory.getModuleName(), longitude));
183 Rational altitude = gpsDir.getRational(GpsDirectory.TAG_ALTITUDE);
184 if (altitude != null) {
185 attributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE, ExifParserModuleFactory.getModuleName(), altitude.doubleValue()));
190 ExifIFD0Directory devDir = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);
191 if (devDir != null) {
192 String model = devDir.getString(ExifIFD0Directory.TAG_MODEL);
193 if (model != null && !model.isEmpty()) {
194 attributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DEVICE_MODEL, ExifParserModuleFactory.getModuleName(), model));
197 String make = devDir.getString(ExifIFD0Directory.TAG_MAKE);
198 if (make != null && !make.isEmpty()) {
199 attributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DEVICE_MAKE, ExifParserModuleFactory.getModuleName(), make));
204 if (!attributes.isEmpty()) {
205 BlackboardArtifact bba = f.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF);
206 bba.addAttributes(attributes);
211 }
catch (Blackboard.BlackboardException ex) {
212 logger.log(Level.SEVERE,
"Unable to index blackboard artifact " + bba.getArtifactID(), ex);
213 MessageNotifyUtil.Notify.error(
214 Bundle.ExifParserFileIngestModule_indexError_message(), bba.getDisplayName());
219 return ProcessResult.OK;
220 }
catch (TskCoreException ex) {
221 logger.log(Level.WARNING,
"Failed to create blackboard artifact for exif metadata ({0}).", ex.getLocalizedMessage());
222 return ProcessResult.ERROR;
223 }
catch (ImageProcessingException ex) {
224 logger.log(Level.WARNING,
"Failed to process the image file: {0}/{1}({2})",
new Object[]{f.getParentPath(), f.getName(), ex.getLocalizedMessage()});
225 return ProcessResult.ERROR;
226 }
catch (IOException ex) {
227 logger.log(Level.WARNING,
"IOException when parsing image file: " + f.getParentPath() +
"/" + f.getName(), ex);
228 return ProcessResult.ERROR;
237 }
catch (IOException ex) {
238 logger.log(Level.WARNING,
"Failed to close InputStream.", ex);
239 return ProcessResult.ERROR;
255 if (mimeType != null) {
256 return supportedMimeTypes.contains(mimeType);
261 logger.log(Level.SEVERE,
"Failed to detect file type", ex);
synchronized long decrementAndGet(long jobId)
void startUp(IngestJobContext context)
TskData.TSK_DB_FILES_TYPE_ENUM getType()
ProcessResult process(AbstractFile content)
synchronized long incrementAndGet(long jobId)
TskData.FileKnown getKnown()
void fireModuleDataEvent(ModuleDataEvent moduleDataEvent)
Blackboard getBlackboard()
synchronized void indexArtifact(BlackboardArtifact artifact)
static Case getCurrentCase()
synchronized static Logger getLogger(String name)
FileTypeDetector fileTypeDetector
String getFileType(AbstractFile file)
boolean parsableFormat(AbstractFile f)
static synchronized IngestServices getInstance()