Added the new MessageBrowser to the VisualizationPanel

This commit is contained in:
Kelly Kelly
2019-04-16 10:48:56 -04:00
parent febbb12123
commit 7e7ba8b4de
12 changed files with 42 additions and 749 deletions
@@ -1,88 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2017 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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.communications;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.python.google.common.collect.Iterables;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.AccountDeviceInstance;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.CommunicationsFilter;
import org.sleuthkit.datamodel.CommunicationsManager;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.TskCoreException;
/**
* 'Root' Node for the Account/Messages area. Has children which are all the
* relationships of all the accounts in this node.
*
*/
final class AccountDetailsNode extends AbstractNode {
private final static Logger logger = Logger.getLogger(AccountDetailsNode.class.getName());
AccountDetailsNode(Set<AccountDeviceInstance> accountDeviceInstances, CommunicationsFilter filter, CommunicationsManager commsManager) {
super(Children.create(new AccountRelationshipChildren(accountDeviceInstances, commsManager, filter), true));
String displayName = (accountDeviceInstances.size() == 1)
? Iterables.getOnlyElement(accountDeviceInstances).getAccount().getTypeSpecificID()
: accountDeviceInstances.size() + " accounts";
setDisplayName(displayName);
}
/**
* Children object for the relationships that the accounts are part of.
*/
private static class AccountRelationshipChildren extends ChildFactory<Content> {
private final Set<AccountDeviceInstance> accountDeviceInstances;
private final CommunicationsManager commsManager;
private final CommunicationsFilter filter;
private AccountRelationshipChildren(Set<AccountDeviceInstance> accountDeviceInstances, CommunicationsManager commsManager, CommunicationsFilter filter) {
this.accountDeviceInstances = accountDeviceInstances;
this.commsManager = commsManager;
this.filter = filter;
}
@Override
protected boolean createKeys(List<Content> list) {
try {
list.addAll(commsManager.getRelationshipSources(accountDeviceInstances, filter));
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error getting communications", ex);
}
return true;
}
@Override
protected Node createNodeForKey(Content t) {
if (t instanceof BlackboardArtifact) {
return new RelationshipNode((BlackboardArtifact) t);
} else {
throw new UnsupportedOperationException("Cannot create a RelationshipNode for non BlackboardArtifact content.");
}
}
}
}
@@ -19,6 +19,7 @@
package org.sleuthkit.autopsy.communications;
import com.google.common.eventbus.Subscribe;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.util.List;
@@ -61,8 +62,11 @@ public final class CVTTopComponent extends TopComponent {
associateLookup(proxyLookup);
// Make sure the Global Actions Context is proxying the selection of the active tab.
browseVisualizeTabPane.addChangeListener(changeEvent -> {
Lookup.Provider selectedComponent = (Lookup.Provider) browseVisualizeTabPane.getSelectedComponent();
proxyLookup.setNewLookups(selectedComponent.getLookup());
Component selectedComponent = browseVisualizeTabPane.getSelectedComponent();
if(selectedComponent instanceof Lookup.Provider) {
Lookup lookup = ((Lookup.Provider)selectedComponent).getLookup();
proxyLookup.setNewLookups(lookup);
}
filtersPane.setDeviceAccountTypeEnabled(browseVisualizeTabPane.getSelectedIndex() != 0);
});
@@ -1,56 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.4" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<EmptySpace min="-2" pref="0" max="-2" attributes="0"/>
<Component id="splitPane" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="0" max="-2" attributes="0"/>
<Component id="splitPane" pref="1083" max="32767" attributes="0"/>
<EmptySpace min="-2" pref="0" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Container class="javax.swing.JSplitPane" name="splitPane">
<Properties>
<Property name="dividerLocation" type="int" value="400"/>
<Property name="dividerSize" type="int" value="10"/>
<Property name="orientation" type="int" value="0"/>
<Property name="resizeWeight" type="double" value="0.5"/>
</Properties>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
<SubComponents>
<Component class="org.sleuthkit.autopsy.communications.MessageDataContent" name="messageDataContent">
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
<JSplitPaneConstraints position="bottom"/>
</Constraint>
</Constraints>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Form>
@@ -1,231 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2017-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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 obt ain 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.communications;
import java.awt.Component;
import java.awt.KeyboardFocusManager;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.HashSet;
import java.util.Set;
import javax.swing.JPanel;
import static javax.swing.SwingUtilities.isDescendingFrom;
import org.openide.explorer.ExplorerManager;
import static org.openide.explorer.ExplorerUtils.createLookup;
import org.openide.nodes.Node;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.corecomponents.DataResultPanel;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
import org.sleuthkit.autopsy.corecomponents.TableFilterNode;
import org.sleuthkit.autopsy.directorytree.DataResultFilterNode;
/**
* The right hand side of the CVT. Has a DataResultPanel to show a listing of
* messages and other account details, and a ContentViewer to show individual
* messages.
*/
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
public final class MessageBrowser extends JPanel implements ExplorerManager.Provider, Lookup.Provider {
private static final long serialVersionUID = 1L;
private final ExplorerManager tableEM;
private final ExplorerManager gacExplorerManager;
private final DataResultPanel messagesResultPanel;
/* lookup that will be exposed through the (Global Actions Context) */
private final ModifiableProxyLookup proxyLookup = new ModifiableProxyLookup();
private final PropertyChangeListener focusPropertyListener = new PropertyChangeListener() {
/**
* Listener that keeps the proxyLookup in sync with the focused area of
* the UI.
*
* Since the embedded MessageContentViewer (attachments panel) is not in
* its own TopComponenet, its selection does not get proxied into the
* Global Actions Context (GAC), and many of the available actions don't
* work on it. Further, we can't put the selection from both the
* Messages table and the Attachments table in the GAC because they
* could both include AbstractFiles, muddling the selection seen by the
* actions. Instead, depending on where the focus is in the window, we
* want to put different Content in the Global Actions Context to be
* picked up by, e.g., the tagging actions. The best way I could figure
* to do this was to listen to all focus events and swap out what is in
* the lookup appropriately. An alternative to this would be to
* investigate using the ContextAwareAction interface.
*
* @see org.sleuthkit.autopsy.timeline.TimeLineTopComponent for a
* similar situation and a similar solution.
*
* @param focusEvent The focus change event.
*/
@Override
public void propertyChange(final PropertyChangeEvent focusEvent) {
if (focusEvent.getPropertyName().equalsIgnoreCase("focusOwner")) {
final Component newFocusOwner = (Component) focusEvent.getNewValue();
if (newFocusOwner == null) {
return;
}
if (isDescendingFrom(newFocusOwner, messageDataContent)) {
//if the focus owner is within the MessageContentViewer ( the attachments table)
proxyLookup.setNewLookups(createLookup(messageDataContent.getExplorerManager(), getActionMap()));
} else if (isDescendingFrom(newFocusOwner, messagesResultPanel)) {
//... or if it is within the Messages table.
proxyLookup.setNewLookups(createLookup(gacExplorerManager, getActionMap()));
}
}
}
};
/**
* Constructs the right hand side of the Communications Visualization Tool
* (CVT).
*
* @param tableEM An explorer manager to listen to as the driver
* of the Message Table.
* @param gacExplorerManager An explorer manager associated with the
* GlobalActionsContext (GAC) so that selections
* in the messages browser can be exposed to
* context-sensitive actions.
*/
@NbBundle.Messages({"MessageBrowser.DataResultViewerTable.title=Messages"})
MessageBrowser(final ExplorerManager tableEM, final ExplorerManager gacExplorerManager) {
this.tableEM = tableEM;
this.gacExplorerManager = gacExplorerManager;
initComponents();
//create an uninitialized DataResultPanel so we can control the ResultViewers that get added.
messagesResultPanel = DataResultPanel.createInstanceUninitialized("Account", "", Node.EMPTY, 0, messageDataContent);
splitPane.setTopComponent(messagesResultPanel);
splitPane.setBottomComponent(messageDataContent);
messagesResultPanel.addResultViewer(new DataResultViewerTable(gacExplorerManager,
Bundle.MessageBrowser_DataResultViewerTable_title()));
messagesResultPanel.open();
this.tableEM.addPropertyChangeListener(new PropertyChangeListener() {
/**
* Listener that pushes selections in the tableEM (the Accounts
* table) into the Messages table.
*
* @param pce The ExplorerManager event.
*/
@Override
public void propertyChange(PropertyChangeEvent pce) {
if (pce.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES)) {
final Node[] selectedNodes = MessageBrowser.this.tableEM.getSelectedNodes();
messagesResultPanel.setNumberOfChildNodes(0);
messagesResultPanel.setNode(null);
messagesResultPanel.setPath("");
if (selectedNodes.length > 0) {
Node rootNode;
final Node selectedNode = selectedNodes[0];
if (selectedNode instanceof AccountDeviceInstanceNode) {
rootNode = makeRootNodeFromAccountDeviceInstanceNodes(selectedNodes);
} else {
rootNode = selectedNode;
}
messagesResultPanel.setPath(rootNode.getDisplayName());
messagesResultPanel.setNode(new TableFilterNode(new DataResultFilterNode(rootNode, gacExplorerManager), true));
}
}
}
private Node makeRootNodeFromAccountDeviceInstanceNodes(final Node[] selectedNodes) {
//Use lookup here?
final AccountDeviceInstanceNode adiNode = (AccountDeviceInstanceNode) selectedNodes[0];
final Set<AccountDeviceInstanceKey> accountDeviceInstances = new HashSet<>();
for (final Node n : selectedNodes) {
//Use lookup here?
accountDeviceInstances.add(((AccountDeviceInstanceNode) n).getAccountDeviceInstanceKey());
}
return SelectionNode.createFromAccounts(accountDeviceInstances, adiNode.getFilter(), adiNode.getCommsManager());
}
}
);
}
@Override
public ExplorerManager getExplorerManager() {
return gacExplorerManager;
}
@Override
public Lookup getLookup() {
return proxyLookup;
}
@Override
public void addNotify() {
super.addNotify();
//add listener that maintains correct selection in the Global Actions Context
KeyboardFocusManager.getCurrentKeyboardFocusManager()
.addPropertyChangeListener("focusOwner", focusPropertyListener);
}
@Override
public void removeNotify() {
super.removeNotify();
KeyboardFocusManager.getCurrentKeyboardFocusManager()
.removePropertyChangeListener("focusOwner", focusPropertyListener);
}
/**
* 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
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
splitPane = new javax.swing.JSplitPane();
messageDataContent = new org.sleuthkit.autopsy.communications.MessageDataContent();
splitPane.setDividerLocation(400);
splitPane.setDividerSize(10);
splitPane.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);
splitPane.setResizeWeight(0.5);
splitPane.setBottomComponent(messageDataContent);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addGap(0, 0, 0)
.addComponent(splitPane))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(0, 0, 0)
.addComponent(splitPane, javax.swing.GroupLayout.DEFAULT_SIZE, 1083, Short.MAX_VALUE)
.addGap(0, 0, 0))
);
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
private org.sleuthkit.autopsy.communications.MessageDataContent messageDataContent;
private javax.swing.JSplitPane splitPane;
// End of variables declaration//GEN-END:variables
}
@@ -48,7 +48,7 @@ import org.sleuthkit.datamodel.TskCoreException;
*/
final class MessageNode extends BlackboardArtifactNode {
private static final Logger logger = Logger.getLogger(RelationshipNode.class.getName());
private static final Logger logger = Logger.getLogger(MessageNode.class.getName());
MessageNode(BlackboardArtifact artifact) {
super(artifact);
@@ -109,6 +109,9 @@ public final class MessagesViewer extends JPanel implements RelationshipsViewer,
if (nodes != null && nodes.length > 0) {
contentViewer.setNode(nodes[0]);
}
else {
contentViewer.setNode(null);
}
}
});
@@ -127,7 +130,6 @@ public final class MessagesViewer extends JPanel implements RelationshipsViewer,
@Override
public void setSelectionInfo(SelectionInfo info) {
// tableEM.setRootContext(new TableFilterNode(new DataResultFilterNode(new AbstractNode(Children.create(new MessagesChildNodeFactory(info), true)), getExplorerManager()), true));
nodeFactory.refresh(info);
}
@@ -19,7 +19,6 @@
package org.sleuthkit.autopsy.communications;
import javax.swing.JPanel;
import org.openide.util.Lookup;
/**
* Displays the Relationship information for the currently selected accounts.
@@ -28,16 +27,21 @@ import org.openide.util.Lookup;
final class RelationshipBrowser extends JPanel {
private SelectionInfo currentSelection;
private final MessagesViewer messagesViewer;
private final ContactsViewer contactsViewer;
/**
* Creates new form RelationshipBrowser
*/
public RelationshipBrowser() {
initComponents();
Lookup.getDefault().lookupAll(RelationshipsViewer.class).forEach((viewer) -> {
tabPane.add(viewer.getDisplayName(), viewer.getPanel());
});
messagesViewer = new MessagesViewer();
contactsViewer = new ContactsViewer();
tabPane.add(messagesViewer.getDisplayName(), messagesViewer);
tabPane.add(contactsViewer.getDisplayName(), contactsViewer);
}
/**
@@ -81,7 +85,9 @@ final class RelationshipBrowser extends JPanel {
}// </editor-fold>//GEN-END:initComponents
private void tabPaneStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_tabPaneStateChanged
((RelationshipsViewer) tabPane.getSelectedComponent()).setSelectionInfo(currentSelection);
if(currentSelection != null) {
((RelationshipsViewer) tabPane.getSelectedComponent()).setSelectionInfo(currentSelection);
}
}//GEN-LAST:event_tabPaneStateChanged
@@ -1,174 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2017-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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.communications;
import java.util.List;
import java.util.TimeZone;
import java.util.logging.Level;
import org.apache.commons.lang3.StringUtils;
import org.openide.nodes.Sheet;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode;
import org.sleuthkit.autopsy.datamodel.NodeProperty;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME;
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_SENT;
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START;
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL_FROM;
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL_TO;
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM;
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.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME;
import org.sleuthkit.datamodel.Tag;
import org.sleuthkit.datamodel.TimeUtilities;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Node for a relationship, as represented by a BlackboardArtifact.
*/
final class RelationshipNode extends BlackboardArtifactNode {
private static final Logger logger = Logger.getLogger(RelationshipNode.class.getName());
RelationshipNode(BlackboardArtifact artifact) {
super(artifact);
final String stripEnd = StringUtils.stripEnd(artifact.getDisplayName(), "s");
String removeEndIgnoreCase = StringUtils.removeEndIgnoreCase(stripEnd, "message");
setDisplayName(removeEndIgnoreCase.isEmpty() ? stripEnd : removeEndIgnoreCase);
}
@Override
protected Sheet createSheet() {
Sheet sheet = new Sheet();
List<Tag> tags = getAllTagsFromDatabase();
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
if (sheetSet == null) {
sheetSet = Sheet.createPropertiesSet();
sheet.put(sheetSet);
}
sheetSet.put(new NodeProperty<>("Type", "Type", "Type", getDisplayName()));
addScoreProperty(sheetSet, tags);
CorrelationAttributeInstance correlationAttribute = null;
if (UserPreferences.hideCentralRepoCommentsAndOccurrences()== false) {
correlationAttribute = getCorrelationAttributeInstance();
}
addCommentProperty(sheetSet, tags, correlationAttribute);
if (UserPreferences.hideCentralRepoCommentsAndOccurrences()== false) {
addCountProperty(sheetSet, correlationAttribute);
}
final BlackboardArtifact artifact = getArtifact();
BlackboardArtifact.ARTIFACT_TYPE fromID = BlackboardArtifact.ARTIFACT_TYPE.fromID(getArtifact().getArtifactTypeID());
if (null != fromID) {
//Consider refactoring this to reduce boilerplate
switch (fromID) {
case TSK_EMAIL_MSG:
sheetSet.put(new NodeProperty<>("From", "From", "From",
StringUtils.strip(getAttributeDisplayString(artifact, TSK_EMAIL_FROM), " \t\n;")));
sheetSet.put(new NodeProperty<>("To", "To", "To",
StringUtils.strip(getAttributeDisplayString(artifact, TSK_EMAIL_TO), " \t\n;")));
sheetSet.put(new NodeProperty<>("Date", "Date", "Date",
getAttributeDisplayString(artifact, TSK_DATETIME_SENT)));
sheetSet.put(new NodeProperty<>("Subject", "Subject", "Subject",
getAttributeDisplayString(artifact, TSK_SUBJECT)));
try {
sheetSet.put(new NodeProperty<>("Attms", "Attms", "Attms", artifact.getChildrenCount()));
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Error loading attachment count for " + artifact, ex);
}
break;
case TSK_MESSAGE:
sheetSet.put(new NodeProperty<>("From", "From", "From",
getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_FROM)));
sheetSet.put(new NodeProperty<>("To", "To", "To",
getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_TO)));
sheetSet.put(new NodeProperty<>("Date", "Date", "Date",
getAttributeDisplayString(artifact, TSK_DATETIME)));
sheetSet.put(new NodeProperty<>("Subject", "Subject", "Subject",
getAttributeDisplayString(artifact, TSK_SUBJECT)));
try {
sheetSet.put(new NodeProperty<>("Attms", "Attms", "Attms", artifact.getChildrenCount()));
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Error loading attachment count for " + artifact, ex);
}
break;
case TSK_CALLLOG:
sheetSet.put(new NodeProperty<>("From", "From", "From",
getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_FROM)));
sheetSet.put(new NodeProperty<>("To", "To", "To",
getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_TO)));
sheetSet.put(new NodeProperty<>("Date", "Date", "Date",
getAttributeDisplayString(artifact, TSK_DATETIME_START)));
break;
default:
break;
}
}
return sheet;
}
/**
*
* Get the display string for the attribute of the given type from the given
* artifact.
*
* @param artifact the value of artifact
* @param attributeType the value of TSK_SUBJECT1
*
* @return The display string, or an empty string if there is no such
* attribute or an an error.
*/
private static String getAttributeDisplayString(final BlackboardArtifact artifact, final ATTRIBUTE_TYPE attributeType) {
try {
BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.fromID(attributeType.getTypeID())));
if (attribute == null) {
return "";
} else if (attributeType.getValueType() == DATETIME) {
return TimeUtilities.epochToTime(attribute.getValueLong(),
TimeZone.getTimeZone(Utils.getUserPreferredZoneId()));
} else {
return attribute.getDisplayString();
}
} catch (TskCoreException tskCoreException) {
logger.log(Level.WARNING, "Error getting attribute value.", tskCoreException);
return "";
}
}
/**
* Circumvent DataResultFilterNode's slightly odd delegation to
* BlackboardArtifactNode.getSourceName().
*
* @return the displayName of this Node, which is the type.
*/
@Override
public String getSourceName() {
return getDisplayName();
}
}
@@ -1,140 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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.communications;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.stream.Collectors;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.openide.util.Lookup;
import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.AccountDeviceInstance;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.CommunicationsFilter;
import org.sleuthkit.datamodel.CommunicationsManager;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.TskCoreException;
/**
* 'Root' Node for the Account/Messages area. Represents all the relationships
* that are selected in the AccountsBrowser or the VisualizationPanel. Can be
* populated with AccountDeviceInstance and/or directly with relationships
* (Content).
*/
final class SelectionNode extends AbstractNode {
private SelectionNode(Children children, Lookup lookup) {
super(children, lookup);
}
static SelectionNode createFromAccountsAndRelationships(
Set<Content> edgeRelationshipArtifacts,
Set<AccountDeviceInstanceKey> accountDeviceInstanceKeys,
CommunicationsFilter filter,
CommunicationsManager commsManager) {
Set<AccountDeviceInstance> accountDeviceInstances = accountDeviceInstanceKeys.stream()
.map(AccountDeviceInstanceKey::getAccountDeviceInstance)
.collect(Collectors.toSet());
SelectionNode node = new SelectionNode(Children.create(
new RelationshipChildren(
edgeRelationshipArtifacts,
accountDeviceInstances,
commsManager,
filter),
true), Lookups.fixed(accountDeviceInstanceKeys.toArray()));
//This is not good for internationalization!!!
String name = "";
final int accounts = accountDeviceInstances.size();
if (accounts > 1) {
name = accounts + " accounts";
} else if (accounts == 1) {
name = Iterables.getOnlyElement(accountDeviceInstances).getAccount().getTypeSpecificID();
}
final int edges = edgeRelationshipArtifacts.size();
if (edges > 0) {
name = name + (name.isEmpty() ? "" : " and ") + edges + " relationship" + (edges > 1 ? "s" : "");
}
node.setDisplayName(name);
return node;
}
static SelectionNode createFromAccounts(
Set<AccountDeviceInstanceKey> accountDeviceInstances,
CommunicationsFilter filter,
CommunicationsManager commsManager) {
return createFromAccountsAndRelationships(Collections.emptySet(), accountDeviceInstances, filter, commsManager);
}
/**
* Children object for the relationships that the accounts are part of.
*/
private static class RelationshipChildren extends ChildFactory<Content> {
static final private Logger logger = Logger.getLogger(RelationshipChildren.class.getName());
private final Set<Content> edgeRelationshipArtifacts;
private final Set<AccountDeviceInstance> accountDeviceInstances;
private final CommunicationsManager commsManager;
private final CommunicationsFilter filter;
private RelationshipChildren(Set<Content> selectedEdgeRelationshipSources, Set<AccountDeviceInstance> selecedAccountDeviceInstances, CommunicationsManager commsManager, CommunicationsFilter filter) {
this.edgeRelationshipArtifacts = selectedEdgeRelationshipSources;
this.accountDeviceInstances = selecedAccountDeviceInstances;
this.commsManager = commsManager;
this.filter = filter;
}
@Override
protected boolean createKeys(List<Content> list) {
try {
final Set<Content> relationshipSources = commsManager.getRelationshipSources(accountDeviceInstances, filter);
list.addAll(Sets.union(relationshipSources, edgeRelationshipArtifacts));
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error getting communications", ex);
}
return true;
}
@Override
protected Node createNodeForKey(Content content) {
if (content instanceof BlackboardArtifact) {
return new RelationshipNode((BlackboardArtifact) content);
} else {
throw new UnsupportedOperationException("Cannot create a RelationshipNode for non BlackboardArtifact content.");
}
}
}
}
@@ -55,7 +55,6 @@ import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyVetoException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -63,11 +62,11 @@ import java.nio.file.Paths;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
@@ -99,12 +98,8 @@ import org.apache.commons.lang3.StringUtils;
import org.controlsfx.control.Notifications;
import org.jdesktop.layout.GroupLayout;
import org.jdesktop.layout.LayoutStyle;
import org.openide.explorer.ExplorerManager;
import org.openide.explorer.ExplorerUtils;
import org.openide.nodes.Node;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.openide.util.lookup.ProxyLookup;
import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
@@ -113,9 +108,9 @@ import org.sleuthkit.autopsy.coreutils.FileUtil;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.progress.ModalDialogProgressIndicator;
import org.sleuthkit.datamodel.AccountDeviceInstance;
import org.sleuthkit.datamodel.CommunicationsFilter;
import org.sleuthkit.datamodel.CommunicationsManager;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.TskCoreException;
/**
* A panel that goes in the Visualize tab of the Communications Visualization
@@ -128,7 +123,7 @@ import org.sleuthkit.datamodel.TskCoreException;
* actions to work correctly.
*/
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
final public class VisualizationPanel extends JPanel implements Lookup.Provider {
final public class VisualizationPanel extends JPanel {
private static final long serialVersionUID = 1L;
private static final Logger logger = Logger.getLogger(VisualizationPanel.class.getName());
@@ -141,9 +136,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
@NbBundle.Messages("VisualizationPanel.cancelButton.text=Cancel")
private static final String CANCEL = Bundle.VisualizationPanel_cancelButton_text();
private final ExplorerManager vizEM = new ExplorerManager();
private final ExplorerManager gacEM = new ExplorerManager();
private final ProxyLookup proxyLookup;
private Frame windowAncestor;
private CommunicationsManager commsManager;
@@ -162,6 +154,8 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
private final Map<NamedGraphLayout, JButton> layoutButtons = new HashMap<>();
private NamedGraphLayout currentLayout;
private final RelationshipBrowser relationshipBrowser;
@NbBundle.Messages("VisalizationPanel.paintingError=Problem painting visualization.")
public VisualizationPanel() {
@@ -224,13 +218,9 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
final GraphMouseListener graphMouseListener = new GraphMouseListener();
graphComponent.getGraphControl().addMouseWheelListener(graphMouseListener);
graphComponent.getGraphControl().addMouseListener(graphMouseListener);
final MessageBrowser messageBrowser = new MessageBrowser(vizEM, gacEM);
splitPane.setRightComponent(messageBrowser);
proxyLookup = new ProxyLookup(
ExplorerUtils.createLookup(vizEM, getActionMap()),
messageBrowser.getLookup()
);
relationshipBrowser = new RelationshipBrowser();
splitPane.setRightComponent(relationshipBrowser);
//feed selection to explorermanager
graph.getSelectionModel().addListener(mxEvent.CHANGE, new SelectionListener());
@@ -259,12 +249,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
applyLayout(fastOrganicLayout);
}
@Override
public Lookup getLookup() {
return proxyLookup;
}
@Subscribe
void handle(LockedVertexModel.VertexLockEvent event) {
final Set<mxCell> vertices = event.getVertices();
@@ -884,40 +869,25 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
@Override
public void invoke(Object sender, mxEventObject evt) {
Object[] selectionCells = graph.getSelectionCells();
Node rootNode = Node.EMPTY;
Node[] selectedNodes = new Node[0];
if (selectionCells.length > 0) {
mxICell[] selectedCells = Arrays.asList(selectionCells).toArray(new mxCell[selectionCells.length]);
HashSet<Content> relationshipSources = new HashSet<>();
HashSet<AccountDeviceInstanceKey> adis = new HashSet<>();
HashSet<AccountDeviceInstance> deviceInstances = new HashSet<>();
for (mxICell cell : selectedCells) {
if (cell.isEdge()) {
mxICell source = (mxICell) graph.getModel().getTerminal(cell, true);
AccountDeviceInstanceKey account1 = (AccountDeviceInstanceKey) source.getValue();
mxICell target = (mxICell) graph.getModel().getTerminal(cell, false);
AccountDeviceInstanceKey account2 = (AccountDeviceInstanceKey) target.getValue();
try {
final List<Content> relationshipSources1 = commsManager.getRelationshipSources(
account1.getAccountDeviceInstance(),
account2.getAccountDeviceInstance(),
currentFilter);
relationshipSources.addAll(relationshipSources1);
} catch (TskCoreException tskCoreException) {
logger.log(Level.SEVERE, " Error getting relationsips....", tskCoreException);
}
deviceInstances.add(((AccountDeviceInstanceKey) source.getValue()).getAccountDeviceInstance());
deviceInstances.add(((AccountDeviceInstanceKey) target.getValue()).getAccountDeviceInstance());
} else if (cell.isVertex()) {
adis.add((AccountDeviceInstanceKey) cell.getValue());
deviceInstances.add(((AccountDeviceInstanceKey) cell.getValue()).getAccountDeviceInstance());
}
}
rootNode = SelectionNode.createFromAccountsAndRelationships(relationshipSources, adis, currentFilter, commsManager);
selectedNodes = new Node[]{rootNode};
}
vizEM.setRootContext(rootNode);
try {
vizEM.setSelectedNodes(selectedNodes);
} catch (PropertyVetoException ex) {
logger.log(Level.SEVERE, "Selection vetoed.", ex);
relationshipBrowser.setSelectionInfo(new SelectionInfo(deviceInstances, currentFilter));
} else {
relationshipBrowser.setSelectionInfo(new SelectionInfo(Collections.EMPTY_SET, currentFilter));
}
}
}