2841: Attachments are children of messages.

2692: Messages & Attachments show in DataSource tree
This commit is contained in:
Raman 2017-07-31 17:25:25 -04:00
parent 4fdea244af
commit a4dc97af6e
10 changed files with 185 additions and 60 deletions

View File

@ -45,6 +45,7 @@ import org.sleuthkit.datamodel.LocalFilesDataSource;
import org.sleuthkit.datamodel.TskDataException; import org.sleuthkit.datamodel.TskDataException;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.AbstractContent;
import org.sleuthkit.datamodel.CarvingResult; import org.sleuthkit.datamodel.CarvingResult;
import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.TskData;
@ -295,7 +296,7 @@ public class FileManager implements Closeable {
* @param atime The accessed time of the file. * @param atime The accessed time of the file.
* @param mtime The modified time of the file. * @param mtime The modified time of the file.
* @param isFile True if a file, false if a directory. * @param isFile True if a file, false if a directory.
* @param parentFile The parent file from which the file was derived. * @param parentObj The parent object from which the file was derived.
* @param rederiveDetails The details needed to re-derive file (will be * @param rederiveDetails The details needed to re-derive file (will be
* specific to the derivation method), currently * specific to the derivation method), currently
* unused. * unused.
@ -317,7 +318,7 @@ public class FileManager implements Closeable {
long size, long size,
long ctime, long crtime, long atime, long mtime, long ctime, long crtime, long atime, long mtime,
boolean isFile, boolean isFile,
AbstractFile parentFile, Content parentObj,
String rederiveDetails, String toolName, String toolVersion, String otherDetails, String rederiveDetails, String toolName, String toolVersion, String otherDetails,
TskData.EncodingType encodingType) throws TskCoreException { TskData.EncodingType encodingType) throws TskCoreException {
if (null == caseDb) { if (null == caseDb) {
@ -325,7 +326,7 @@ public class FileManager implements Closeable {
} }
return caseDb.addDerivedFile(fileName, localPath, size, return caseDb.addDerivedFile(fileName, localPath, size,
ctime, crtime, atime, mtime, ctime, crtime, atime, mtime,
isFile, parentFile, rederiveDetails, toolName, toolVersion, otherDetails, encodingType); isFile, parentObj, rederiveDetails, toolName, toolVersion, otherDetails, encodingType);
} }
/** /**

View File

@ -25,6 +25,7 @@ import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.datamodel.FileTypes.FileTypesNode; import org.sleuthkit.autopsy.datamodel.FileTypes.FileTypesNode;
import org.sleuthkit.autopsy.datamodel.accounts.Accounts; import org.sleuthkit.autopsy.datamodel.accounts.Accounts;
import org.sleuthkit.autopsy.datamodel.accounts.Accounts.AccountsRootNode; import org.sleuthkit.autopsy.datamodel.accounts.Accounts.AccountsRootNode;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.DerivedFile; import org.sleuthkit.datamodel.DerivedFile;
import org.sleuthkit.datamodel.Directory; import org.sleuthkit.datamodel.Directory;
@ -117,6 +118,11 @@ abstract class AbstractContentChildren<T> extends Keys<T> {
return new SlackFileNode(sf); return new SlackFileNode(sf);
} }
@Override
public AbstractContentNode<? extends Content> visit(BlackboardArtifact art) {
return new BlackboardArtifactNode(art);
}
@Override @Override
protected AbstractContentNode<? extends Content> defaultVisit(SleuthkitVisitableItem di) { protected AbstractContentNode<? extends Content> defaultVisit(SleuthkitVisitableItem di) {
throw new UnsupportedOperationException(NbBundle.getMessage(this.getClass(), throw new UnsupportedOperationException(NbBundle.getMessage(this.getClass(),

View File

@ -21,8 +21,8 @@ package org.sleuthkit.autopsy.datamodel;
import java.util.List; import java.util.List;
import java.util.logging.Level; import java.util.logging.Level;
import org.openide.util.NbBundle;
import org.openide.util.lookup.Lookups; import org.openide.util.lookup.Lookups;
import org.openide.util.Lookup;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
@ -48,8 +48,18 @@ public abstract class AbstractContentNode<T extends Content> extends ContentNode
* @param content Underlying Content instances * @param content Underlying Content instances
*/ */
AbstractContentNode(T content) { AbstractContentNode(T content) {
//TODO consider child factory for the content children this(content, Lookups.singleton(content) );
super(new ContentChildren(content), Lookups.singleton(content)); }
/**
* Handles aspects that depend on the Content object
*
* @param content Underlying Content instances
* @param lookup The Lookup object for the node.
*/
AbstractContentNode(T content, Lookup lookup) {
//TODO consider child factory for the content children
super(new ContentChildren(content), lookup);
this.content = content; this.content = content;
//super.setName(ContentUtils.getSystemName(content)); //super.setName(ContentUtils.getSystemName(content));
super.setName("content_" + Long.toString(content.getId())); //NON-NLS super.setName("content_" + Long.toString(content.getId())); //NON-NLS
@ -66,8 +76,7 @@ public abstract class AbstractContentNode<T extends Content> extends ContentNode
@Override @Override
public void setName(String name) { public void setName(String name) {
throw new UnsupportedOperationException( super.setName(name);
NbBundle.getMessage(this.getClass(), "AbstractContentNode.exception.cannotChangeSysName.msg"));
} }
@Override @Override

View File

@ -25,6 +25,7 @@ import java.beans.PropertyChangeListener;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -34,6 +35,7 @@ import java.util.logging.Level;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.swing.Action; import javax.swing.Action;
import org.openide.nodes.Children; import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.openide.nodes.Sheet; import org.openide.nodes.Sheet;
import org.openide.util.Lookup; import org.openide.util.Lookup;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
@ -43,9 +45,11 @@ import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagAddedEvent;
import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagDeletedEvent; import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagDeletedEvent;
import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent; import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent;
import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent; import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent;
import static org.sleuthkit.autopsy.datamodel.DataModelActionsFactory.VIEW_IN_NEW_WINDOW;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import static org.sleuthkit.autopsy.datamodel.DisplayableItemNode.findLinked; import static org.sleuthkit.autopsy.datamodel.DisplayableItemNode.findLinked;
import org.sleuthkit.autopsy.directorytree.NewWindowViewAction;
import org.sleuthkit.autopsy.timeline.actions.ViewArtifactInTimelineAction; import org.sleuthkit.autopsy.timeline.actions.ViewArtifactInTimelineAction;
import org.sleuthkit.autopsy.timeline.actions.ViewFileInTimelineAction; import org.sleuthkit.autopsy.timeline.actions.ViewFileInTimelineAction;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
@ -61,7 +65,7 @@ import org.sleuthkit.datamodel.TskCoreException;
* Node wrapping a blackboard artifact object. This is generated from several * Node wrapping a blackboard artifact object. This is generated from several
* places in the tree. * places in the tree.
*/ */
public class BlackboardArtifactNode extends DisplayableItemNode { public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifact> {
private static final Logger LOGGER = Logger.getLogger(BlackboardArtifactNode.class.getName()); private static final Logger LOGGER = Logger.getLogger(BlackboardArtifactNode.class.getName());
@ -72,6 +76,7 @@ public class BlackboardArtifactNode extends DisplayableItemNode {
private final BlackboardArtifact artifact; private final BlackboardArtifact artifact;
private Content associated = null; private Content associated = null;
private List<NodeProperty<? extends Object>> customProperties; private List<NodeProperty<? extends Object>> customProperties;
/* /*
* Artifact types which should have the full unique path of the associated * Artifact types which should have the full unique path of the associated
* content as a property. * content as a property.
@ -130,22 +135,17 @@ public class BlackboardArtifactNode extends DisplayableItemNode {
* @param iconPath icon to use for the artifact * @param iconPath icon to use for the artifact
*/ */
public BlackboardArtifactNode(BlackboardArtifact artifact, String iconPath) { public BlackboardArtifactNode(BlackboardArtifact artifact, String iconPath) {
super(Children.LEAF, createLookup(artifact)); super(artifact, createLookup(artifact));
this.artifact = artifact; this.artifact = artifact;
// Look for associated Content i.e. the source file for the artifact // Look for associated Content i.e. the source file for the artifact
if (this.getLookup().lookupAll(Content.class).size() > 1) { for (Content content : this.getLookup().lookupAll(Content.class)) {
for (Content content : this.getLookup().lookupAll(Content.class)) { if ( (content != null) && (!(content instanceof BlackboardArtifact)) ){
if ( (content != null) && (!(content instanceof BlackboardArtifact)) ){ this.associated = content;
this.associated = content; break;
break;
}
} }
} }
if (null == this.associated ) {
this.associated = this.getLookup().lookup(Content.class);
}
this.setName(Long.toString(artifact.getArtifactID())); this.setName(Long.toString(artifact.getArtifactID()));
this.setDisplayName(); this.setDisplayName();
@ -160,32 +160,18 @@ public class BlackboardArtifactNode extends DisplayableItemNode {
* @param artifact artifact to encapsulate * @param artifact artifact to encapsulate
*/ */
public BlackboardArtifactNode(BlackboardArtifact artifact) { public BlackboardArtifactNode(BlackboardArtifact artifact) {
super(Children.LEAF, createLookup(artifact));
this.artifact = artifact; this(artifact, ExtractedContent.getIconFilePath(artifact.getArtifactTypeID()));
// Look for associated Content - the source file for the artifact
if (this.getLookup().lookupAll(Content.class).size() > 1) {
for (Content content : this.getLookup().lookupAll(Content.class)) {
if ( (content != null) && (!(content instanceof BlackboardArtifact)) ){
this.associated = content;
break;
}
}
}
if (null == this.associated ) {
this.associated = this.getLookup().lookup(Content.class);
}
this.setName(Long.toString(artifact.getArtifactID()));
this.setDisplayName();
this.setIconBaseWithExtension(ExtractedContent.getIconFilePath(artifact.getArtifactTypeID())); //NON-NLS
Case.addPropertyChangeListener(pcl);
} }
private void removeListeners() { private void removeListeners() {
Case.removePropertyChangeListener(pcl); Case.removePropertyChangeListener(pcl);
} }
public BlackboardArtifact getArtifact() {
return this.artifact;
}
@Override @Override
@NbBundle.Messages({ @NbBundle.Messages({
"BlackboardArtifactNode.getAction.errorTitle=Error getting actions", "BlackboardArtifactNode.getAction.errorTitle=Error getting actions",
@ -235,9 +221,6 @@ public class BlackboardArtifactNode extends DisplayableItemNode {
*/ */
private void setDisplayName() { private void setDisplayName() {
String displayName = ""; //NON-NLS String displayName = ""; //NON-NLS
if (associated != null) {
displayName = associated.getName();
}
// If this is a node for a keyword hit on an artifact, we set the // If this is a node for a keyword hit on an artifact, we set the
// display name to be the artifact type name followed by " Artifact" // display name to be the artifact type name followed by " Artifact"
@ -262,9 +245,30 @@ public class BlackboardArtifactNode extends DisplayableItemNode {
// Do nothing since the display name will be set to the file name. // Do nothing since the display name will be set to the file name.
} }
} }
if (displayName.isEmpty() && artifact != null) {
displayName = artifact.getName();
}
this.setDisplayName(displayName); this.setDisplayName(displayName);
} }
/**
* Return the name of the associated source file/content
*
* @return source file/content name
*/
public String getSrcName() {
String srcName = "";
if (associated != null) {
srcName = associated.getName();
}
return srcName;
}
@NbBundle.Messages({ @NbBundle.Messages({
"BlackboardArtifactNode.createSheet.artifactType.displayName=Artifact Type", "BlackboardArtifactNode.createSheet.artifactType.displayName=Artifact Type",
"BlackboardArtifactNode.createSheet.artifactType.name=Artifact Type", "BlackboardArtifactNode.createSheet.artifactType.name=Artifact Type",
@ -288,7 +292,7 @@ public class BlackboardArtifactNode extends DisplayableItemNode {
ss.put(new NodeProperty<>(NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.srcFile.name"), ss.put(new NodeProperty<>(NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.srcFile.name"),
NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.srcFile.displayName"), NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.srcFile.displayName"),
NO_DESCR, NO_DESCR,
this.getDisplayName())); this.getSrcName()));
if (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID()) { if (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID()) {
try { try {
BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT)); BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT));
@ -579,4 +583,9 @@ public class BlackboardArtifactNode extends DisplayableItemNode {
public String getItemType() { public String getItemType() {
return getClass().getName(); return getClass().getName();
} }
@Override
public <T> T accept(ContentNodeVisitor<T> v) {
return v.visit(this);
}
} }

View File

@ -44,6 +44,9 @@ interface ContentNodeVisitor<T> {
T visit(SlackFileNode sfn); T visit(SlackFileNode sfn);
T visit(BlackboardArtifactNode bban);
/** /**
* Visitor with an implementable default behavior for all types. Override * Visitor with an implementable default behavior for all types. Override
* specific visit types to not use the default behavior. * specific visit types to not use the default behavior.
@ -100,5 +103,10 @@ interface ContentNodeVisitor<T> {
public T visit(SlackFileNode sfn) { public T visit(SlackFileNode sfn) {
return defaultVisit(sfn); return defaultVisit(sfn);
} }
@Override
public T visit(BlackboardArtifactNode bban) {
return defaultVisit(bban);
}
} }
} }

View File

@ -428,7 +428,7 @@ public class EmailExtracted implements AutopsyVisitableItem {
@Override @Override
public boolean isLeafTypeNode() { public boolean isLeafTypeNode() {
return true; return false;
} }
@Override @Override

View File

@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.directorytree;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.beans.PropertyVetoException; import java.beans.PropertyVetoException;
import java.text.MessageFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
@ -43,6 +44,7 @@ import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction;
import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint; import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode.AbstractFilePropertyType; import org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode.AbstractFilePropertyType;
import org.sleuthkit.autopsy.datamodel.AbstractFsContentNode; import org.sleuthkit.autopsy.datamodel.AbstractFsContentNode;
import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode; import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode;
@ -71,6 +73,8 @@ import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.datamodel.TskException; import org.sleuthkit.datamodel.TskException;
import org.sleuthkit.datamodel.VirtualDirectory; import org.sleuthkit.datamodel.VirtualDirectory;
import static org.sleuthkit.autopsy.directorytree.Bundle.*; import static org.sleuthkit.autopsy.directorytree.Bundle.*;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
import org.sleuthkit.datamodel.TskCoreException;
/** /**
* A node used to wrap another node before passing it to the result viewers. The * A node used to wrap another node before passing it to the result viewers. The
@ -210,6 +214,26 @@ public class DataResultFilterNode extends FilterNode {
return propertySets; return propertySets;
} }
/**
* Gets the display name for the wrapped node.
*
* OutlineView used in the DataResult table uses getDisplayName() to populate
* the first column, which is Source File.
*
* Hence this override to return the 'correct' displayName for the wrapped node.
*
* @return The display name for the node.
*/
@Override
public String getDisplayName() {
final Node orig = getOriginal();
String name = orig.getDisplayName();
if ((orig instanceof BlackboardArtifactNode)) {
name = ((BlackboardArtifactNode) orig).getSrcName();
}
return name;
}
/** /**
* Adds information about which child node of this node, if any, should be * Adds information about which child node of this node, if any, should be
* selected. Can be null. * selected. Can be null.
@ -248,16 +272,20 @@ public class DataResultFilterNode extends FilterNode {
private boolean filterKnown; private boolean filterKnown;
private boolean filterSlack; private boolean filterSlack;
private boolean filterArtifacts; // display message artifacts in the DataSource subtree
/** /**
* the constructor * the constructor
*/ */
private DataResultFilterChildren(Node arg, ExplorerManager sourceEm) { private DataResultFilterChildren(Node arg, ExplorerManager sourceEm) {
super(arg); super(arg);
this.filterArtifacts = false;
switch (SelectionContext.getSelectionContext(arg)) { switch (SelectionContext.getSelectionContext(arg)) {
case DATA_SOURCES: case DATA_SOURCES:
filterSlack = filterSlackFromDataSources; filterSlack = filterSlackFromDataSources;
filterKnown = filterKnownFromDataSources; filterKnown = filterKnownFromDataSources;
filterArtifacts = true;
break; break;
case VIEWS: case VIEWS:
filterSlack = filterSlackFromViews; filterSlack = filterSlackFromViews;
@ -291,6 +319,16 @@ public class DataResultFilterNode extends FilterNode {
return new Node[]{}; return new Node[]{};
} }
} }
// filter out all non-message artifacts, if displaying the results from the Data Source tree
BlackboardArtifact art = key.getLookup().lookup(BlackboardArtifact.class);
if (art != null && filterArtifacts) {
if ( (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, filterKnown, filterSlack)}; return new Node[]{new DataResultFilterNode(key, sourceEm, filterKnown, filterSlack)};
} }
} }
@ -459,8 +497,20 @@ public class DataResultFilterNode extends FilterNode {
@Override @Override
public AbstractAction visit(BlackboardArtifactNode ban) { public AbstractAction visit(BlackboardArtifactNode ban) {
return new ViewContextAction( BlackboardArtifact artifact = ban.getArtifact();
NbBundle.getMessage(this.getClass(), "DataResultFilterNode.action.viewInDir.text"), ban); try {
if ( (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID()) ||
(artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_MESSAGE.getTypeID()) ) {
if (artifact.hasChildren()) {
return openChild(ban);
}
}
}
catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, MessageFormat.format("Error getting children from blackboard artifact{0}.", artifact.getArtifactID()), ex); //NON-NLS
}
return new ViewContextAction(
NbBundle.getMessage(this.getClass(), "DataResultFilterNode.action.viewInDir.text"), ban);
} }
@Override @Override

View File

@ -26,6 +26,7 @@ import org.sleuthkit.autopsy.datamodel.DirectoryNode;
import org.openide.nodes.FilterNode; import org.openide.nodes.FilterNode;
import org.openide.nodes.Node; import org.openide.nodes.Node;
import org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode; import org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode;
import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode;
import org.sleuthkit.autopsy.datamodel.DisplayableItemNode; import org.sleuthkit.autopsy.datamodel.DisplayableItemNode;
import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor; import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor;
import org.sleuthkit.autopsy.datamodel.FileNode; import org.sleuthkit.autopsy.datamodel.FileNode;
@ -36,6 +37,7 @@ import org.sleuthkit.autopsy.datamodel.SlackFileNode;
import org.sleuthkit.autopsy.datamodel.VirtualDirectoryNode; import org.sleuthkit.autopsy.datamodel.VirtualDirectoryNode;
import org.sleuthkit.autopsy.datamodel.VolumeNode; import org.sleuthkit.autopsy.datamodel.VolumeNode;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.Directory; import org.sleuthkit.datamodel.Directory;
import org.sleuthkit.datamodel.LayoutFile; import org.sleuthkit.datamodel.LayoutFile;
@ -198,7 +200,7 @@ class DirectoryTreeFilterChildren extends FilterNode.Children {
List<Content> derivedChildren = node.getContentChildren(); List<Content> derivedChildren = node.getContentChildren();
//child of a file, must be a (derived) file too //child of a file, must be a (derived) file too
for (Content childContent : derivedChildren) { for (Content childContent : derivedChildren) {
if (((AbstractFile) childContent).isDir()) { if ((childContent instanceof AbstractFile) && ((AbstractFile) childContent).isDir()) {
return false; return false;
} else { } else {
try { try {
@ -249,6 +251,16 @@ class DirectoryTreeFilterChildren extends FilterNode.Children {
return defaultVisit(ft); return defaultVisit(ft);
} }
@Override
public Boolean visit(BlackboardArtifactNode bbafn) {
// Only show Message arttifacts with children
if ( (bbafn.getArtifact().getArtifactTypeID() == ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID()) ||
(bbafn.getArtifact().getArtifactTypeID() == ARTIFACT_TYPE.TSK_MESSAGE.getTypeID()) ) {
return bbafn.hasContentChildren();
}
return false;
}
} }
private static class ShowItemVisitor extends DisplayableItemNodeVisitor.Default<Boolean> { private static class ShowItemVisitor extends DisplayableItemNodeVisitor.Default<Boolean> {
@ -292,10 +304,23 @@ class DirectoryTreeFilterChildren extends FilterNode.Children {
//return vdn.hasContentChildren(); //return vdn.hasContentChildren();
} }
@Override @Override
public Boolean visit(FileTypesNode fileTypes) { public Boolean visit(FileTypesNode fileTypes) {
return defaultVisit(fileTypes); return defaultVisit(fileTypes);
} }
@Override
public Boolean visit(BlackboardArtifactNode bbafn) {
// Only show Message arttifacts with children
if ( (bbafn.getArtifact().getArtifactTypeID() == ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID()) ||
(bbafn.getArtifact().getArtifactTypeID() == ARTIFACT_TYPE.TSK_MESSAGE.getTypeID()) ) {
return bbafn.hasContentChildren();
}
return false;
}
} }
} }

View File

@ -25,14 +25,17 @@ import java.util.logging.Level;
import javax.swing.Action; import javax.swing.Action;
import org.openide.nodes.FilterNode; import org.openide.nodes.FilterNode;
import org.openide.nodes.Node; import org.openide.nodes.Node;
import org.openide.util.Exceptions;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.lookup.Lookups; import org.openide.util.lookup.Lookups;
import org.openide.util.lookup.ProxyLookup; import org.openide.util.lookup.ProxyLookup;
import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.AbstractContentNode; import org.sleuthkit.autopsy.datamodel.AbstractContentNode;
import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode;
import org.sleuthkit.autopsy.ingest.runIngestModuleWizard.RunIngestModulesAction; import org.sleuthkit.autopsy.ingest.runIngestModuleWizard.RunIngestModulesAction;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.Directory; import org.sleuthkit.datamodel.Directory;
import org.sleuthkit.datamodel.Image; import org.sleuthkit.datamodel.Image;
@ -77,7 +80,7 @@ class DirectoryTreeFilterNode extends FilterNode {
String name = orig.getDisplayName(); String name = orig.getDisplayName();
if (orig instanceof AbstractContentNode) { if (orig instanceof AbstractContentNode) {
AbstractFile file = getLookup().lookup(AbstractFile.class); AbstractFile file = getLookup().lookup(AbstractFile.class);
if (file != null) { if ((file != null) && (false == (orig instanceof BlackboardArtifactNode)) ){
try { try {
int numVisibleChildren = getVisibleChildCount(file); int numVisibleChildren = getVisibleChildCount(file);
@ -92,6 +95,15 @@ class DirectoryTreeFilterNode extends FilterNode {
logger.log(Level.SEVERE, "Error getting children count to display for file: " + file, ex); //NON-NLS logger.log(Level.SEVERE, "Error getting children count to display for file: " + file, ex); //NON-NLS
} }
} }
else if (orig instanceof BlackboardArtifactNode) {
BlackboardArtifact artifact = ((BlackboardArtifactNode) orig).getArtifact();
try {
int numAttachments = artifact.getChildrenCount();
name = name + " \u200E(\u200E" + numAttachments + ")\u200E"; //NON-NLS
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error getting chidlren count for atifact: " + artifact, ex); //NON-NLS
}
}
} }
return name; return name;
} }
@ -115,7 +127,7 @@ class DirectoryTreeFilterNode extends FilterNode {
if (purgeKnownFiles || purgeSlackFiles) { if (purgeKnownFiles || purgeSlackFiles) {
// Purge known and/or slack files from the file count // Purge known and/or slack files from the file count
for (int i = 0; i < childList.size(); i++) { for (int i = 0; i < childList.size(); i++) {
Content child = (Content) childList.get(i); Content child = childList.get(i);
if (child instanceof AbstractFile) { if (child instanceof AbstractFile) {
AbstractFile childFile = (AbstractFile) child; AbstractFile childFile = (AbstractFile) child;
if ((purgeKnownFiles && childFile.getKnown() == TskData.FileKnown.KNOWN) if ((purgeKnownFiles && childFile.getKnown() == TskData.FileKnown.KNOWN)
@ -126,6 +138,7 @@ class DirectoryTreeFilterNode extends FilterNode {
} }
} }
return numVisibleChildren; return numVisibleChildren;
} }

View File

@ -295,11 +295,15 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
*/ */
private void processEmails(List<EmailMessage> emails, AbstractFile abstractFile) { private void processEmails(List<EmailMessage> emails, AbstractFile abstractFile) {
List<AbstractFile> derivedFiles = new ArrayList<>(); List<AbstractFile> derivedFiles = new ArrayList<>();
for (EmailMessage email : emails) { for (EmailMessage email : emails) {
if (email.hasAttachment()) { BlackboardArtifact msgArtifact = addArtifact(email, abstractFile);
derivedFiles.addAll(handleAttachments(email.getAttachments(), abstractFile));
if ((msgArtifact != null) && (email.hasAttachment())) {
derivedFiles.addAll(handleAttachments(email.getAttachments(), abstractFile, msgArtifact ));
} }
addArtifact(email, abstractFile);
} }
if (derivedFiles.isEmpty() == false) { if (derivedFiles.isEmpty() == false) {
@ -320,7 +324,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
* *
* @return * @return
*/ */
private List<AbstractFile> handleAttachments(List<EmailMessage.Attachment> attachments, AbstractFile abstractFile) { private List<AbstractFile> handleAttachments(List<EmailMessage.Attachment> attachments, AbstractFile abstractFile, BlackboardArtifact messageArtifact) {
List<AbstractFile> files = new ArrayList<>(); List<AbstractFile> files = new ArrayList<>();
for (EmailMessage.Attachment attach : attachments) { for (EmailMessage.Attachment attach : attachments) {
String filename = attach.getName(); String filename = attach.getName();
@ -334,7 +338,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
try { try {
DerivedFile df = fileManager.addDerivedFile(filename, relPath, DerivedFile df = fileManager.addDerivedFile(filename, relPath,
size, cTime, crTime, aTime, mTime, true, abstractFile, "", size, cTime, crTime, aTime, mTime, true, messageArtifact, "",
EmailParserModuleFactory.getModuleName(), EmailParserModuleFactory.getModuleVersion(), "", encodingType); EmailParserModuleFactory.getModuleName(), EmailParserModuleFactory.getModuleVersion(), "", encodingType);
files.add(df); files.add(df);
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
@ -356,7 +360,8 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
* @param abstractFile * @param abstractFile
*/ */
@Messages({"ThunderbirdMboxFileIngestModule.addArtifact.indexError.message=Failed to index email message detected artifact for keyword search."}) @Messages({"ThunderbirdMboxFileIngestModule.addArtifact.indexError.message=Failed to index email message detected artifact for keyword search."})
private void addArtifact(EmailMessage email, AbstractFile abstractFile) { private BlackboardArtifact addArtifact(EmailMessage email, AbstractFile abstractFile) {
BlackboardArtifact bbart = null;
List<BlackboardAttribute> bbattributes = new ArrayList<>(); List<BlackboardAttribute> bbattributes = new ArrayList<>();
String to = email.getRecipients(); String to = email.getRecipients();
String cc = email.getCc(); String cc = email.getCc();
@ -415,11 +420,8 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_RTF, EmailParserModuleFactory.getModuleName(), rtf)); bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_RTF, EmailParserModuleFactory.getModuleName(), rtf));
} }
try { try {
BlackboardArtifact bbart;
bbart = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG); bbart = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG);
bbart.addAttributes(bbattributes); bbart.addAttributes(bbattributes);
@ -433,6 +435,8 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
logger.log(Level.WARNING, null, ex); logger.log(Level.WARNING, null, ex);
} }
return bbart;
} }
void postErrorMessage(String subj, String details) { void postErrorMessage(String subj, String details) {