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;
55 import org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
61 import org.
sleuthkit.datamodel.TskData.TSK_DB_FILES_TYPE_ENUM;
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();
107 if (content.getType().equals(TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS)) {
111 if (content.isFile() ==
false) {
116 if (content.getKnown().equals(TskData.FileKnown.KNOWN)) {
121 final int filesProcessedValue = filesProcessed.incrementAndGet();
122 if ((filesProcessedValue % 1000 == 0)) {
130 if (!parsableFormat(content)) {
134 return processFile(content);
137 @Messages({
"ExifParserFileIngestModule.indexError.message=Failed to index EXIF Metadata artifact for keyword search."})
138 ProcessResult processFile(AbstractFile f) {
139 InputStream in = null;
140 BufferedInputStream bin = null;
143 in =
new ReadContentInputStream(f);
144 bin =
new BufferedInputStream(in);
146 Collection<BlackboardAttribute> attributes =
new ArrayList<>();
147 Metadata metadata = ImageMetadataReader.readMetadata(bin);
150 ExifSubIFDDirectory exifDir = metadata.getFirstDirectoryOfType(ExifSubIFDDirectory.class);
151 if (exifDir != null) {
154 if (timeZone == null) {
156 Content dataSource = f.getDataSource();
157 if ((dataSource != null) && (dataSource instanceof Image)) {
158 Image image = (Image) dataSource;
159 timeZone = TimeZone.getTimeZone(image.getTimeZone());
161 }
catch (TskCoreException ex) {
162 logger.log(Level.INFO,
"Error getting time zones", ex);
165 Date date = exifDir.getDate(ExifSubIFDDirectory.TAG_DATETIME_ORIGINAL, timeZone);
167 attributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED, ExifParserModuleFactory.getModuleName(), date.getTime() / 1000));
172 GpsDirectory gpsDir = metadata.getFirstDirectoryOfType(GpsDirectory.class);
173 if (gpsDir != null) {
174 GeoLocation loc = gpsDir.getGeoLocation();
176 double latitude = loc.getLatitude();
177 double longitude = loc.getLongitude();
178 attributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_GEO_LATITUDE, ExifParserModuleFactory.getModuleName(), latitude));
179 attributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE, ExifParserModuleFactory.getModuleName(), longitude));
182 Rational altitude = gpsDir.getRational(GpsDirectory.TAG_ALTITUDE);
183 if (altitude != null) {
184 attributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE, ExifParserModuleFactory.getModuleName(), altitude.doubleValue()));
189 ExifIFD0Directory devDir = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);
190 if (devDir != null) {
191 String model = devDir.getString(ExifIFD0Directory.TAG_MODEL);
192 if (model != null && !model.isEmpty()) {
193 attributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DEVICE_MODEL, ExifParserModuleFactory.getModuleName(), model));
196 String make = devDir.getString(ExifIFD0Directory.TAG_MAKE);
197 if (make != null && !make.isEmpty()) {
198 attributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DEVICE_MAKE, ExifParserModuleFactory.getModuleName(), make));
203 if (!attributes.isEmpty()) {
204 BlackboardArtifact bba = f.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF);
205 bba.addAttributes(attributes);
210 }
catch (Blackboard.BlackboardException ex) {
211 logger.log(Level.SEVERE,
"Unable to index blackboard artifact " + bba.getArtifactID(), ex);
212 MessageNotifyUtil.Notify.error(
213 Bundle.ExifParserFileIngestModule_indexError_message(), bba.getDisplayName());
218 return ProcessResult.OK;
219 }
catch (TskCoreException ex) {
220 logger.log(Level.WARNING,
"Failed to create blackboard artifact for exif metadata ({0}).", ex.getLocalizedMessage());
221 return ProcessResult.ERROR;
222 }
catch (ImageProcessingException ex) {
223 logger.log(Level.WARNING,
"Failed to process the image file: {0}/{1}({2})",
new Object[]{f.getParentPath(), f.getName(), ex.getLocalizedMessage()});
224 return ProcessResult.ERROR;
225 }
catch (IOException ex) {
226 logger.log(Level.WARNING,
"IOException when parsing image file: " + f.getParentPath() +
"/" + f.getName(), ex);
227 return ProcessResult.ERROR;
236 }
catch (IOException ex) {
237 logger.log(Level.WARNING,
"Failed to close InputStream.", ex);
238 return ProcessResult.ERROR;
254 if (mimeType != null) {
255 return supportedMimeTypes.contains(mimeType);
259 }
catch (TskCoreException ex) {
260 logger.log(Level.SEVERE,
"Failed to detect file type", ex);
synchronized long decrementAndGet(long jobId)
void startUp(IngestJobContext context)
ProcessResult process(AbstractFile content)
synchronized long incrementAndGet(long jobId)
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()