Autopsy  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  return ProcessResult.OK;
86  }
87 
88  if ((abstractFile.isFile() == false)) {
89  return ProcessResult.OK;
90  }
91 
92  // check its signature
93  boolean isMbox = false;
94  try {
95  byte[] t = new byte[64];
96  if (abstractFile.getSize() > 64) {
97  int byteRead = abstractFile.read(t, 0, 64);
98  if (byteRead > 0) {
99  isMbox = MboxParser.isValidMimeTypeMbox(t);
100  }
101  }
102  } catch (TskException ex) {
103  logger.log(Level.WARNING, null, ex);
104  }
105 
106  if (isMbox) {
107  return processMBox(abstractFile);
108  }
109 
110  if (PstParser.isPstFile(abstractFile)) {
111  return processPst(abstractFile);
112  }
113 
114  return ProcessResult.OK;
115  }
116 
124  @Messages({"ThunderbirdMboxFileIngestModule.processPst.indexError.message=Failed to index encryption detected artifact for keyword search."})
125  private ProcessResult processPst(AbstractFile abstractFile) {
126  String fileName = getTempPath() + File.separator + abstractFile.getName()
127  + "-" + String.valueOf(abstractFile.getId());
128  File file = new File(fileName);
129 
130  long freeSpace = services.getFreeDiskSpace();
131  if ((freeSpace != IngestMonitor.DISK_FREE_SPACE_UNKNOWN) && (abstractFile.getSize() >= freeSpace)) {
132  logger.log(Level.WARNING, "Not enough disk space to write file to disk."); //NON-NLS
134  NbBundle.getMessage(this.getClass(),
135  "ThunderbirdMboxFileIngestModule.processPst.errMsg.outOfDiskSpace",
136  abstractFile.getName()));
137  services.postMessage(msg);
138  return ProcessResult.OK;
139  }
140 
141  try {
142  ContentUtils.writeToFile(abstractFile, file, context::fileIngestIsCancelled);
143  } catch (IOException ex) {
144  logger.log(Level.WARNING, "Failed writing pst file to disk.", ex); //NON-NLS
145  return ProcessResult.OK;
146  }
147 
148  PstParser parser = new PstParser(services);
149  PstParser.ParseResult result = parser.parse(file, abstractFile.getId());
150 
151  if (result == PstParser.ParseResult.OK) {
152  // parse success: Process email and add artifacts
153  processEmails(parser.getResults(), abstractFile);
154  } else if (result == PstParser.ParseResult.ENCRYPT) {
155  // encrypted pst: Add encrypted file artifact
156  try {
157  BlackboardArtifact artifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED);
158  artifact.addAttribute(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME, EmailParserModuleFactory.getModuleName(), NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.encryptionFileLevel")));
159 
160  try {
161  // index the artifact for keyword search
162  blackboard.indexArtifact(artifact);
163  } catch (Blackboard.BlackboardException ex) {
164  MessageNotifyUtil.Notify.error(Bundle.ThunderbirdMboxFileIngestModule_processPst_indexError_message(), artifact.getDisplayName());
165  logger.log(Level.SEVERE, "Unable to index blackboard artifact " + artifact.getArtifactID(), ex); //NON-NLS
166  }
167 
168  services.fireModuleDataEvent(new ModuleDataEvent(EmailParserModuleFactory.getModuleName(), BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED));
169  } catch (TskCoreException ex) {
170  logger.log(Level.INFO, "Failed to add encryption attribute to file: {0}", abstractFile.getName()); //NON-NLS
171  }
172  } else {
173  // parsing error: log message
174  postErrorMessage(
175  NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.processPst.errProcFile.msg",
176  abstractFile.getName()),
177  NbBundle.getMessage(this.getClass(),
178  "ThunderbirdMboxFileIngestModule.processPst.errProcFile.details"));
179  logger.log(Level.INFO, "PSTParser failed to parse {0}", abstractFile.getName()); //NON-NLS
180  return ProcessResult.ERROR;
181  }
182 
183  if (file.delete() == false) {
184  logger.log(Level.INFO, "Failed to delete temp file: {0}", file.getName()); //NON-NLS
185  }
186 
187  String errors = parser.getErrors();
188  if (errors.isEmpty() == false) {
189  postErrorMessage(
190  NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.processPst.errProcFile.msg2",
191  abstractFile.getName()), errors);
192  }
193 
194  return ProcessResult.OK;
195  }
196 
204  private ProcessResult processMBox(AbstractFile abstractFile) {
205  String mboxFileName = abstractFile.getName();
206  String mboxParentDir = abstractFile.getParentPath();
207  // use the local path to determine the e-mail folder structure
208  String emailFolder = "";
209  // email folder is everything after "Mail" or ImapMail
210  if (mboxParentDir.contains("/Mail/")) { //NON-NLS
211  emailFolder = mboxParentDir.substring(mboxParentDir.indexOf("/Mail/") + 5); //NON-NLS
212  } else if (mboxParentDir.contains("/ImapMail/")) { //NON-NLS
213  emailFolder = mboxParentDir.substring(mboxParentDir.indexOf("/ImapMail/") + 9); //NON-NLS
214  }
215  emailFolder = emailFolder + mboxFileName;
216  emailFolder = emailFolder.replaceAll(".sbd", ""); //NON-NLS
217 
218  String fileName = getTempPath() + File.separator + abstractFile.getName()
219  + "-" + String.valueOf(abstractFile.getId());
220  File file = new File(fileName);
221 
222  long freeSpace = services.getFreeDiskSpace();
223  if ((freeSpace != IngestMonitor.DISK_FREE_SPACE_UNKNOWN) && (abstractFile.getSize() >= freeSpace)) {
224  logger.log(Level.WARNING, "Not enough disk space to write file to disk."); //NON-NLS
225  postErrorMessage(
226  NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.processMBox.errProcFile.msg",
227  abstractFile.getName()),
228  NbBundle.getMessage(this.getClass(),
229  "ThunderbirdMboxFileIngestModule.processMBox.errProfFile.details"));
230  return ProcessResult.OK;
231  }
232 
233  try {
234  ContentUtils.writeToFile(abstractFile, file, context::fileIngestIsCancelled);
235  } catch (IOException ex) {
236  logger.log(Level.WARNING, "Failed writing mbox file to disk.", ex); //NON-NLS
237  return ProcessResult.OK;
238  }
239 
240  MboxParser parser = new MboxParser(services, emailFolder);
241  List<EmailMessage> emails = parser.parse(file, abstractFile.getId());
242  processEmails(emails, abstractFile);
243 
244  if (file.delete() == false) {
245  logger.log(Level.INFO, "Failed to delete temp file: {0}", file.getName()); //NON-NLS
246  }
247 
248  String errors = parser.getErrors();
249  if (errors.isEmpty() == false) {
250  postErrorMessage(
251  NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.processMBox.errProcFile.msg2",
252  abstractFile.getName()), errors);
253  }
254 
255  return ProcessResult.OK;
256  }
257 
263  public static String getTempPath() {
264  String tmpDir = Case.getCurrentCase().getTempDirectory() + File.separator
265  + "EmailParser"; //NON-NLS
266  File dir = new File(tmpDir);
267  if (dir.exists() == false) {
268  dir.mkdirs();
269  }
270  return tmpDir;
271  }
272 
273  public static String getModuleOutputPath() {
274  String outDir = Case.getCurrentCase().getModuleDirectory() + File.separator
275  + EmailParserModuleFactory.getModuleName();
276  File dir = new File(outDir);
277  if (dir.exists() == false) {
278  dir.mkdirs();
279  }
280  return outDir;
281  }
282 
283  public static String getRelModuleOutputPath() {
284  return Case.getCurrentCase().getModuleOutputDirectoryRelativePath() + File.separator
285  + EmailParserModuleFactory.getModuleName();
286  }
287 
295  private void processEmails(List<EmailMessage> emails, AbstractFile abstractFile) {
296  List<AbstractFile> derivedFiles = new ArrayList<>();
297  for (EmailMessage email : emails) {
298  if (email.hasAttachment()) {
299  derivedFiles.addAll(handleAttachments(email.getAttachments(), abstractFile));
300  }
301  addArtifact(email, abstractFile);
302  }
303 
304  if (derivedFiles.isEmpty() == false) {
305  for (AbstractFile derived : derivedFiles) {
306  services.fireModuleContentEvent(new ModuleContentEvent(derived));
307  }
308  }
309  context.addFilesToJob(derivedFiles);
310  services.fireModuleDataEvent(new ModuleDataEvent(EmailParserModuleFactory.getModuleName(), BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG));
311  }
312 
322  private List<AbstractFile> handleAttachments(List<EmailMessage.Attachment> attachments, AbstractFile abstractFile) {
323  List<AbstractFile> files = new ArrayList<>();
324  for (EmailMessage.Attachment attach : attachments) {
325  String filename = attach.getName();
326  long crTime = attach.getCrTime();
327  long mTime = attach.getmTime();
328  long aTime = attach.getaTime();
329  long cTime = attach.getcTime();
330  String relPath = attach.getLocalPath();
331  long size = attach.getSize();
332  TskData.EncodingType encodingType = attach.getEncodingType();
333 
334  try {
335  DerivedFile df = fileManager.addDerivedFile(filename, relPath,
336  size, cTime, crTime, aTime, mTime, true, abstractFile, "",
337  EmailParserModuleFactory.getModuleName(), EmailParserModuleFactory.getModuleVersion(), "", encodingType);
338  files.add(df);
339  } catch (TskCoreException ex) {
340  postErrorMessage(
341  NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.handleAttch.errMsg",
342  abstractFile.getName()),
343  NbBundle.getMessage(this.getClass(),
344  "ThunderbirdMboxFileIngestModule.handleAttch.errMsg.details", filename));
345  logger.log(Level.INFO, "", ex);
346  }
347  }
348  return files;
349  }
350 
357  @Messages({"ThunderbirdMboxFileIngestModule.addArtifact.indexError.message=Failed to index email message detected artifact for keyword search."})
358  private void addArtifact(EmailMessage email, AbstractFile abstractFile) {
359  List<BlackboardAttribute> bbattributes = new ArrayList<>();
360  String to = email.getRecipients();
361  String cc = email.getCc();
362  String bcc = email.getBcc();
363  String from = email.getSender();
364  long dateL = email.getSentDate();
365  String body = email.getTextBody();
366  String bodyHTML = email.getHtmlBody();
367  String rtf = email.getRtfBody();
368  String subject = email.getSubject();
369  long id = email.getId();
370  String localPath = email.getLocalPath();
371 
372  if (to.isEmpty() == false) {
373  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_EMAIL_TO, EmailParserModuleFactory.getModuleName(), to));
374  }
375  if (cc.isEmpty() == false) {
376  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_EMAIL_CC, EmailParserModuleFactory.getModuleName(), cc));
377  }
378  if (bcc.isEmpty() == false) {
379  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_EMAIL_BCC, EmailParserModuleFactory.getModuleName(), bcc));
380  }
381  if (from.isEmpty() == false) {
382  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_EMAIL_FROM, EmailParserModuleFactory.getModuleName(), from));
383  }
384  if (dateL > 0) {
385  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_RCVD, EmailParserModuleFactory.getModuleName(), dateL));
386  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_SENT, EmailParserModuleFactory.getModuleName(), dateL));
387  }
388  if (body.isEmpty() == false) {
389  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_PLAIN, EmailParserModuleFactory.getModuleName(), body));
390  }
391  if (bodyHTML.isEmpty() == false) {
392  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_HTML, EmailParserModuleFactory.getModuleName(), bodyHTML));
393  }
394  if (rtf.isEmpty() == false) {
395  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_RTF, EmailParserModuleFactory.getModuleName(), rtf));
396  }
397  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_MSG_ID, EmailParserModuleFactory.getModuleName(), ((id < 0L) ? NbBundle
398  .getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.notAvail") : String.valueOf(id))));
399  if (subject.isEmpty() == false) {
400  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SUBJECT, EmailParserModuleFactory.getModuleName(), subject));
401  }
402  if (localPath.isEmpty() == false) {
403  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH, EmailParserModuleFactory.getModuleName(), localPath));
404  } else {
405  bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH, EmailParserModuleFactory.getModuleName(), "/foo/bar")); //NON-NLS
406  }
407 
408  try {
409  BlackboardArtifact bbart;
410  bbart = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG);
411  bbart.addAttributes(bbattributes);
412 
413  try {
414  // index the artifact for keyword search
415  blackboard.indexArtifact(bbart);
416  } catch (Blackboard.BlackboardException ex) {
417  logger.log(Level.SEVERE, "Unable to index blackboard artifact " + bbart.getArtifactID(), ex); //NON-NLS
418  MessageNotifyUtil.Notify.error(Bundle.ThunderbirdMboxFileIngestModule_addArtifact_indexError_message(), bbart.getDisplayName());
419  }
420  } catch (TskCoreException ex) {
421  logger.log(Level.WARNING, null, ex);
422  }
423  }
424 
425  void postErrorMessage(String subj, String details) {
426  IngestMessage ingestMessage = IngestMessage.createErrorMessage(EmailParserModuleFactory.getModuleVersion(), subj, details);
427  services.postMessage(ingestMessage);
428  }
429 
430  IngestServices getServices() {
431  return services;
432  }
433 
434  @Override
435  public void shutDown() {
436  }
437 }
static IngestMessage createErrorMessage(String source, String subject, String detailsHtml)
static< T > long writeToFile(Content content, java.io.File outputFile, ProgressHandle progress, Future< T > worker, boolean source)
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)
synchronized DerivedFile addDerivedFile(String fileName, String localPath, long size, long ctime, long crtime, long atime, long mtime, boolean isFile, AbstractFile parentFile, String rederiveDetails, String toolName, String toolVersion, String otherDetails, TskData.EncodingType encodingType)
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)
static synchronized IngestServices getInstance()

Copyright © 2012-2016 Basis Technology. Generated on: Tue Oct 25 2016
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.