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;
56 import org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
60 import org.
sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException;
63 import org.
sleuthkit.datamodel.TskData.TSK_DB_FILES_TYPE_ENUM;
71 "CannotRunFileTypeDetection=Cannot run file type detection."
77 private final AtomicInteger filesProcessed =
new AtomicInteger(0);
78 private volatile boolean filesToFire =
false;
79 private final List<BlackboardArtifact> listOfFacesDetectedArtifacts =
new ArrayList<>();
83 private final HashSet<String> supportedMimeTypes =
new HashSet<>();
84 private TimeZone timeZone = null;
88 supportedMimeTypes.add(
"audio/x-wav");
89 supportedMimeTypes.add(
"image/jpeg");
90 supportedMimeTypes.add(
"image/tiff");
95 jobId = context.getJobId();
109 logger.log(Level.INFO,
"Exception while getting open case.", ex);
113 if ((content.getType().equals(TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS)
114 || (content.getType().equals(TSK_DB_FILES_TYPE_ENUM.SLACK)))) {
118 if (content.isFile() ==
false) {
123 if (content.getKnown().equals(TskData.FileKnown.KNOWN)) {
128 final int filesProcessedValue = filesProcessed.incrementAndGet();
129 if ((filesProcessedValue % 1000 == 0)) {
137 if (!parsableFormat(content)) {
141 return processFile(content);
144 @Messages({
"ExifParserFileIngestModule.indexError.message=Failed to index EXIF Metadata artifact for keyword search."})
145 ProcessResult processFile(AbstractFile f) {
146 InputStream in = null;
147 BufferedInputStream bin = null;
150 in =
new ReadContentInputStream(f);
151 bin =
new BufferedInputStream(in);
153 Collection<BlackboardAttribute> attributes =
new ArrayList<>();
154 Metadata metadata = ImageMetadataReader.readMetadata(bin);
157 ExifSubIFDDirectory exifDir = metadata.getFirstDirectoryOfType(ExifSubIFDDirectory.class);
158 if (exifDir != null) {
161 if (timeZone == null) {
163 Content dataSource = f.getDataSource();
164 if ((dataSource != null) && (dataSource instanceof Image)) {
165 Image image = (Image) dataSource;
166 timeZone = TimeZone.getTimeZone(image.getTimeZone());
168 }
catch (TskCoreException ex) {
169 logger.log(Level.INFO,
"Error getting time zones", ex);
172 Date date = exifDir.getDate(ExifSubIFDDirectory.TAG_DATETIME_ORIGINAL, timeZone);
174 attributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED, ExifParserModuleFactory.getModuleName(), date.getTime() / 1000));
179 GpsDirectory gpsDir = metadata.getFirstDirectoryOfType(GpsDirectory.class);
180 if (gpsDir != null) {
181 GeoLocation loc = gpsDir.getGeoLocation();
183 double latitude = loc.getLatitude();
184 double longitude = loc.getLongitude();
185 attributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_GEO_LATITUDE, ExifParserModuleFactory.getModuleName(), latitude));
186 attributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE, ExifParserModuleFactory.getModuleName(), longitude));
189 Rational altitude = gpsDir.getRational(GpsDirectory.TAG_ALTITUDE);
190 if (altitude != null) {
191 attributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE, ExifParserModuleFactory.getModuleName(), altitude.doubleValue()));
196 ExifIFD0Directory devDir = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);
197 if (devDir != null) {
198 String model = devDir.getString(ExifIFD0Directory.TAG_MODEL);
199 if (model != null && !model.isEmpty()) {
200 attributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DEVICE_MODEL, ExifParserModuleFactory.getModuleName(), model));
203 String make = devDir.getString(ExifIFD0Directory.TAG_MAKE);
204 if (make != null && !make.isEmpty()) {
205 attributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DEVICE_MAKE, ExifParserModuleFactory.getModuleName(), make));
210 if (!attributes.isEmpty()) {
211 BlackboardArtifact bba = f.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF);
212 bba.addAttributes(attributes);
217 }
catch (Blackboard.BlackboardException ex) {
218 logger.log(Level.SEVERE,
"Unable to index blackboard artifact " + bba.getArtifactID(), ex);
219 MessageNotifyUtil.Notify.error(
220 Bundle.ExifParserFileIngestModule_indexError_message(), bba.getDisplayName());
225 return ProcessResult.OK;
226 }
catch (TskCoreException ex) {
227 logger.log(Level.WARNING,
"Failed to create blackboard artifact for exif metadata ({0}).", ex.getLocalizedMessage());
228 return ProcessResult.ERROR;
229 }
catch (ImageProcessingException ex) {
230 logger.log(Level.WARNING, String.format(
"Failed to process the image file '%s/%s' (id=%d).", f.getParentPath(), f.getName(), f.getId()), ex);
231 return ProcessResult.ERROR;
232 }
catch (ReadContentInputStreamException ex) {
233 logger.log(Level.WARNING, String.format(
"Error while trying to read image file '%s/%s' (id=%d).", f.getParentPath(), f.getName(), f.getId()), ex);
234 return ProcessResult.ERROR;
235 }
catch (IOException ex) {
236 logger.log(Level.WARNING, String.format(
"IOException when parsing image file '%s/%s' (id=%d).", f.getParentPath(), f.getName(), f.getId()), ex);
237 return ProcessResult.ERROR;
246 }
catch (IOException ex) {
247 logger.log(Level.WARNING,
"Failed to close InputStream.", ex);
248 return ProcessResult.ERROR;
263 return supportedMimeTypes.contains(mimeType);
synchronized long decrementAndGet(long jobId)
void startUp(IngestJobContext context)
ProcessResult process(AbstractFile content)
synchronized long incrementAndGet(long jobId)
String getMIMEType(AbstractFile file)
void fireModuleDataEvent(ModuleDataEvent moduleDataEvent)
Blackboard getBlackboard()
synchronized void indexArtifact(BlackboardArtifact artifact)
synchronized static Logger getLogger(String name)
static Case getCurrentCaseThrows()
FileTypeDetector fileTypeDetector
boolean parsableFormat(AbstractFile f)
static synchronized IngestServices getInstance()