Autopsy  4.4.1
Graphical digital forensics platform for The Sleuth Kit and other tools.
ThunderbirdMboxFileIngestModule.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2011-2014 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.thunderbirdparser;
20 
21 import java.io.File;
22 import java.io.IOException;
23 import java.util.ArrayList;
24 import java.util.List;
25 import java.util.logging.Level;
26 import org.openide.util.NbBundle;
27 import org.openide.util.NbBundle.Messages;
42 import org.sleuthkit.datamodel.AbstractFile;
43 import org.sleuthkit.datamodel.BlackboardArtifact;
44 import org.sleuthkit.datamodel.BlackboardAttribute;
45 import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
46 import org.sleuthkit.datamodel.DerivedFile;
47 import org.sleuthkit.datamodel.TskCoreException;
48 import org.sleuthkit.datamodel.TskData;
49 import org.sleuthkit.datamodel.TskException;
50 
56 public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
57 
58  private static final Logger logger = Logger.getLogger(ThunderbirdMboxFileIngestModule.class.getName());
63 
65  }
66 
67  @Override
68  public void startUp(IngestJobContext context) throws IngestModuleException {
69  this.context = context;
70  fileManager = Case.getCurrentCase().getServices().getFileManager();
71  }
72 
73  @Override
74  public ProcessResult process(AbstractFile abstractFile) {
75 
76  blackboard = Case.getCurrentCase().getServices().getBlackboard();
77 
78  // skip known
79  if (abstractFile.getKnown().equals(TskData.FileKnown.KNOWN)) {
80  return ProcessResult.OK;
81  }
82 
83  //skip unalloc
84  if ((abstractFile.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS)) ||
85  (abstractFile.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.SLACK))) {
86  return ProcessResult.OK;
87  }
88 
89  if ((abstractFile.isFile() == false)) {
90  return ProcessResult.OK;
91  }
92 
93  // check its signature
94  boolean isMbox = false;
95  try {
96  byte[] t = new byte[64];
97  if (abstractFile.getSize() > 64) {
98  int byteRead = abstractFile.read(t, 0, 64);
99  if (byteRead > 0) {
100  isMbox = MboxParser.isValidMimeTypeMbox(t);
101  }
102  }
103  } catch (TskException ex) {
104  logger.log(Level.WARNING, null, ex);
105  }
106 
107  if (isMbox) {
108  return processMBox(abstractFile);
109  }
110 
111  if (PstParser.isPstFile(abstractFile)) {
112  return processPst(abstractFile);
113  }
114 
115  return ProcessResult.OK;
116  }
117 
125  @Messages({"ThunderbirdMboxFileIngestModule.processPst.indexError.message=Failed to index encryption detected artifact for keyword search."})
126  private ProcessResult processPst(AbstractFile abstractFile) {
127  String fileName = getTempPath() + File.separator + abstractFile.getName()
128  + "-" + String.valueOf(abstractFile.getId());
129  File file = new File(fileName);
130 
131  long freeSpace = services.getFreeDiskSpace();
132  if ((freeSpace != IngestMonitor.DISK_FREE_SPACE_UNKNOWN) && (abstractFile.getSize() >= freeSpace)) {
133  logger.log(Level.WARNING, "Not enough disk space to write file to disk."); //NON-NLS
135  NbBundle.getMessage(this.getClass(),
136  "ThunderbirdMboxFileIngestModule.processPst.errMsg.outOfDiskSpace",
137  abstractFile.getName()));
138  services.postMessage(msg);
139  return ProcessResult.OK;
140  }
141 
142  try {
143  ContentUtils.writeToFile(abstractFile, file, context::fileIngestIsCancelled);
144  } catch (IOException ex) {
145  logger.log(Level.WARNING, "Failed writing pst file to disk.", ex); //NON-NLS
146  return ProcessResult.OK;
147  }
148 
149  PstParser parser = new PstParser(services);
150  PstParser.ParseResult result = parser.parse(file, abstractFile.getId());
151 
152  if (result == PstParser.ParseResult.OK) {
153  // parse success: Process email and add artifacts
154  processEmails(parser.getResults(), abstractFile);
155  } else if (result == PstParser.ParseResult.ENCRYPT) {
156  // encrypted pst: Add encrypted file artifact
157  try {
158  BlackboardArtifact artifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED);
159  artifact.addAttribute(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME, EmailParserModuleFactory.getModuleName(), NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.encryptionFileLevel")));
160 
161  try {
162  // index the artifact for keyword search
163  blackboard.indexArtifact(artifact);
164  } catch (Blackboard.BlackboardException ex) {
165  MessageNotifyUtil.Notify.error(Bundle.ThunderbirdMboxFileIngestModule_processPst_indexError_message(), artifact.getDisplayName());
166  logger.log(Level.SEVERE, "Unable to index blackboard artifact " + artifact.getArtifactID(), ex); //NON-NLS
167  }
168 
169  services.fireModuleDataEvent(new ModuleDataEvent(EmailParserModuleFactory.getModuleName(), BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED));
170  } catch (TskCoreException ex) {
171  logger.log(Level.INFO, "Failed to add encryption attribute to file: {0}", abstractFile.getName()); //NON-NLS
172  }
173  } else {
174  // parsing error: log message
175  postErrorMessage(
176  NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.processPst.errProcFile.msg",
177  abstractFile.getName()),
178  NbBundle.getMessage(this.getClass(),
179  "ThunderbirdMboxFileIngestModule.processPst.errProcFile.details"));
180  logger.log(Level.INFO, "PSTParser failed to parse {0}", abstractFile.getName()); //NON-NLS
181  return ProcessResult.ERROR;
182  }
183 
184  if (file.delete() == false) {
185  logger.log(Level.INFO, "Failed to delete temp file: {0}", file.getName()); //NON-NLS
186  }
187 
188  String errors = parser.getErrors();
189  if (errors.isEmpty() == false) {
190  postErrorMessage(
191  NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.processPst.errProcFile.msg2",
192  abstractFile.getName()), errors);
193  }
194 
195  return ProcessResult.OK;
196  }
197 
205  private ProcessResult processMBox(AbstractFile abstractFile) {
206  String mboxFileName = abstractFile.getName();
207  String mboxParentDir = abstractFile.getParentPath();
208  // use the local path to determine the e-mail folder structure
209  String emailFolder = "";
210  // email folder is everything after "Mail" or ImapMail
211  if (mboxParentDir.contains("/Mail/")) { //NON-NLS
212  emailFolder = mboxParentDir.substring(mboxParentDir.indexOf("/Mail/") + 5); //NON-NLS
213  } else if (mboxParentDir.contains("/ImapMail/")) { //NON-NLS
214  emailFolder = mboxParentDir.substring(mboxParentDir.indexOf("/ImapMail/") + 9); //NON-NLS
215  }
216  emailFolder = emailFolder + mboxFileName;
217  emailFolder = emailFolder.replaceAll(".sbd", ""); //NON-NLS
218 
219  String fileName = getTempPath() + File.separator + abstractFile.getName()
220  + "-" + String.valueOf(abstractFile.getId());
221  File file = new File(fileName);
222 
223  long freeSpace = services.getFreeDiskSpace();
224  if ((freeSpace != IngestMonitor.DISK_FREE_SPACE_UNKNOWN) && (abstractFile.getSize() >= freeSpace)) {
225  logger.log(Level.WARNING, "Not enough disk space to write file to disk."); //NON-NLS
226  postErrorMessage(
227  NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.processMBox.errProcFile.msg",
228  abstractFile.getName()),
229  NbBundle.getMessage(this.getClass(),
230  "ThunderbirdMboxFileIngestModule.processMBox.errProfFile.details"));
231  return ProcessResult.OK;
232  }
233 
234  try {
235  ContentUtils.writeToFile(abstractFile, file, context::fileIngestIsCancelled);
236  } catch (IOException ex) {
237  logger.log(Level.WARNING, "Failed writing mbox file to disk.", ex); //NON-NLS
238  return ProcessResult.OK;
239  }
240 
241  MboxParser parser = new MboxParser(services, emailFolder);
242  List<EmailMessage> emails = parser.parse(file, abstractFile.getId());
243  processEmails(emails, abstractFile);
244 
245  if (file.delete() == false) {
246  logger.log(Level.INFO, "Failed to delete temp file: {0}", file.getName()); //NON-NLS
247  }
248 
249  String errors = parser.getErrors();
250  if (errors.isEmpty() == false) {
251  postErrorMessage(
252  NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.processMBox.errProcFile.msg2",
253  abstractFile.getName()), errors);
254  }
255 
256  return ProcessResult.OK;
257  }
258 
264  public static String getTempPath() {
265  String tmpDir = Case.getCurrentCase().getTempDirectory() + File.separator
266  + "EmailParser"; //NON-NLS
267  File dir = new File(tmpDir);
268  if (dir.exists() == false) {
269  dir.mkdirs();
270  }
271  return tmpDir;
272  }
273 
274  public static String getModuleOutputPath() {
275  String outDir = Case.getCurrentCase().getModuleDirectory() + File.separator
276  + EmailParserModuleFactory.getModuleName();
277  File dir = new File(outDir);
278  if (dir.exists() == false) {
279  dir.mkdirs();
280  }
281  return outDir;
282  }
283 
284  public static String getRelModuleOutputPath() {
285  return Case.getCurrentCase().getModuleOutputDirectoryRelativePath() + File.separator
286  + EmailParserModuleFactory.getModuleName();
287  }
288 
296  private void processEmails(List<EmailMessage> emails, AbstractFile abstractFile) {
297  List<AbstractFile> derivedFiles = new ArrayList<>();
298 
299 
300 
301  for (EmailMessage email : emails) {
302  BlackboardArtifact msgArtifact = addArtifact(email, abstractFile);
303 
304  if ((msgArtifact != null) && (email.hasAttachment())) {
305  derivedFiles.addAll(handleAttachments(email.getAttachments(), abstractFile, msgArtifact ));
306  }
307  }
308 
309  if (derivedFiles.isEmpty() == false) {
310  for (AbstractFile derived : derivedFiles) {
311  services.fireModuleContentEvent(new ModuleContentEvent(derived));
312  }
313  }
314  context.addFilesToJob(derivedFiles);
315  services.fireModuleDataEvent(new ModuleDataEvent(EmailParserModuleFactory.getModuleName(), BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG));
316  }
317 
327  private List<AbstractFile> handleAttachments(List<EmailMessage.Attachment> attachments, AbstractFile abstractFile, BlackboardArtifact messageArtifact) {
328  List<AbstractFile> files = new ArrayList<>();
329  for (EmailMessage.Attachment attach : attachments) {
330  String filename = attach.getName();
331  long crTime = attach.getCrTime();
332  long mTime = attach.getmTime();
333  long aTime = attach.getaTime();
334  long cTime = attach.getcTime();
335  String relPath = attach.getLocalPath();
336  long size = attach.getSize();
337  TskData.EncodingType encodingType = attach.getEncodingType();
338 
339  try {
340  DerivedFile df = fileManager.addDerivedFile(filename, relPath,
341  size, cTime, crTime, aTime, mTime, true, messageArtifact, "",
342  EmailParserModuleFactory.getModuleName(), EmailParserModuleFactory.getModuleVersion(), "", encodingType);
343  files.add(df);
344  } catch (TskCoreException ex) {
345  postErrorMessage(
346  NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.handleAttch.errMsg",
347  abstractFile.getName()),
348  NbBundle.getMessage(this.getClass(),
349  "ThunderbirdMboxFileIngestModule.handleAttch.errMsg.details", filename));
350  logger.log(Level.INFO, "", ex);
351  }
352  }
353  return files;
354  }
355 
362  @Messages({"ThunderbirdMboxFileIngestModule.addArtifact.indexError.message=Failed to index email message detected artifact for keyword search."})
363  private BlackboardArtifact addArtifact(EmailMessage email, AbstractFile abstractFile) {
364  BlackboardArtifact bbart = null;
365  List<BlackboardAttribute> bbattributes = new ArrayList<>();
366  String to = email.getRecipients();
367  String cc = email.getCc();
368  String bcc = email.getBcc();
369  String from = email.getSender();
370  long dateL = email.getSentDate();
371  String headers = email.getHeaders();
372  String body = email.getTextBody();
373  String bodyHTML = email.getHtmlBody();
374  String rtf = email.getRtfBody();
375  String subject = email.getSubject();
376  long id = email.getId();
377  String localPath = email.getLocalPath();
378 
379  if (headers.isEmpty() == false) {
380  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_HEADERS, EmailParserModuleFactory.getModuleName(), headers));
381  }
382  if (from.isEmpty() == false) {
383  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_EMAIL_FROM, EmailParserModuleFactory.getModuleName(), from));
384  }
385  if (to.isEmpty() == false) {
386  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_EMAIL_TO, EmailParserModuleFactory.getModuleName(), to));
387  }
388  if (subject.isEmpty() == false) {
389  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SUBJECT, EmailParserModuleFactory.getModuleName(), subject));
390  }
391 
392  if (dateL > 0) {
393  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_RCVD, EmailParserModuleFactory.getModuleName(), dateL));
394  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_SENT, EmailParserModuleFactory.getModuleName(), dateL));
395  }
396  if (body.isEmpty() == false) {
397  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_PLAIN, EmailParserModuleFactory.getModuleName(), body));
398  }
399 
400  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_MSG_ID, EmailParserModuleFactory.getModuleName(), ((id < 0L) ? NbBundle
401  .getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.notAvail") : String.valueOf(id))));
402 
403  if (localPath.isEmpty() == false) {
404  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH, EmailParserModuleFactory.getModuleName(), localPath));
405  } else {
406  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH, EmailParserModuleFactory.getModuleName(), "/foo/bar")); //NON-NLS
407  }
408 
409  if (cc.isEmpty() == false) {
410  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_EMAIL_CC, EmailParserModuleFactory.getModuleName(), cc));
411  }
412  if (bcc.isEmpty() == false) {
413  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_EMAIL_BCC, EmailParserModuleFactory.getModuleName(), bcc));
414  }
415 
416  if (bodyHTML.isEmpty() == false) {
417  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_HTML, EmailParserModuleFactory.getModuleName(), bodyHTML));
418  }
419  if (rtf.isEmpty() == false) {
420  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_RTF, EmailParserModuleFactory.getModuleName(), rtf));
421  }
422 
423  try {
424 
425  bbart = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG);
426  bbart.addAttributes(bbattributes);
427 
428  try {
429  // index the artifact for keyword search
430  blackboard.indexArtifact(bbart);
431  } catch (Blackboard.BlackboardException ex) {
432  logger.log(Level.SEVERE, "Unable to index blackboard artifact " + bbart.getArtifactID(), ex); //NON-NLS
433  MessageNotifyUtil.Notify.error(Bundle.ThunderbirdMboxFileIngestModule_addArtifact_indexError_message(), bbart.getDisplayName());
434  }
435  } catch (TskCoreException ex) {
436  logger.log(Level.WARNING, null, ex);
437  }
438 
439  return bbart;
440  }
441 
442  void postErrorMessage(String subj, String details) {
443  IngestMessage ingestMessage = IngestMessage.createErrorMessage(EmailParserModuleFactory.getModuleVersion(), subj, details);
444  services.postMessage(ingestMessage);
445  }
446 
447  IngestServices getServices() {
448  return services;
449  }
450 
451  @Override
452  public void shutDown() {
453  }
454 }
static IngestMessage createErrorMessage(String source, String subject, String detailsHtml)
BlackboardArtifact addArtifact(EmailMessage email, AbstractFile abstractFile)
static< T > long writeToFile(Content content, java.io.File outputFile, ProgressHandle progress, Future< T > worker, boolean source)
synchronized DerivedFile addDerivedFile(String fileName, String localPath, long size, long ctime, long crtime, long atime, long mtime, boolean isFile, Content parentObj, String rederiveDetails, String toolName, String toolVersion, String otherDetails, TskData.EncodingType encodingType)
void processEmails(List< EmailMessage > emails, AbstractFile abstractFile)
void addFilesToJob(List< AbstractFile > files)
void postMessage(final IngestMessage message)
void fireModuleDataEvent(ModuleDataEvent moduleDataEvent)
void fireModuleContentEvent(ModuleContentEvent moduleContentEvent)
static void error(String title, String message)
synchronized void indexArtifact(BlackboardArtifact artifact)
Definition: Blackboard.java:59
synchronized static Logger getLogger(String name)
Definition: Logger.java:161
List< AbstractFile > handleAttachments(List< EmailMessage.Attachment > attachments, AbstractFile abstractFile, BlackboardArtifact messageArtifact)
static synchronized IngestServices getInstance()

Copyright © 2012-2016 Basis Technology. Generated on: Fri Sep 29 2017
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.