Autopsy  4.4.1
Graphical digital forensics platform for The Sleuth Kit and other tools.
EmailExtracted.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2012-2017 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.datamodel;
20 
21 import java.beans.PropertyChangeEvent;
22 import java.beans.PropertyChangeListener;
23 import java.sql.ResultSet;
24 import java.sql.SQLException;
25 import java.util.ArrayList;
26 import java.util.EnumSet;
27 import java.util.HashMap;
28 import java.util.LinkedHashMap;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Observable;
32 import java.util.Observer;
33 import java.util.Set;
34 import java.util.logging.Level;
35 import org.openide.nodes.ChildFactory;
36 import org.openide.nodes.Children;
37 import org.openide.nodes.Node;
38 import org.openide.nodes.Sheet;
39 import org.openide.util.NbBundle;
40 import org.openide.util.lookup.Lookups;
45 import org.sleuthkit.datamodel.BlackboardArtifact;
46 import org.sleuthkit.datamodel.BlackboardAttribute;
47 import org.sleuthkit.datamodel.SleuthkitCase;
48 import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery;
49 import org.sleuthkit.datamodel.TskCoreException;
50 
57 public class EmailExtracted implements AutopsyVisitableItem {
58 
59  private static final String LABEL_NAME = BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getLabel();
60  private static final String DISPLAY_NAME = BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getDisplayName();
61  private static final Logger logger = Logger.getLogger(EmailExtracted.class.getName());
62  private static final String MAIL_ACCOUNT = NbBundle.getMessage(EmailExtracted.class, "EmailExtracted.mailAccount.text");
63  private static final String MAIL_FOLDER = NbBundle.getMessage(EmailExtracted.class, "EmailExtracted.mailFolder.text");
64  private static final String MAIL_PATH_SEPARATOR = "/";
74  public static final Map<String, String> parsePath(String path) {
75  Map<String, String> parsed = new HashMap<>();
76  String[] split = path.split(MAIL_PATH_SEPARATOR);
77  if (split.length < 4) {
78  parsed.put(MAIL_ACCOUNT, NbBundle.getMessage(EmailExtracted.class, "EmailExtracted.defaultAcct.text"));
79  parsed.put(MAIL_FOLDER, NbBundle.getMessage(EmailExtracted.class, "EmailExtracted.defaultFolder.text"));
80  return parsed;
81  }
82  parsed.put(MAIL_ACCOUNT, split[2]);
83  parsed.put(MAIL_FOLDER, split[3]);
84  return parsed;
85  }
86  private SleuthkitCase skCase;
87  private final EmailResults emailResults;
88 
89 
90  public EmailExtracted(SleuthkitCase skCase) {
91  this.skCase = skCase;
92  emailResults = new EmailResults();
93  }
94 
95 
96  @Override
97  public <T> T accept(AutopsyItemVisitor<T> v) {
98  return v.visit(this);
99  }
100  private final class EmailResults extends Observable {
101 
102  // NOTE: the map can be accessed by multiple worker threads and needs to be synchronized
103  private final Map<String, Map<String, List<Long>>> accounts = new LinkedHashMap<>();
104 
105  EmailResults() {
106  update();
107  }
108 
109  public Set<String> getAccounts() {
110  synchronized (accounts) {
111  return accounts.keySet();
112  }
113  }
114 
115  public Set<String> getFolders(String account) {
116  synchronized (accounts) {
117  return accounts.get(account).keySet();
118  }
119  }
120 
121  public List<Long> getArtifactIds(String account, String folder) {
122  synchronized (accounts) {
123  return accounts.get(account).get(folder);
124  }
125  }
126 
127  @SuppressWarnings("deprecation")
128  public void update() {
129  synchronized (accounts) {
130  accounts.clear();
131  }
132  if (skCase == null) {
133  return;
134  }
135 
136  int artId = BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID();
137  int pathAttrId = BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH.getTypeID();
138  String query = "SELECT value_text,blackboard_attributes.artifact_id,attribute_type_id " //NON-NLS
139  + "FROM blackboard_attributes,blackboard_artifacts WHERE " //NON-NLS
140  + "attribute_type_id=" + pathAttrId //NON-NLS
141  + " AND blackboard_attributes.artifact_id=blackboard_artifacts.artifact_id" //NON-NLS
142  + " AND blackboard_artifacts.artifact_type_id=" + artId; //NON-NLS
143 
144  try (CaseDbQuery dbQuery = skCase.executeQuery(query)) {
145  ResultSet resultSet = dbQuery.getResultSet();
146  synchronized (accounts) {
147  while (resultSet.next()) {
148  final String path = resultSet.getString("value_text"); //NON-NLS
149  final long artifactId = resultSet.getLong("artifact_id"); //NON-NLS
150  final Map<String, String> parsedPath = parsePath(path);
151  final String account = parsedPath.get(MAIL_ACCOUNT);
152  final String folder = parsedPath.get(MAIL_FOLDER);
153 
154  Map<String, List<Long>> folders = accounts.get(account);
155  if (folders == null) {
156  folders = new LinkedHashMap<>();
157  accounts.put(account, folders);
158  }
159  List<Long> messages = folders.get(folder);
160  if (messages == null) {
161  messages = new ArrayList<>();
162  folders.put(folder, messages);
163  }
164  messages.add(artifactId);
165  }
166  }
167  } catch (TskCoreException | SQLException ex) {
168  logger.log(Level.WARNING, "Cannot initialize email extraction: ", ex); //NON-NLS
169  }
170  setChanged();
171  notifyObservers();
172  }
173  }
174 
179  public class RootNode extends DisplayableItemNode {
180 
181  public RootNode() {
182  super(Children.create(new AccountFactory(), true), Lookups.singleton(DISPLAY_NAME));
183  super.setName(LABEL_NAME);
184  super.setDisplayName(DISPLAY_NAME);
185  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/mail-icon-16.png"); //NON-NLS
186  emailResults.update();
187  }
188 
189  @Override
190  public boolean isLeafTypeNode() {
191  return false;
192  }
193 
194  @Override
195  public <T> T accept(DisplayableItemNodeVisitor<T> v) {
196  return v.visit(this);
197  }
198 
199  @Override
200  protected Sheet createSheet() {
201  Sheet s = super.createSheet();
202  Sheet.Set ss = s.get(Sheet.PROPERTIES);
203  if (ss == null) {
204  ss = Sheet.createPropertiesSet();
205  s.put(ss);
206  }
207 
208  ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "EmailExtracted.createSheet.name.name"),
209  NbBundle.getMessage(this.getClass(), "EmailExtracted.createSheet.name.displayName"),
210  NbBundle.getMessage(this.getClass(), "EmailExtracted.createSheet.name.desc"),
211  getName()));
212 
213  return s;
214  }
215 
216  @Override
217  public String getItemType() {
218  return getClass().getName();
219  }
220  }
221 
225  private class AccountFactory extends ChildFactory.Detachable<String> implements Observer {
226 
227  /*
228  * The pcl is in the class because it has the easiest mechanisms to add
229  * and remove itself during its life cycles.
230  */
231  private final PropertyChangeListener pcl = new PropertyChangeListener() {
232  @Override
233  public void propertyChange(PropertyChangeEvent evt) {
234  String eventType = evt.getPropertyName();
235  if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
242  try {
250  ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
251  if (null != eventData && eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID()) {
252  emailResults.update();
253  }
254  } catch (IllegalStateException notUsed) {
258  }
259  } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
260  || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
267  try {
269  emailResults.update();
270  } catch (IllegalStateException notUsed) {
274  }
275  } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
276  // case was closed. Remove listeners so that we don't get called with a stale case handle
277  if (evt.getNewValue() == null) {
278  removeNotify();
279  skCase = null;
280  }
281  }
282  }
283  };
284 
285  @Override
286  protected void addNotify() {
290  emailResults.update();
291  emailResults.addObserver(this);
292  }
293 
294  @Override
295  protected void removeNotify() {
299  emailResults.deleteObserver(this);
300  }
301 
302  @Override
303  protected boolean createKeys(List<String> list) {
304  list.addAll(emailResults.getAccounts());
305  return true;
306  }
307 
308  @Override
309  protected Node createNodeForKey(String key) {
310  return new AccountNode(key);
311  }
312 
313  @Override
314  public void update(Observable o, Object arg) {
315  refresh(true);
316  }
317  }
318 
322  public class AccountNode extends DisplayableItemNode implements Observer {
323 
324  private final String accountName;
325 
326  public AccountNode(String accountName) {
327  super(Children.create(new FolderFactory(accountName), true), Lookups.singleton(accountName));
328  super.setName(accountName);
329  this.accountName = accountName;
330  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/account-icon-16.png"); //NON-NLS
332  emailResults.addObserver(this);
333  }
334 
335  private void updateDisplayName() {
336  super.setDisplayName(accountName + " (" + emailResults.getFolders(accountName) + ")");
337  }
338 
339  @Override
340  protected Sheet createSheet() {
341  Sheet s = super.createSheet();
342  Sheet.Set ss = s.get(Sheet.PROPERTIES);
343  if (ss == null) {
344  ss = Sheet.createPropertiesSet();
345  s.put(ss);
346  }
347 
348  ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "EmailExtracted.createSheet.name.name"),
349  NbBundle.getMessage(this.getClass(), "EmailExtracted.createSheet.name.displayName"),
350  NbBundle.getMessage(this.getClass(), "EmailExtracted.createSheet.name.desc"),
351  getName()));
352 
353  return s;
354  }
355 
356  @Override
357  public boolean isLeafTypeNode() {
358  return false;
359  }
360 
361  @Override
362  public <T> T accept(DisplayableItemNodeVisitor<T> v) {
363  return v.visit(this);
364  }
365 
366  @Override
367  public void update(Observable o, Object arg) {
369  }
370 
371  @Override
372  public String getItemType() {
373  return getClass().getName();
374  }
375  }
376 
380  private class FolderFactory extends ChildFactory<String> implements Observer {
381 
382  private final String accountName;
383 
384  private FolderFactory(String accountName) {
385  super();
386  this.accountName = accountName;
387  emailResults.addObserver(this);
388  }
389 
390  @Override
391  protected boolean createKeys(List<String> list) {
392  list.addAll(emailResults.getFolders(accountName));
393  return true;
394  }
395 
396  @Override
397  protected Node createNodeForKey(String folderName) {
398  return new FolderNode(accountName, folderName);
399  }
400 
401  @Override
402  public void update(Observable o, Object arg) {
403  refresh(true);
404  }
405  }
406 
410  public class FolderNode extends DisplayableItemNode implements Observer {
411 
412  private final String accountName;
413  private final String folderName;
414 
415  public FolderNode(String accountName, String folderName) {
416  super(Children.create(new MessageFactory(accountName, folderName), true), Lookups.singleton(accountName));
417  super.setName(folderName);
418  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/folder-icon-16.png"); //NON-NLS
419  this.accountName = accountName;
420  this.folderName = folderName;
422  emailResults.addObserver(this);
423  }
424 
425  private void updateDisplayName() {
426  super.setDisplayName(folderName + " (" + emailResults.getArtifactIds(accountName, folderName).size() + ")");
427 
428  }
429 
430  @Override
431  public boolean isLeafTypeNode() {
432  return false;
433  }
434 
435  @Override
436  protected Sheet createSheet() {
437  Sheet s = super.createSheet();
438  Sheet.Set ss = s.get(Sheet.PROPERTIES);
439  if (ss == null) {
440  ss = Sheet.createPropertiesSet();
441  s.put(ss);
442  }
443 
444  ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "EmailExtracted.createSheet.name.name"),
445  NbBundle.getMessage(this.getClass(), "EmailExtracted.createSheet.name.displayName"),
446  NbBundle.getMessage(this.getClass(), "EmailExtracted.createSheet.name.desc"),
447  getName()));
448 
449  return s;
450  }
451 
452  @Override
453  public <T> T accept(DisplayableItemNodeVisitor<T> v) {
454  return v.visit(this);
455  }
456 
457  @Override
458  public void update(Observable o, Object arg) {
460  }
461 
462  @Override
463  public String getItemType() {
464  return getClass().getName();
465  }
466  }
467 
471  private class MessageFactory extends ChildFactory<Long> implements Observer {
472 
473  private final String accountName;
474  private final String folderName;
475 
476  private MessageFactory(String accountName, String folderName) {
477  super();
478  this.accountName = accountName;
479  this.folderName = folderName;
480  emailResults.addObserver(this);
481  }
482 
483  @Override
484  protected boolean createKeys(List<Long> list) {
485  list.addAll(emailResults.getArtifactIds(accountName, folderName));
486  return true;
487  }
488 
489  @Override
490  protected Node createNodeForKey(Long artifactId) {
491  if (skCase == null) {
492  return null;
493  }
494  try {
495  BlackboardArtifact artifact = skCase.getBlackboardArtifact(artifactId);
496  return new BlackboardArtifactNode(artifact);
497  } catch (TskCoreException ex) {
498  logger.log(Level.WARNING, "Error creating mail messages nodes", ex); //NON-NLS
499  }
500  return null;
501  }
502 
503  @Override
504  public void update(Observable o, Object arg) {
505  refresh(true);
506  }
507  }
508 }
static final Map< String, String > parsePath(String path)
BlackboardArtifact.Type getBlackboardArtifactType()
void removeIngestModuleEventListener(final PropertyChangeListener listener)
static synchronized IngestManager getInstance()
void removeIngestJobEventListener(final PropertyChangeListener listener)
void addIngestJobEventListener(final PropertyChangeListener listener)
List< Long > getArtifactIds(String account, String folder)
FolderNode(String accountName, String folderName)
void addIngestModuleEventListener(final PropertyChangeListener listener)
synchronized static Logger getLogger(String name)
Definition: Logger.java:161
static void addEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:395
static void removeEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:440
final Map< String, Map< String, List< Long > > > accounts

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.