Autopsy  4.9.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-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.Objects;
32 import java.util.Observable;
33 import java.util.Observer;
34 import java.util.Set;
35 import java.util.logging.Level;
36 import org.openide.nodes.ChildFactory;
37 import org.openide.nodes.Children;
38 import org.openide.nodes.Node;
39 import org.openide.nodes.Sheet;
40 import org.openide.util.NbBundle;
41 import org.openide.util.lookup.Lookups;
49 import org.sleuthkit.datamodel.BlackboardArtifact;
50 import org.sleuthkit.datamodel.BlackboardAttribute;
51 import org.sleuthkit.datamodel.SleuthkitCase;
52 import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery;
53 import org.sleuthkit.datamodel.TskCoreException;
54 
61 public class EmailExtracted implements AutopsyVisitableItem {
62 
63  private static final String LABEL_NAME = BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getLabel();
64  private static final String DISPLAY_NAME = BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getDisplayName();
65  private static final Logger logger = Logger.getLogger(EmailExtracted.class.getName());
66  private static final String MAIL_ACCOUNT = NbBundle.getMessage(EmailExtracted.class, "EmailExtracted.mailAccount.text");
67  private static final String MAIL_FOLDER = NbBundle.getMessage(EmailExtracted.class, "EmailExtracted.mailFolder.text");
68  private static final String MAIL_PATH_SEPARATOR = "/";
78  public static final Map<String, String> parsePath(String path) {
79  Map<String, String> parsed = new HashMap<>();
80  String[] split = path.split(MAIL_PATH_SEPARATOR);
81  if (split.length < 4) {
82  parsed.put(MAIL_ACCOUNT, NbBundle.getMessage(EmailExtracted.class, "EmailExtracted.defaultAcct.text"));
83  parsed.put(MAIL_FOLDER, NbBundle.getMessage(EmailExtracted.class, "EmailExtracted.defaultFolder.text"));
84  return parsed;
85  }
86  parsed.put(MAIL_ACCOUNT, split[2]);
87  parsed.put(MAIL_FOLDER, split[3]);
88  return parsed;
89  }
90  private SleuthkitCase skCase;
91  private final EmailResults emailResults;
92  private final long datasourceObjId;
93 
94 
95 
101  public EmailExtracted(SleuthkitCase skCase) {
102  this(skCase, 0);
103  }
104 
112  public EmailExtracted(SleuthkitCase skCase, long objId) {
113  this.skCase = skCase;
114  this.datasourceObjId = objId;
115  emailResults = new EmailResults();
116  }
117 
118 
119  @Override
120  public <T> T accept(AutopsyItemVisitor<T> visitor) {
121  return visitor.visit(this);
122  }
123  private final class EmailResults extends Observable {
124 
125  // NOTE: the map can be accessed by multiple worker threads and needs to be synchronized
126  private final Map<String, Map<String, List<Long>>> accounts = new LinkedHashMap<>();
127 
128  EmailResults() {
129  update();
130  }
131 
132  public Set<String> getAccounts() {
133  synchronized (accounts) {
134  return accounts.keySet();
135  }
136  }
137 
138  public Set<String> getFolders(String account) {
139  synchronized (accounts) {
140  return accounts.get(account).keySet();
141  }
142  }
143 
144  public List<Long> getArtifactIds(String account, String folder) {
145  synchronized (accounts) {
146  return accounts.get(account).get(folder);
147  }
148  }
149 
150  @SuppressWarnings("deprecation")
151  public void update() {
152  synchronized (accounts) {
153  accounts.clear();
154  }
155  if (skCase == null) {
156  return;
157  }
158 
159  int artId = BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID();
160  int pathAttrId = BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH.getTypeID();
161  String query = "SELECT value_text,blackboard_attributes.artifact_id,attribute_type_id " //NON-NLS
162  + "FROM blackboard_attributes,blackboard_artifacts WHERE " //NON-NLS
163  + "attribute_type_id=" + pathAttrId //NON-NLS
164  + " AND blackboard_attributes.artifact_id=blackboard_artifacts.artifact_id" //NON-NLS
165  + " AND blackboard_artifacts.artifact_type_id=" + artId; //NON-NLS
166  if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) {
167  query += " AND blackboard_artifacts.data_source_obj_id = " + datasourceObjId;
168  }
169 
170  try (CaseDbQuery dbQuery = skCase.executeQuery(query)) {
171  ResultSet resultSet = dbQuery.getResultSet();
172  synchronized (accounts) {
173  while (resultSet.next()) {
174  final String path = resultSet.getString("value_text"); //NON-NLS
175  final long artifactId = resultSet.getLong("artifact_id"); //NON-NLS
176  final Map<String, String> parsedPath = parsePath(path);
177  final String account = parsedPath.get(MAIL_ACCOUNT);
178  final String folder = parsedPath.get(MAIL_FOLDER);
179 
180  Map<String, List<Long>> folders = accounts.get(account);
181  if (folders == null) {
182  folders = new LinkedHashMap<>();
183  accounts.put(account, folders);
184  }
185  List<Long> messages = folders.get(folder);
186  if (messages == null) {
187  messages = new ArrayList<>();
188  folders.put(folder, messages);
189  }
190  messages.add(artifactId);
191  }
192  }
193  } catch (TskCoreException | SQLException ex) {
194  logger.log(Level.WARNING, "Cannot initialize email extraction: ", ex); //NON-NLS
195  }
196  setChanged();
197  notifyObservers();
198  }
199  }
200 
205  public class RootNode extends DisplayableItemNode {
206 
207  public RootNode() {
208  super(Children.create(new AccountFactory(), true), Lookups.singleton(DISPLAY_NAME));
209  super.setName(LABEL_NAME);
210  super.setDisplayName(DISPLAY_NAME);
211  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/mail-icon-16.png"); //NON-NLS
212  emailResults.update();
213  }
214 
215  @Override
216  public boolean isLeafTypeNode() {
217  return false;
218  }
219 
220  @Override
221  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
222  return visitor.visit(this);
223  }
224 
225  @Override
226  protected Sheet createSheet() {
227  Sheet sheet = super.createSheet();
228  Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
229  if (sheetSet == null) {
230  sheetSet = Sheet.createPropertiesSet();
231  sheet.put(sheetSet);
232  }
233 
234  sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "EmailExtracted.createSheet.name.name"),
235  NbBundle.getMessage(this.getClass(), "EmailExtracted.createSheet.name.displayName"),
236  NbBundle.getMessage(this.getClass(), "EmailExtracted.createSheet.name.desc"),
237  getName()));
238 
239  return sheet;
240  }
241 
242  @Override
243  public String getItemType() {
244  return getClass().getName();
245  }
246  }
247 
251  private class AccountFactory extends ChildFactory.Detachable<String> implements Observer {
252 
253  /*
254  * The pcl is in the class because it has the easiest mechanisms to add
255  * and remove itself during its life cycles.
256  */
257  private final PropertyChangeListener pcl = new PropertyChangeListener() {
258  @Override
259  public void propertyChange(PropertyChangeEvent evt) {
260  String eventType = evt.getPropertyName();
261  if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
268  try {
276  ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
277  if (null != eventData && eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID()) {
278  emailResults.update();
279  }
280  } catch (NoCurrentCaseException notUsed) {
284  }
285  } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
286  || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
293  try {
295  emailResults.update();
296  } catch (NoCurrentCaseException notUsed) {
300  }
301  } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
302  // case was closed. Remove listeners so that we don't get called with a stale case handle
303  if (evt.getNewValue() == null) {
304  removeNotify();
305  skCase = null;
306  }
307  }
308  }
309  };
310 
311  @Override
312  protected void addNotify() {
316  emailResults.update();
317  emailResults.addObserver(this);
318  }
319 
320  @Override
321  protected void removeNotify() {
325  emailResults.deleteObserver(this);
326  }
327 
328  @Override
329  protected boolean createKeys(List<String> list) {
330  list.addAll(emailResults.getAccounts());
331  return true;
332  }
333 
334  @Override
335  protected Node createNodeForKey(String key) {
336  return new AccountNode(key);
337  }
338 
339  @Override
340  public void update(Observable o, Object arg) {
341  refresh(true);
342  }
343  }
344 
348  public class AccountNode extends DisplayableItemNode implements Observer {
349 
350  private final String accountName;
351 
352  public AccountNode(String accountName) {
353  super(Children.create(new FolderFactory(accountName), true), Lookups.singleton(accountName));
354  super.setName(accountName);
355  this.accountName = accountName;
356  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/account-icon-16.png"); //NON-NLS
358  emailResults.addObserver(this);
359  }
360 
361  private void updateDisplayName() {
362  super.setDisplayName(accountName + " (" + emailResults.getFolders(accountName) + ")");
363  }
364 
365  @Override
366  protected Sheet createSheet() {
367  Sheet sheet = super.createSheet();
368  Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
369  if (sheetSet == null) {
370  sheetSet = Sheet.createPropertiesSet();
371  sheet.put(sheetSet);
372  }
373 
374  sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "EmailExtracted.createSheet.name.name"),
375  NbBundle.getMessage(this.getClass(), "EmailExtracted.createSheet.name.displayName"),
376  NbBundle.getMessage(this.getClass(), "EmailExtracted.createSheet.name.desc"),
377  getName()));
378 
379  return sheet;
380  }
381 
382  @Override
383  public boolean isLeafTypeNode() {
384  return false;
385  }
386 
387  @Override
388  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
389  return visitor.visit(this);
390  }
391 
392  @Override
393  public void update(Observable o, Object arg) {
395  }
396 
397  @Override
398  public String getItemType() {
399  return getClass().getName();
400  }
401  }
402 
406  private class FolderFactory extends ChildFactory<String> implements Observer {
407 
408  private final String accountName;
409 
410  private FolderFactory(String accountName) {
411  super();
412  this.accountName = accountName;
413  emailResults.addObserver(this);
414  }
415 
416  @Override
417  protected boolean createKeys(List<String> list) {
418  list.addAll(emailResults.getFolders(accountName));
419  return true;
420  }
421 
422  @Override
423  protected Node createNodeForKey(String folderName) {
424  return new FolderNode(accountName, folderName);
425  }
426 
427  @Override
428  public void update(Observable o, Object arg) {
429  refresh(true);
430  }
431  }
432 
436  public class FolderNode extends DisplayableItemNode implements Observer {
437 
438  private final String accountName;
439  private final String folderName;
440 
441  public FolderNode(String accountName, String folderName) {
442  super(Children.create(new MessageFactory(accountName, folderName), true), Lookups.singleton(accountName));
443  super.setName(folderName);
444  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/folder-icon-16.png"); //NON-NLS
445  this.accountName = accountName;
446  this.folderName = folderName;
448  emailResults.addObserver(this);
449  }
450 
451  private void updateDisplayName() {
452  super.setDisplayName(folderName + " (" + emailResults.getArtifactIds(accountName, folderName).size() + ")");
453 
454  }
455 
456  @Override
457  public boolean isLeafTypeNode() {
458  return false;
459  }
460 
461  @Override
462  protected Sheet createSheet() {
463  Sheet sheet = super.createSheet();
464  Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
465  if (sheetSet == null) {
466  sheetSet = Sheet.createPropertiesSet();
467  sheet.put(sheetSet);
468  }
469 
470  sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "EmailExtracted.createSheet.name.name"),
471  NbBundle.getMessage(this.getClass(), "EmailExtracted.createSheet.name.displayName"),
472  NbBundle.getMessage(this.getClass(), "EmailExtracted.createSheet.name.desc"),
473  getName()));
474 
475  return sheet;
476  }
477 
478  @Override
479  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
480  return visitor.visit(this);
481  }
482 
483  @Override
484  public void update(Observable o, Object arg) {
486  }
487 
488  @Override
489  public String getItemType() {
490  return getClass().getName();
491  }
492  }
493 
497  private class MessageFactory extends ChildFactory<Long> implements Observer {
498 
499  private final String accountName;
500  private final String folderName;
501 
502  private MessageFactory(String accountName, String folderName) {
503  super();
504  this.accountName = accountName;
505  this.folderName = folderName;
506  emailResults.addObserver(this);
507  }
508 
509  @Override
510  protected boolean createKeys(List<Long> list) {
511  list.addAll(emailResults.getArtifactIds(accountName, folderName));
512  return true;
513  }
514 
515  @Override
516  protected Node createNodeForKey(Long artifactId) {
517  if (skCase == null) {
518  return null;
519  }
520  try {
521  BlackboardArtifact artifact = skCase.getBlackboardArtifact(artifactId);
522  return new BlackboardArtifactNode(artifact);
523  } catch (TskCoreException ex) {
524  logger.log(Level.WARNING, "Error creating mail messages nodes", ex); //NON-NLS
525  }
526  return null;
527  }
528 
529  @Override
530  public void update(Observable o, Object arg) {
531  refresh(true);
532  }
533  }
534 }
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)
EmailExtracted(SleuthkitCase skCase, long objId)
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:429
static void removeEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:474
final Map< String, Map< String, List< Long > > > accounts

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