rewire explorermanagers and views to support multiple selection on account table.

This commit is contained in:
millmanorama 2018-01-08 15:16:06 +01:00
parent d961ea6062
commit 0630f46897
13 changed files with 471 additions and 303 deletions

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2017 Basis Technology Corp.
* Copyright 2017-18 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -19,23 +19,30 @@
package org.sleuthkit.autopsy.communications;
import java.util.Objects;
import java.util.logging.Level;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.AccountDeviceInstance;
import org.sleuthkit.datamodel.CommunicationsFilter;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Key for AccountDeviceInstance node.
*
* Encapsulates a AccountDeviceInstance, and CommunicationsFilter.
* Encapsulates a AccountDeviceInstanc,some meta data about it, and
* CommunicationsFilter.
*/
class AccountDeviceInstanceKey {
final class AccountDeviceInstanceKey {
private static final Logger logger = Logger.getLogger(AccountDeviceInstanceKey.class.getName());
private final AccountDeviceInstance accountDeviceInstance;
private final CommunicationsFilter filter;
private final long messageCount;
private final String dataSourceName;
AccountDeviceInstanceKey(AccountDeviceInstance accountDeviceInstance, CommunicationsFilter filter, long msgCount, String dataSourceName) {
this.accountDeviceInstance = accountDeviceInstance;
this.filter = filter;
@ -43,6 +50,13 @@ class AccountDeviceInstanceKey {
this.dataSourceName = dataSourceName;
}
AccountDeviceInstanceKey(AccountDeviceInstance accountDeviceInstance, CommunicationsFilter filter, long msgCount) {
this.accountDeviceInstance = accountDeviceInstance;
this.filter = filter;
this.messageCount = msgCount;
this.dataSourceName = getDataSourceName(accountDeviceInstance, Case.getCurrentCase().getSleuthkitCase());
}
AccountDeviceInstance getAccountDeviceInstance() {
return accountDeviceInstance;
}
@ -54,7 +68,7 @@ class AccountDeviceInstanceKey {
long getMessageCount() {
return messageCount;
}
String getDataSourceName() {
return dataSourceName;
}
@ -96,5 +110,17 @@ class AccountDeviceInstanceKey {
}
return true;
}
private static String getDataSourceName(AccountDeviceInstance accountDeviceInstance, SleuthkitCase db) {
try {
for (DataSource dataSource : db.getDataSources()) {
if (dataSource.getDeviceId().equals(accountDeviceInstance.getDeviceId())) {
return db.getContentById(dataSource.getId()).getName();
}
}
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error getting datasource name, falling back on device ID.", ex);
}
return accountDeviceInstance.getDeviceId();
}
}

View File

@ -0,0 +1,132 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2017-18 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.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import javax.swing.AbstractAction;
import javax.swing.Action;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.Children;
import org.openide.nodes.Sheet;
import org.openide.util.NbBundle;
import org.openide.util.Utilities;
import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.datamodel.NodeProperty;
import org.sleuthkit.datamodel.Account;
import org.sleuthkit.datamodel.AccountDeviceInstance;
import org.sleuthkit.datamodel.CommunicationsFilter;
import org.sleuthkit.datamodel.CommunicationsManager;
/**
* Node to represent an Account Device Instance in the CVT
*/
final class AccountDeviceInstanceNode extends AbstractNode {
private final AccountDeviceInstanceKey accountDeviceInstanceKey;
private final CommunicationsManager commsManager;
private final Account account;
AccountDeviceInstanceNode(AccountDeviceInstanceKey accountDeviceInstanceKey, CommunicationsManager commsManager) {
super(Children.LEAF, Lookups.fixed(accountDeviceInstanceKey, commsManager));
this.accountDeviceInstanceKey = accountDeviceInstanceKey;
this.commsManager = commsManager;
this.account = accountDeviceInstanceKey.getAccountDeviceInstance().getAccount();
setName(account.getTypeSpecificID());
setIconBaseWithExtension("org/sleuthkit/autopsy/communications/images/" + Utils.getIconFileName(account.getAccountType()));
}
public AccountDeviceInstance getAccountDeviceInstance() {
return accountDeviceInstanceKey.getAccountDeviceInstance();
}
public AccountDeviceInstanceKey getAccountDeviceInstanceKey() {
return accountDeviceInstanceKey;
}
public CommunicationsManager getCommsManager() {
return commsManager;
}
public long getMessageCount() {
return accountDeviceInstanceKey.getMessageCount();
}
public CommunicationsFilter getFilter() {
return accountDeviceInstanceKey.getCommunicationsFilter();
}
@Override
@NbBundle.Messages(value = {"AccountNode.device=Device", "AccountNode.accountName=Account", "AccountNode.accountType=Type", "AccountNode.messageCount=Msgs"})
protected Sheet createSheet() {
Sheet s = super.createSheet();
Sheet.Set properties = s.get(Sheet.PROPERTIES);
if (properties == null) {
properties = Sheet.createPropertiesSet();
s.put(properties);
}
properties.put(new NodeProperty<>("type",
Bundle.AccountNode_accountType(),
"type",
account.getAccountType().getDisplayName())); // NON-NLS
properties.put(new NodeProperty<>("count",
Bundle.AccountNode_messageCount(),
"count",
accountDeviceInstanceKey.getMessageCount())); // NON-NLS
properties.put(new NodeProperty<>("device",
Bundle.AccountNode_device(),
"device",
accountDeviceInstanceKey.getDataSourceName())); // NON-NLS
return s;
}
@Override
public Action[] getActions(boolean context) {
ArrayList<Action> actions = new ArrayList<>(Arrays.asList(super.getActions(context)));
actions.add(PinAccountsAction.getInstance());
return actions.toArray(new Action[actions.size()]);
}
/**
* Action that pins the selected AccountDeviceInstances to the
* visualization.
*/
static private class PinAccountsAction extends AbstractAction {
private static final long serialVersionUID = 1L;
private static PinAccountsAction instance = new PinAccountsAction();
private static PinAccountsAction getInstance() {
return instance;
}
private PinAccountsAction() {
super("Visualize Account");
}
@Override
public void actionPerformed(ActionEvent e) {
Collection<? extends AccountDeviceInstanceKey> lookupAll =
Utilities.actionsGlobalContext().lookupAll(AccountDeviceInstanceKey.class);
CVTEvents.getCVTEventBus().post(new PinAccountEvent(lookupAll));
}
}
}

View File

@ -0,0 +1,71 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2017-18 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.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.openide.nodes.ChildFactory;
import org.openide.nodes.Node;
import org.sleuthkit.datamodel.AccountDeviceInstance;
import org.sleuthkit.datamodel.CommunicationsFilter;
import org.sleuthkit.datamodel.CommunicationsManager;
import org.sleuthkit.datamodel.TskCoreException;
/**
* ChildFactory that creates AccountDeviceInstanceKeys and
* AccountDeviceInstanceNodes using a provided CommunicationsManager and
* CommunicationsFilter
*/
final class AccountDeviceInstanceNodeFactory extends ChildFactory<AccountDeviceInstanceKey> {
private static final Logger logger = Logger.getLogger(AccountDeviceInstanceNodeFactory.class.getName());
private final CommunicationsManager commsManager;
private final CommunicationsFilter commsFilter;
AccountDeviceInstanceNodeFactory(CommunicationsManager commsManager, CommunicationsFilter commsFilter) {
this.commsManager = commsManager;
this.commsFilter = commsFilter;
}
@Override
protected boolean createKeys(List<AccountDeviceInstanceKey> list) {
List<AccountDeviceInstanceKey> accountDeviceInstanceKeys = new ArrayList<>();
try {
final List<AccountDeviceInstance> accountDeviceInstancesWithRelationships =
commsManager.getAccountDeviceInstancesWithRelationships(commsFilter);
for (AccountDeviceInstance accountDeviceInstance : accountDeviceInstancesWithRelationships) {
long communicationsCount = commsManager.getRelationshipSourcesCount(accountDeviceInstance, commsFilter);
accountDeviceInstanceKeys.add(new AccountDeviceInstanceKey(accountDeviceInstance, commsFilter, communicationsCount));
};
} catch (TskCoreException tskCoreException) {
logger.log(Level.SEVERE, "Error getting filtered account device instances", tskCoreException);
}
list.addAll(accountDeviceInstanceKeys);
return true;
}
@Override
protected Node createNodeForKey(AccountDeviceInstanceKey key) {
return new AccountDeviceInstanceNode(key, commsManager);
}
}

View File

@ -11,7 +11,7 @@
<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"/>
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,44,0,0,1,-112"/>
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,-76,0,0,2,68"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2017 Basis Technology Corp.
* Copyright 2017-18 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -26,22 +26,34 @@ import javax.swing.table.TableCellRenderer;
import org.netbeans.swing.outline.DefaultOutlineModel;
import org.netbeans.swing.outline.Outline;
import org.openide.explorer.ExplorerManager;
import org.openide.explorer.ExplorerUtils;
import org.openide.util.Lookup;
import org.openide.util.lookup.ProxyLookup;
/**
* A panel that goes in the Browse tab of the Communications Visualization Tool.
* Hosts an OutlineView that shows information about Accounts.
* Hosts an OutlineView that shows information about Accounts, and a
* MessageBrowser for viewing details of communications.
*
* The Lookup provided by getLookup will be proxied by the lookup of the
* CVTTopComponent when this tab is active allowing for context sensitive
* actions to work correctly.
*/
public class AccountsBrowser extends JPanel implements ExplorerManager.Provider, CVTTopComponent.ProxiedExplorerManagerProvider {
public class AccountsBrowser extends JPanel implements ExplorerManager.Provider, Lookup.Provider {
private static final long serialVersionUID = 1L;
private final Outline outline;
private final ExplorerManager gacEM = new ExplorerManager();
private ExplorerManager tableEM;
/**
* Creates new form AccountsBrowser
private final ExplorerManager messageBrowserEM = new ExplorerManager();
private ExplorerManager accountsTableEM;
/*
* This lookup proxies the selection lookup of both he accounts table and
* the messages table.
*/
private ProxyLookup proxyLookup;
public AccountsBrowser() {
initComponents();
outline = outlineView.getOutline();
@ -59,7 +71,7 @@ public class AccountsBrowser extends JPanel implements ExplorerManager.Provider,
}
void init(ExplorerManager tableExplorerManager) {
this.tableEM = tableExplorerManager;
this.accountsTableEM = tableExplorerManager;
tableExplorerManager.addPropertyChangeListener(evt -> {
if (ExplorerManager.PROP_ROOT_CONTEXT.equals(evt.getPropertyName())) {
SwingUtilities.invokeLater(this::setColumnWidths);
@ -68,7 +80,11 @@ public class AccountsBrowser extends JPanel implements ExplorerManager.Provider,
}
});
jSplitPane1.setRightComponent(new MessageBrowser(tableExplorerManager, gacEM));
jSplitPane1.setRightComponent(new MessageBrowser(tableExplorerManager, messageBrowserEM));
proxyLookup = new ProxyLookup(
ExplorerUtils.createLookup(messageBrowserEM, getActionMap()),
ExplorerUtils.createLookup(accountsTableEM, getActionMap()));
}
private void setColumnWidths() {
@ -122,11 +138,11 @@ public class AccountsBrowser extends JPanel implements ExplorerManager.Provider,
@Override
public ExplorerManager getExplorerManager() {
return tableEM;
return accountsTableEM;
}
@Override
public ExplorerManager getProxiedExplorerManager() {
return gacEM;
public Lookup getLookup() {
return proxyLookup;
}
}

View File

@ -1,178 +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.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.AbstractAction;
import javax.swing.Action;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.ChildFactory;
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.Lookups;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.datamodel.NodeProperty;
import org.sleuthkit.datamodel.Account;
import org.sleuthkit.datamodel.AccountDeviceInstance;
import org.sleuthkit.datamodel.CommunicationsFilter;
import org.sleuthkit.datamodel.CommunicationsManager;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
class AccountsRootChildren extends ChildFactory<AccountDeviceInstanceKey> {
private static final Logger logger = Logger.getLogger(AccountsRootChildren.class.getName());
private final CommunicationsManager commsManager;
private final CommunicationsFilter commsFilter;
AccountsRootChildren(CommunicationsManager commsManager, CommunicationsFilter commsFilter) {
super();
this.commsManager = commsManager;
this.commsFilter = commsFilter;
}
@Override
protected boolean createKeys(List<AccountDeviceInstanceKey> list) {
List<AccountDeviceInstanceKey> accountDeviceInstanceKeys = new ArrayList<>();
try {
for (AccountDeviceInstance accountDeviceInstance : commsManager.getAccountDeviceInstancesWithRelationships(commsFilter)) {
long communicationsCount = commsManager.getRelationshipSourcesCount(accountDeviceInstance, commsFilter);
String dataSourceName = getDataSourceName(accountDeviceInstance);
accountDeviceInstanceKeys.add(new AccountDeviceInstanceKey(accountDeviceInstance, commsFilter, communicationsCount, dataSourceName));
};
} catch (TskCoreException tskCoreException) {
logger.log(Level.SEVERE, "Error getting filtered account device instances", tskCoreException);
}
list.addAll(accountDeviceInstanceKeys);
return true;
}
@Override
protected Node createNodeForKey(AccountDeviceInstanceKey key) {
return new AccountDeviceInstanceNode(key, commsManager);
}
static String getDataSourceName(AccountDeviceInstance accountDeviceInstance) {
try {
final SleuthkitCase sleuthkitCase = Case.getCurrentCase().getSleuthkitCase();
for (DataSource dataSource : sleuthkitCase.getDataSources()) {
if (dataSource.getDeviceId().equals(accountDeviceInstance.getDeviceId())) {
return sleuthkitCase.getContentById(dataSource.getId()).getName();
}
}
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error getting datasource name, falling back on device ID.", ex);
}
return accountDeviceInstance.getDeviceId();
}
/**
* Node to represent an Account in the AccountsBrowser
*/
static class AccountDeviceInstanceNode extends AbstractNode {
private final AccountDeviceInstanceKey accountDeviceInstanceKey;
private final CommunicationsManager commsManager;
private final Account account;
AccountDeviceInstanceNode(AccountDeviceInstanceKey accountDeviceInstanceKey, CommunicationsManager commsManager) {
super(Children.LEAF, Lookups.fixed(accountDeviceInstanceKey, commsManager));
this.accountDeviceInstanceKey = accountDeviceInstanceKey;
this.commsManager = commsManager;
this.account = accountDeviceInstanceKey.getAccountDeviceInstance().getAccount();
setName(account.getTypeSpecificID());
setIconBaseWithExtension("org/sleuthkit/autopsy/communications/images/" + Utils.getIconFileName(account.getAccountType()));
}
public AccountDeviceInstance getAccountDeviceInstance() {
return accountDeviceInstanceKey.getAccountDeviceInstance();
}
public AccountDeviceInstanceKey getAccountDeviceInstanceKey() {
return accountDeviceInstanceKey;
}
public CommunicationsManager getCommsManager() {
return commsManager;
}
public long getMessageCount() {
return accountDeviceInstanceKey.getMessageCount();
}
public CommunicationsFilter getFilter() {
return accountDeviceInstanceKey.getCommunicationsFilter();
}
@Override
@NbBundle.Messages(value = {"AccountNode.device=Device",
"AccountNode.accountName=Account",
"AccountNode.accountType=Type",
"AccountNode.messageCount=Msgs"})
protected Sheet createSheet() {
Sheet s = super.createSheet();
Sheet.Set properties = s.get(Sheet.PROPERTIES);
if (properties == null) {
properties = Sheet.createPropertiesSet();
s.put(properties);
}
properties.put(new NodeProperty<>("type", Bundle.AccountNode_accountType(), "type",
account.getAccountType().getDisplayName())); // NON-NLS
properties.put(new NodeProperty<>("count", Bundle.AccountNode_messageCount(), "count",
accountDeviceInstanceKey.getMessageCount())); // NON-NLS
properties.put(new NodeProperty<>("device", Bundle.AccountNode_device(), "device",
accountDeviceInstanceKey.getDataSourceName())); // NON-NLS
return s;
}
@Override
public Action[] getActions(boolean context) {
ArrayList<Action> actions = new ArrayList<>(Arrays.asList(super.getActions(context)));
actions.add(new PinAccountAction());
return actions.toArray(new Action[actions.size()]);
}
/**
*
*/
private class PinAccountAction extends AbstractAction {
public PinAccountAction() {
super("Visualize Account");
}
@Override
public void actionPerformed(ActionEvent e) {
CVTEvents.getCVTEventBus().post(new PinAccountEvent(AccountDeviceInstanceNode.this));
}
}
}
}

View File

@ -1,21 +1,34 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
* Autopsy Forensic Browser
*
* Copyright 2017-18 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.eventbus.EventBus;
/**
*
* Provide he singleton EventBus.
*/
public class CVTEvents {
final class CVTEvents {
private final static EventBus cvtEventBus = new EventBus();
private final static EventBus cvtEventBus = new EventBus();
public static EventBus getCVTEventBus() {
return cvtEventBus;
}
}

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2017 Basis Technology Corp.
* Copyright 2017-18 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -22,7 +22,6 @@ import com.google.common.eventbus.Subscribe;
import java.util.List;
import java.util.stream.Collectors;
import org.openide.explorer.ExplorerManager;
import static org.openide.explorer.ExplorerUtils.createLookup;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.openide.util.lookup.ProxyLookup;
@ -48,35 +47,29 @@ public final class CVTTopComponent extends TopComponent {
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
public CVTTopComponent() {
initComponents();
filtersPane.setExplorerManager(filterToTableEXplorerManager);
setName(Bundle.CVTTopComponent_name());
/*
* Connect the filtersPane and the accountsBrowser via a shared
* ExplorerMmanager
*/
filtersPane.setExplorerManager(filterToTableEXplorerManager);
accountsBrowser.init(filterToTableEXplorerManager);
/*
* Associate an explorer manager with the GlobalActionContext (GAC) for
* use by the messages browsers so that selections in the messages
* browser can be exposed to context-sensitive actions.
* Associate an Lookup with the GlobalActionContext (GAC) so that
* selections in the sub views can be exposed to context-sensitive
* actions.
*/
// ExplorerManager browserExplorerManager = new ExplorerManager();
// ExplorerManager vizExplorerManager = new ExplorerManager();
ProxyLookupImpl proxyLookup = new ProxyLookupImpl();
ProxyLookupImpl proxyLookup = new ProxyLookupImpl(accountsBrowser.getLookup());
associateLookup(proxyLookup);
accountsBrowser.init(filterToTableEXplorerManager);
// browseSplitPane.setRightComponent(new MessageBrowser(browserExplorerManager));
// vizSplitPane.setRightComponent(new MessageBrowser(vizExplorerManager));
// /*
// * Create a second explorer manager to be discovered by the accounts
// * browser and the message browser so that the browsers can both listen
// * for explorer manager property events for the outline view of the
// * accounts browser. This provides a mechanism for pushing selected
// * Nodes from the accounts browser to the messages browser.
// */
// acctsBrowserExplorerManager = new ExplorerManager();
// Make sure the GAC is proxying the selection of the active tab.
browseVisualizeTabPane.addChangeListener(changeEvent -> {
ProxiedExplorerManagerProvider selectedComponent = (ProxiedExplorerManagerProvider) browseVisualizeTabPane.getSelectedComponent();
proxyLookup.changeLookup(createLookup(selectedComponent.getProxiedExplorerManager(), getActionMap()));
Lookup.Provider selectedComponent = (Lookup.Provider) browseVisualizeTabPane.getSelectedComponent();
proxyLookup.changeLookups(selectedComponent.getLookup());
});
vizPanel.setFilterProvider(filtersPane);
CVTEvents.getCVTEventBus().register(this);
}
@ -169,16 +162,12 @@ public final class CVTTopComponent extends TopComponent {
private static class ProxyLookupImpl extends ProxyLookup {
ProxyLookupImpl() {
ProxyLookupImpl(Lookup... l) {
super(l);
}
void changeLookup(Lookup l) {
void changeLookups(Lookup... l) {
setLookups(l);
}
}
static interface ProxiedExplorerManagerProvider {
ExplorerManager getProxiedExplorerManager();
}
}

View File

@ -0,0 +1,26 @@
/*
* 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 org.sleuthkit.datamodel.CommunicationsFilter;
interface FilterProvider {
CommunicationsFilter getFilter();
}

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2017 Basis Technology Corp.
* Copyright 2017-18 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -57,13 +57,13 @@ import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Panel that holds the Filter control widgets and translates user filtering
* changes into queries against the CommunicationsManager.
* Panel that holds the Filter control widgets and triggers queries against the
* CommunicationsManager on user filtering changes.
*/
final public class FiltersPanel extends JPanel {
final public class FiltersPanel extends JPanel implements FilterProvider {
private static final Logger logger = Logger.getLogger(FiltersPanel.class.getName());
private static final long serialVersionUID = 1L;
private static final Logger logger = Logger.getLogger(FiltersPanel.class.getName());
private ExplorerManager em;
@ -134,6 +134,10 @@ final public class FiltersPanel extends JPanel {
refreshButton.addActionListener(e -> applyFilters());
}
void setExplorerManager(ExplorerManager explorerManager) {
em = explorerManager;
}
/**
* 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
@ -166,6 +170,9 @@ final public class FiltersPanel extends JPanel {
dateRangeLabel.setText("Date Range ( " + Utils.getUserPreferredZoneId().toString() + "):");
}
/**
* Updates the filter widgets to reflect he data sources/types in the case.
*/
private void updateFilters() {
updateAccountTypeFilter();
updateDeviceFilter();
@ -176,6 +183,7 @@ final public class FiltersPanel extends JPanel {
super.addNotify();
IngestManager.getInstance().addIngestModuleEventListener(ingestListener);
Case.addEventTypeSubscriber(EnumSet.of(CURRENT_CASE), evt -> {
//clear the device filter widget when the case changes.
devicesMap.clear();
devicesPane.removeAll();
});
@ -482,20 +490,17 @@ final public class FiltersPanel extends JPanel {
}// </editor-fold>//GEN-END:initComponents
/**
* Query for accounts using the selected filters, and send the results to
* the AccountsBrowser via the ExplorerManager.
* Push a new root AccountDeviceInstanceNodeFactory with he current filters
* into the explorer manager. The factory will do he actual queries.
*
*
*/
private void applyFilters() {
CommunicationsFilter commsFilter = new CommunicationsFilter();
commsFilter.addAndFilter(getDeviceFilter());
commsFilter.addAndFilter(getAccountTypeFilter());
commsFilter.addAndFilter(getDateRangeFilter());
commsFilter.addAndFilter(new CommunicationsFilter.RelationshipTypeFilter(
ImmutableSet.of(CALL_LOG, MESSAGE)));
CommunicationsFilter commsFilter = getFilter();
try {
final CommunicationsManager commsManager = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager();
em.setRootContext(new AbstractNode(Children.create(new AccountsRootChildren(commsManager, commsFilter), true)));
em.setRootContext(new AbstractNode(Children.create(new AccountDeviceInstanceNodeFactory(commsManager, commsFilter), true)));
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "There was an error getting the CommunicationsManager for the current case.", ex);
}
@ -504,6 +509,17 @@ final public class FiltersPanel extends JPanel {
validateFilters();
}
@Override
public CommunicationsFilter getFilter() {
CommunicationsFilter commsFilter = new CommunicationsFilter();
commsFilter.addAndFilter(getDeviceFilter());
commsFilter.addAndFilter(getAccountTypeFilter());
commsFilter.addAndFilter(getDateRangeFilter());
commsFilter.addAndFilter(new CommunicationsFilter.RelationshipTypeFilter(
ImmutableSet.of(CALL_LOG, MESSAGE)));
return commsFilter;
}
/**
* Get a DeviceFilter that matches the state of the UI widgets.
*
@ -532,6 +548,11 @@ final public class FiltersPanel extends JPanel {
return accountTypeFilter;
}
/**
* Get an DateRangeFilter that matches the state of the UI widgets
*
* @return an DateRangeFilter
*/
private DateRangeFilter getDateRangeFilter() {
ZoneId zone = Utils.getUserPreferredZoneId();
long start = startDatePicker.isEnabled() ? startDatePicker.getDate().atStartOfDay(zone).toEpochSecond() : 0;
@ -622,7 +643,4 @@ final public class FiltersPanel extends JPanel {
private final javax.swing.JButton unCheckAllDevicesButton = new javax.swing.JButton();
// End of variables declaration//GEN-END:variables
void setExplorerManager(ExplorerManager explorerManager) {
em = explorerManager;
}
}

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2017 Basis Technology Corp.
* Copyright 2017-18 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -25,7 +25,6 @@ import java.util.stream.Stream;
import javax.swing.JPanel;
import org.openide.explorer.ExplorerManager;
import org.openide.nodes.Node;
import org.sleuthkit.autopsy.communications.AccountsRootChildren.AccountDeviceInstanceNode;
import org.sleuthkit.autopsy.corecomponents.DataResultPanel;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
import org.sleuthkit.autopsy.corecomponents.TableFilterNode;
@ -50,6 +49,8 @@ public final class MessageBrowser extends JPanel implements ExplorerManager.Prov
* 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
@ -63,11 +64,9 @@ public final class MessageBrowser extends JPanel implements ExplorerManager.Prov
messagesResultPanel = DataResultPanel.createInstanceUninitialized("Account", "", Node.EMPTY, 0, messageDataContent);
splitPane.setTopComponent(messagesResultPanel);
splitPane.setBottomComponent(messageDataContent);
}
@Override
public void addNotify() {
super.addNotify();
dataResultViewerTable = new DataResultViewerTable(gacExplorerManager, "Messages");
messagesResultPanel.addResultViewer(dataResultViewerTable);
messagesResultPanel.open();
tableEM.addPropertyChangeListener(pce -> {
if (pce.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES)) {
@ -107,13 +106,11 @@ public final class MessageBrowser extends JPanel implements ExplorerManager.Prov
}
}
});
}
//add the required result viewers and THEN open the panel
if (null == dataResultViewerTable) {
dataResultViewerTable = new DataResultViewerTable(gacExplorerManager, "Messages");
messagesResultPanel.addResultViewer(dataResultViewerTable);
}
messagesResultPanel.open();
@Override
public ExplorerManager getExplorerManager() {
return gacExplorerManager;
}
/**
@ -157,8 +154,4 @@ public final class MessageBrowser extends JPanel implements ExplorerManager.Prov
private javax.swing.JSplitPane splitPane;
// End of variables declaration//GEN-END:variables
@Override
public ExplorerManager getExplorerManager() {
return gacExplorerManager;
}
}

View File

@ -5,20 +5,21 @@
*/
package org.sleuthkit.autopsy.communications;
import org.sleuthkit.autopsy.communications.AccountsRootChildren.AccountDeviceInstanceNode;
import com.google.common.collect.ImmutableSet;
import java.util.Collection;
/**
*
*/
class PinAccountEvent {
private final AccountDeviceInstanceNode accountDeviceInstance;
private final ImmutableSet<AccountDeviceInstanceKey> accountDeviceInstances;
public AccountDeviceInstanceNode getAccountDeviceInstanceNode() {
return accountDeviceInstance;
public ImmutableSet<AccountDeviceInstanceKey> getAccountDeviceInstances() {
return accountDeviceInstances;
}
PinAccountEvent(AccountDeviceInstanceNode accountDeviceInstance) {
this.accountDeviceInstance = accountDeviceInstance;
PinAccountEvent(Collection<? extends AccountDeviceInstanceKey> accountDeviceInstances) {
this.accountDeviceInstances = ImmutableSet.copyOf(accountDeviceInstances);
}
}

View File

@ -29,21 +29,25 @@ import com.mxgraph.view.mxStylesheet;
import java.awt.BorderLayout;
import java.awt.Color;
import java.beans.PropertyVetoException;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.logging.Level;
import javax.swing.JPanel;
import org.apache.commons.lang3.StringUtils;
import org.openide.explorer.ExplorerManager;
import org.openide.explorer.ExplorerUtils;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.openide.util.Exceptions;
import org.openide.util.Lookup;
import org.openide.util.lookup.ProxyLookup;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.communications.AccountsRootChildren.AccountDeviceInstanceNode;
import org.sleuthkit.autopsy.communications.CVTTopComponent.ProxiedExplorerManagerProvider;
import static org.sleuthkit.autopsy.casemodule.Case.Events.CURRENT_CASE;
import static org.sleuthkit.autopsy.communications.RelationshipNode.getAttributeDisplayString;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.AccountDeviceInstance;
@ -57,25 +61,43 @@ import org.sleuthkit.datamodel.CommunicationsManager;
import org.sleuthkit.datamodel.TskCoreException;
/**
* * A panel that goes in the Visualize tab of the Communications Visualization
* Tool. Hosts an JGraphX mxGraphComponent that host the communications network
* visualization and a MessageBrowser for viewing details of communications.
*
* The Lookup provided by getLookup will be proxied by the lookup of the
* CVTTopComponent when this tab is active allowing for context sensitive
* actions to work correctly.
*/
public class VisualizationPanel extends JPanel implements ProxiedExplorerManagerProvider {
final public class VisualizationPanel extends JPanel implements Lookup.Provider {
private static final long serialVersionUID = 1L;
private Logger logger = Logger.getLogger(VisualizationPanel.class.getName());
static final private mxStylesheet mxStylesheet = new mxStylesheet();
static {
//initialize defaul cell (Vertex and/or Edge) properties
mxStylesheet.getDefaultVertexStyle().put(mxConstants.STYLE_SHAPE, mxConstants.SHAPE_ELLIPSE);
mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_NOLABEL, true);
}
private final ExplorerManager vizEM = new ExplorerManager();
private final ExplorerManager gacEM = new ExplorerManager();
private final ProxyLookup proxyLookup = new ProxyLookup(
ExplorerUtils.createLookup(gacEM, getActionMap()),
ExplorerUtils.createLookup(vizEM, getActionMap()));
private final mxGraphComponent graphComponent;
private final mxGraph graph;
private final Map<String, mxCell> nodeMap = new HashMap<>();
static {
//initialize defaul cell properties
mxStylesheet.getDefaultVertexStyle().put(mxConstants.STYLE_SHAPE, mxConstants.SHAPE_ELLIPSE);
mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_NOLABEL, true);
private CommunicationsManager commsManager;
void setFilterProvider(FilterProvider filterProvider) {
this.filterProvider = filterProvider;
}
private FilterProvider filterProvider;
public VisualizationPanel() {
initComponents();
@ -103,7 +125,6 @@ public class VisualizationPanel extends JPanel implements ProxiedExplorerManager
if (selectionCells.length == 1) {
mxCell selectionCell = (mxCell) selectionCells[0];
try {
CommunicationsManager commsManager = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager();
if (selectionCell.isVertex()) {
final AccountDeviceInstanceNode accountDeviceInstanceNode =
@ -116,19 +137,16 @@ public class VisualizationPanel extends JPanel implements ProxiedExplorerManager
System.out.println(selectionCell.getId());
// explorerManager.setRootContext(new CommunicationsBundleNode(adiKey, commsManager));
}
} catch (TskCoreException tskCoreException) {
Logger.getLogger(VisualizationPanel.class.getName()).log(Level.SEVERE,
"Could not get communications manager for current case", tskCoreException);
} catch (PropertyVetoException ex) {
Exceptions.printStackTrace(ex);
logger.log(Level.SEVERE, "Account selection vetoed.", ex);
}
}
});
}
@Override
public ExplorerManager getProxiedExplorerManager() {
return gacEM;
public Lookup getLookup() {
return proxyLookup;
}
private void addEdge(mxCell pinnedAccountVertex, mxCell relatedAccountVertex) {
@ -192,29 +210,29 @@ public class VisualizationPanel extends JPanel implements ProxiedExplorerManager
}
@Subscribe
public void pinAccount(PinAccountEvent pinEvent) {
public void pinAccounts(PinAccountEvent pinEvent) {
final AccountDeviceInstanceNode adiNode = pinEvent.getAccountDeviceInstanceNode();
final AccountDeviceInstanceKey adiKey = adiNode.getAccountDeviceInstanceKey();
final Set<AccountDeviceInstanceKey> adiKeys = pinEvent.getAccountDeviceInstances();
final CommunicationsFilter commsFilter = filterProvider.getFilter();
graph.getModel().beginUpdate();
try {
nodeMap.clear();
graph.removeCells(graph.getChildCells(graph.getDefaultParent(), true, true));
mxCell pinnedAccountVertex = getOrCreateVertex(adiKey);
CommunicationsManager commsManager = adiNode.getCommsManager();
final CommunicationsFilter commsFilter = adiNode.getFilter();
List<AccountDeviceInstance> relatedAccountDeviceInstances =
commsManager.getRelatedAccountDeviceInstances(adiNode.getAccountDeviceInstance(), commsFilter);
for (AccountDeviceInstanceKey adiKey : adiKeys) {
mxCell pinnedAccountVertex = getOrCreateVertex(adiKey);
for (AccountDeviceInstance relatedADI : relatedAccountDeviceInstances) {
long communicationsCount = commsManager.getRelationshipSourcesCount(relatedADI, commsFilter);
String dataSourceName = AccountsRootChildren.getDataSourceName(relatedADI);
AccountDeviceInstanceKey relatedADIKey = new AccountDeviceInstanceKey(relatedADI, commsFilter, communicationsCount, dataSourceName);
mxCell relatedAccountVertex = getOrCreateVertex(relatedADIKey);
List<AccountDeviceInstance> relatedAccountDeviceInstances =
commsManager.getRelatedAccountDeviceInstances(adiKey.getAccountDeviceInstance(), commsFilter);
addEdge(pinnedAccountVertex, relatedAccountVertex);
for (AccountDeviceInstance relatedADI : relatedAccountDeviceInstances) {
long communicationsCount = commsManager.getRelationshipSourcesCount(relatedADI, commsFilter);
AccountDeviceInstanceKey relatedADIKey = new AccountDeviceInstanceKey(relatedADI, commsFilter, communicationsCount);
mxCell relatedAccountVertex = getOrCreateVertex(relatedADIKey);
addEdge(pinnedAccountVertex, relatedAccountVertex);
}
}
} catch (TskCoreException ex) {
Exceptions.printStackTrace(ex);
@ -265,6 +283,49 @@ public class VisualizationPanel extends JPanel implements ProxiedExplorerManager
return vertex;
}
@Override
public void addNotify() {
super.addNotify();
// IngestManager.getInstance().addIngestModuleEventListener(ingestListener);
try {
commsManager = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager();
} catch (IllegalStateException ex) {
logger.log(Level.SEVERE, "Can't get CommunicationsManager when there is no case open.", ex);
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error getting CommunicationsManager for the current case.", ex);
}
Case.addEventTypeSubscriber(EnumSet.of(CURRENT_CASE), evt -> {
graph.getModel().beginUpdate();
try {
nodeMap.clear();
graph.removeCells(graph.getChildCells(graph.getDefaultParent(), true, true));
} finally {
graph.getModel().endUpdate();
}
if (evt.getNewValue() != null) {
Case currentCase = (Case) evt.getNewValue();
try {
commsManager = currentCase.getSleuthkitCase().getCommunicationsManager();
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error getting CommunicationsManager for the current case.", ex);
}
} else {
commsManager = null;
}
});
}
@Override
public void removeNotify() {
super.removeNotify();
// IngestManager.getInstance().removeIngestModuleEventListener(ingestListener);
}
/**
* 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