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

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.