From 242ad6170acfd993de8b6ce2f63e05711df8c909 Mon Sep 17 00:00:00 2001 From: Raman Arora Date: Tue, 12 Nov 2019 11:01:37 -0500 Subject: [PATCH 1/3] 5707: Show attachments in Message Content Viewer. --- ...java => AttachmentThumbnailsChildren.java} | 54 +++-- .../relationships/MediaViewer.java | 2 +- .../contentviewers/MessageContentViewer.java | 90 ++++---- .../autopsy/datamodel/AttachmentNode.java | 207 ++++++++++++++++++ .../datamodel/Bundle.properties-MERGED | 4 + .../datamodel/DisplayableItemNodeVisitor.java | 12 + .../autopsy/images/document-question-16.png | Bin 0 -> 269 bytes .../org/sleuthkit/autopsy/images/url-16.png | Bin 0 -> 753 bytes 8 files changed, 305 insertions(+), 64 deletions(-) rename Core/src/org/sleuthkit/autopsy/communications/relationships/{AttachmentsChildren.java => AttachmentThumbnailsChildren.java} (60%) create mode 100644 Core/src/org/sleuthkit/autopsy/datamodel/AttachmentNode.java create mode 100644 Core/src/org/sleuthkit/autopsy/images/document-question-16.png create mode 100644 Core/src/org/sleuthkit/autopsy/images/url-16.png diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/AttachmentsChildren.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/AttachmentThumbnailsChildren.java similarity index 60% rename from Core/src/org/sleuthkit/autopsy/communications/relationships/AttachmentsChildren.java rename to Core/src/org/sleuthkit/autopsy/communications/relationships/AttachmentThumbnailsChildren.java index 7dbe2133e0..816eb05d75 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/AttachmentsChildren.java +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/AttachmentThumbnailsChildren.java @@ -18,10 +18,13 @@ */ package org.sleuthkit.autopsy.communications.relationships; +import com.google.gson.Gson; import java.util.Arrays; +import java.util.Collection; import java.util.HashSet; import java.util.Set; import java.util.TreeSet; +import java.util.function.Consumer; import java.util.logging.Level; import org.openide.nodes.Children; import org.openide.nodes.Node; @@ -32,15 +35,18 @@ import org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode; import org.sleuthkit.autopsy.datamodel.FileNode; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.blackboardutils.FileAttachment; +import org.sleuthkit.datamodel.blackboardutils.MessageAttachments; /** * Factory for creating thumbnail children nodes. */ -final class AttachmentsChildren extends Children.Keys { +final class AttachmentThumbnailsChildren extends Children.Keys { - private static final Logger logger = Logger.getLogger(AttachmentsChildren.class.getName()); + private static final Logger LOGGER = Logger.getLogger(AttachmentThumbnailsChildren.class.getName()); private final Set artifacts; @@ -51,17 +57,16 @@ final class AttachmentsChildren extends Children.Keys { * The thumbnails will be initialls sorted by size, then name so that they * appear sorted by size by default. */ - AttachmentsChildren(Set artifacts) { + AttachmentThumbnailsChildren(Set artifacts) { super(false); this.artifacts = artifacts; - } @Override protected Node[] createNodes(AbstractFile t) { - return new Node[]{new AttachementNode(t)}; + return new Node[]{new AttachementThumbnailNode(t)}; } @Override @@ -77,15 +82,36 @@ final class AttachmentsChildren extends Children.Keys { return result; }); - artifacts.forEach((bba) -> { - try { - for (Content childContent : bba.getChildren()) { - if (childContent instanceof AbstractFile) { - thumbnails.add((AbstractFile) childContent); + artifacts.forEach(new Consumer() { + @Override + public void accept(BlackboardArtifact bba) { + try { + // Get the attachments from TSK_ATTACHMENTS attribute. + BlackboardAttribute attachmentsAttr = bba.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ATTACHMENTS)); + if (attachmentsAttr != null) { + + String jsonVal = attachmentsAttr.getValueString(); + MessageAttachments msgAttachments = new Gson().fromJson(jsonVal, MessageAttachments.class); + + Collection fileAttachments = msgAttachments.getFileAttachments(); + for (FileAttachment fileAttachment : fileAttachments) { + long attachedFileObjId = fileAttachment.getObjectId(); + if (attachedFileObjId >= 0) { + AbstractFile attachedFile = bba.getSleuthkitCase().getAbstractFileById(attachedFileObjId); + thumbnails.add(attachedFile); + } + } + } else { // backward compatibility - email message attachments are derived files, children of the message. + for (Content childContent : bba.getChildren()) { + if (childContent instanceof AbstractFile) { + thumbnails.add((AbstractFile) childContent); + } + } } + + } catch (TskCoreException ex) { + LOGGER.log(Level.WARNING, "Unable to get children from artifact.", ex); //NON-NLS } - } catch (TskCoreException ex) { - logger.log(Level.WARNING, "Unable to get children from artifact.", ex); //NON-NLS } }); @@ -95,9 +121,9 @@ final class AttachmentsChildren extends Children.Keys { /** * A node for representing a thumbnail. */ - static class AttachementNode extends FileNode { + static class AttachementThumbnailNode extends FileNode { - AttachementNode(AbstractFile file) { + AttachementThumbnailNode(AbstractFile file) { super(file, false); } diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/MediaViewer.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/MediaViewer.java index 07e6223a03..3bdb7a7a25 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/MediaViewer.java +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/MediaViewer.java @@ -129,7 +129,7 @@ final class MediaViewer extends JPanel implements RelationshipsViewer, ExplorerM thumbnailViewer.resetComponent(); - thumbnailViewer.setNode(new TableFilterNode(new DataResultFilterNode(new AbstractNode(new AttachmentsChildren(artifactList)), tableEM), true, this.getClass().getName())); + thumbnailViewer.setNode(new TableFilterNode(new DataResultFilterNode(new AbstractNode(new AttachmentThumbnailsChildren(artifactList)), tableEM), true, this.getClass().getName())); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java index 0f36c84b9e..1046c3177b 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java @@ -18,15 +18,17 @@ */ package org.sleuthkit.autopsy.contentviewers; +import org.sleuthkit.autopsy.datamodel.AttachmentNode; +import com.google.gson.Gson; import java.awt.Color; import java.awt.Component; import java.util.Arrays; +import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.logging.Level; -import java.util.stream.Collectors; import javax.swing.text.JTextComponent; import org.apache.commons.lang3.StringUtils; import org.jsoup.Jsoup; @@ -35,15 +37,12 @@ import org.openide.explorer.ExplorerManager; import org.openide.nodes.AbstractNode; import org.openide.nodes.Children; import org.openide.nodes.Node; -import org.openide.nodes.Sheet; import org.openide.util.NbBundle; import org.openide.util.lookup.ServiceProvider; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer; import org.sleuthkit.autopsy.corecomponents.DataResultPanel; import org.sleuthkit.autopsy.corecomponents.TableFilterNode; import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode; -import org.sleuthkit.autopsy.datamodel.FileNode; import org.sleuthkit.autopsy.directorytree.DataResultFilterNode; import org.sleuthkit.autopsy.directorytree.NewWindowViewAction; import org.sleuthkit.datamodel.AbstractFile; @@ -67,7 +66,12 @@ import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHO import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SUBJECT; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT; +import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.blackboardutils.FileAttachment; +import org.sleuthkit.datamodel.blackboardutils.MessageAttachments; +import org.sleuthkit.datamodel.blackboardutils.Attachment; +import org.sleuthkit.datamodel.blackboardutils.URLAttachment; /** * Shows SMS/MMS/EMail messages @@ -541,11 +545,34 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont } private void configureAttachments() throws TskCoreException { - //TODO: Replace this with code to get the actual attachements! - final Set attachments = artifact.getChildren().stream() - .filter(AbstractFile.class::isInstance) - .map(AbstractFile.class::cast) - .collect(Collectors.toSet()); + + final Set attachments; + + // Attachments are specified in an attribute TSK_ATTACHMENTS as JSON attribute + BlackboardAttribute attachmentsAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ATTACHMENTS)); + if(attachmentsAttr != null) { + + attachments = new HashSet<>(); + String jsonVal = attachmentsAttr.getValueString(); + MessageAttachments msgAttachments = new Gson().fromJson(jsonVal, MessageAttachments.class); + + Collection fileAttachments = msgAttachments.getFileAttachments(); + for (FileAttachment fileAttachment: fileAttachments) { + attachments.add(fileAttachment); + } + Collection urlAttachments = msgAttachments.getUrlAttachments(); + for (URLAttachment urlAttachment: urlAttachments) { + attachments.add(urlAttachment); + } + } else { // For backward compatibility - email attachements are derived files and children of the email message artifact + attachments = new HashSet<>(); + for (Content child: artifact.getChildren()) { + if (child instanceof AbstractFile) { + attachments.add(new FileAttachment((AbstractFile)child)); + } + } + } + final int numberOfAttachments = attachments.size(); msgbodyTabbedPane.setEnabledAt(ATTM_TAB_INDEX, numberOfAttachments > 0); @@ -633,16 +660,17 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont return doc.html(); } - private static class AttachmentsChildren extends Children.Keys { + + final class AttachmentsChildren extends Children.Keys { - private final Set attachments; + private final Set attachments; - AttachmentsChildren(Set attachments) { + AttachmentsChildren(Set attachments) { this.attachments = attachments; } @Override - protected Node[] createNodes(AbstractFile t) { + protected Node[] createNodes(Attachment t) { return new Node[]{new AttachmentNode(t)}; } @@ -652,40 +680,4 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont setKeys(attachments); } } - - /** - * Extension of FileNode customized for viewing attachments in the - * MessageContentViewer. It overrides createSheet() to customize what - * properties are shown in the table, and could also override getActions(), - * getPreferedAction(), etc. - */ - private static class AttachmentNode extends FileNode { - - AttachmentNode(AbstractFile file) { - super(file, false); - } - - @Override - protected Sheet createSheet() { - Sheet sheet = super.createSheet(); - Set keepProps = new HashSet<>(Arrays.asList( - NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.nameColLbl"), - NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.createSheet.score.name"), - NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.createSheet.comment.name"), - NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.createSheet.count.name"), - NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.sizeColLbl"), - NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.mimeType"), - NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.knownColLbl"))); - - //Remove all other props except for the ones above - Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES); - for (Property p : sheetSet.getProperties()) { - if (!keepProps.contains(p.getName())) { - sheetSet.remove(p.getName()); - } - } - - return sheet; - } - } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AttachmentNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/AttachmentNode.java new file mode 100644 index 0000000000..cc9e364790 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AttachmentNode.java @@ -0,0 +1,207 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2017-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.datamodel; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.logging.Level; +import javax.swing.Action; +import org.apache.commons.lang3.StringUtils; +import org.openide.nodes.Children; +import org.openide.nodes.Sheet; +import org.openide.util.Lookup; +import org.openide.util.NbBundle; +import org.openide.util.Utilities; +import org.openide.util.lookup.Lookups; +import org.sleuthkit.autopsy.actions.AddContentTagAction; +import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.contentviewers.MessageContentViewer; +import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint; +import org.sleuthkit.autopsy.coreutils.Logger; +import static org.sleuthkit.autopsy.datamodel.FileNode.getIconForFileType; +import org.sleuthkit.autopsy.directorytree.ExportCSVAction; +import org.sleuthkit.autopsy.directorytree.ExternalViewerAction; +import org.sleuthkit.autopsy.directorytree.ExternalViewerShortcutAction; +import org.sleuthkit.autopsy.directorytree.ExtractAction; +import org.sleuthkit.autopsy.directorytree.NewWindowViewAction; +import org.sleuthkit.autopsy.directorytree.ViewContextAction; +import org.sleuthkit.autopsy.timeline.actions.ViewFileInTimelineAction; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.TskException; +import org.sleuthkit.datamodel.blackboardutils.Attachment; +import org.sleuthkit.datamodel.blackboardutils.FileAttachment; +import org.sleuthkit.datamodel.blackboardutils.URLAttachment; + +/** + * Node for a message attachment. + * + */ +public class AttachmentNode extends DisplayableItemNode { + + private static final Logger LOGGER = Logger.getLogger(MessageContentViewer.class.getName()); + + private final Attachment attachment; + private final AbstractFile attachmentFile; + + public AttachmentNode(Attachment attachment) { + + super(Children.LEAF, createLookup(attachment)); + + super.setName(attachment.getLocation()); + super.setDisplayName(attachment.getLocation()); // SET NODE DISPLAY NAME, I.E., TEXT IN FIRST TABLE CELL + + this.attachment = attachment; + Long attachmentObjId = attachment.getObjId(); + AbstractFile attchmentAbstractFile = null; + + if (attachmentObjId != null && attachmentObjId > 0) { + try { + attchmentAbstractFile = Case.getCurrentCaseThrows().getSleuthkitCase().getAbstractFileById(attachmentObjId); + } catch (TskException | NoCurrentCaseException ex) { + LOGGER.log(Level.WARNING, "Error loading attachment file with object id " + attachmentObjId, ex); //NON-NLS + } + } + attachmentFile = attchmentAbstractFile; + + // set the icon for node + setIcon(); + } + + @Override + @NbBundle.Messages({ + "AttachmentNode.getActions.viewFileInDir.text=View File in Directory", + "AttachmentNode.getActions.viewInNewWin.text=View in New Window", + "AttachmentNode.getActions.openInExtViewer.text=Open in External Viewer Ctrl+E", + "AttachmentNode.getActions.searchFilesSameMD5.text=Search for files with the same MD5 hash"}) + public Action[] getActions(boolean context) { + + List actionsList = new ArrayList<>(); + actionsList.addAll(Arrays.asList(super.getActions(true))); + + // If there is an attachment file + if (this.attachmentFile != null) { + actionsList.add(new ViewContextAction(Bundle.AttachmentNode_getActions_viewFileInDir_text(), this.attachmentFile)); + actionsList.add(null); // Creates an item separator + + actionsList.add(new NewWindowViewAction(Bundle.AttachmentNode_getActions_viewInNewWin_text(), this)); + final Collection selectedFilesList + = new HashSet<>(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class)); + if (selectedFilesList.size() == 1) { + actionsList.add(new ExternalViewerAction( + Bundle.AttachmentNode_getActions_openInExtViewer_text(), this)); + } else { + actionsList.add(ExternalViewerShortcutAction.getInstance()); + } + actionsList.add(ViewFileInTimelineAction.createViewFileAction(this.attachmentFile)); + actionsList.add(null); // Creates an item separator + + actionsList.add(ExtractAction.getInstance()); + actionsList.add(ExportCSVAction.getInstance()); + actionsList.add(null); // Creates an item separator + + actionsList.add(AddContentTagAction.getInstance()); + if (1 == selectedFilesList.size()) { + actionsList.add(DeleteFileContentTagAction.getInstance()); + } + actionsList.addAll(ContextMenuExtensionPoint.getActions()); + + } + return actionsList.toArray(new Action[actionsList.size()]); + } + + @Override + protected Sheet createSheet() { + + // Create a new property sheet. + Sheet sheet = new Sheet(); + Sheet.Set sheetSet = Sheet.createPropertiesSet(); + sheet.put(sheetSet); + + sheetSet.put(new NodeProperty<>("Location", "Location", "", this.attachment.getLocation())); + + if (attachmentFile != null) { + long size = attachmentFile.getSize(); + String mimeType = attachmentFile.getMIMEType(); + + // @TODO Vik-5762: get SCO Columns + + sheetSet.put(new NodeProperty<>("Size", "Size", "", size)); + if (StringUtils.isNotEmpty(mimeType)) { + sheetSet.put(new NodeProperty<>("Mime type", "Mime type", "", mimeType)); + } + sheetSet.put(new NodeProperty<>("Known", "Known", "", attachmentFile.getKnown().getName())); + } + + return sheet; + } + + @Override + public T accept(DisplayableItemNodeVisitor visitor) { + return visitor.visit(this); + } + + @Override + public boolean isLeafTypeNode() { + return true; + } + + @Override + public String getItemType() { + return getClass().getName(); + } + + private static Lookup createLookup(Attachment attachment) { + Long attachmentObjId = attachment.getObjId(); + if (attachmentObjId != null && attachmentObjId > 0) { + AbstractFile attachmentFile = null; + try { + attachmentFile = Case.getCurrentCaseThrows().getSleuthkitCase().getAbstractFileById(attachmentObjId); + if (attachmentFile != null) { + return Lookups.fixed(attachment, attachmentFile); + } else { + return Lookups.fixed(attachment); + } + } catch (TskException | NoCurrentCaseException ex) { + return Lookups.fixed(attachment); + } + } + return Lookups.fixed(attachment); + } + + /** + * Set the icon based on attachment type + */ + private void setIcon() { + if (attachmentFile != null) { + this.setIconBaseWithExtension(getIconForFileType(attachmentFile)); + } else if (attachment instanceof FileAttachment) { + this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/document-question-16.png"); + } else if (attachment instanceof URLAttachment) { + this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/url-16.png"); + } else { + this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file-icon-deleted.png"); + } + + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED index 7f0a4d2419..f834765885 100755 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED @@ -43,6 +43,10 @@ ArtifactStringContent.attrsTableHeader.type=Type ArtifactStringContent.attrsTableHeader.value=Value ArtifactStringContent.failedToGetAttributes.message=Failed to get some or all attributes from case database ArtifactStringContent.failedToGetSourcePath.message=Failed to get source file path from case database +AttachmentNode.getActions.openInExtViewer.text=Open in External Viewer Ctrl+E +AttachmentNode.getActions.searchFilesSameMD5.text=Search for files with the same MD5 hash +AttachmentNode.getActions.viewFileInDir.text=View File in Directory +AttachmentNode.getActions.viewInNewWin.text=View in New Window # {0} - node name BaseChildFactory.NoSuchEventBusException.message=No event bus for node: {0} BlackboardArtifactNode.createSheet.artifactDetails.displayName=Result Details diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java b/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java index bb324bc7bb..660368de81 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java @@ -32,6 +32,7 @@ import org.sleuthkit.autopsy.datamodel.FileSize.FileSizeRootNode; import org.sleuthkit.autopsy.datamodel.FileTypes.FileTypesNode; import org.sleuthkit.autopsy.datamodel.accounts.Accounts; import org.sleuthkit.autopsy.allcasessearch.CorrelationAttributeInstanceNode; +import org.sleuthkit.autopsy.contentviewers.MessageContentViewer; /** * Visitor pattern that goes over all nodes in the directory tree. This includes @@ -186,6 +187,11 @@ public interface DisplayableItemNodeVisitor { T visit(InterestingHits.InterestingItemTypeNode aThis); + /* + * Attachments + */ + T visit(AttachmentNode node); + /** * Visitor with an implementable default behavior for all types. Override * specific visit types to not use the default behavior. @@ -522,5 +528,11 @@ public interface DisplayableItemNodeVisitor { public T visit(Accounts.DefaultAccountTypeNode node) { return defaultVisit(node); } + + @Override + public T visit(AttachmentNode node) { + return defaultVisit(node); + } + } } diff --git a/Core/src/org/sleuthkit/autopsy/images/document-question-16.png b/Core/src/org/sleuthkit/autopsy/images/document-question-16.png new file mode 100644 index 0000000000000000000000000000000000000000..c084ead81a1ed7c285437952ccf7399fa29809e5 GIT binary patch literal 269 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`EX7WqAsj$Z!;#Vf4nJ za0`PlBg3pY5?<$RSB zi#vEIj$7HoOmE}Ic14xRd-j|Zxb^1ai4Li%ix1h7*94RtF;czI^2GAtEA3Q?)eC;5 zP162Q7T~_^yhKvF@HUAO&LtL)gzm%?{%}oblQv7-cTDzjMY5zm3ty)+_xf+CmK~7` z-iKa0&!paC;IF!W>IcuYi`QE6b*)S3b9nvm-#dkFuY2Nowa?Xetk;g~lMk~3I-SAO L)z4*}Q$iB}LVaXP literal 0 HcmV?d00001 diff --git a/Core/src/org/sleuthkit/autopsy/images/url-16.png b/Core/src/org/sleuthkit/autopsy/images/url-16.png new file mode 100644 index 0000000000000000000000000000000000000000..414cff505ddee9ce483e13a6d08027c3be0b90c9 GIT binary patch literal 753 zcmV`hv$7iJ^~wQ0Pqt3 zrVc#V{|_4!H*dqlF20eem~mV+tJ_O z53AJ*nO7tLQk|96c&x7W(w)wSXgXPs^R*2)S91oX`%hqGTEe3jZ!j@64YSz{gTa7c zZ~-otYb_;6^6;rEZROXl-msMK+Y4hJja-8o5)vrLr%|%ogwd%ex?jInrfh9(MOW7| z^!E0y8R+x|+flQ%K8M+%KU`ENKMwLpXL|%{A>qSdT j%lKx4HUJOs|6KkBwK_NX>lS^`00000NkvXXu0mjfc2-dl literal 0 HcmV?d00001 From 2c23b5b52f54e3995b5090883170545b1762f34e Mon Sep 17 00:00:00 2001 From: Raman Arora Date: Tue, 12 Nov 2019 13:04:31 -0500 Subject: [PATCH 2/3] Address Codacy comments on previous commit. --- .../sleuthkit/autopsy/contentviewers/MessageContentViewer.java | 3 +++ Core/src/org/sleuthkit/autopsy/datamodel/AttachmentNode.java | 2 +- .../autopsy/datamodel/DisplayableItemNodeVisitor.java | 1 - 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java index 1046c3177b..66f0af29fe 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java @@ -661,6 +661,9 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont } + /** + * Creates child nodes for message attachments. + */ final class AttachmentsChildren extends Children.Keys { private final Set attachments; diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AttachmentNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/AttachmentNode.java index cc9e364790..7932b1e421 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AttachmentNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AttachmentNode.java @@ -127,7 +127,7 @@ public class AttachmentNode extends DisplayableItemNode { actionsList.addAll(ContextMenuExtensionPoint.getActions()); } - return actionsList.toArray(new Action[actionsList.size()]); + return actionsList.toArray(new Action[0]); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java b/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java index 660368de81..541cbc38f3 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java @@ -32,7 +32,6 @@ import org.sleuthkit.autopsy.datamodel.FileSize.FileSizeRootNode; import org.sleuthkit.autopsy.datamodel.FileTypes.FileTypesNode; import org.sleuthkit.autopsy.datamodel.accounts.Accounts; import org.sleuthkit.autopsy.allcasessearch.CorrelationAttributeInstanceNode; -import org.sleuthkit.autopsy.contentviewers.MessageContentViewer; /** * Visitor pattern that goes over all nodes in the directory tree. This includes From 18bffe4d186bfb5aa5bde3eb81e45923e3a90fad Mon Sep 17 00:00:00 2001 From: Raman Arora Date: Wed, 13 Nov 2019 14:24:26 -0500 Subject: [PATCH 3/3] 5708: fix the attachment count in CVT messages table. --- .../relationships/MessageNode.java | 22 ++++++++++++++++++- .../contentviewers/MessageContentViewer.java | 2 +- .../autopsy/datamodel/AttachmentNode.java | 2 +- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/MessageNode.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/MessageNode.java index 1c18ba5952..aad258ed0f 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/MessageNode.java +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/MessageNode.java @@ -18,6 +18,7 @@ */ package org.sleuthkit.autopsy.communications.relationships; +import com.google.gson.Gson; import java.util.logging.Level; import javax.swing.Action; import org.apache.commons.lang3.StringUtils; @@ -38,6 +39,8 @@ import static org.sleuthkit.autopsy.communications.relationships.RelationshipsNo import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode; import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG; import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE; +import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.blackboardutils.MessageAttachments; /** * Wraps a BlackboardArtifact as an AbstractNode for use in an OutlookView @@ -97,7 +100,7 @@ class MessageNode extends BlackboardArtifactNode { sheetSet.put(new NodeProperty<>("Subject", Bundle.MessageNode_Node_Property_Subject(), "", getAttributeDisplayString(artifact, TSK_SUBJECT))); //NON-NLS try { - sheetSet.put(new NodeProperty<>("Attms", Bundle.MessageNode_Node_Property_Attms(), "", artifact.getChildrenCount())); //NON-NLS + sheetSet.put(new NodeProperty<>("Attms", Bundle.MessageNode_Node_Property_Attms(), "", getAttachmentsCount())); //NON-NLS } catch (TskCoreException ex) { logger.log(Level.WARNING, "Error loading attachment count for " + artifact, ex); //NON-NLS } @@ -144,4 +147,21 @@ class MessageNode extends BlackboardArtifactNode { public Action getPreferredAction() { return preferredAction; } + + private int getAttachmentsCount() throws TskCoreException { + final BlackboardArtifact artifact = getArtifact(); + int attachmentsCount; + + // Attachments are specified in an attribute TSK_ATTACHMENTS as JSON attribute + BlackboardAttribute attachmentsAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ATTACHMENTS)); + if (attachmentsAttr != null) { + String jsonVal = attachmentsAttr.getValueString(); + MessageAttachments msgAttachments = new Gson().fromJson(jsonVal, MessageAttachments.class); + attachmentsCount = msgAttachments.getAttachmentsCount(); + } else { // legacy attachments may be children of message artifact. + attachmentsCount = artifact.getChildrenCount(); + } + + return attachmentsCount; + } } diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java index 66f0af29fe..35f9efeddb 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java @@ -664,7 +664,7 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont /** * Creates child nodes for message attachments. */ - final class AttachmentsChildren extends Children.Keys { + private static class AttachmentsChildren extends Children.Keys { private final Set attachments; diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AttachmentNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/AttachmentNode.java index 7932b1e421..c095f48fee 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AttachmentNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AttachmentNode.java @@ -57,7 +57,7 @@ import org.sleuthkit.datamodel.blackboardutils.URLAttachment; * Node for a message attachment. * */ -public class AttachmentNode extends DisplayableItemNode { +public final class AttachmentNode extends DisplayableItemNode { private static final Logger LOGGER = Logger.getLogger(MessageContentViewer.class.getName());