Autopsy  4.7.0
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-2018 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;
46 import org.sleuthkit.datamodel.BlackboardArtifact;
47 import org.sleuthkit.datamodel.BlackboardAttribute;
48 import org.sleuthkit.datamodel.SleuthkitCase;
49 import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery;
50 import org.sleuthkit.datamodel.TskCoreException;
51 
58 public class EmailExtracted implements AutopsyVisitableItem {
59 
60  private static final String LABEL_NAME = BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getLabel();
61  private static final String DISPLAY_NAME = BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getDisplayName();
62  private static final Logger logger = Logger.getLogger(EmailExtracted.class.getName());
63  private static final String MAIL_ACCOUNT = NbBundle.getMessage(EmailExtracted.class, "EmailExtracted.mailAccount.text");
64  private static final String MAIL_FOLDER = NbBundle.getMessage(EmailExtracted.class, "EmailExtracted.mailFolder.text");
65  private static final String MAIL_PATH_SEPARATOR = "/";
75  public static final Map<String, String> parsePath(String path) {
76  Map<String, String> parsed = new HashMap<>();
77  String[] split = path.split(MAIL_PATH_SEPARATOR);
78  if (split.length < 4) {
79  parsed.put(MAIL_ACCOUNT, NbBundle.getMessage(EmailExtracted.class, "EmailExtracted.defaultAcct.text"));
80  parsed.put(MAIL_FOLDER, NbBundle.getMessage(EmailExtracted.class, "EmailExtracted.defaultFolder.text"));
81  return parsed;
82  }
83  parsed.put(MAIL_ACCOUNT, split[2]);
84  parsed.put(MAIL_FOLDER, split[3]);
85  return parsed;
86  }
87  private SleuthkitCase skCase;
88  private final EmailResults emailResults;
89 
90 
91  public EmailExtracted(SleuthkitCase skCase) {
92  this.skCase = skCase;
93  emailResults = new EmailResults();
94  }
95 
96 
97  @Override
98  public <T> T accept(AutopsyItemVisitor<T> visitor) {
99  return visitor.visit(this);
100  }
101  private final class EmailResults extends Observable {
102 
103  // NOTE: the map can be accessed by multiple worker threads and needs to be synchronized
104  private final Map<String, Map<String, List<Long>>> accounts = new LinkedHashMap<>();
105 
106  EmailResults() {
107  update();
108  }
109 
110  public Set<String> getAccounts() {
111  synchronized (accounts) {
112  return accounts.keySet();
113  }
114  }
115 
116  public Set<String> getFolders(String account) {
117  synchronized (accounts) {
118  return accounts.get(account).keySet();
119  }
120  }
121 
122  public List<Long> getArtifactIds(String account, String folder) {
123  synchronized (accounts) {
124  return accounts.get(account).get(folder);
125  }
126  }
127 
128  @SuppressWarnings("deprecation")
129  public void update() {
130  synchronized (accounts) {
131  accounts.clear();
132  }
133  if (skCase == null) {
134  return;
135  }
136 
137  int artId = BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID();
138  int pathAttrId = BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH.getTypeID();
139  String query = "SELECT value_text,blackboard_attributes.artifact_id,attribute_type_id " //NON-NLS
140  + "FROM blackboard_attributes,blackboard_artifacts WHERE " //NON-NLS
141  + "attribute_type_id=" + pathAttrId //NON-NLS
142  + " AND blackboard_attributes.artifact_id=blackboard_artifacts.artifact_id" //NON-NLS
143  + " AND blackboard_artifacts.artifact_type_id=" + artId; //NON-NLS
144 
145  try (CaseDbQuery dbQuery = skCase.executeQuery(query)) {
146  ResultSet resultSet = dbQuery.getResultSet();
147  synchronized (accounts) {
148  while (resultSet.next()) {
149  final String path = resultSet.getString("value_text"); //NON-NLS
150  final long artifactId = resultSet.getLong("artifact_id"); //NON-NLS
151  final Map<String, String> parsedPath = parsePath(path);
152  final String account = parsedPath.get(MAIL_ACCOUNT);
153  final String folder = parsedPath.get(MAIL_FOLDER);
154 
155  Map<String, List<Long>> folders = accounts.get(account);
156  if (folders == null) {
157  folders = new LinkedHashMap<>();
158  accounts.put(account, folders);
159  }
160  List<Long> messages = folders.get(folder);
161  if (messages == null) {
162  messages = new ArrayList<>();
163  folders.put(folder, messages);
164  }
165  messages.add(artifactId);
166  }
167  }
168  } catch (TskCoreException | SQLException ex) {
169  logger.log(Level.WARNING, "Cannot initialize email extraction: ", ex); //NON-NLS
170  }
171  setChanged();
172  notifyObservers();
173  }
174  }
175 
180  public class RootNode extends DisplayableItemNode {
181 
182  public RootNode() {
183  super(Children.create(new AccountFactory(), true), Lookups.singleton(DISPLAY_NAME));
184  super.setName(LABEL_NAME);
185  super.setDisplayName(DISPLAY_NAME);
186  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/mail-icon-16.png"); //NON-NLS
187  emailResults.update();
188  }
189 
190  @Override
191  public boolean isLeafTypeNode() {
192  return false;
193  }
194 
195  @Override
196  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
197  return visitor.visit(this);
198  }
199 
200  @Override
201  protected Sheet createSheet() {
202  Sheet sheet = super.createSheet();
203  Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
204  if (sheetSet == null) {
205  sheetSet = Sheet.createPropertiesSet();
206  sheet.put(sheetSet);
207  }
208 
209  sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "EmailExtracted.createSheet.name.name"),
210  NbBundle.getMessage(this.getClass(), "EmailExtracted.createSheet.name.displayName"),
211  NbBundle.getMessage(this.getClass(), "EmailExtracted.createSheet.name.desc"),
212  getName()));
213 
214  return sheet;
215  }
216 
217  @Override
218  public String getItemType() {
219  return getClass().getName();
220  }
221  }
222 
226  private class AccountFactory extends ChildFactory.Detachable<String> implements Observer {
227 
228  /*
229  * The pcl is in the class because it has the easiest mechanisms to add
230  * and remove itself during its life cycles.
231  */
232  private final PropertyChangeListener pcl = new PropertyChangeListener() {
233  @Override
234  public void propertyChange(PropertyChangeEvent evt) {
235  String eventType = evt.getPropertyName();
236  if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
243  try {
251  ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
252  if (null != eventData && eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID()) {
253  emailResults.update();
254  }
255  } catch (NoCurrentCaseException notUsed) {
259  }
260  } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
261  || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
268  try {
270  emailResults.update();
271  } catch (NoCurrentCaseException notUsed) {
275  }
276  } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
277  // case was closed. Remove listeners so that we don't get called with a stale case handle
278  if (evt.getNewValue() == null) {
279  removeNotify();
280  skCase = null;
281  }
282  }
283  }
284  };
285 
286  @Override
287  protected void addNotify() {
291  emailResults.update();
292  emailResults.addObserver(this);
293  }
294 
295  @Override
296  protected void removeNotify() {
300  emailResults.deleteObserver(this);
301  }
302 
303  @Override
304  protected boolean createKeys(List<String> list) {
305  list.addAll(emailResults.getAccounts());
306  return true;
307  }
308 
309  @Override
310  protected Node createNodeForKey(String key) {
311  return new AccountNode(key);
312  }
313 
314  @Override
315  public void update(Observable o, Object arg) {
316  refresh(true);
317  }
318  }
319 
323  public class AccountNode extends DisplayableItemNode implements Observer {
324 
325  private final String accountName;
326 
327  public AccountNode(String accountName) {
328  super(Children.create(new FolderFactory(accountName), true), Lookups.singleton(accountName));
329  super.setName(accountName);
330  this.accountName = accountName;
331  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/account-icon-16.png"); //NON-NLS
333  emailResults.addObserver(this);
334  }
335 
336  private void updateDisplayName() {
337  super.setDisplayName(accountName + " (" + emailResults.getFolders(accountName) + ")");
338  }
339 
340  @Override
341  protected Sheet createSheet() {
342  Sheet sheet = super.createSheet();
343  Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
344  if (sheetSet == null) {
345  sheetSet = Sheet.createPropertiesSet();
346  sheet.put(sheetSet);
347  }
348 
349  sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "EmailExtracted.createSheet.name.name"),
350  NbBundle.getMessage(this.getClass(), "EmailExtracted.createSheet.name.displayName"),
351  NbBundle.getMessage(this.getClass(), "EmailExtracted.createSheet.name.desc"),
352  getName()));
353 
354  return sheet;
355  }
356 
357  @Override
358  public boolean isLeafTypeNode() {
359  return false;
360  }
361 
362  @Override
363  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
364  return visitor.visit(this);
365  }
366 
367  @Override
368  public void update(Observable o, Object arg) {
370  }
371 
372  @Override
373  public String getItemType() {
374  return getClass().getName();
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 false;
434  }
435 
436  @Override
437  protected Sheet createSheet() {
438  Sheet sheet = super.createSheet();
439  Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
440  if (sheetSet == null) {
441  sheetSet = Sheet.createPropertiesSet();
442  sheet.put(sheetSet);
443  }
444 
445  sheetSet.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 sheet;
451  }
452 
453  @Override
454  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
455  return visitor.visit(this);
456  }
457 
458  @Override
459  public void update(Observable o, Object arg) {
461  }
462 
463  @Override
464  public String getItemType() {
465  return getClass().getName();
466  }
467  }
468 
472  private class MessageFactory extends ChildFactory<Long> implements Observer {
473 
474  private final String accountName;
475  private final String folderName;
476 
477  private MessageFactory(String accountName, String folderName) {
478  super();
479  this.accountName = accountName;
480  this.folderName = folderName;
481  emailResults.addObserver(this);
482  }
483 
484  @Override
485  protected boolean createKeys(List<Long> list) {
486  list.addAll(emailResults.getArtifactIds(accountName, folderName));
487  return true;
488  }
489 
490  @Override
491  protected Node createNodeForKey(Long artifactId) {
492  if (skCase == null) {
493  return null;
494  }
495  try {
496  BlackboardArtifact artifact = skCase.getBlackboardArtifact(artifactId);
497  return new BlackboardArtifactNode(artifact);
498  } catch (TskCoreException ex) {
499  logger.log(Level.WARNING, "Error creating mail messages nodes", ex); //NON-NLS
500  }
501  return null;
502  }
503 
504  @Override
505  public void update(Observable o, Object arg) {
506  refresh(true);
507  }
508  }
509 }
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:124
static void addEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:420
static void removeEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:465
final Map< String, Map< String, List< Long > > > accounts

Copyright © 2012-2016 Basis Technology. Generated on: Mon Jun 18 2018
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.