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