Autopsy  4.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
ExifParserFileIngestModule.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2011-2015 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.modules.exif;
20 
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;
51 import org.sleuthkit.datamodel.AbstractFile;
52 import org.sleuthkit.datamodel.BlackboardArtifact;
53 import org.sleuthkit.datamodel.BlackboardAttribute;
54 import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
55 import org.sleuthkit.datamodel.Content;
56 import org.sleuthkit.datamodel.Image;
57 import org.sleuthkit.datamodel.ReadContentInputStream;
58 import org.sleuthkit.datamodel.TskCoreException;
59 import org.sleuthkit.datamodel.TskData;
60 import org.sleuthkit.datamodel.TskData.TSK_DB_FILES_TYPE_ENUM;
61 
67 public final class ExifParserFileIngestModule implements FileIngestModule {
68 
69  private static final Logger logger = Logger.getLogger(ExifParserFileIngestModule.class.getName());
71  private final AtomicInteger filesProcessed = new AtomicInteger(0);
72  private volatile boolean filesToFire = false;
73  private final List<BlackboardArtifact> listOfFacesDetectedArtifacts = new ArrayList<>();
74  private long jobId;
77  private final HashSet<String> supportedMimeTypes = new HashSet<>();
78  private TimeZone timeZone = null;
80 
82  supportedMimeTypes.add("audio/x-wav"); //NON-NLS
83  supportedMimeTypes.add("image/jpeg"); //NON-NLS
84  supportedMimeTypes.add("image/tiff"); //NON-NLS
85  }
86 
87  @Override
88  public void startUp(IngestJobContext context) throws IngestModuleException {
89  jobId = context.getJobId();
90  refCounter.incrementAndGet(jobId);
91  try {
92  fileTypeDetector = new FileTypeDetector();
94  throw new IngestModuleException(NbBundle.getMessage(this.getClass(), "ExifParserFileIngestModule.startUp.fileTypeDetectorInitializationException.msg"), ex);
95  }
96  }
97 
98  @Override
99  public ProcessResult process(AbstractFile content) {
100  blackboard = Case.getCurrentCase().getServices().getBlackboard();
101 
102  //skip unalloc
103  if (content.getType().equals(TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS)) {
104  return ProcessResult.OK;
105  }
106 
107  if (content.isFile() == false) {
108  return ProcessResult.OK;
109  }
110 
111  // skip known
112  if (content.getKnown().equals(TskData.FileKnown.KNOWN)) {
113  return ProcessResult.OK;
114  }
115 
116  // update the tree every 1000 files if we have EXIF data that is not being being displayed
117  final int filesProcessedValue = filesProcessed.incrementAndGet();
118  if ((filesProcessedValue % 1000 == 0)) {
119  if (filesToFire) {
120  services.fireModuleDataEvent(new ModuleDataEvent(ExifParserModuleFactory.getModuleName(), BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF));
121  filesToFire = false;
122  }
123  }
124 
125  //skip unsupported
126  if (!parsableFormat(content)) {
127  return ProcessResult.OK;
128  }
129 
130  return processFile(content);
131  }
132 
133  ProcessResult processFile(AbstractFile f) {
134  InputStream in = null;
135  BufferedInputStream bin = null;
136 
137  try {
138  in = new ReadContentInputStream(f);
139  bin = new BufferedInputStream(in);
140 
141  Collection<BlackboardAttribute> attributes = new ArrayList<>();
142  Metadata metadata = ImageMetadataReader.readMetadata(bin);
143 
144  // Date
145  ExifSubIFDDirectory exifDir = metadata.getFirstDirectoryOfType(ExifSubIFDDirectory.class);
146  if (exifDir != null) {
147 
148  // set the timeZone for the current datasource.
149  if (timeZone == null) {
150  try {
151  Content dataSource = f.getDataSource();
152  if ((dataSource != null) && (dataSource instanceof Image)) {
153  Image image = (Image) dataSource;
154  timeZone = TimeZone.getTimeZone(image.getTimeZone());
155  }
156  } catch (TskCoreException ex) {
157  logger.log(Level.INFO, "Error getting time zones", ex); //NON-NLS
158  }
159  }
160  Date date = exifDir.getDate(ExifSubIFDDirectory.TAG_DATETIME_ORIGINAL, timeZone);
161  if (date != null) {
162  attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED, ExifParserModuleFactory.getModuleName(), date.getTime() / 1000));
163  }
164  }
165 
166  // GPS Stuff
167  GpsDirectory gpsDir = metadata.getFirstDirectoryOfType(GpsDirectory.class);
168  if (gpsDir != null) {
169  GeoLocation loc = gpsDir.getGeoLocation();
170  if (loc != null) {
171  double latitude = loc.getLatitude();
172  double longitude = loc.getLongitude();
173  attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_GEO_LATITUDE, ExifParserModuleFactory.getModuleName(), latitude));
174  attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE, ExifParserModuleFactory.getModuleName(), longitude));
175  }
176 
177  Rational altitude = gpsDir.getRational(GpsDirectory.TAG_ALTITUDE);
178  if (altitude != null) {
179  attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE, ExifParserModuleFactory.getModuleName(), altitude.doubleValue()));
180  }
181  }
182 
183  // Device info
184  ExifIFD0Directory devDir = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);
185  if (devDir != null) {
186  String model = devDir.getString(ExifIFD0Directory.TAG_MODEL);
187  if (model != null && !model.isEmpty()) {
188  attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DEVICE_MODEL, ExifParserModuleFactory.getModuleName(), model));
189  }
190 
191  String make = devDir.getString(ExifIFD0Directory.TAG_MAKE);
192  if (make != null && !make.isEmpty()) {
193  attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DEVICE_MAKE, ExifParserModuleFactory.getModuleName(), make));
194  }
195  }
196 
197  // Add the attributes, if there are any, to a new artifact
198  if (!attributes.isEmpty()) {
199  BlackboardArtifact bba = f.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF);
200  bba.addAttributes(attributes);
201 
202  try {
203  // index the artifact for keyword search
204  blackboard.indexArtifact(bba);
205  } catch (Blackboard.BlackboardException ex) {
206  logger.log(Level.SEVERE, NbBundle.getMessage(Blackboard.class, "Blackboard.unableToIndexArtifact.error.msg", bba.getDisplayName()), ex); //NON-NLS
207  MessageNotifyUtil.Notify.error(
208  NbBundle.getMessage(Blackboard.class, "Blackboard.unableToIndexArtifact.exception.msg"), bba.getDisplayName());
209  }
210  filesToFire = true;
211  }
212 
213  return ProcessResult.OK;
214  } catch (TskCoreException ex) {
215  logger.log(Level.WARNING, "Failed to create blackboard artifact for exif metadata ({0}).", ex.getLocalizedMessage()); //NON-NLS
216  return ProcessResult.ERROR;
217  } catch (ImageProcessingException ex) {
218  logger.log(Level.WARNING, "Failed to process the image file: {0}/{1}({2})", new Object[]{f.getParentPath(), f.getName(), ex.getLocalizedMessage()}); //NON-NLS
219  return ProcessResult.ERROR;
220  } catch (IOException ex) {
221  logger.log(Level.WARNING, "IOException when parsing image file: " + f.getParentPath() + "/" + f.getName(), ex); //NON-NLS
222  return ProcessResult.ERROR;
223  } finally {
224  try {
225  if (in != null) {
226  in.close();
227  }
228  if (bin != null) {
229  bin.close();
230  }
231  } catch (IOException ex) {
232  logger.log(Level.WARNING, "Failed to close InputStream.", ex); //NON-NLS
233  return ProcessResult.ERROR;
234  }
235  }
236  }
237 
246  private boolean parsableFormat(AbstractFile f) {
247  try {
248  String mimeType = fileTypeDetector.getFileType(f);
249  if (mimeType != null) {
250  return supportedMimeTypes.contains(mimeType);
251  } else {
252  return false;
253  }
254  } catch (TskCoreException ex) {
255  logger.log(Level.SEVERE, "Failed to detect file type", ex); //NON-NLS
256  return false;
257  }
258  }
259 
260  @Override
261  public void shutDown() {
262  // We only need to check for this final event on the last module per job
263  if (refCounter.decrementAndGet(jobId) == 0) {
264  timeZone = null;
265  if (filesToFire) {
266  //send the final new data event
267  services.fireModuleDataEvent(new ModuleDataEvent(ExifParserModuleFactory.getModuleName(), BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF));
268  }
269  }
270  }
271 }
void indexArtifact(BlackboardArtifact artifact)
Definition: Blackboard.java:45
void fireModuleDataEvent(ModuleDataEvent moduleDataEvent)
synchronized static Logger getLogger(String name)
Definition: Logger.java:166
static synchronized IngestServices getInstance()

Copyright © 2012-2015 Basis Technology. Generated on: Wed Apr 6 2016
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.