diff --git a/Core/nbproject/project.xml b/Core/nbproject/project.xml
index 83aefea7c5..17e5d82284 100644
--- a/Core/nbproject/project.xml
+++ b/Core/nbproject/project.xml
@@ -345,6 +345,7 @@
org.sleuthkit.autopsy.textextractors.configs
org.sleuthkit.autopsy.texttranslation
org.sleuthkit.datamodel
+ org.sleuthkit.datamodel.blackboardutils
ext/commons-lang3-3.8.1.jar
diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java
index 548fb881be..262ea986a7 100644
--- a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java
+++ b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java
@@ -41,7 +41,6 @@ import org.sleuthkit.autopsy.actions.DeleteFileBlackboardArtifactTagAction;
import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction;
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
import org.sleuthkit.autopsy.coreutils.Logger;
-import org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode.AbstractFilePropertyType;
import org.sleuthkit.autopsy.datamodel.AbstractFsContentNode;
import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode;
import org.sleuthkit.autopsy.datamodel.DataModelActionsFactory;
@@ -258,16 +257,20 @@ public class DataResultFilterNode extends FilterNode {
@Override
protected Node[] createNodes(Node key) {
- // filter out all non-message artifacts, if displaying the results from the Data Source tree
+ // if displaying the results from the Data Source tree
+ // filter out artifacts
+ // unless there are message artifacts with attachments as children
BlackboardArtifact art = key.getLookup().lookup(BlackboardArtifact.class);
- if (art != null
- && filterArtifacts
- && art.getArtifactTypeID() != BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID()
- && art.getArtifactTypeID() != BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE.getTypeID()) {
- return new Node[]{};
- }
-
- return new Node[]{new DataResultFilterNode(key, sourceEm)};
+ if (art != null && filterArtifacts) {
+
+ if ((DirectoryTreeUtils.showMessagesInDirTree() == false) ||
+ (DirectoryTreeUtils.showMessagesInDirTree() &&
+ art.getArtifactTypeID() != BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID() &&
+ art.getArtifactTypeID() != BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE.getTypeID()) ) {
+ return new Node[]{};
+ }
+ }
+ return new Node[]{new DataResultFilterNode(key, sourceEm)};
}
}
diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeFilterNode.java b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeFilterNode.java
index c9070015f2..c5d5712ec8 100644
--- a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeFilterNode.java
+++ b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeFilterNode.java
@@ -20,7 +20,6 @@ package org.sleuthkit.autopsy.directorytree;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import javax.swing.Action;
@@ -33,17 +32,12 @@ import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.AbstractContentNode;
import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode;
-import org.sleuthkit.autopsy.ingest.runIngestModuleWizard.RunIngestModulesAction;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
import org.sleuthkit.datamodel.Content;
-import org.sleuthkit.datamodel.Directory;
-import org.sleuthkit.datamodel.Image;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
-import org.sleuthkit.datamodel.VirtualDirectory;
-import org.sleuthkit.datamodel.Volume;
/**
* A node filter (decorator) that sets the actions for a node in the tree view
@@ -137,11 +131,18 @@ class DirectoryTreeFilterNode extends FilterNode {
numVisibleChildren--;
}
} else if (child instanceof BlackboardArtifact) {
- BlackboardArtifact bba = (BlackboardArtifact) child;
-
- // Only message type artifacts are displayed in the tree
- if ((bba.getArtifactTypeID() != ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID())
- && (bba.getArtifactTypeID() != ARTIFACT_TYPE.TSK_MESSAGE.getTypeID())) {
+
+ if (DirectoryTreeUtils.showMessagesInDirTree()) {
+ // In older versions of Autopsy, attachments were children of email/message artifacts
+ // and hence email/messages with attachments are shown in the directory tree.
+ BlackboardArtifact bba = (BlackboardArtifact) child;
+ // Only message type artifacts are displayed in the tree
+ if ((bba.getArtifactTypeID() != ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID())
+ && (bba.getArtifactTypeID() != ARTIFACT_TYPE.TSK_MESSAGE.getTypeID())) {
+ numVisibleChildren--;
+ }
+ }
+ else {
numVisibleChildren--;
}
}
diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeUtils.java b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeUtils.java
new file mode 100644
index 0000000000..bf84561f21
--- /dev/null
+++ b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeUtils.java
@@ -0,0 +1,60 @@
+/*
+ * Autopsy Forensic Browser
+ *
+ * Copyright 2019 Basis Technology Corp.
+ * Contact: carrier sleuthkit org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.sleuthkit.autopsy.directorytree;
+
+import org.sleuthkit.autopsy.casemodule.Case;
+import org.sleuthkit.datamodel.CaseDbSchemaVersionNumber;
+
+/**
+ * Utility class for Directory tree.
+ *
+ */
+final class DirectoryTreeUtils {
+
+ private static final int ATTACHMENT_CHILDOF_MSG_MAX_DB_MAJOR_VER = 8;
+ private static final int ATTACHMENT_CHILDOF_MSG_MAX_DB_MINOR_VER = 4;
+
+
+ /**
+ * Prior to schema version 8.4, attachments were children of messages and
+ * hence messages with any attachment children are shown in the directory
+ * tree.
+ *
+ * At 8.4, attachments are tracked as an attribute, and the message artifact
+ * don't need to be shown in the directory tree.
+ *
+ * This method may be used to check the schema version and behave
+ * accordingly, in order to maintain backward compatibility.
+ *
+ * @return True if messages with attachment children should be shown in
+ * directory tree.
+ */
+ static boolean showMessagesInDirTree() {
+ boolean showMessagesInDirTree = true;
+ if (Case.isCaseOpen()) {
+ CaseDbSchemaVersionNumber version = Case.getCurrentCase().getSleuthkitCase().getDBSchemaCreationVersion();
+ showMessagesInDirTree
+ = ((version.getMajor() < ATTACHMENT_CHILDOF_MSG_MAX_DB_MAJOR_VER)
+ || (version.getMajor() == ATTACHMENT_CHILDOF_MSG_MAX_DB_MAJOR_VER && version.getMinor() < ATTACHMENT_CHILDOF_MSG_MAX_DB_MINOR_VER));
+ }
+ return showMessagesInDirTree;
+ }
+
+}
diff --git a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/Bundle.properties-MERGED b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/Bundle.properties-MERGED
index cdfd241886..90ce00170c 100755
--- a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/Bundle.properties-MERGED
+++ b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/Bundle.properties-MERGED
@@ -15,6 +15,7 @@ ThunderbirdMboxFileIngestModule.addArtifact.indexError.message=Failed to index e
# {0} - file name
# {1} - file ID
ThunderbirdMboxFileIngestModule.errorMessage.outOfDiskSpace=Out of disk space. Cannot copy '{0}' (id={1}) to parse.
+ThunderbirdMboxFileIngestModule.handleAttch.addAttachmentsErrorMsg=Failed to add attachments to email message.
ThunderbirdMboxFileIngestModule.moduleName=Email Parser
ThunderbirdMboxFileIngestModule.noOpenCase.errMsg=Exception while getting open case.
ThunderbirdMboxFileIngestModule.processPst.errMsg.outOfDiskSpace=Out of disk space. Cannot copy {0} to parse.
diff --git a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/ThunderbirdMboxFileIngestModule.java b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/ThunderbirdMboxFileIngestModule.java
index f1260f269f..419173fcff 100644
--- a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/ThunderbirdMboxFileIngestModule.java
+++ b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/ThunderbirdMboxFileIngestModule.java
@@ -22,6 +22,7 @@ import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
@@ -58,6 +59,9 @@ import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.datamodel.TskDataException;
import org.sleuthkit.datamodel.TskException;
+import org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper;
+import org.sleuthkit.datamodel.blackboardutils.FileAttachment;
+import org.sleuthkit.datamodel.blackboardutils.MessageAttachments;
/**
* File-level ingest module that detects MBOX, PST, and vCard files based on
@@ -70,6 +74,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
private FileManager fileManager;
private IngestJobContext context;
private Blackboard blackboard;
+ private CommunicationArtifactsHelper communicationArtifactsHelper;
private Case currentCase;
@@ -129,6 +134,15 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
logger.log(Level.WARNING, null, ex);
}
+ try {
+ communicationArtifactsHelper = new CommunicationArtifactsHelper(currentCase.getSleuthkitCase(),
+ EmailParserModuleFactory.getModuleName(), abstractFile, Account.Type.EMAIL);
+ } catch (TskCoreException ex) {
+ logger.log(Level.SEVERE, String.format("Failed to create CommunicationArtifactsHelper for file %s", abstractFile.getName()), ex);
+ return ProcessResult.ERROR;
+ }
+
+
if (isMbox) {
return processMBox(abstractFile);
}
@@ -267,7 +281,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
} else if (mboxParentDir.contains("/ImapMail/")) { //NON-NLS
emailFolder = mboxParentDir.substring(mboxParentDir.indexOf("/ImapMail/") + 9); //NON-NLS
}
- emailFolder = emailFolder + mboxFileName;
+ emailFolder += mboxFileName;
emailFolder = emailFolder.replaceAll(".sbd", ""); //NON-NLS
String fileName;
@@ -487,8 +501,12 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
*
* @return List of attachments
*/
+ @NbBundle.Messages({
+ "ThunderbirdMboxFileIngestModule.handleAttch.addAttachmentsErrorMsg=Failed to add attachments to email message."
+})
private List handleAttachments(List attachments, AbstractFile abstractFile, BlackboardArtifact messageArtifact) {
List files = new ArrayList<>();
+ List fileAttachments = new ArrayList<>();
for (EmailMessage.Attachment attach : attachments) {
String filename = attach.getName();
long crTime = attach.getCrTime();
@@ -501,12 +519,14 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
try {
DerivedFile df = fileManager.addDerivedFile(filename, relPath,
- size, cTime, crTime, aTime, mTime, true, messageArtifact, "",
+ size, cTime, crTime, aTime, mTime, true, abstractFile, "",
EmailParserModuleFactory.getModuleName(), EmailParserModuleFactory.getModuleVersion(), "", encodingType);
associateAttachmentWithMesssge(messageArtifact, df);
files.add(df);
+
+ fileAttachments.add(new FileAttachment(df));
} catch (TskCoreException ex) {
postErrorMessage(
NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.handleAttch.errMsg",
@@ -516,6 +536,17 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
logger.log(Level.INFO, "", ex);
}
}
+
+
+ try {
+ communicationArtifactsHelper.addAttachments(messageArtifact, new MessageAttachments(fileAttachments, Collections.emptyList()));
+ } catch (TskCoreException ex) {
+ postErrorMessage(
+ NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.handleAttch.addAttachmentsErrorMsg"),
+ "");
+ logger.log(Level.INFO, "Failed to add attachments to email message.", ex);
+ }
+
return files;
}