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

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