Autopsy  4.13.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-2019 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 = "/";
68 
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 filteringDSObjId; // 0 if not filtering/grouping by data source
93 
99  public EmailExtracted(SleuthkitCase skCase) {
100  this(skCase, 0);
101  }
102 
110  public EmailExtracted(SleuthkitCase skCase, long objId) {
111  this.skCase = skCase;
112  this.filteringDSObjId = objId;
113  emailResults = new EmailResults();
114  }
115 
116  @Override
117  public <T> T accept(AutopsyItemVisitor<T> visitor) {
118  return visitor.visit(this);
119  }
120 
121  private final class EmailResults extends Observable {
122 
123  // NOTE: the map can be accessed by multiple worker threads and needs to be synchronized
124  private final Map<String, Map<String, List<Long>>> accounts = new LinkedHashMap<>();
125 
126  EmailResults() {
127  update();
128  }
129 
130  public Set<String> getAccounts() {
131  synchronized (accounts) {
132  return accounts.keySet();
133  }
134  }
135 
136  public Set<String> getFolders(String account) {
137  synchronized (accounts) {
138  return accounts.get(account).keySet();
139  }
140  }
141 
142  public List<Long> getArtifactIds(String account, String folder) {
143  synchronized (accounts) {
144  return accounts.get(account).get(folder);
145  }
146  }
147 
148  @SuppressWarnings("deprecation")
149  public void update() {
150  synchronized (accounts) {
151  accounts.clear();
152  }
153  if (skCase == null) {
154  return;
155  }
156 
157  int artId = BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID();
158  int pathAttrId = BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH.getTypeID();
159  String query = "SELECT value_text,blackboard_attributes.artifact_id,attribute_type_id " //NON-NLS
160  + "FROM blackboard_attributes,blackboard_artifacts WHERE " //NON-NLS
161  + "attribute_type_id=" + pathAttrId //NON-NLS
162  + " AND blackboard_attributes.artifact_id=blackboard_artifacts.artifact_id" //NON-NLS
163  + " AND blackboard_artifacts.artifact_type_id=" + artId; //NON-NLS
164  if (filteringDSObjId > 0) {
165  query += " AND blackboard_artifacts.data_source_obj_id = " + filteringDSObjId;
166  }
167 
168  try (CaseDbQuery dbQuery = skCase.executeQuery(query)) {
169  ResultSet resultSet = dbQuery.getResultSet();
170  synchronized (accounts) {
171  while (resultSet.next()) {
172  final String path = resultSet.getString("value_text"); //NON-NLS
173  final long artifactId = resultSet.getLong("artifact_id"); //NON-NLS
174  final Map<String, String> parsedPath = parsePath(path);
175  final String account = parsedPath.get(MAIL_ACCOUNT);
176  final String folder = parsedPath.get(MAIL_FOLDER);
177 
178  Map<String, List<Long>> folders = accounts.get(account);
179  if (folders == null) {
180  folders = new LinkedHashMap<>();
181  accounts.put(account, folders);
182  }
183  List<Long> messages = folders.get(folder);
184  if (messages == null) {
185  messages = new ArrayList<>();
186  folders.put(folder, messages);
187  }
188  messages.add(artifactId);
189  }
190  }
191  } catch (TskCoreException | SQLException ex) {
192  logger.log(Level.WARNING, "Cannot initialize email extraction: ", ex); //NON-NLS
193  }
194  setChanged();
195  notifyObservers();
196  }
197  }
198 
203  public class RootNode extends DisplayableItemNode {
204 
205  public RootNode() {
206  super(Children.create(new AccountFactory(), true), Lookups.singleton(DISPLAY_NAME));
207  super.setName(LABEL_NAME);
208  super.setDisplayName(DISPLAY_NAME);
209  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/mail-icon-16.png"); //NON-NLS
210  emailResults.update();
211  }
212 
213  @Override
214  public boolean isLeafTypeNode() {
215  return false;
216  }
217 
218  @Override
219  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
220  return visitor.visit(this);
221  }
222 
223  @Override
224  protected Sheet createSheet() {
225  Sheet sheet = super.createSheet();
226  Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
227  if (sheetSet == null) {
228  sheetSet = Sheet.createPropertiesSet();
229  sheet.put(sheetSet);
230  }
231 
232  sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "EmailExtracted.createSheet.name.name"),
233  NbBundle.getMessage(this.getClass(), "EmailExtracted.createSheet.name.displayName"),
234  NbBundle.getMessage(this.getClass(), "EmailExtracted.createSheet.name.desc"),
235  getName()));
236 
237  return sheet;
238  }
239 
240  @Override
241  public String getItemType() {
242  return getClass().getName();
243  }
244  }
245 
249  private class AccountFactory extends ChildFactory.Detachable<String> implements Observer {
250 
251  /*
252  * The pcl is in the class because it has the easiest mechanisms to add
253  * and remove itself during its life cycles.
254  */
255  private final PropertyChangeListener pcl = new PropertyChangeListener() {
256  @Override
257  public void propertyChange(PropertyChangeEvent evt) {
258  String eventType = evt.getPropertyName();
259  if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
266  try {
274  ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
275  if (null != eventData && eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID()) {
276  emailResults.update();
277  }
278  } catch (NoCurrentCaseException notUsed) {
282  }
283  } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
284  || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
291  try {
293  emailResults.update();
294  } catch (NoCurrentCaseException notUsed) {
298  }
299  } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
300  // case was closed. Remove listeners so that we don't get called with a stale case handle
301  if (evt.getNewValue() == null) {
302  removeNotify();
303  skCase = null;
304  }
305  }
306  }
307  };
308 
309  @Override
310  protected void addNotify() {
314  emailResults.update();
315  emailResults.addObserver(this);
316  }
317 
318  @Override
319  protected void removeNotify() {
323  emailResults.deleteObserver(this);
324  }
325 
326  @Override
327  protected boolean createKeys(List<String> list) {
328  list.addAll(emailResults.getAccounts());
329  return true;
330  }
331 
332  @Override
333  protected Node createNodeForKey(String key) {
334  return new AccountNode(key);
335  }
336 
337  @Override
338  public void update(Observable o, Object arg) {
339  refresh(true);
340  }
341  }
342 
346  public class AccountNode extends DisplayableItemNode implements Observer {
347 
348  private final String accountName;
349 
350  public AccountNode(String accountName) {
351  super(Children.create(new FolderFactory(accountName), true), Lookups.singleton(accountName));
352  super.setName(accountName);
353  this.accountName = accountName;
354  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/account-icon-16.png"); //NON-NLS
356  emailResults.addObserver(this);
357  }
358 
359  private void updateDisplayName() {
360  super.setDisplayName(accountName + " (" + emailResults.getFolders(accountName) + ")");
361  }
362 
363  @Override
364  protected Sheet createSheet() {
365  Sheet sheet = super.createSheet();
366  Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
367  if (sheetSet == null) {
368  sheetSet = Sheet.createPropertiesSet();
369  sheet.put(sheetSet);
370  }
371 
372  sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "EmailExtracted.createSheet.name.name"),
373  NbBundle.getMessage(this.getClass(), "EmailExtracted.createSheet.name.displayName"),
374  NbBundle.getMessage(this.getClass(), "EmailExtracted.createSheet.name.desc"),
375  getName()));
376 
377  return sheet;
378  }
379 
380  @Override
381  public boolean isLeafTypeNode() {
382  return false;
383  }
384 
385  @Override
386  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
387  return visitor.visit(this);
388  }
389 
390  @Override
391  public void update(Observable o, Object arg) {
393  }
394 
395  @Override
396  public String getItemType() {
397  return getClass().getName();
398  }
399  }
400 
404  private class FolderFactory extends ChildFactory<String> implements Observer {
405 
406  private final String accountName;
407 
408  private FolderFactory(String accountName) {
409  super();
410  this.accountName = accountName;
411  emailResults.addObserver(this);
412  }
413 
414  @Override
415  protected boolean createKeys(List<String> list) {
416  list.addAll(emailResults.getFolders(accountName));
417  return true;
418  }
419 
420  @Override
421  protected Node createNodeForKey(String folderName) {
422  return new FolderNode(accountName, folderName);
423  }
424 
425  @Override
426  public void update(Observable o, Object arg) {
427  refresh(true);
428  }
429  }
430 
434  public class FolderNode extends DisplayableItemNode implements Observer {
435 
436  private final String accountName;
437  private final String folderName;
438 
439  public FolderNode(String accountName, String folderName) {
440  super(Children.create(new MessageFactory(accountName, folderName), true), Lookups.singleton(accountName));
441  super.setName(folderName);
442  this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/folder-icon-16.png"); //NON-NLS
443  this.accountName = accountName;
444  this.folderName = folderName;
446  emailResults.addObserver(this);
447  }
448 
449  private void updateDisplayName() {
450  super.setDisplayName(folderName + " (" + emailResults.getArtifactIds(accountName, folderName).size() + ")");
451 
452  }
453 
454  @Override
455  public boolean isLeafTypeNode() {
456  return false;
457  }
458 
459  @Override
460  protected Sheet createSheet() {
461  Sheet sheet = super.createSheet();
462  Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
463  if (sheetSet == null) {
464  sheetSet = Sheet.createPropertiesSet();
465  sheet.put(sheetSet);
466  }
467 
468  sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "EmailExtracted.createSheet.name.name"),
469  NbBundle.getMessage(this.getClass(), "EmailExtracted.createSheet.name.displayName"),
470  NbBundle.getMessage(this.getClass(), "EmailExtracted.createSheet.name.desc"),
471  getName()));
472 
473  return sheet;
474  }
475 
476  @Override
477  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
478  return visitor.visit(this);
479  }
480 
481  @Override
482  public void update(Observable o, Object arg) {
484  }
485 
486  @Override
487  public String getItemType() {
488  return getClass().getName();
489  }
490  }
491 
495  private class MessageFactory extends BaseChildFactory<BlackboardArtifact> implements Observer {
496 
497  private final String accountName;
498  private final String folderName;
499 
500  private MessageFactory(String accountName, String folderName) {
501  super(accountName + "_" + folderName);
502  this.accountName = accountName;
503  this.folderName = folderName;
504  emailResults.addObserver(this);
505  }
506 
507  @Override
508  protected Node createNodeForKey(BlackboardArtifact art) {
509  return new BlackboardArtifactNode(art);
510  }
511 
512  @Override
513  public void update(Observable o, Object arg) {
514  refresh(true);
515  }
516 
517  @Override
518  protected List<BlackboardArtifact> makeKeys() {
519  List<BlackboardArtifact> keys = new ArrayList<>();
520 
521  if (skCase != null) {
522  emailResults.getArtifactIds(accountName, folderName).forEach((id) -> {
523  try {
524  keys.add(skCase.getBlackboardArtifact(id));
525  } catch (TskCoreException ex) {
526  logger.log(Level.WARNING, "Error getting mail messages keys", ex); //NON-NLS
527  }
528  });
529  }
530  return keys;
531  }
532 
533  @Override
534  protected void onAdd() {
535  // No-op
536  }
537 
538  @Override
539  protected void onRemove() {
540  // No-op
541  }
542  }
543 }
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)
static final Set< IngestManager.IngestModuleEvent > INGEST_MODULE_EVENTS_OF_INTEREST
static final Set< IngestManager.IngestJobEvent > INGEST_JOB_EVENTS_OF_INTEREST
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:486
static void removeEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:531
final Map< String, Map< String, List< Long > > > accounts

Copyright © 2012-2019 Basis Technology. Generated on: Tue Jan 7 2020
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.