diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/EmailExtracted.java b/Core/src/org/sleuthkit/autopsy/datamodel/EmailExtracted.java index 674568dd3e..58becb183c 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/EmailExtracted.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/EmailExtracted.java @@ -32,6 +32,7 @@ import java.util.Observable; import java.util.Observer; import java.util.Set; import java.util.logging.Level; +import org.apache.commons.lang3.tuple.Pair; import org.openide.nodes.ChildFactory; import org.openide.nodes.Children; import org.openide.nodes.Node; @@ -77,7 +78,7 @@ public class EmailExtracted implements AutopsyVisitableItem { */ public static final Map parsePath(String path) { Map parsed = new HashMap<>(); - String[] split = path.split(MAIL_PATH_SEPARATOR); + String[] split = path == null ? new String[0] : path.split(MAIL_PATH_SEPARATOR); if (split.length < 4) { parsed.put(MAIL_ACCOUNT, NbBundle.getMessage(EmailExtracted.class, "EmailExtracted.defaultAcct.text")); parsed.put(MAIL_FOLDER, NbBundle.getMessage(EmailExtracted.class, "EmailExtracted.defaultFolder.text")); @@ -146,55 +147,59 @@ public class EmailExtracted implements AutopsyVisitableItem { } @SuppressWarnings("deprecation") - public void update() { - synchronized (accounts) { - accounts.clear(); - } + public void update() { + // clear cache if no case if (skCase == null) { + synchronized (accounts) { + accounts.clear(); + } return; } - int artId = BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID(); + // get artifact id and path (if present) of all email artifacts + int emailArtifactId = BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID(); int pathAttrId = BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH.getTypeID(); - String query = "SELECT value_text,blackboard_attributes.artifact_id,attribute_type_id " //NON-NLS - + "FROM blackboard_attributes,blackboard_artifacts WHERE " //NON-NLS - + "attribute_type_id=" + pathAttrId //NON-NLS - + " AND blackboard_attributes.artifact_id=blackboard_artifacts.artifact_id" //NON-NLS - + " AND blackboard_artifacts.artifact_type_id=" + artId; //NON-NLS - if (filteringDSObjId > 0) { - query += " AND blackboard_artifacts.data_source_obj_id = " + filteringDSObjId; - } - + + String query = "SELECT \n" + + " art.artifact_id AS artifact_id,\n" + + " (SELECT value_text FROM blackboard_attributes attr\n" + + " WHERE attr.artifact_id = art.artifact_id AND attr.attribute_type_id = " + pathAttrId + "\n" + + " LIMIT 1) AS value_text\n" + + "FROM \n" + + " blackboard_artifacts art\n" + + " WHERE art.artifact_type_id = " + emailArtifactId + "\n" + + ((filteringDSObjId > 0) ? " AND art.data_source_obj_id = " + filteringDSObjId : ""); + + // form hierarchy of account -> folder -> account id + Map>> newMapping = new HashMap<>(); + try (CaseDbQuery dbQuery = skCase.executeQuery(query)) { ResultSet resultSet = dbQuery.getResultSet(); - synchronized (accounts) { - while (resultSet.next()) { - final String path = resultSet.getString("value_text"); //NON-NLS - final long artifactId = resultSet.getLong("artifact_id"); //NON-NLS - final Map parsedPath = parsePath(path); - final String account = parsedPath.get(MAIL_ACCOUNT); - final String folder = parsedPath.get(MAIL_FOLDER); - - Map> folders = accounts.get(account); - if (folders == null) { - folders = new LinkedHashMap<>(); - accounts.put(account, folders); - } - List messages = folders.get(folder); - if (messages == null) { - messages = new ArrayList<>(); - folders.put(folder, messages); - } - messages.add(artifactId); - } + while (resultSet.next()) { + Long artifactId = resultSet.getLong("artifact_id"); + Map accountFolderMap = parsePath(resultSet.getString("value_text")); + String account = accountFolderMap.get(MAIL_ACCOUNT); + String folder = accountFolderMap.get(MAIL_FOLDER); + + Map> folders = newMapping.computeIfAbsent(account, (str) -> new LinkedHashMap<>()); + List messages = folders.computeIfAbsent(folder, (str) -> new ArrayList<>()); + messages.add(artifactId); } } catch (TskCoreException | SQLException ex) { logger.log(Level.WARNING, "Cannot initialize email extraction: ", ex); //NON-NLS } + + + synchronized (accounts) { + accounts.clear(); + accounts.putAll(newMapping); + } + setChanged(); notifyObservers(); } } + /** * Mail root node grouping all mail accounts, supports account-> folder