From 4bed42af89a3d7eafdc6040f64508dd817d2aa04 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Thu, 7 Dec 2017 11:37:03 +0100 Subject: [PATCH 1/7] select all th devices initially --- .../sleuthkit/autopsy/communications/FiltersPanel.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java index 93e3230576..3aabed3a46 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java @@ -89,6 +89,7 @@ final public class FiltersPanel extends javax.swing.JPanel { updateTimeZone(); updateFilters(); + setAllDevicesSelected(true); UserPreferences.addChangeListener(preferenceChangeEvent -> { if (preferenceChangeEvent.getKey().equals(UserPreferences.DISPLAY_TIMES_IN_LOCAL_TIME)) { updateTimeZone(); @@ -203,7 +204,6 @@ final public class FiltersPanel extends javax.swing.JPanel { } } - /** * This method is called from within the constructor to initialize the form. * WARNING: Do NOT modify this code. The content of this method is always @@ -430,7 +430,7 @@ final public class FiltersPanel extends javax.swing.JPanel { commsFilter.addAndFilter(getDateRangeFilter()); commsFilter.addAndFilter(new CommunicationsFilter.RelationshipTypeFilter( ImmutableSet.of(CALL_LOG, MESSAGE))); - + try { final CommunicationsManager commsManager = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager(); em.setRootContext(new AbstractNode(Children.create(new AccountsRootChildren(commsManager, commsFilter), true))); @@ -449,9 +449,9 @@ final public class FiltersPanel extends javax.swing.JPanel { private DeviceFilter getDeviceFilter() { DeviceFilter deviceFilter = new DeviceFilter( devicesMap.entrySet().stream() - .filter(entry -> entry.getValue().isSelected()) - .map(Entry::getKey) - .collect(Collectors.toSet())); + .filter(entry -> entry.getValue().isSelected()) + .map(Entry::getKey) + .collect(Collectors.toSet())); return deviceFilter; } From e3c5264b2d3fe2631f0a6725d31daede7a2f04fc Mon Sep 17 00:00:00 2001 From: millmanorama Date: Fri, 8 Dec 2017 12:22:30 +0100 Subject: [PATCH 2/7] validate that at least one device and one account type is selected --- .../autopsy/communications/Bundle.properties | 2 + .../autopsy/communications/FiltersPanel.form | 44 +++++++++++++++-- .../autopsy/communications/FiltersPanel.java | 49 ++++++++++++++++--- 3 files changed, 85 insertions(+), 10 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties index af6d79d8ab..11b58aab80 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties @@ -12,3 +12,5 @@ FiltersPanel.dateRangeLabel.text=Date Range: FiltersPanel.startCheckBox.text=Start: FiltersPanel.endCheckBox.text=End: FiltersPanel.refreshButton.text=Refresh +FiltersPanel.deviceRequiredLabel.text=Select at least one. +FiltersPanel.accountTypeRequiredLabel.text=Select at least one. diff --git a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.form b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.form index da402cb4e6..9ae8db86bb 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.form +++ b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.form @@ -87,9 +87,10 @@ - + - + + @@ -111,7 +112,10 @@ - + + + + @@ -166,6 +170,20 @@ + + + + + + + + + + + + + + @@ -176,6 +194,7 @@ + @@ -194,7 +213,10 @@ - + + + + @@ -261,6 +283,20 @@ + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java index 3aabed3a46..5b2d1dbadd 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java @@ -19,6 +19,7 @@ package org.sleuthkit.autopsy.communications; import com.google.common.collect.ImmutableSet; +import java.awt.event.ItemListener; import java.beans.PropertyChangeListener; import java.time.LocalDate; import java.time.ZoneId; @@ -69,11 +70,15 @@ final public class FiltersPanel extends javax.swing.JPanel { private final Map devicesMap = new HashMap<>(); private final PropertyChangeListener ingestListener; + private final ItemListener validationListener; + private boolean needsRefresh; @NbBundle.Messages({"refreshText=Refresh Results", "applyText=Apply"}) public FiltersPanel() { initComponents(); + deviceRequiredLabel.setVisible(false); + accountTypeRequiredLabel.setVisible(false); startDatePicker.setDate(LocalDate.now().minusWeeks(3)); endDatePicker.setDateToToday(); startDatePicker.getSettings().setVetoPolicy( @@ -88,6 +93,16 @@ final public class FiltersPanel extends javax.swing.JPanel { ); updateTimeZone(); + validationListener = itemEvent -> { + boolean someDevice = devicesMap.values().stream().anyMatch(JCheckBox::isSelected); + boolean someAccountType = accountTypeMap.values().stream().anyMatch(JCheckBox::isSelected); + deviceRequiredLabel.setVisible(someDevice == false); + accountTypeRequiredLabel.setVisible(someAccountType == false); + + applyFiltersButton.setEnabled(someDevice && someAccountType); + refreshButton.setEnabled(someDevice && someAccountType && needsRefresh); + }; + updateFilters(); setAllDevicesSelected(true); UserPreferences.addChangeListener(preferenceChangeEvent -> { @@ -100,6 +115,7 @@ final public class FiltersPanel extends javax.swing.JPanel { String eventType = pce.getPropertyName(); if (eventType.equals(DATA_ADDED.toString())) { updateFilters(); + needsRefresh = true; refreshButton.setEnabled(true); } }; @@ -172,6 +188,7 @@ final public class FiltersPanel extends javax.swing.JPanel { + "\"/>" + type.getDisplayName() + "", true ); + jCheckBox.addItemListener(validationListener); accountTypePane.add(jCheckBox); return jCheckBox; }); @@ -192,6 +209,7 @@ final public class FiltersPanel extends javax.swing.JPanel { //store the device id in the map, but display a datasource name in the UI. devicesMap.computeIfAbsent(dataSource.getDeviceId(), ds -> { final JCheckBox jCheckBox = new JCheckBox(dsName, false); + jCheckBox.addItemListener(validationListener); devicesPane.add(jCheckBox); return jCheckBox; }); @@ -217,9 +235,9 @@ final public class FiltersPanel extends javax.swing.JPanel { applyFiltersButton.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.applyFiltersButton.text")); // NOI18N applyFiltersButton.setPreferredSize(null); - filtersTitleLabel.setFont(new java.awt.Font("Tahoma", 0, 16)); // NOI18N filtersTitleLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/funnel.png"))); // NOI18N filtersTitleLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.filtersTitleLabel.text")); // NOI18N + filtersTitleLabel.setFont(new java.awt.Font("Tahoma", 0, 16)); // NOI18N unCheckAllAccountTypesButton.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.unCheckAllAccountTypesButton.text")); // NOI18N unCheckAllAccountTypesButton.addActionListener(new java.awt.event.ActionListener() { @@ -241,15 +259,21 @@ final public class FiltersPanel extends javax.swing.JPanel { accountTypePane.setLayout(new javax.swing.BoxLayout(accountTypePane, javax.swing.BoxLayout.Y_AXIS)); jScrollPane3.setViewportView(accountTypePane); + accountTypeRequiredLabel.setForeground(new java.awt.Color(255, 0, 0)); + accountTypeRequiredLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/error-icon-16.png"))); // NOI18N + accountTypeRequiredLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.accountTypeRequiredLabel.text")); // NOI18N + accountTypeRequiredLabel.setHorizontalTextPosition(javax.swing.SwingConstants.LEFT); + javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2); jPanel2.setLayout(jPanel2Layout); jPanel2Layout.setHorizontalGroup( jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel2Layout.createSequentialGroup() .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addGroup(javax.swing.GroupLayout.Alignment.LEADING, jPanel2Layout.createSequentialGroup() + .addGroup(jPanel2Layout.createSequentialGroup() .addComponent(accountTypesLabel) - .addGap(0, 0, Short.MAX_VALUE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(accountTypeRequiredLabel)) .addGroup(jPanel2Layout.createSequentialGroup() .addContainerGap() .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) @@ -264,7 +288,9 @@ final public class FiltersPanel extends javax.swing.JPanel { jPanel2Layout.setVerticalGroup( jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(jPanel2Layout.createSequentialGroup() - .addComponent(accountTypesLabel) + .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(accountTypesLabel) + .addComponent(accountTypeRequiredLabel)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jScrollPane3, javax.swing.GroupLayout.DEFAULT_SIZE, 245, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) @@ -297,13 +323,19 @@ final public class FiltersPanel extends javax.swing.JPanel { devicesPane.setLayout(new javax.swing.BoxLayout(devicesPane, javax.swing.BoxLayout.Y_AXIS)); jScrollPane2.setViewportView(devicesPane); + deviceRequiredLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/error-icon-16.png"))); // NOI18N + deviceRequiredLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.deviceRequiredLabel.text")); // NOI18N + deviceRequiredLabel.setForeground(new java.awt.Color(255, 0, 0)); + deviceRequiredLabel.setHorizontalTextPosition(javax.swing.SwingConstants.LEFT); + javax.swing.GroupLayout jPanel3Layout = new javax.swing.GroupLayout(jPanel3); jPanel3.setLayout(jPanel3Layout); jPanel3Layout.setHorizontalGroup( jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(jPanel3Layout.createSequentialGroup() .addComponent(devicesLabel) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(deviceRequiredLabel)) .addGroup(jPanel3Layout.createSequentialGroup() .addContainerGap() .addGroup(jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -317,7 +349,9 @@ final public class FiltersPanel extends javax.swing.JPanel { jPanel3Layout.setVerticalGroup( jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(jPanel3Layout.createSequentialGroup() - .addComponent(devicesLabel) + .addGroup(jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(devicesLabel) + .addComponent(deviceRequiredLabel)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 96, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) @@ -438,6 +472,7 @@ final public class FiltersPanel extends javax.swing.JPanel { logger.log(Level.SEVERE, "There was an error getting the CommunicationsManager for the current case.", ex); } + needsRefresh = false; refreshButton.setEnabled(false); } @@ -532,11 +567,13 @@ final public class FiltersPanel extends javax.swing.JPanel { // Variables declaration - do not modify//GEN-BEGIN:variables private final javax.swing.JPanel accountTypePane = new javax.swing.JPanel(); + private final javax.swing.JLabel accountTypeRequiredLabel = new javax.swing.JLabel(); private final javax.swing.JLabel accountTypesLabel = new javax.swing.JLabel(); private final javax.swing.JButton applyFiltersButton = new javax.swing.JButton(); private final javax.swing.JButton checkAllAccountTypesButton = new javax.swing.JButton(); private final javax.swing.JButton checkAllDevicesButton = new javax.swing.JButton(); private final javax.swing.JLabel dateRangeLabel = new javax.swing.JLabel(); + private final javax.swing.JLabel deviceRequiredLabel = new javax.swing.JLabel(); private final javax.swing.JLabel devicesLabel = new javax.swing.JLabel(); private final javax.swing.JPanel devicesPane = new javax.swing.JPanel(); private final javax.swing.JCheckBox endCheckBox = new javax.swing.JCheckBox(); From 60ba9f5fe8ec4f433bacb68b94eb10345645dd4a Mon Sep 17 00:00:00 2001 From: millmanorama Date: Fri, 8 Dec 2017 12:45:49 +0100 Subject: [PATCH 3/7] refactor validation listener --- .../autopsy/communications/FiltersPanel.java | 51 +++++++++++++------ 1 file changed, 36 insertions(+), 15 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java index 5b2d1dbadd..44d46ceecd 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java @@ -69,10 +69,19 @@ final public class FiltersPanel extends javax.swing.JPanel { @ThreadConfined(type = ThreadConfined.ThreadType.AWT) private final Map devicesMap = new HashMap<>(); + /** + * Listens to ingest events to enable refresh button + */ private final PropertyChangeListener ingestListener; - private final ItemListener validationListener; private boolean needsRefresh; + /** + * Listen to check box state changes and validates that at least one box is + * selected for device and account type ( other wise there will be no + * results) + */ + private final ItemListener validationListener; + @NbBundle.Messages({"refreshText=Refresh Results", "applyText=Apply"}) public FiltersPanel() { @@ -93,15 +102,7 @@ final public class FiltersPanel extends javax.swing.JPanel { ); updateTimeZone(); - validationListener = itemEvent -> { - boolean someDevice = devicesMap.values().stream().anyMatch(JCheckBox::isSelected); - boolean someAccountType = accountTypeMap.values().stream().anyMatch(JCheckBox::isSelected); - deviceRequiredLabel.setVisible(someDevice == false); - accountTypeRequiredLabel.setVisible(someAccountType == false); - - applyFiltersButton.setEnabled(someDevice && someAccountType); - refreshButton.setEnabled(someDevice && someAccountType && needsRefresh); - }; + validationListener = itemEvent -> validateFilters(); updateFilters(); setAllDevicesSelected(true); @@ -116,7 +117,7 @@ final public class FiltersPanel extends javax.swing.JPanel { if (eventType.equals(DATA_ADDED.toString())) { updateFilters(); needsRefresh = true; - refreshButton.setEnabled(true); + validateFilters(); } }; @@ -124,6 +125,24 @@ final public class FiltersPanel extends javax.swing.JPanel { refreshButton.addActionListener(e -> applyFilters()); } + /** + * Validate that filters are in a consistent state and will result in some + * results. Checks that at least one device and at least one account type is + * selected. Disables the apply and refresh button and shows warnings if the + * filters are not valid. + */ + private void validateFilters() { + boolean someDevice = devicesMap.values().stream().anyMatch(JCheckBox::isSelected); + boolean someAccountType = accountTypeMap.values().stream().anyMatch(JCheckBox::isSelected); + + deviceRequiredLabel.setVisible(someDevice == false); + accountTypeRequiredLabel.setVisible(someAccountType == false); + + applyFiltersButton.setEnabled(someDevice && someAccountType); + refreshButton.setEnabled(someDevice && someAccountType && needsRefresh); + + } + /** * Update the filter widgets, and apply them. */ @@ -473,7 +492,7 @@ final public class FiltersPanel extends javax.swing.JPanel { } needsRefresh = false; - refreshButton.setEnabled(false); + validateFilters(); } /** @@ -496,9 +515,11 @@ final public class FiltersPanel extends javax.swing.JPanel { * @return an AccountTypeFilter */ private AccountTypeFilter getAccountTypeFilter() { - AccountTypeFilter accountTypeFilter = new AccountTypeFilter(accountTypeMap.entrySet().stream() - .filter(entry -> entry.getValue().isSelected()) - .map(entry -> entry.getKey()).collect(Collectors.toSet())); + AccountTypeFilter accountTypeFilter = new AccountTypeFilter( + accountTypeMap.entrySet().stream() + .filter(entry -> entry.getValue().isSelected()) + .map(entry -> entry.getKey()) + .collect(Collectors.toSet())); return accountTypeFilter; } From 2f7d8614a203d9d14fc901b3da06ef47c118d513 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Fri, 8 Dec 2017 13:34:20 +0100 Subject: [PATCH 4/7] restrict indexed text viewer to credit card accounts --- .../keywordsearch/ExtractedContentViewer.java | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/ExtractedContentViewer.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/ExtractedContentViewer.java index 315066cbbe..dbed68e660 100755 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/ExtractedContentViewer.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/ExtractedContentViewer.java @@ -34,6 +34,7 @@ import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.Account; import org.sleuthkit.datamodel.BlackboardArtifact; import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT; import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT; @@ -53,6 +54,7 @@ public class ExtractedContentViewer implements DataContentViewer { private static final long INVALID_DOCUMENT_ID = 0L; private static final BlackboardAttribute.Type TSK_ASSOCIATED_ARTIFACT_TYPE = new BlackboardAttribute.Type(TSK_ASSOCIATED_ARTIFACT); + public static final BlackboardAttribute.Type TSK_ACCOUNT_TYPE = new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE); private ExtractedContentPanel panel; private volatile Node currentNode = null; @@ -173,14 +175,13 @@ public class ExtractedContentViewer implements DataContentViewer { } } panel.updateControls(currentSource); - + String contentName = ""; if (content != null) { contentName = content.getName(); } setPanel(contentName, sources); - - + } static private IndexedText getRawArtifactText(Lookup nodeLookup) throws TskCoreException { @@ -261,7 +262,7 @@ public class ExtractedContentViewer implements DataContentViewer { @Override public void resetComponent() { - + panel.resetDisplay(); currentNode = null; currentSource = null; @@ -274,14 +275,22 @@ public class ExtractedContentViewer implements DataContentViewer { } /* - * Is there a credit card artifact in the lookup + * Is there a credit card or keyword hit artifact in the lookup */ Collection artifacts = node.getLookup().lookupAll(BlackboardArtifact.class); if (artifacts != null) { for (BlackboardArtifact art : artifacts) { final int artifactTypeID = art.getArtifactTypeID(); - if (artifactTypeID == TSK_ACCOUNT.getTypeID() - || artifactTypeID == TSK_KEYWORD_HIT.getTypeID()) { + if (artifactTypeID == TSK_ACCOUNT.getTypeID()) { + try { + BlackboardAttribute attribute = art.getAttribute(TSK_ACCOUNT_TYPE); + if (attribute != null && Account.Type.CREDIT_CARD.getTypeName().equals(attribute.getValueString())) { + return true; + } + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error getting TSK_ACCOUNT_TYPE attribute from artifact " + art.getArtifactID(), ex); + } + } else if (artifactTypeID == TSK_KEYWORD_HIT.getTypeID()) { return true; } } From 79a40100023a25da9390ebd712a4c30f6086784c Mon Sep 17 00:00:00 2001 From: millmanorama Date: Fri, 8 Dec 2017 15:38:19 +0100 Subject: [PATCH 5/7] WIP --- .../contentviewers/MessageContentViewer.java | 30 +++++++++++++++---- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java index ec49948b78..8a51b8a87e 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java @@ -47,8 +47,10 @@ import org.sleuthkit.autopsy.directorytree.NewWindowViewAction; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT; import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE; import org.sleuthkit.datamodel.BlackboardAttribute; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_RCVD; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION; @@ -106,8 +108,8 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont drp.open(); drpExplorerManager = drp.getExplorerManager(); - drpExplorerManager.addPropertyChangeListener(evt - -> viewInNewWindowButton.setEnabled(drpExplorerManager.getSelectedNodes().length == 1)); + drpExplorerManager.addPropertyChangeListener(evt -> + viewInNewWindowButton.setEnabled(drpExplorerManager.getSelectedNodes().length == 1)); } /** @@ -463,10 +465,26 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont @Override public boolean isSupported(Node node) { - BlackboardArtifact artifact = node.getLookup().lookup(BlackboardArtifact.class); - return ((artifact != null) - && ((artifact.getArtifactTypeID() == TSK_EMAIL_MSG.getTypeID()) - || (artifact.getArtifactTypeID() == TSK_MESSAGE.getTypeID()))); + BlackboardArtifact nodeArtifact = node.getLookup().lookup(BlackboardArtifact.class); + if (nodeArtifact != null && nodeArtifact.getArtifactTypeID() == TSK_KEYWORD_HIT.getTypeID()) { + try { + BlackboardAttribute attribute = nodeArtifact.getAttribute(new BlackboardAttribute.Type(TSK_ASSOCIATED_ARTIFACT)); + if (attribute != null) { + final long associatedArtifactID = attribute.getValueLong(); + BlackboardArtifact assoc = (BlackboardArtifact) nodeArtifact.getSleuthkitCase().getArtifactByArtifactID(associatedArtifactID); + if ((assoc != null) + && ((assoc.getArtifactTypeID() == TSK_EMAIL_MSG.getTypeID()) + || (assoc.getArtifactTypeID() == TSK_MESSAGE.getTypeID()))) { + return true; + } + } + } catch (TskCoreException ex) { + LOGGER.log(Level.SEVERE, "error getting associated artifact", ex); + } + } + return ((nodeArtifact != null) + && ((nodeArtifact.getArtifactTypeID() == TSK_EMAIL_MSG.getTypeID()) + || (nodeArtifact.getArtifactTypeID() == TSK_MESSAGE.getTypeID()))); } @Override From a292bb83d210d5135eee39cf1ade47bf85cd7fb8 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Mon, 11 Dec 2017 11:51:09 +0100 Subject: [PATCH 6/7] enable MessageContentViewer for key word hit artifacts where the hit is in the indexe text of a message/email artifact --- .../contentviewers/MessageContentViewer.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java index 8a51b8a87e..bffc4934e3 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java @@ -408,6 +408,17 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont return; } + if (artifact.getArtifactTypeID() == TSK_KEYWORD_HIT.getTypeID()) { + try { + BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(TSK_ASSOCIATED_ARTIFACT)); + if (attribute != null) { + final long associatedArtifactID = attribute.getValueLong(); + artifact = artifact.getSleuthkitCase().getArtifactByArtifactId(associatedArtifactID); + } + } catch (TskCoreException ex) { + LOGGER.log(Level.SEVERE, "error getting associated artifact", ex); + } + } if (artifact.getArtifactTypeID() == TSK_MESSAGE.getTypeID()) { displayMsg(); } else if (artifact.getArtifactTypeID() == TSK_EMAIL_MSG.getTypeID()) { @@ -471,7 +482,7 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont BlackboardAttribute attribute = nodeArtifact.getAttribute(new BlackboardAttribute.Type(TSK_ASSOCIATED_ARTIFACT)); if (attribute != null) { final long associatedArtifactID = attribute.getValueLong(); - BlackboardArtifact assoc = (BlackboardArtifact) nodeArtifact.getSleuthkitCase().getArtifactByArtifactID(associatedArtifactID); + BlackboardArtifact assoc = nodeArtifact.getSleuthkitCase().getArtifactByArtifactId(associatedArtifactID); if ((assoc != null) && ((assoc.getArtifactTypeID() == TSK_EMAIL_MSG.getTypeID()) || (assoc.getArtifactTypeID() == TSK_MESSAGE.getTypeID()))) { From d981789c3bf73afbe15d3ee67397de3b406319c4 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Mon, 11 Dec 2017 12:54:23 +0100 Subject: [PATCH 7/7] cleanup and refactoring in MessageContentViewer --- .../contentviewers/MessageContentViewer.java | 74 ++++++++++++++----- 1 file changed, 54 insertions(+), 20 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java index bffc4934e3..df678a6635 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2017 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -75,6 +75,7 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont private static final long serialVersionUID = 1L; private static final Logger LOGGER = Logger.getLogger(MessageContentViewer.class.getName()); + private static final BlackboardAttribute.Type TSK_ASSOCIATED_TYPE = new BlackboardAttribute.Type(TSK_ASSOCIATED_ARTIFACT); private static final int HDR_TAB_INDEX = 0; private static final int TEXT_TAB_INDEX = 1; @@ -94,9 +95,10 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont /** * Creates new MessageContentViewer */ + @NbBundle.Messages("MessageContentViewer.AtrachmentsPanel.title=Attachments") public MessageContentViewer() { initComponents(); - drp = DataResultPanel.createInstanceUninitialized("Attachments", "", Node.EMPTY, 0, null); + drp = DataResultPanel.createInstanceUninitialized(Bundle.MessageContentViewer_AtrachmentsPanel_title(), "", Node.EMPTY, 0, null); attachmentsScrollPane.setViewportView(drp); msgbodyTabbedPane.setEnabledAt(ATTM_TAB_INDEX, true); @@ -408,17 +410,20 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont return; } + /* + * If the artifact is a keyword hit, use the associated artifact as the + * one to show in this viewer + */ if (artifact.getArtifactTypeID() == TSK_KEYWORD_HIT.getTypeID()) { try { - BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(TSK_ASSOCIATED_ARTIFACT)); - if (attribute != null) { - final long associatedArtifactID = attribute.getValueLong(); - artifact = artifact.getSleuthkitCase().getArtifactByArtifactId(associatedArtifactID); - } + getAssociatedArtifact(artifact).ifPresent(associatedArtifact -> { + artifact = associatedArtifact; + }); } catch (TskCoreException ex) { LOGGER.log(Level.SEVERE, "error getting associated artifact", ex); } } + if (artifact.getArtifactTypeID() == TSK_MESSAGE.getTypeID()) { displayMsg(); } else if (artifact.getArtifactTypeID() == TSK_EMAIL_MSG.getTypeID()) { @@ -428,6 +433,24 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont } } + /** + * Get the artifact associated with the given artifact, if there is one. + * + * @param artifact The artifact to get the associated artifact from. Must + * not be null + * + * @throws TskCoreException If there is a critical error querying the DB. + * @return An optional containing the artifact associated with the given + * artifact, if there is one. + */ + private static Optional getAssociatedArtifact(final BlackboardArtifact artifact) throws TskCoreException { + BlackboardAttribute attribute = artifact.getAttribute(TSK_ASSOCIATED_TYPE); + if (attribute != null) { + return Optional.of(artifact.getSleuthkitCase().getArtifactByArtifactId(attribute.getValueLong())); + } + return Optional.empty(); + } + @Override @NbBundle.Messages("MessageContentViewer.title=Message") public String getTitle() { @@ -477,25 +500,36 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont @Override public boolean isSupported(Node node) { BlackboardArtifact nodeArtifact = node.getLookup().lookup(BlackboardArtifact.class); - if (nodeArtifact != null && nodeArtifact.getArtifactTypeID() == TSK_KEYWORD_HIT.getTypeID()) { + + if (nodeArtifact == null) { + return false; + } + //if the artifact is a keyword hit, check if its associated artifact is a message or email. + if (nodeArtifact.getArtifactTypeID() == TSK_KEYWORD_HIT.getTypeID()) { try { - BlackboardAttribute attribute = nodeArtifact.getAttribute(new BlackboardAttribute.Type(TSK_ASSOCIATED_ARTIFACT)); - if (attribute != null) { - final long associatedArtifactID = attribute.getValueLong(); - BlackboardArtifact assoc = nodeArtifact.getSleuthkitCase().getArtifactByArtifactId(associatedArtifactID); - if ((assoc != null) - && ((assoc.getArtifactTypeID() == TSK_EMAIL_MSG.getTypeID()) - || (assoc.getArtifactTypeID() == TSK_MESSAGE.getTypeID()))) { - return true; - } + if (getAssociatedArtifact(nodeArtifact).map(MessageContentViewer::isMessageArtifact).orElse(false)) { + return true; } } catch (TskCoreException ex) { LOGGER.log(Level.SEVERE, "error getting associated artifact", ex); } } - return ((nodeArtifact != null) - && ((nodeArtifact.getArtifactTypeID() == TSK_EMAIL_MSG.getTypeID()) - || (nodeArtifact.getArtifactTypeID() == TSK_MESSAGE.getTypeID()))); + return isMessageArtifact(nodeArtifact); + } + + /** + * Is the given artifact one that can be shown in this viewer? + * + * @param nodeArtifact An artifact that might be a message. Must not be + * null. + * + * @return True if the given artifact can be shown as a message in this + * viewer. + */ + private static boolean isMessageArtifact(BlackboardArtifact nodeArtifact) { + final int artifactTypeID = nodeArtifact.getArtifactTypeID(); + return artifactTypeID == TSK_EMAIL_MSG.getTypeID() + || artifactTypeID == TSK_MESSAGE.getTypeID(); } @Override